Only this pageAll pages
Powered by GitBook
Couldn't generate the PDF for 176 pages, generation stopped at 100.
Extend with 50 more pages.
1 of 100

Injective | 中文文档

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

开发者

Loading...

指南

本节包含一些简单的开发者指南,帮助您在Injective上进行构建。

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

创建钱包

了解如何使用不同方法在 Injective 上创建钱包。

在 Injective 上创建钱包和向您的 Injective 地址发送一些资金一样简单。在本文档中,我们将概述如何使用一些流行的钱包解决方案(如 Metamask、Ledger 等)创建钱包,同时也介绍如何在本地 CLI 环境中使用 injectived 创建和资助钱包。

浏览器钱包

本地钱包

您还可以在以下页面了解更多关于使用 injectived 进行密钥管理的信息 设置密钥环

您可以通过按照以下步骤使用您首选的钱包创建一个 Injective 钱包: .

您可以在 CLI 环境中使用injectived创建一个 Injective 钱包用于开发目的。您可以使用injectived keys .

tutorial on our blog
command

关于 Injective

Injective 是一个高性能、可互操作的 Layer 1 区块链,专为构建顶级 Web3 金融应用而设计。

Injective 是专为金融而构建的区块链。


什么是 Injective?

Injective 是唯一一个允许开发者使用预构建且可定制模块 来创建其他网络上无法实现的动态应用程序的区块链。结合对其核心架构的优化和增强的跨链互操作性,Injective 提供了一个高性能网络,准备高效且安全地将全球金融系统引入链上

​
modules

钱包

Injective 钱包允许您监控在 Injective 上的资产。资产可以是 Injective 上的原生代币,也可以是从 Ethereum、Solana、Polygon 以及各种支持 IBC 的链桥接过来的资产。

Injective 支持多种不同的钱包。用户可以选择使用 Ethereum 或 Cosmos 原生钱包来提交 Injective 上的交易。

简介

Injective 的账户类型使用 Ethereum 的 ECDSA secp256k1 曲线来生成密钥。简单来说,Injective 的账户与 Ethereum 账户兼容,使得像 MetaMask 这样的 Ethereum 原生钱包能够与 Injective 进行交互。像 Keplr 和 Leap 这样的流行 Cosmos 钱包也已经与 Injective 集成。

基于 Ethereum 的钱包

如上所述,基于 Ethereum 的钱包可以用来与 Injective 进行交互。目前,Injective 支持最流行的基于 Ethereum 的钱包,包括:

在 Injective 上使用 Ethereum 原生钱包签署交易的过程包括:

  1. 将交易转换为 EIP712 类型数据(TypedData),

  2. 使用 Ethereum 原生钱包签署 EIP712 类型数据,

  3. 将交易打包成原生 Cosmos 交易(包括签名),并将交易广播到区块链上。

这个过程对最终用户是透明的。如果您之前使用过 Ethereum 原生钱包,用户体验将是相同的。

基于 Cosmos 的钱包

Injective 支持与 Cosmos 和 IBC 兼容的主流钱包,包括:

Injective 原生钱包

CEX 的钱包

现在也有一些由中心化交易所(CEX)开发的钱包支持 Injective。如果您是这些 CEX 的活跃用户,使用它们的钱包可以提供更流畅的 Web3 体验。目前,支持 Injective 的 CEX 钱包包括:

目前 是唯一的 Injective 原生钱包。这样的钱包专门构建用于与更广泛的 Injective 生态系统协同工作。

MetaMask
Ledger
Trezor
Torus
Cosmostation
Keplr
Leap
Ninji Wallet
Bitget Wallet
OKX Wallet

质押

什么是质押?

质押是将您的资产(在此情况下为 INJ)锁定起来,以验证区块链上的交易的过程。质押资产的用户通常有资格获得质押奖励。 Injective 采用权益证明机制(proof-of-stake),用户可以将他们的 INJ 代币质押给某些验证交易的 Injective 节点(验证者)。作为回报,用户可以获得 INJ 形式的质押奖励。

质押 INJ

提取质押奖励

重新委托

重新委托允许您将质押的 INJ 从一个验证者即时转移到另一个验证者,而无需经过 21 天的解锁期。

解除委托

解除委托 INJ 是将您的资产从验证者处撤回的过程,完成此过程需要 21 天。

将您的 INJ 给验证者,开始赚取奖励。

一旦您质押了 INJ,奖励就会开始累积。您可以在 的质押部分监控您的奖励。一旦获得足够的奖励,您可以随时提取它们。

质押
Injective Hub

快速入门

Injective 概述及其独特特点

如何使用这些文档

欢迎来到您的 Injective 探索之旅!在提问之前,请先尝试使用文档中的搜索功能。我们的目标是让文档具备自给自足的能力,确保上手过程顺畅,让每个人都能轻松了解 Injective。

Injective 快速入门指南

需要帮助?

如果您有任何问题或反馈,欢迎通过 Discord 或 Telegram 与我们联系:

加入 并寻找相关的频道

加入 .

Injective Discord server
Injective Developer Telegram channel

治理

治理

Injective 是一个社区运营的区块链,质押了 INJ 的用户可以参与与区块链相关的治理。可以提交提案,以对 Injective 程序、技术升级或任何其他影响整个 Injective 生态系统的变化进行修订。 在 Injective Hub 内,新的提案通过治理门户提交,并由整个社区投票决定。

提交请求

要提交新的请求,您必须创建一个提案,概述您希望更改的内容、该更改可能影响 Injective 的哪些领域,以及为什么您请求进行此更改。 创建提案后,您必须至少存入 10 INJ(10e19 inj)才能提交提案进行治理。这是为了确保您是 Injective 社区的活跃参与者,因此有资格提出提案请求。 为了使提案进入投票阶段,还必须存入 100 INJ(10e20 inj)。这些资金可以由您直接存入,也可以与其他社区成员合作存入。

投票期

一旦提案和存款提交,提案将进入为期四天的投票期。 必须有 33.4% 的 INJ 持有者对提案进行投票,投票才会被视为有效,并且其中 50% 的投票必须是“同意”才能通过该提案。

投票结果

如果提案通过,Injective 的贡献者将开始共同努力实施新的请求。如果 33.4% 的总投票为 NoWithVeto、未达到法定人数,或未达到最低存款要求,则存款将被销毁。所有其他投票结果将退还存款。

快速开始

有关治理提案流程的更多细节,请阅读 Injective 的。

要参与区块链治理,请加入 或 。

访问 查看 Injective 提案并参与投票

博客
Injective Discord
Injective 治理论坛
Injective Hub

拍卖

销毁拍卖

INJ 的完全多功能性通过一系列协同运作的机制得以实现。基于 Injective 对供应动态的创新方法,该资产通过一个精心设计的系统展现出通缩特性,旨在将 INJ 从流通中移除。这个过程通过 Injective 的新颖销毁拍卖系统得以实现,该系统有效地减少了总供应量。

销毁拍卖定期举行,邀请参与者竞标一篮子代币,这些代币来自参与应用程序生成的部分收入以及个人用户的直接贡献。拍卖以英式拍卖方式进行,竞标使用 INJ。最高出价者将在拍卖结束时获得整个代币篮子。获胜的 INJ 出价将被销毁,从总供应量中移除。 销毁拍卖由 Injective 的两个原生模块实现:交易和拍卖。这些模块是 Injective 核心功能的一部分,作为即插即用的金融原语,供任何在 Injective 上构建的项目使用。

强化和参与的历史

交易所模块

交易所模块是 Injective 与其他区块链的主要区别之一。这个技术工具为 Injective 上的共享流动性环境提供支持,并推动销毁拍卖的运作。订单簿管理、交易执行、订单匹配和结算的整个过程都通过该模块的逻辑在链上完成。 销毁拍卖的关键设计特性是为使用交易模块的应用程序内置的收入共享结构。在此结构中,部分累积的收入被分配给拍卖模块,以便纳入当前的销毁拍卖事件,而剩余部分则由使用该模块的应用程序保留,用于支持其交易服务。

拍卖模块

拍卖模块为销毁拍卖的运作提供了两个关键服务:代币收集和拍卖协调。对于代币收集,该模块定期从交易模块收集代币,将它们汇集到一个拍卖基金中。值得注意的是,拍卖基金还接收来自未使用交易模块但选择参与的应用程序的代币,以及来自个人用户的贡献。拍卖过程本身涉及多个由拍卖模块管理的任务,包括协调竞标过程、确定赢家、交付获胜资产以及销毁获胜的 INJ 出价。

Token Factory

TokenFactory 代币是原生集成到 Cosmos SDK 的银行模块中的代币。它们的命名格式为 factory/{creatorAddress}/{subdenom}。由于代币是按创建者地址命名空间进行命名的,这使得代币铸造无需权限,因为不需要解决名称冲突。

这种集成提供了对所有资产总供应量的跟踪和查询支持,不像 CW20 标准那样需要直接查询智能合约。因此,推荐使用 TokenFactory 标准。例如,像 Helix 或 Mito 这样的产品是基于 Injective 交换模块构建的,它们专门使用银行代币。TokenFactory 代币可以通过 injectived CLI 或智能合约创建。通过 Wormhole 桥接到 Injective 的代币也是 TokenFactory 代币。

代币标准

Injective 提供了多种不同的代币标准,供在创建 dApp 时使用。在本文件中,我们将介绍不同类型的代币,以及使用每种代币的建议和指南。

Denom

Denom 是 Injective 的银行模块中用于表示资产的方式。这些资产可以用于交易、在交易模块上创建新市场、参与拍卖、转账到另一个地址等。 根据 denom 的来源及其在 Injective 上的创建方式,我们有不同类型的 denom:

  • 本地 denom - 这种类型只有一个 denom,即 inj denom,代表 Injective 的本地代币。

  • Peggy denom - 这些 denom 代表通过 Peggy 桥从以太坊桥接到 Injective 的资产。它们的格式为 peggy{ERC20_CONTRACT_ADDRESS}。

  • IBC denom - 这些 denom 代表通过 IBC 从其他 IBC 兼容链桥接过来的资产。它们的格式为 ibc/{hash}。

  • 保险基金 denom - 这些 denom 代表在 Injective 上创建的保险基金的代币份额。它们的格式为 share{id}。

  • 工厂 denom - 这些 tokenfactory denom 允许任何账户使用名称 factory/{creator address}/{subdenom} 创建新代币。由于代币是按创建者地址命名空间组织的,这使得代币铸造变得无需许可,因为不需要解决名称冲突。对于这些 denom 的一个特殊用例是表示来自 Cosmwasm 的 CW20 代币,在 Injective 的本地银行模块中使用。它们的格式为 factory/{CW20_ADAPTER_CONTRACT}/{CW20_CONTRACT_ADDRESS},其中 CW20_ADAPTER_CONTRACT 是将 CW20 和本地银行模块转换的适配器合约地址。

INJ 2.0 于 2023 年发布,使得任何应用程序都可以向拍卖基金贡献,不仅限于使用交易模块的应用程序。Injective 于 2024 年 4 月发布的 INJ 销毁升级扩展了该功能的访问权限,允许个人用户进行贡献。因此,任何项目或用户都可以直接向 Injective 销毁拍卖贡献,这反过来可以提高销毁拍卖的整体价值和效果。 销毁拍卖每周举行,结束时间为 UTC-4:00 9:00。参与者可以通过 Injective Hub 或直接与区块链交互进行参与。 和 提供了销毁拍卖至今销毁的总 INJ 的实时跟踪。

要了解更多关于在 Injective 上创建代币的信息,请查看。要阅读更多关于 TokenFactory 标准的信息,请查看。

我们将在本文件后续部分提供关于这些 denom 类型的更多细节。 在了解如何获取 denom 元数据。

Injective Hub
Injective Explorer
这里
这里
这里

指南

本节包含面向用户的(一般性)指南。基本上是针对 Injective 上最常见问题的指南。

现有指南列表

CW20 标准

CW20 代币标准提供了一个框架,用于无权限创建和管理可替代代币,其结构更接近于。如上所述,推荐使用 TokenFactory 标准,因为它与 Cosmos SDK 原生集成,但如果您出于某种原因希望使用 CW20 标准,您可以通过 将 CW20 代币转换为 TokenFactory 代币,反之亦然。有关 CW20 标准的更多信息,请参阅其正式规范 。

ERC20 标准
CW20 适配器
这里

发布代币

在本文中,我们将解释如何在Injective上启动一个代币。 在Injective上启动代币有两种选择:桥接现有代币或创建新代币。

桥接

在Injective上启动代币的最简单方法是通过桥接来自Injective支持的互操作网络中的现有资产。你可以参考桥接中的指南,了解如何将资产从其他网络桥接到Injective。 一旦桥接过程完成,代币将在Injective上创建,你就可以使用它来发布市场。

创建一个新的代币

你也可以使用TokenFactory模块在Injective上创建一个新代币。有多种方式可以实现这一目标。

使用 Injective Hub

通过编程

使用 TypeScript

使用 Injective CLI

在继续本教程之前,您必须先在本地安装 Injectived。您可以在节点部分的快速入门页面上了解更多信息。

一旦您安装了 injectived 并添加了密钥,您可以使用 CLI 启动您的代币:

  1. 创建一个 TokenFactory denom

创建工厂 denom 的费用为 0.1 INJ。

injectived tx tokenfactory create-denom [subdenom] [name] [symbol] [decimals] --from=YOUR_KEY --chain-id=injective-888 --node=https://testnet.tm.injective.network:443 --gas-prices=500000000inj --gas 1000000

代币按创建者地址命名空间进行管理,以实现无需许可并避免名称冲突。在上述示例中,subdenom 是 ak,但 denom 的命名方式将是 factory/{creator address}/{subdenom}。

  1. 提交代币元数据

要使您的代币在 Injective dApp 上可见,您必须提交其元数据。

injectived tx tokenfactory set-denom-metadata "My Token Description" 'factory/inj17vytdwqczqz72j65saukplrktd4gyfme5agf6c/ak' AKK AKCoin AK '' '' '[
{"denom":"factory/inj17vytdwqczqz72j65saukplrktd4gyfme5agf6c/ak","exponent":0,"aliases":[]},
{"denom":"AKK","exponent":6,"aliases":[]}
]' 6 --from=YOUR_KEY --chain-id=injective-888 --node=https://testnet.sentry.tm.injective.network:443 --gas-prices=500000000inj --gas 1000000

这条指令参数如下:

injectived tx tokenfactory set-denom-metadata [description] [base] [display] [name] [symbol] [uri] [uri-hash] [denom-unit (json)] [decimals]
  1. 铸造代币

一旦您创建了代币并提交了代币元数据,就可以开始铸造您的代币了。

injectived tx tokenfactory mint 1000000factory/inj17vytdwqczqz72j65saukplrktd4gyfme5agf6c/ak --from=gov --chain-id=injective-888 --node=https://testnet.sentry.tm.injective.network:443 --gas-prices=500000000inj --gas 1000000

此命令将铸造1个代币,假设您的代币有6个小数位。

  1. 销毁代币

代币的管理者,也可以销毁代币。

injectived tx tokenfactory burn 1000000factory/inj17vytdwqczqz72j65saukplrktd4gyfme5agf6c/ak --from=gov --chain-id=injective-888 --node=https://testnet.sentry.tm.injective.network:443 --gas-prices=500000000inj --gas 1000000
  1. 更换管理者

一旦铸造了初始供应量,建议将管理员更改为null地址,以确保代币的供应量无法被篡改。再次强调,代币的管理员可以随时铸造和销毁供应量。NEW_ADDRESS,如上所述,在大多数情况下应设置为 inj1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqe2hm49。

injectived tx tokenfactory change-admin factory/inj17vytdwqczqz72j65saukplrktd4gyfme5agf6c/ak NEW_ADDRESS --from=gov --chain-id=injective-888 --node=https://testnet.sentry.tm.injective.network:443 --gas-prices=500000000inj --gas 1000000

以上示例适用于测试网。如果您想在主网上运行,请进行以下更改:

injective-888 > injective-1

https://testnet.sentry.tm.injective.network:443 > http://sentry.tm.injective.network:443

使用 Cosmwasm

create_new_denom_msg

pub fn create_new_denom_msg(sender: String, subdenom: String) -> CosmosMsg<InjectiveMsgWrapper> {
    InjectiveMsgWrapper {
        route: InjectiveRoute::Tokenfactory,
        msg_data: InjectiveMsg::CreateDenom { sender, subdenom },
    }
    .into()
}

目的:创建一个消息,用于通过 tokenfactory 模块创建一个新的denomination。

参数:

  • sender:发起创建的账户地址。

  • subdenom:新代币的sub-denomination标识符。

返回:一个包装在 InjectiveMsgWrapper 中的 CosmosMsg,准备发送到 Injective 区块链。

示例:

let new_denom_message = create_new_denom_msg(
    env.contract.address,  // Sender's address
    "mytoken".to_string(), // Sub-denomination identifier
);

create_set_token_metadata_msg

pub fn create_set_token_metadata_msg(denom: String, name: String, symbol: String, decimals: u8) -> CosmosMsg<InjectiveMsgWrapper> {
    InjectiveMsgWrapper {
        route: InjectiveRoute::Tokenfactory,
        msg_data: InjectiveMsg::SetTokenMetadata {
            denom,
            name,
            symbol,
            decimals,
        },
    }
    .into()
}

目的:创建一个消息,用于设置或更新代币的元数据。

参数:

  • denom:代币的denomination标识符。

  • name:代币的完整名称。

  • symbol:代币的符号。

  • decimals:代币使用的十进制位数。

  • 返回:一个包装在 InjectiveMsgWrapper 中的 CosmosMsg,准备发送到 Injective 区块链。

示例:

let metadata_message = create_set_token_metadata_msg(
    "mytoken".to_string(),         // Denomination identifier
    "My Custom Token".to_string(), // Full name
    "MYT".to_string(),             // Symbol
    18,                            // Number of decimals
);

create_mint_tokens_msg

pub fn create_mint_tokens_msg(sender: Addr, amount: Coin, mint_to: String) -> CosmosMsg<InjectiveMsgWrapper> {
    InjectiveMsgWrapper {
        route: InjectiveRoute::Tokenfactory,
        msg_data: InjectiveMsg::Mint { sender, amount, mint_to },
    }
    .into()
}

目的:创建一个消息,用于铸造新代币。该代币必须是 TokenFactory 代币,且发送者必须是代币管理员。

参数:

  • sender:发起铸币操作的账户地址。

  • amount:要铸造的代币数量。

  • mint_to:接收新铸造代币的地址。

返回:一个包装在 InjectiveMsgWrapper 中的 CosmosMsg,准备发送到 Injective 区块链。

示例:

let mint_message = create_mint_tokens_msg(
    env.contract.address,                                   // Sender's address
    Coin::new(1000, "factory/<creator-address>/mytoken"),   // Amount to mint
    "inj1...".to_string(),                                  // Recipient's address
);

create_burn_tokens_msg

pub fn create_burn_tokens_msg(sender: Addr, amount: Coin) -> CosmosMsg<InjectiveMsgWrapper> {
    InjectiveMsgWrapper {
        route: InjectiveRoute::Tokenfactory,
        msg_data: InjectiveMsg::Burn { sender, amount },
    }
    .into()
}

目的:创建一个消息,用于销毁代币。该代币必须是 TokenFactory 代币,且发送者必须是代币管理员。

参数:

  • sender:发起销毁操作的账户地址。

  • amount:要销毁的代币数量。

返回:一个包装在 InjectiveMsgWrapper 中的 CosmosMsg,准备发送到 Injective 区块链。

示例:

let burn_message = create_burn_tokens_msg(
    env.contract.address,                                    // Sender's address
    Coin::new(500, "factory/<creator-address>/mytoken"),     // Amount to burn
);

web 应用程序让你能够轻松创建和管理代币,在Injective的上创建市场等。

使用 TokenStation

web 应用程序让你能够轻松创建和管理代币,在 Injective 的上创建市场,启动空投等功能。

使用 DojoSwap

类似于上述,你可以使用 来创建、管理和列出你的代币,以及其他一些有用的功能。

你可以学些更多关于发布代币的内容在 。

要通过智能合约以编程方式创建和管理银行代币,可以使用以下在 包中找到的消息:

Injective Hub
原生订单簿
​
TokenStation
原生订单簿
​
DojoSwap 的市场创建模块
TypeScript 文档
injective-cosmwasm

桥接

将资产桥接到Injective生态系统非常简单,支持23个以上的网络,并且还在不断增长。在这里,您将找到关于如何通过以太坊、Solana、IBC以及使用Wormhole协议桥接资产的逐步操作指南,使您能够轻松连接到Injective生态系统。

发布市场

发布市场的前置条件是 发布代币

在 Injective 上启动交易对快速、简单,而且最重要的是,无需许可!

以下教程假设交易对是以从以太坊桥接的 ERC-20 代币作为基础资产,并与 INJ 作为报价资产配对。

对于 Injective 原生代币,请跳过桥接部分,直接进入第 6 步。

  1. 使用代币地址桥接 ERC-20 代币,您可能需要在可信的来源如 CoinGecko 上验证该地址。

  1. 复制并粘贴正确的合约地址,然后点击“add”。

  1. 现在输入您希望桥接的ERC-20代币的数量,点击“approve”,确认交易,然后点击“review”,确认交易并等待。

  1. 一旦批准支出和存款交易在以太坊区块链上得到确认,您将看到桥接交易的进度。一旦交易在Injective上确认,您的桥接ERC-20代币将会出现在您的Injective钱包中。(注意,如果您在源链使用了MetaMask,默认情况下,您的桥接代币将会发送到与您的MetaMask关联的INJ地址。您可以通过点击第4步开始时收款地址旁边的锁定图标来更改此设置。)

  1. 从第一个下拉菜单中选择“instant spot market launch”,并指定一个交易对。在本例中,我们使用 PEPE/INJ。然后从下拉菜单中选择基础代币。然而,请注意,可能会有多个代币使用相同的交易对名称。请始终确保匹配正确的代币地址。在本例中,由于代币是通过Peggy桥接完成的,地址将是以 peggy 开头,后面跟随ERC-20合约地址。

  1. 现在选择正确的报价代币,在本例中是 INJ。(注意,如果您希望将代币与 USDT 配对,请确保选择“正确”的 USDT 地址,即 peggy 后跟 USDT 的 ERC-20 合约地址。)最后,指定最小价格刻度和最小数量刻度。由于 PEPE/INJ 将以不到一美分的价格交易,因此最小刻度已相应设置。

导航到 页面,开始使用 Peggy 桥接将您选择的 ERC-20 代币从以太坊桥接到 Injective。

桥接交易完成后,您可以通过导航到,未经许可地在Injective上列出该代币。

Injective Bridge
Injective Hub

injectived

injectived是连接到Injective的命令行界面和节点守护程序。Injective core是Injective节点软件的官方Golang参考实现。

快速入门

工具包

我们为开发者提供了多种工具包,以便与 Injective 生态系统进行交互。

以下开发者工具包括 SDK、API 和库,这些都是在 Injective 上构建 dApps 的重要组件。

工具包列表

Denom 元数据

一个 denom 是 Injective 的Bank模块中如何表示代币的方式。这些资产可以用于交易、在交易模块上创建新市场、参与拍卖、转移到其他地址等。

对于开发者和交易者来说,最大的痛点之一就是获取这些 denoms 的元数据。元数据包括小数位数(decimals)、符号(symbol)、名称(name)等。

本指南将展示如何直接从 injective-lists 仓库获取 denom 元数据并将其映射到您的 denom。您还可以使用这种方法来映射现货和衍生品市场的 denom's 元数据。

Injective Lists

元数据每 30 分钟自动从链上获取新 denoms,并重新生成 json 文件。

一旦您获得了 JSON 文件,您需要将元数据与特定的 denom 映射。 该元数据的接口如下:

export interface Token {
  name: string
  logo: string
  symbol: string
  decimals: number
  coinGeckoId: string
  denom: string
  address: string
  tokenType: string
  tokenVerification: string
}

Bank 余额

假设您获取了某个地址的Bank余额(如下例所示,使用 TypeScript),您可以轻松地将其映射到上述 JSON 文件中的元数据。

import { config } from "dotenv";
import {
  ChainGrpcBankApi,
  IndexerGrpcAccountPortfolioApi,
} from "@injectivelabs/sdk-ts";
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";

config();

/** Querying Example */
(async () => {
  const endpoints = getNetworkEndpoints(Network.MainnetSentry);
  const chainGrpcBankApi = new ChainGrpcBankApi(endpoints.grpc);

  const injectiveAddress = "inj...";
  const { balances } = chainGrpcBankApi.fetchBalances(injectiveAddress);

  console.log(bankBalances);

 const metadata = JSON.parse(await readFile("./mainnet.json", "utf8")) as {
    denom: string;
    address: string;
    decimals: string;
    logo: string;
    name: string;
    tokenType: string;
    coinGeckoId: string
  }[];
  const balances = bankBalances.map((balance) => {
    const meta = metadata.find((m) => m.denom === balance.denom);

    return {
      ...balance,
      ...meta,
    };
  }
  
  console.log(balances)
})();

现在,您的银行余额包含了所有您需要的元数据(包括小数位(decimals)、符号(symbol)、名称(namename)、logo 等)。

现货市场

与银行余额类似,您可以使用相同的方法将现货市场中的代币 denom 映射到其元数据。

import { config } from "dotenv";
import { IndexerGrpcSpotApi } from "@injectivelabs/sdk-ts";
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
import { readFile } from "fs/promises";

config();

/** Querying Example */
(async () => {
  const endpoints = getNetworkEndpoints(Network.Testnet);
  const indexerGrpcSpotApi = new IndexerGrpcSpotApi(endpoints.indexer);

  const markets = await indexerGrpcSpotApi.fetchMarkets();

  console.log(markets);

  const metadata = JSON.parse(await readFile("./mainnet.json", "utf8")) as {
    denom: string;
    address: string;
    decimals: string;
    logo: string;
    name: string;
    tokenType: string;
    coinGeckoId: string;
  }[];
  const marketsWithMetadata = markets.map((market) => {
    const baseTokenMetadata = metadata.find(
      (m) => m.denom === market.baseDenom
    );
    const quoteTokenMetadata = metadata.find(
      (m) => m.denom === market.quoteDenom
    );

    return {
      ...market,
      baseTokenMetadata,
      quoteTokenMetadata,
    };
  });

  console.log(marketsWithMetadata);
})();

衍生品市场

与Bank余额类似,您可以使用相同的方法将衍生品市场中的代币 denom 映射到其元数据。

import { config } from "dotenv";
import { IndexerGrpcDerivativeApi } from "@injectivelabs/sdk-ts";
import { getNetworkEndpoints, Network } from "@injectivelabs/networks";
import { readFile } from "fs/promises";

config();

/** Querying Example */
(async () => {
  const endpoints = getNetworkEndpoints(Network.Testnet);
  const indexerGrpcDerivativeApi = new IndexerGrpcDerivativeApi(endpoints.indexer);

  const markets = await indexerGrpcDerivativeApi.fetchMarkets();

  console.log(markets);

  const metadata = JSON.parse(await readFile("./mainnet.json", "utf8")) as {
    denom: string;
    address: string;
    decimals: string;
    logo: string;
    name: string;
    tokenType: string;
    coinGeckoId: string;
  }[];
  const marketsWithMetadata = markets.map((market) => {
    const baseTokenMetadata = metadata.find(
      (m) => m.denom === market.baseDenom
    );

    return {
      ...market,
      quoteTokenMetadata,
    };
  });

  console.log(marketsWithMetadata);
})();

账户

本节介绍 Injective 内置的账户系统。

本文档介绍了 Injective 内置的账户系统。 前置阅读:

地址和公钥

Injective 默认提供 3 种主要类型的地址和公钥(PubKeys):

  1. 账户地址和密钥:用于标识用户(例如消息的发送者),使用 eth_secp256k1 曲线生成。

  2. 验证者操作员地址和密钥:用于标识验证者的操作员,使用 eth_secp256k1 曲线生成。

  3. 共识节点地址和密钥:用于标识参与共识的验证者节点,使用 ed25519 曲线生成。

Address bech32 Prefix
Pubkey bech32 Prefix
Curve
Address byte length
Pubkey byte length

账户地址

inj

injpub

eth_secp256k1

20

33 (compressed)

验证者操作员

injvaloper

injvaloperpub

eth_secp256k1

20

33 (compressed)

共识节点

injvalcons

injvalconspub

ed25519

20

32

客户端的地址格式

EthAccounts 可以采用 Bech32 和 十六进制(Hex) 两种格式表示,以兼容 Ethereum 的 Web3 工具。

  • Bech32 格式:是 Cosmos-SDK 进行 CLI 和 REST 客户端查询与交易的默认格式。

  • 十六进制(EIP55 Hex)格式:是 Cosmos sdk.AccAddress 在 Ethereum 中的 common.Address 表示方式。

示例:

  • 地址(Bech32):inj14au322k9munkmx5wrchz9q30juf5wjgz2cfqku

  • 地址(EIP55 Hex):0xAF79152AC5dF276D9A8e1E2E22822f9713474902

  • 压缩公钥:

    {
      "@type": "/injective.crypto.v1beta1.ethsecp256k1.PubKey",
      "key": "ApNNebT58zlZxO2yjHiRTJ7a7ufjIzeq5HhLrbmtg9Y/"
    }

您可以使用 Cosmos CLI 或 REST 客户端查询账户地址。

# NOTE: the --output (-o) flag will define the output format in JSON or YAML (text)
injectived q auth account $(injectived keys show <MYKEY> -a) -o text
|
  '@type': /injective.types.v1beta1.EthAccount
  base_account:
    account_number: "3"
    address: inj14au322k9munkmx5wrchz9q30juf5wjgz2cfqku
    pub_key: null
    sequence: "0"
  code_hash: xdJGAYb3IzySfn2y3McDwOUAtlPKgic7e/rYBF2FpHA=
# GET /cosmos/auth/v1beta1/accounts/{address}
curl -X GET "http://localhost:10337/cosmos/auth/v1beta1/accounts/inj14au322k9munkmx5wrchz9q30juf5wjgz2cfqku" -H "accept: application/json"

Cosmos SDK 密钥环输出(即 injectived keys)仅支持 Bech32 格式的地址。

从私钥/助记词派生Injective账户

下面是如何从私钥和/或助记词派生 Injective 账户的示例:

import { Wallet } from 'ethers'
import { Address as EthereumUtilsAddress } from 'ethereumjs-util'

const mnemonic = "indoor dish desk flag debris potato excuse depart ticket judge file exit"
const privateKey = "afdfd9c3d2095ef696594f6cedcae59e72dcd697e2a7521b1578140422a4f890"
const defaultDerivationPath = "m/44'/60'/0'/0/0"
const defaultBech32Prefix = 'inj'
const isPrivateKey: boolean = true /* just for the example */

const wallet = isPrivateKey ? Wallet.fromMnemonic(mnemonic, defaultDerivationPath) : new Wallet(privateKey)
const ethereumAddress = wallet.address
const addressBuffer = EthereumUtilsAddress.fromString(ethereumAddress.toString()).toBuffer()
const injectiveAddress = bech32.encode(defaultBech32Prefix, bech32.toWords(addressBuffer))

下面是如何从私钥派生公钥的示例:

import secp256k1 from 'secp256k1'

const privateKey = "afdfd9c3d2095ef696594f6cedcae59e72dcd697e2a7521b1578140422a4f890"
const privateKeyHex = Buffer.from(privateKey.toString(), 'hex')
const publicKeyByte = secp256k1.publicKeyCreate(privateKeyHex)

const buf1 = Buffer.from([10])
const buf2 = Buffer.from([publicKeyByte.length])
const buf3 = Buffer.from(publicKeyByte)

const publicKey = Buffer.concat([buf1, buf2, buf3]).toString('base64')
const type = '/injective.crypto.v1beta1.ethsecp256k1.PubKey'

交易

当用户想要与Injective进行交互并进行状态变更时,他们会创建交易。一旦交易创建完成,它需要由与发起该状态变更的账户关联的私钥签名。签名完成后,交易会被广播到Injective网络。

在广播并通过所有验证(包括签名验证、值验证等)后,交易会被包含在一个区块中,并通过共识过程经过网络的批准。

消息

简单来说,消息是向Injective提供的关于期望状态变更的指令。消息是特定模块的对象,用于触发属于其模块范围内的状态转换。每个交易必须至少包含一条消息。

交易上下文

除了消息,每个交易还有一个上下文。上下文包括费用(fees)、账户详情(accountDetails)、备注(memo)、签名(signatures)等信息。

交易流

我们想要广播到Injective的每个交易都有相同的流程。这个流程包括三个步骤:准备、签名和广播交易。当交易被包含在区块中时,使用消息指定的状态变化将在Injective上应用。

指令

本节描述了 injectived 提供的命令,这是连接运行中的 injectived 进程(节点)的命令行界面。

一些 injectived 命令需要子命令、参数或标志才能运行。要查看这些信息,可以在命令后添加 --help 或 -h 标志。有关帮助标志的使用示例,请参见 query 或 tx。

对于 chain-id 参数,主网应使用 injective-1,测试网应使用 injective-888。

add-genesis-account

语法

例子

collect-gentxs

语法

debug

用于调试应用程序。要查看语法和子命令列表,请在 debug 命令后添加 --help 或 -h 标志运行:

子命令:

  • addr:在十六进制(hex)和 bech32 地址格式之间转换。

  • pubkey:从 proto JSON 解码公钥。

  • raw-bytes:将原始字节输出(例如 [72 101 108 108 111 44 32 112 108 97 121 103 114 111 117 110 100])转换为十六进制(hex)。

export

将状态导出为 JSON。

语法

gentx

Note: gentx 命令提供了多个可用的标志。运行 gentx 命令并添加 --help 或 -h 以查看所有标志。

语法

例子

help

显示可用命令的概览。

语法

init

初始化节点的配置文件。

语法

例子

keys

管理 Keyring 相关命令。这些密钥可以采用 Tendermint 加密库支持的任何格式,并可用于轻客户端、全节点或其他需要使用私钥签名的应用程序。

要查看语法和子命令列表,请在 keys 命令后添加 --help 或 -h 标志运行:

子命令:

  • add:添加加密的私钥(新生成或恢复的),加密后保存到指定文件。

  • delete:删除指定的密钥。

  • export:导出私钥。

  • import:将私钥导入本地 Keybase。

  • list:列出所有密钥。

  • migrate:将密钥从旧版(基于数据库的)Keybase 迁移。

  • mnemonic:根据输入的熵计算 BIP39 助记词。

  • parse:在十六进制(hex)和 bech32 地址格式之间解析转换。

  • show:通过名称或地址检索密钥信息。

  • unsafe-export-eth-key:以明文形式导出 Ethereum 私钥。

  • unsafe-import-eth-key:将 Ethereum 私钥导入本地 Keybase。

migrate

语法

例子

query

管理查询命令。要查看语法和子命令列表,请在 query 子命令后添加 --help 或 -h 标志运行:

子命令:

  • account:通过地址查询账户。

  • auction:拍卖模块的查询命令。

  • auth:身份验证模块的查询命令。

  • authz:授权模块的查询命令。

  • bank:银行模块的查询命令。

  • block:获取指定高度区块的验证数据。

  • chainlink:预言机模块的查询命令。

  • distribution:分发模块的查询命令。

  • evidence:根据哈希或查询所有(分页)提交的证据。

  • exchange:交换模块的查询命令。

  • feegrant:费用授权模块的查询命令。

  • gov:治理模块的查询命令。

  • ibc:IBC 模块的查询命令。

  • ibc-fee:IBC 中继激励查询子命令。

  • ibc-transfer:IBC 同质化代币转账查询子命令。

  • insurance:保险模块的查询命令。

  • interchain-accounts:跨链账户子命令。

  • mint:铸币模块的查询命令。

  • oracle:预言机模块的查询命令。

  • params:参数模块的查询命令。

  • peggy:Peggy 模块的查询命令。

  • slashing:惩罚模块的查询命令。

  • staking:质押模块的查询命令。

  • tendermint-validator-set:获取给定高度的完整 Tendermint 验证者集合。

  • tokenfactory:代币工厂模块的查询命令。

  • tx:通过哈希、账户序列或已提交区块中的签名组合或逗号分隔的签名查询交易。

  • txs:查询匹配一组事件的分页交易。

  • upgrade:升级模块的查询命令。

  • wasm:WASM 模块的查询命令。

  • xwasm:wasmx 模块的查询命令。

rollback

状态回滚是为了从错误的应用状态转换中恢复,当 Tendermint 持久化了错误的应用哈希,导致无法继续前进时,执行回滚操作。回滚将高度为 n 的状态覆盖为高度 n - 1 的状态。应用程序也会回滚到高度 n - 1。没有区块被删除,因此当重新启动 Tendermint 时,第 n 区块中的交易将会重新执行。

语法

rosetta

创建一个 Rosetta 服务器。

语法

start

运行全节点应用程序,可以选择在进程内或进程外与 Tendermint 一起运行。默认情况下,应用程序与 Tendermint 在同一进程中运行。

start 命令提供了多个可用的标志。运行 start 命令并添加 --help 或 -h 以查看所有标志。

语法

status

显示远程节点的状态。使用 --node 或 -n 标志来指定节点端点。

语法

tendermint

管理 Tendermint 协议。要查看语法和子命令列表,请在 query 子命令后添加 --help 或 -h 标志运行:

子命令:

  • reset-state:删除所有数据和 WAL(写前日志)。

  • show-address:显示该节点的 Tendermint 验证者共识地址。

  • show-node-id:显示该节点的 ID。

  • show-validator:显示该节点的 Tendermint 验证者信息。

  • unsafe-reset-all:删除所有数据和 WAL,将该节点的验证者重置为创世状态。

  • version:显示 Tendermint 库的版本信息。

testnet

创建一个测试网,指定目录的数量,并为每个目录填充必要的文件。

testnet 命令提供了多个可用的标志。运行 testnet 命令并添加 --help 或 -h 以查看所有标志。

语法

例子

tx

管理交易的生成、签名和广播。有关示例,请参阅使用 Injectived。 要查看语法、可用的子命令及其详细信息,请在 tx 命令后添加 --help 或 -h 标志运行:

子命令:

  • auction:拍卖交易子命令

  • authz:授权交易子命令

  • bank:银行交易子命令

  • broadcast:广播离线生成的交易

  • chainlink:链下报告(OCR)子命令

  • crisis:危机交易子命令

  • decode:解码二进制编码的交易字符串

  • distribution:分发交易子命令

  • encode:编码离线生成的交易

  • evidence:证据交易子命令

  • exchange:交换交易子命令

  • feegrant:费用授权交易子命令

  • gov:治理交易子命令

  • ibc:IBC 交易子命令

  • ibc-fee:IBC 中继激励交易子命令

  • ibc-transfer:IBC 同质化代币转账交易子命令

  • insurance:保险交易子命令

  • multisign:为离线生成的交易生成多签名

  • oracle:预言机交易子命令

  • peggy:Peggy 交易子命令

  • sign:签署离线生成的交易

  • sign-batch:签署交易批处理文件

  • slashing:惩罚交易子命令

  • staking:质押交易子命令

  • tokenfactory:代币工厂交易子命令

  • validate-signatures:验证交易签名

  • vesting:归属交易子命令

  • wasm:WASM 交易子命令

  • xwasm:wasmx 交易子命令

validate-genesis

语法

version

返回你正在运行的 Injective 版本。

语法

快速入门

本节的目标是帮助开发者在 Injective 上构建他们的项目。

Injective 是唯一专为跨链交易、衍生品、DeFi 和 Web3 应用设计的区块链。作为全球 DeFi 生态系统构建者的首选平台,Injective 为开发者提供众多优势,使其能够在更短的时间内构建更强大的应用程序。

为什么选择Injective?

您感兴趣的是?

INJ 代币

INJ 是驱动 Injective 及其更广泛生态系统的本地资产。INJ 的每个组成部分都是精心设计的,旨在培养一个繁荣的 Web3 生态系统。作为区块链的本地资产,INJ 在促进 Injective 上的各种操作中发挥着核心作用。作为 Injective 定制的 Tendermint 权益证明(PoS)共识框架的重要组成部分,INJ 对于通过质押保障网络安全至关重要。此外,INJ 还作为 Injective 的治理代币,并作为更广泛 Injective 生态系统中的交换媒介。值得注意的是,INJ 通过利用核心 Injective 模块,通过创新的销毁机制和动态供应机制,打造了通缩特性,从而与其他 PoS 链上的本地资产区别开来。

基础单位

这与以太坊的单位相匹配:

Injective 代币经济学与效用

1. 安全性与质押

Injective 通过质押机制来保障安全性,这是 INJ 的一个重要应用场景。验证者和委托者可以自由参与 Injective 网络的质押过程。验证者在 Injective 上运行节点,而委托者可以将 INJ 分配给其选择的特定节点。被质押的 INJ 通过惩罚和奖励机制,确保了一个稳健的去中心化环境。

如果验证者存在恶意行为或未能有效履行职责,其被质押的 INJ 可能会被削减(惩罚)。此外,INJ 还用于奖励验证者,以激励他们参与交易验证和区块创建。验证者的奖励包括新铸造的 INJ(区块奖励)以及部分相关交易费用。

INJ 持有者无需运行节点即可参与质押并获得验证者奖励的一部分。用户可以通过支持的浏览器钱包或直接在 Injective Hub 上将 INJ 委托给验证者。作为回报,用户在锁定 INJ 后,将按照比例获得验证者的 INJ 奖励,扣除所选验证者收取的佣金(手续费)。如果被委托的验证者发生惩罚性削减事件,用户的质押 INJ 也可能会受到影响。这一机制确保了验证者和委托者共同为网络安全做出贡献。

除了保障 Injective 链的安全,INJ 还通过 Electro Chains 扩展其安全服务能力。这些基于 Injective 的 Rollup 方案提供了多种技术优势,例如支持多个虚拟机(如 inEVM)。由于这些 Rollup 需要在 Injective 上结算,INJ 便成为这些网络的基础安全层。这个互联的安全框架进一步凸显了 INJ 在维护 Injective 网络完整性和稳定性,以及支持 Electro Chains 生态系统中的关键作用。

2. 治理

INJ 被用于社区主导的治理,涵盖链上所有参数的管理。Injective 具有独特的智能合约上传权限层,这意味着要在主网部署智能合约,必须经过质押者社区的投票批准。这使得社区能够直接管理 Injective 的所有参数。

在治理方面,INJ 用于创建提案,并在活跃提案上进行基于代币权重的投票。为防止垃圾提案,Injective 要求提案必须达到最低存款要求(以 INJ 支付)才能进入投票阶段。这一存款门槛可以由提案人独自承担,或由其他用户共同出资补足。如果在最大存款期限内未达到最低存款要求,提案将自动被拒绝,且存款将被销毁(烧毁)。此外,如果提案在投票期结束后未能通过,提案存款也将被烧毁。

提案投票在预设的投票周期内进行,该周期由治理机制设定,并适用于所有治理投票。在投票过程中,只有已质押的 INJ 才有资格参与投票,因此仅限验证者和委托者可以对活跃提案投票。投票权重基于代币数量计算,即 1 INJ 等于 1 票。委托者无需主动参与治理即可保持其委托者身份,但他们可以选择直接对提案投票。如果委托者未投票,他们的投票权将在该特定投票事件中自动继承给他们所委托的验证者。

INJ 可用于治理链上的所有方面,包括:

  • 拍卖(Auction)模块参数

  • 交易(Exchange)模块的自定义提案和参数

  • 保险(Insurance)模块参数

  • 预言机(Oracle)模块的自定义提案

  • Peggy 模块参数

  • Wasmx 模块参数

  • 软件升级

3. 交换媒介

INJ 作为默认资产,用于在区块链上促进各方之间的商品和服务交易。常见示例包括支付交易费用(Gas 费)、购买/出售 NFT、支付交易手续费或将资产作为抵押品存入。尽管大多数商品和服务可以用任何资产计价,但在 Injective 上产生的所有交易费用均以 INJ 支付。此外,所有利用 Injective 共享流动性层的应用程序(通过交易模块)所产生的协议收入,最终都会以 INJ 形式累积。

4. 交易 dApp 激励措施

交易协议实施了全球最低交易费用:做市商(Maker)为 0.1%,吃单者(Taker)为 0.2%。作为激励机制,旨在鼓励交易 dApp 在交易协议上引入交易活动,向共享订单簿提交订单的交易 dApp 将获得其促成订单所产生交易费用的 40% 作为奖励。

5. 交易费用价值累积

6. 衍生品的担保品

INJ 可以作为稳定币的替代品,用作 Injective 衍生品市场的保证金和担保品。在一些衍生品市场中,INJ 还可以作为保险池质押的担保品,质押者可以在锁仓的代币上赚取利息。

injective-lists 是一个公开的仓库,保存了 Injective 上所有代币的元数据。它是该信息的最新和最可靠来源。您可以通过为此仓库创建一个 PR 来提交您的代币信息。确保正确指定字段,特别是 "denom" 字段(请阅读),该字段应根据代币标准使用相应的 ibc、peggy 和 factory 前缀。

您可以访问 文件夹,基于环境下载元数据。

Injective 定义了其自定义的账户类型,使用 Ethereum 的 ECDSA secp256k1 曲线来生成密钥。这符合 规范,适用于完整的 路径。Injective 账户的根 HD 路径为 m/44'/60'/0'/0。

请参阅 文档,获取有关账户 API 的完整文档。

此外,多个消息可以打包在同一交易中。每个模块可用的消息可以在中找到。

将一个创世账户添加到 genesis.json 文件中。有关 genesis.json 的更多信息,请参阅加入测试网或指南。

收集创世交易并将其输出到 genesis.json 文件中。有关 genesis.json 的更多信息,请参阅加入测试网或指南。

将创世交易添加到 genesis.json 文件中。有关 genesis.json 的更多信息,请参阅加入测试网或指南。

将源创世数据迁移到目标版本,并打印到标准输出(STDOUT)。有关 genesis.json 的更多信息,请参阅加入测试网或指南。

验证默认位置或指定位置的创世文件。有关创世文件的更多信息,请参阅加入测试网或指南。

INJ 使用 作为基础单位,以保持与以太坊的对等性。

Cosmos-SDK 模块参数,包括 , , , , , , 和 模块

完整的治理流程详情可在查看。

剩余的 60% 交易费用将进行链上回购和销毁事件,其中所有交易费用的总额将被打包,并通过拍卖出售给最高竞标者,竞标者支付 INJ。该拍卖所得的 INJ 将被销毁,从而减少总的 INJ 供应量。 有关拍卖机制的更多细节可以在找到。

代币标准
https://github.com/InjectiveLabs/injective-lists/tree/master/json/tokens
Mainnet Raw JSON
Testnet Raw JSON
Cosmos SDK Accounts
Ethereum Accounts
EIP84
BIP44
Swagger API
相关章节
injectived add-genesis-account <address-or-key-name> <amount><coin-denominator>
injectived add-genesis-account acc1 100000000000inj
injectived collect-gentxs
injectived debug -h
injectived debug [subcommand]
injectived export
injectived gentx <key-name> <amount><coin-denominator>
injectived gentx myKey 100000000000inj --home=/path/to/home/dir --keyring-backend=os --chain-id=injective-1 \
    --moniker="myValidator" \
    --commission-max-change-rate=0.01 \
    --commission-max-rate=1.0 \
    --commission-rate=0.07 \
    --details="..." \
    --security-contact="..." \
    --website="..."
injectived help
injectived init <moniker>
injectived init myNode
injectived keys -h
injectived keys [subcommand]
injectived migrate <target version> <path-to-genesis-file>
injectived migrate v1.9.0 /path/to/genesis.json --chain-id=injective-888 --genesis-time=2023-03-07T17:00:00Z 
injectived query -h
injectived query [subcommand]
injectived rollback
injectived rosetta [flags]
injectived start [flags]
injectived status
injectived tendermint -h
injectived tendermint [subcommand]
injectived testnet [flags]
injectived testnet --v 4 --keyring-backend test --output-dir ./output --ip-addresses 192.168.10.2
injectived tx -h
injectived tx [subcommand]
injectived validate-genesis </path-to-file>
injectived version
1 inj = 1×10⁻¹⁸ INJ
1 wei = 1x10⁻¹⁸ ETH

为 DeFi 优化

⇾ 专用模块 ⇾ 低延迟基础设施 ⇾ 更快的开发速度 ⇾ 更强的功能能力 ⇾ 解锁全新潜力

商业级可扩展性

⇾ 25,000+ TPS(每秒交易数) ⇾ 650ms 区块时间 ⇾ 即时最终确认 ⇾ 抵抗 MEV(最大可提取价值)攻击 ⇾ 每笔交易成本仅 $0.0003

高度互操作性

⇾ 原生互操作性,支持 23+ 网络,包括 Ethereum 和 Solana ⇾ IBC 支持,连接 110+ 条链

直观的开发者体验

⇾ 原生集成的执行层

⇾ WASM + EVM ⇾ 由 Rust、Golang 和 Solidity 提供支持

加入主网
加入主网
加入主网
加入主网
加入主网
Atto
auth
bank
crisis
distribution
gov
mint
slashing
staking
此处
这里

使用 injectived

以下页面介绍了如何使用 injectived,这是一个连接到 Injective 的命令行界面。您可以使用 injectived 与 Injective 区块链交互,例如上传智能合约、查询数据、管理质押活动、处理治理提案等。

前置条件

确保已安装 injectived

injectived version

请调整您的命令以正确使用主目录。

injectived keys list --home ~/.injective

使用 Docker 化的 CLI

如果从 Docker 运行,您需要将主目录挂载到容器中。

docker run -it --rm -v ~/.injective:/root/.injective injectivelabs/injective-core:v1.14.1 injectived keys list --home /root/.injective

使用 Docker 化的 CLI 添加密钥很简单。

docker run -it --rm -v ~/.injective:/root/.injective injectivelabs/injective-core:v1.14.1 injectived keys add my_key --home /root/.injective

以下是该命令的解析:

  • docker 运行镜像 injectivelabs/injective-core:v1.14.1。

  • injectived 是在容器内运行 CLI 的命令。

  • keys add 是用于添加密钥的命令。

  • my_key 是密钥的名称。

  • --home /root/.injective 指定 CLI 在容器内的主目录。

  • -v ~/.injective:/root/.injective 将主机的 ~/.injective 目录挂载到容器的 /root/.injective 目录。

此命令将创建一个密钥对,并将其保存到容器的 /root/.injective/keyring-file 目录,该目录与主机的 ~/.injective/keyring-file 目录相同。

你可以运行以下命令列出所有密钥:

docker run -it --rm -v ~/.injective:/root/.injective injectivelabs/injective-core:v1.14.1 injectived keys list --home /root/.injective

使用 RPC 端点

在访问 Injective 区块链之前,你需要运行一个节点。你可以选择运行自己的完整节点,或者连接到其他人的节点。

要查询状态并发送交易,你必须连接到一个节点,它是整个网络的访问入口。你可以选择运行自己的完整节点,或者连接到其他人的节点。

要设置 RPC 端点,你可以使用以下命令:

injectived config set client node https://sentry.tm.injective.network:443
injectived config set client chain-id injective-1

对于使用测试网,您可以使用: https://k8s.testnet.tm.injective.network:443 (chain-id injective-888)

现在试着查询状态:

injectived q bank balances inj1yu75ch9u6twffwp94gdtf4sa7hqm6n7egsu09s

balances:
- amount: "28748617927330656"
  denom: inj

通用帮助

关于使用 injectived 的通用信息, 运行:

injectived --help

要获取有关特定 injectived 命令的更多信息,可以在命令后附加 -h 或 --help 标志。例如:

injectived query --help.

配置 injectived 客户端

要配置 injectived 的更多选项,请编辑 ~/.injective/config/ 目录中的 config.toml 文件。当 keyring-backend 设置为 file 时,密钥存储文件位于 ~/.injective/keyring-file 目录中。此外,keyring-backend 也可以设置为 test 或 os。 如果设置为 test,密钥将存储在 ~/.injective/keyring-test 文件中,但不会受密码保护。

配置文件中的所有选项都可以使用 CLI 进行设置: injectived config set client <option> <value>

生成, 签名, 和广播交易

运行以下命令可将 INJ 代币从发送方账户转账至接收方账户。1000inj 表示要发送的 INJ 代币数量,其中 1 INJ = 10¹⁸ inj,因此 1000inj 是一个非常小的数额。

injectived tx bank send MY_WALLET RECEIVER_WALLET 1000inj --from MY_WALLET

以下步骤被执行:

  1. 生成一个包含 MsgSend(x/bank 的 MsgSend)的交易,并在控制台打印生成的交易。

  2. 请求用户确认是否从 $MY_WALLET 账户发送交易。

  3. 从密钥存储(keyring)中获取 $MY_WALLET,这可以实现是因为在前面的步骤中已设置 CLI 的密钥存储。

  4. 使用密钥存储中的账户对生成的交易进行签名。

  5. 将已签名的交易广播到网络,这可以实现是因为 CLI 连接到了 Injective 公共节点的 RPC 端点。

CLI 将所有必要的步骤整合在一起,提供了简洁易用的用户体验。但也可以分别运行每个步骤来完成交易。

(仅) 生成一个交易

生成交易可以通过在任何交易命令后添加 --generate-only 标志来简单完成,例如:

injectived tx bank send MY_WALLET RECEIVER_WALLET 1000inj --from MY_WALLET --generate-only

这将把未签名的交易以 JSON 格式输出到控制台。我们还可以通过在上述命令后添加 > unsigned_tx.json 将未签名的交易保存到文件中(以便在签名者之间更方便地传递)。

对一个预生成的交易签名

使用 CLI 签名交易需要将未签名的交易保存到文件中。假设未签名的交易保存在当前目录中的一个名为 unsigned_tx.json 的文件中(参考前一段了解如何操作)。然后,只需运行以下命令:

injectived tx sign unsigned_tx.json --from=MY_WALLET

此命令将解码未签名的交易,并使用我们在密钥存储中已经设置的 MY_WALLET 的密钥以 SIGN_MODE_DIRECT 模式对其进行签名。签名后的交易将以 JSON 格式输出到控制台,像上面一样,我们可以通过在命令后添加 > signed_tx.json 将其保存到文件中。

injectived tx sign unsigned_tx.json --from=MY_WALLET > signed_tx.json

在 tx sign 命令中,有一些有用的标志可以考虑:

  • --sign-mode:你可以使用 amino-json 来通过 SIGN_MODE_LEGACY_AMINO_JSON 签名交易。

  • --offline:在离线模式下签名。这意味着 tx sign 命令不会连接到节点以获取签名者的账户号码和序列号,这两个信息在签名时需要。在这种情况下,你必须手动提供 --account-number 和 --sequence 标志。这对于离线签名非常有用,例如在没有互联网连接的安全环境中进行签名。

多个签名者签名 (多签)

使用多个签名者进行签名是通过 tx multi-sign 命令完成的。此命令假设所有签名者使用 SIGN_MODE_LEGACY_AMINO_JSON。其流程与 tx sign 命令类似,但不同之处在于,代替签署未签名的交易文件,每个签名者都会签署前一个签名者签署过的文件。tx multi-sign 命令将签名附加到现有的交易上。重要的是,签名者必须按照交易中给定的顺序签署交易,可以通过 GetSigners() 方法检索该顺序。

例如,从 unsigned_tx.json 开始,假设交易有 4 个签名者,我们将运行:

# Let signer1 sign the unsigned tx.
injectived tx multi-sign unsigned_tx.json signer_key_1 > partial_tx_1.json
# Now signer1 will send the partial_tx_1.json to the signer2.
# Signer2 appends their signature:
injectived tx multi-sign partial_tx_1.json signer_key_2 > partial_tx_2.json
# Signer2 sends the partial_tx_2.json file to signer3, and signer3 can append his signature:
injectived tx multi-sign partial_tx_2.json signer_key_3 > partial_tx_3.json

广播一个交易

广播交易是通过以下命令完成的:

injectived tx broadcast tx_signed.json

你可以选择性地传递 --broadcast-mode 标志来指定从节点接收哪种响应:

  • block:CLI 等待交易被包含在区块中。

  • sync:CLI 仅等待 CheckTx 执行响应,手动查询交易结果以确保它已被包含。

  • async:CLI 立即返回(交易可能失败)- 不要使用。

要查询交易结果,你可以使用以下命令:

injectived tx query TX_HASH

额外的故障排查

有时候配置可能没有正确设置。你可以通过在命令行中添加以下内容来强制使用正确的节点 RPC 端点。在与他人分享命令时,建议在命令行中明确设置所有标志(如 chain-id、node、keyring-backend 等)。

injectived --node https://sentry.tm.injective.network:443

转换地址

在本文档中,我们将概述一些示例,展示如何在不同格式和衍生路径之间转换地址。

转换 Hex <> Bech32 地址

正如我们在前面的 钱包章节中提到的,Injective 地址与 Ethereum 地址是兼容的。你可以轻松地在这两种格式之间进行转换。

使用 TypeScript

你可以通过使用 @injectivelabs/sdk-ts 包中的工具函数轻松地在 Injective 地址和 Ethereum 地址之间进行转换:

import { getInjectiveAddress, getEthereumAddress } from '@injectivelabs/sdk-ts'

const injectiveAddress = 'inj1...'
const ethereumAddress = '0x..'

console.log('Injective address from Ethereum address => ', getInjectiveAddress(ethereumAddress))
console.log('Ethereum address from Injective address => ', getEthereumAddress(injectiveAddress))

转换 Cosmos 地址 为 Injective 地址

由于 Injective 使用的派生路径不同于默认的 Cosmos 派生路径,因此你需要账户的 publicKey 才能将 Cosmos publicAddress 转换为 Injective 地址。

使用 TypeScript

import { config } from "dotenv";
import { ChainRestAuthApi, PublicKey } from "@injectivelabs/sdk-ts";

config();

(async () => {
  const chainApi = new ChainRestAuthApi(
    "https://rest.cosmos.directory/cosmoshub"
  );

  const cosmosAddress = "cosmos1..";
  const account = await chainApi.fetchCosmosAccount(cosmosAddress);

  if (!account.pub_key?.key) {
    console.log("No public key found");
    return;
  }

  console.log(
    "injectiveAddress",
    PublicKey.fromBase64(account.pub_key.key || "")
      .toAddress()
      .toBech32()
  );
})();

参考

Important references and links for the Injective Ecosystem

开发者资源

开发者工具和资源

开发者工具和资源,帮助你在 Injective 上进行开发。

资源

描述

Injective 资源的一站式商店。

命令行界面和节点daemon进程,连接到 Injective。

分析平台,允许任何人搜索 Injective 上的地址、交易、代币、交易记录和其他活动。

Injective 测试网和生态系统通过 Docker 容器化,并通过简单的 docker-compose 文件进行编排。

Swagger API 浏览器

使用 TypeScript 在 Injective 上构建 dApps

面向交易者的详细 API 文档,用于与 Injective 进行交互。

Injective 网络及其端点的实时状态。

Injective 测试网网络及其端点的实时状态。

一个围绕 Cosmos SDK 二进制文件的小型进程管理器,监控治理模块。

Cosmos SDK 文档,作为开发者与 Injective 生态系统集成的重要资源。

生态系统工具和资源

由生态系统开发者和合作伙伴开发的工具和资源。

资源

描述

多方计算(MPC)多签解决方案

UI 智能合约查询 / 执行

用于从 Injective 区块链提取数据的 Substreams。

自定义的高性能 RPC 节点

通过 Notifi 接收最新的链更新、生态系统发展、市场洞察等通知。

喂价预言机和市场数据

统一的开发环境,允许开发者启动一个完全模拟的迷你 Cosmos 生态系统并编写端到端测试用例。

开源数据索引器,提供自定义 API。

订阅或发布实时数据流。

跨链消息传递协议

DAO DAO 帮助你构建和运营 DAO,通过提供一个可视化界面,轻松与运行在区块链上的底层智能合约进行交互。DAO DAO 还提供多签解决方案。

代码库

开发和集成 Injective 的有用代码库

代码库

描述

与 2023 Injective 全球黑客松的 CosmWasm 101 演示匹配的指南。

允许交换 CW-20 代币与 Injective 链发行的原生代币(使用 TokenFactory 模块)及其反向操作的合约。

可以用来将 CosmWasm 与 Injective 集成的包。

有用的代码库,用于开始在 injective-ts 库上进行开发。

一系列 UI 包,用于简化在 Injective 上的开发。

开源的原子代币交换合约,展示了两个不同代币之间的即时交换。

用于构建 CosmWasm 智能合约测试环境的通用库。

开发者支持

在 Discord 或 Telegram 上找到开发者支持。

公共端点

关于公共端点列表, 查阅 公共端点

私有 / 专属 节点服务

关于私有节点服务列表, 查阅 高级端点

测试网提案

假设您想在测试网上提交一个提案。由于提案的投票时间较短,我们建议将提案的存款金额调低,以避免提案直接进入投票阶段。基本上,存款金额应略低于 min_deposit 值。

提交提案后,您应该联系团队:

这是 GrantProviderPrivilegeProposal 的示例:

injectived tx oracle grant-provider-privilege-proposal YOUR_PROVIDER \
  YOUR_ADDRESS_HERE \
  --title="TITLE OF THE PROPOSAL" \
  --description="Registering PROVIDER as an oracle provider" \
  --chain-id=injective-888 \
  --from=local_key \
  --node=https://testnet.sentry.tm.injective.network:443 \
  --gas-prices=160000000inj \
  --gas=20000000 \
  --deposit="40000000000000000000inj" <-- use this amount

计算

在本页面,我们将通过一些计算和格式化的示例,深入了解数字在链上是如何表示的,以及它们如何展示给用户。

刻度

最低价格刻度

最低市场订单价格刻度

最低市场订单价格刻度决定了订单价格可以增加或减少的最小增量。例如,如果市场的最低价格刻度为 0.001,则提交价格为 0.0011 的订单将被拒绝,因为它不符合允许的增量。

Note: 计算价格刻度的公式在现货市场和衍生品市场之间有所不同。

现货市场

从可读格式转换为链格式

以 INJ/USDT 市场为例,该市场具有 18 位基础资产小数 和 6 位计价资产小数,其转换为链格式的方式如下:

chainFormat=value×10(quoteDecimals−baseDecimals)\text{chainFormat} = \text{value} \times 10^{(\text{quoteDecimals} - \text{baseDecimals})} chainFormat=value×10(quoteDecimals−baseDecimals)

从链格式转换为可读格式

要转换回可读格式,请按照以下方式进行:

humanReadableFormat=value×10(baseDecimals−quoteDecimals)\text{humanReadableFormat} = \text{value} \times 10^{(\text{baseDecimals} - \text{quoteDecimals})} humanReadableFormat=value×10(baseDecimals−quoteDecimals)

衍生品市场

从可读格式转换为链格式

以INJ/USDT 永续合约市场(具有 6 位报价小数)为例,转换为链上格式的方式如下:

chainFormat=value×10−quoteDecimals \text{chainFormat} = \text{value} \times 10^{-\text{quoteDecimals}} chainFormat=value×10−quoteDecimals

从链格式转换为可读格式

要转换回可读格式,请按照以下方式进行:

humanReadableFormat=value×10−quoteDecimals \text{humanReadableFormat} = \text{value} \times 10^{-\text{quoteDecimals}} humanReadableFormat=value×10−quoteDecimals

Gas 和 Fees

Gas 和 Fees

了解 Gas 和 Fees 在Injective上的区别。

Gas代表执行特定操作所需的计算工作量。

Injective利用gas的概念来跟踪在执行过程中操作的资源使用情况。Injective上的操作表示对区块链存储的读写操作。

在消息执行过程中,会计算并向用户收取Fees。这个Fees是根据消息执行中消耗的所有gas的总和来计算的:

fee = gas * gas price

Gas用于确保操作完成时不会消耗过多的计算力,并且能够防止恶意用户对网络进行垃圾信息攻击。

最低 gas 价格:验证者设置的最低 gas 价格当前为 160,000,000 INJ。要计算支付的 inj 数量,可以将 gas 价格乘以 gas 数量,然后除以 1e18(INJ 有 18 位小数)。

例如:如果 gasWanted 为 104,519,则 gasFees = 160,000,000 * 104,519 / 1e18 = 0.000016723 inj

Cosmos SDK Gas

在 Cosmos SDK 中,gas 通过主 GasMeter 和 BlockGasMeter 进行追踪:

  • GasMeter:用于跟踪执行过程中消耗的 gas,这些执行会导致状态转移。它在每次交易执行时会被重置。

  • BlockGasMeter:用于跟踪一个区块中消耗的 gas,并确保消耗的 gas 不超过预定的限制。这个限制由 Tendermint 共识参数定义,并且可以通过治理参数变更提案进行修改。

在 Cosmos 中,有些操作并不是由交易触发的,但也可能导致状态转移。具体的例子包括 BeginBlock 和 EndBlock 操作,以及 AnteHandler 检查,这些操作可能在运行交易的状态转移之前,也会读取和写入存储。

BeginBlock 和 EndBlock

这些操作由 Tendermint Core 的应用区块链接口(ABCI)定义,并由每个 Cosmos SDK 模块定义。顾名思义,它们分别在每个区块处理的开始和结束时执行(即,在交易执行之前和之后)。

AnteHandler

Cosmos SDK 的 AnteHandler 在交易执行之前执行基本检查。这些检查通常包括签名验证、交易字段验证、交易费用等。

安装 injectived

平台兼容性指南

查看此表以了解支持injectived CLI运行的平台:

Platform
Pre-Built Binaries
Docker
From Source

macOS (M1/ARM)

❌

✅

✅

macOS (Intel)

❌

✅

✅

Windows (x86_64)

❌

✅

❌

Windows (ARM)

❌

✅

❌

Linux (x86_64)

✅

✅

✅

Linux (ARM)

❌

✅

✅

开始使用预购建的二进制文件

wget https://github.com/InjectiveLabs/injective-chain-releases/releases/download/v1.14.1-1740773301/linux-amd64.zip
unzip linux-amd64.zip

此 ZIP 文件将包含以下文件:

  • injectived - Injective daemon 兼 CLI

  • peggo - Injective 以太坊桥接中继 daemon

  • libwasmvm.x86_64.so - WASM 虚拟机支持文件

注意:部署和实例化智能合约时不需要 peggo,它是为验证者准备的。

sudo mv injectived /usr/bin
sudo mv libwasmvm.x86_64.so /usr/lib

确认您的版本与以下输出匹配(如果有更新版本,您的输出可能会略有不同):

injectived version

Version v1.14.1 (0fe59376dc)
Compiled at 20250302-2204 using Go go1.23.1 (amd64)

使用Docker

以下命令将启动一个包含 injectived CLI 的容器:

docker run -it --rm injectivelabs/injective-core:v1.14.1 injectived version

Version v1.14.1 (0fe59376d)
Compiled at 20250302-2220 using Go go1.22.11 (amd64)

使用源代码

以下命令将从源代码构建 injectived CLI:

git clone https://github.com/InjectiveFoundation/injective-core.git
cd injective-core && git checkout v1.14.1
make install

这将把 injectived CLI 安装到您的 Go 路径中。

injectived version

Version v1.14.1 (dd7622f)
Compiled at 20250302-2230 using Go go1.24.0 (amd64)

术语表

Injective 的口袋备忘单。使用这个术语表来了解与 Injective 相关的术语。

Active set

参与共识并获得奖励的验证者。

Airdrops

通过某些验证者向委托人分发的额外奖励,独立于质押奖励。空投通常由 Injective 生态系统中的应用程序发放,以增加可见性。

Arbitrage

一种用户寻求利用市场间价格差异的过程。套利者通常在一个市场购买资产,并在另一个市场以更高的价格出售它们。

Blockchain

一个不可更改的交易账本,复制在一组独立计算机系统的网络中。

Blocks

存储在区块链上的信息组。每个区块包含经验证并由验证者签名的交易。

Bonded validator

参与共识的活跃集中的验证者。绑定的验证者可以获得奖励。

Bonding

当用户将 INJ 委托或绑定到验证者,以获得质押奖励时的过程。即使被绑定,验证者也没有委托人 INJ 的所有权。委托、绑定和质押通常指代相同的过程。

Burn

资产的永久销毁。Injective 在每次燃烧拍卖后销毁 INJ。

Burn Auction

每周举办的一项活动,社区成员可以使用 INJ 竞标 Injective 收集到的 60% 的交易费用。获胜者竞标使用的 INJ 将被销毁。

Commission

验证者在将其余奖励分配给委托人之前所保留的质押奖励的百分比。验证者的收入完全依赖于这一佣金。验证者设置自己的佣金率。

Community pool

专门用于资助社区项目的基金。任何社区成员都可以提出治理提案,使用社区池中的代币。如果提案通过,资金将按照提案中指定的方式支出。

Consensus

Cosmos-SDK

CosmWasm

Injective 用来支持链上智能合约的库。更多信息,请查看 CosmWasm 文档。

dApp

去中心化应用。建立在去中心化平台上的应用

DDoS

分布式拒绝服务攻击。攻击者通过向网络发送大量流量或请求来扰乱服务。

DeFi

去中心化金融。从传统金融向无需金融中介的系统转变。

Delegate

当用户或委托人将他们的 INJ 添加到验证者的质押中以交换奖励时。委托的 INJ 会被绑定到验证者。验证者永远没有委托人 INJ 的所有权。委托、绑定和质押通常指代相同的过程。

Delegator

将 INJ 委托、绑定或质押到验证者以获得奖励的用户。委托、绑定和质押通常指代相同的过程。

Devnet

开发网络。一个独立于主网的网络,允许用户测试新功能或产品,而不会干扰主要网络。

Frequent Batch Auction (FBA)

Injective 用于链上订单匹配的模型。与大多数中心化交易所提供的加密衍生品和传统金融市场使用的连续双向拍卖(CDA)模型相比,该模型更高效地利用资本。FBA 模型还消除了前置交易的可能性。

Full node

连接到 Injective 主网并能够验证交易和与 Injective 交互的计算机。所有活跃的验证者都运行完整节点

Gas Fees

附加到所有交易上的计算费用,用于防止垃圾交易攻击。验证者设置最低的 gas 费用,并拒绝低于该阈值的交易。

Governance

治理是允许用户和验证者对 Injective 进行更改的民主过程。社区成员提交、投票并实施提案。每一单位质押的 INJ 等于一票。

Governance proposal

对 Injective 协议的更改或新增的书面提交。提案的主题可以从社区池支出、软件更改、参数更改或任何与 Injective 相关的更改。

IBC

跨链通信技术(Inter-Blockchain Communication)。使不同区块链之间能够相互交互。IBC 允许在不同区块链之间进行资产交易和转账。

INJ

Injective 的原生代币。

injectived

与 Injective 节点交互的命令行接口。 有关更多信息,请查看 injectived 指南。

Injective core

Injective 的官方源代码。 有关更多信息,请参阅 Injective 核心模块。

Injective Hub

Injective 的平台,用于钱包、治理、质押和 INJ 销毁拍卖。 要了解 Injective Hub 的功能,请访问 Injective Hub 指南。

Inactive set

不在活跃集合中的验证者。这些验证者不参与共识,也不赚取奖励。

Jailed

表现不当的验证者会被监禁或在一段时间内被排除在活跃集合之外。

Maximum Extractable Value (MEV)

通过包含、排除和改变区块中交易顺序,从区块生产中提取的最大价值,超过了标准的区块奖励和 gas 费用。

Injective 是抗 MEV 的。

Module

Injective 核心中的一个模块,表示 Injective 的特定功能。 访问 Injective 核心模块规范了解更多信息。

Oracle

一个使 Injective 能够访问外部实时数据的第三方服务。通常,这是喂价。

Pools

代币的集合。供应池表示市场中的代币总供应量。

Proof of Stake

区块链使用的一种验证方法,根据验证者持有的代币数量来选择提议区块的验证者。

Quorum

使选举有效所需的最少投票数。必须有 33% 的所有质押 INJ 投票才能达到法定人数。如果投票期结束前未达到法定人数,提案将失败,且提案人的押金将被销毁。

Redelegate

当委托人希望将他们的绑定 INJ 转移到另一个验证者时。重新委托 INJ 是即时的,不需要 21 天的解锁期。

Rewards

从费用中产生的收入,给予验证者并分发给委托者。

Self-delegation

验证者将 INJ 绑定到自己身上的数量。也称为自我绑定。

Slashing

对表现不当的验证者的惩罚。验证者在被削减时会失去一部分质押的代币。

Slippage

在交易开始和结束时资产价格之间的差异。

Stake

绑定到验证者的 INJ 数量。

Staking

当用户将他们的 INJ 委托或绑定到活跃的验证者以获得奖励时。绑定的 INJ 会增加验证者的质押量。验证者将其质押作为参与共识过程的抵押。质押量更大的验证者被选中参与的频率更高。验证者因参与而获得质押奖励。如果验证者表现不当,质押量可能会被削减。验证者永远不会拥有委托者的 INJ,即使在质押过程中。

有关质押的更多信息,请访问质押页面。

Tendermint consensus

Mainnet

Injective 的区块链网络,所有交易都在此进行。

Testnet

主网的一个测试版本。测试网不使用真实资产。您可以使用测试网熟悉交易和整体网络。

Total stake

委托人绑定的 INJ 总量,包括自我绑定的 INJ。

Unbonded validator

不在活跃集合中的验证者。这些验证者不参与共识,也不赚取奖励。一些未绑定的验证者可能会被监禁。

Unbonding validator

从活跃集合过渡到非活跃集合的验证者。未绑定的验证者不参与共识,也不赚取奖励。解锁过程需要 21 天。

Unbonded INJ

可以自由交易且未绑定到任何验证者的 INJ。

Unbonding

当委托人决定从验证者那里解除委托其 INJ 时。此过程需要 21 天。在此期间不会获得任何奖励。一旦执行,无法停止此操作。

Unbonding INJ

正在从绑定状态过渡到未绑定状态的 INJ。未绑定的 INJ 不能自由交易。解除绑定的过程需要 21 天。在此期间不会获得任何奖励。一旦执行,无法停止此操作。

Undelegate

当委托人不再希望将其 INJ 绑定到某个验证者时。此过程需要 21 天。在此期间不会获得任何奖励。一旦执行,无法停止此操作。

Uptime

验证者在给定时间段内的活跃时间。活跃时间较低的验证者可能会被削减

Validator

负责验证区块链上的交易的 Injective 区块链矿工。验证者运行称为全节点的程序,使他们能够参与共识、验证区块、参与治理并获得奖励。只有活跃集合中的验证者才能参与共识。

Weight

验证者总质押量的度量。质押量较大的验证者更常被选中提议区块。验证者的权重也是其在治理中投票权重的度量。

预言机提供方

什么是预言机提供方?预言机提供方是一种预言机类型,允许外部参与者向 Injective 链传输价格数据。这些外部参与者被称为提供方(Providers)。提供方用于标识每个外部参与者,并且所有提供的价格数据都存储在该特定提供方名下。这一机制使 Injective 能够创建自定义价格数据,从而支持 Injective 上创新且高级的市场。

开发者需要首先在 Oracle Provider 类型下注册自己的提供方。这可以通过提交 GrantProviderPrivilegeProposal 治理提案来完成。一旦提案通过,提供方即被注册,随后可以向链上传输价格数据。可以在 CLI 环境中使用 injectived 命令行工具执行 (grant-provider-privilege-proposal [providerName] [relayers] --title [title] --description [desc] [flags]) 或者,也可以使用 Injective 提供的 SDK 之一来构造消息并将其广播至链上。

您可以在 Oracle 模块提案 部分查看提交此提案的示例。

Note: GrantProviderPrivilegeProposal 的 relayers 指定了被列入白名单的地址,这些地址将被授权向 Injective 提交价格数据。

一旦提案通过,relayers 便可使用 MsgRelayProviderPrices 在其 Oracle Provider Type 的 provider namespace 内,为指定的 base/quote 交易对提交价格数据。

可以通过 CLI 环境使用 injectived 进行操作:

也可以使用 Injective 提供的 SDK 来构造消息并广播至链上。

最终,这些价格数据可用于创建 衍生品市场(Derivative Markets)。

交易所开发者

Injective 处于专注于交易的第一层区块链前沿,提供去中心化永续合约、期货和现货交易。它充分挖掘了去中心化衍生品和无国界 DeFi 的潜力。每个组件都经过精心设计,确保完全去信任化、抗审查、公正可验证,并能抵御抢先交易。

在 Injective 上构建订单簿 DEX

Injective 的激励机制旨在促进交易平台之间的竞争,以提供更优质的用户体验,更好地服务用户,从而扩大全球范围内用户对 DeFi 的可访问性。

交易平台可以轻松搭建客户端(例如 Web 或移动端 UI)并设置 API 提供服务。

Cosmwasm 开发者

最低数量刻度

最低市场订单数量刻度

最小市场数量刻度(tick size)决定了订单数量可增加或减少的最小增量。例如,如果某个市场的最小数量刻度为 0.001,则提交数量为 0.0011 的订单将被拒绝,因为该数量未对齐到允许的增量。

Note: 衍生品市场的最小数量刻度(minQuantityTickSize)在用户界面和链上使用相同的格式,因此不需要进行格式转换。

现货市场

从可读格式转换为链格式

以 INJ/USDT 市场为例,该市场具有 18 位基础资产小数 和 6 位计价资产小数,其转换为链格式的方式如下:

从链格式转换为可读格式

要转换回可读格式,请按照以下方式进行:

请参阅 获取更多信息。如果您已成功安装 injectived,您应该能够运行以下命令:

仅适用于高级用户。对于大多数用户,建议连接到公共节点。

更多示例可以在 中找到。

加入 并寻找相关的频道

加入 .

加入 并寻找相关的频道

加入 .

主题
描述

最低市场订单价格刻度

最低市场订单数量刻度

此外,请务必查看我们的 。

前置阅读->

有关 Cosmos SDK 中 gas 的更多信息,可以在找到。

目前,唯一支持运行预构建 injectived CLI 的平台是 Linux x86_64。预构建的二进制文件可在 . 页面上获取。

继续阅读 ,了解如何使用 injectived CLI 与 Injective 区块链交互。

这兼容大多数平台及 arm64 / x86_64 架构。 继续前往 ,了解如何使用 injectived CLI 与 Injective 区块链交互。

(提交哈希可能会有所不同,因为开源存储库是与预构建版本分开发布的)。 继续阅读 ,了解如何使用 injectived CLI 与 Injective 区块链交互。

一种由验证者或矿工使用的系统,用于确认区块链中的每个交易区块是正确的。Injective 使用 Tendermint 共识框架。验证者通过参与共识获得奖励。更多信息,请访问 网站。

Injective 区块链构建的开源框架。更多信息,请查看 文档。

Injective 使用的共识框架。首先,验证者提议一个新区块。其他验证者对该区块进行两轮投票。如果区块在两轮投票中都获得超过三分之二的支持票,它将被添加到区块链中。验证者通过区块的交易费用获得奖励。提议者获得额外奖励。每个验证者的提议是根据他们的权重来选择的。有关更多信息,请查看 。

前置阅读

本节旨在为用户提供指南,帮助他们在 Injective 上启动和维护预言机提供方(Oracle Provider)。这些预言机可用于多种用途,例如永续合约市场、到期期货市场、等。

Injective 使交易者能够创建并交易任意的现货和衍生品市场。此外,Injective 还通过 Injective 链的 实现链上限价订单簿管理、链上交易执行、链上订单匹配、链上交易结算以及链上交易激励分配。

Injective 采用激励机制鼓励交易平台在 Injective 上构建并引导交易活动。向 Injective 交易协议的共享订单簿提交订单的交易平台(了解更多)将获得其引导订单的交易费用的一部分( )作为奖励。交易协议实施了全球最低交易费用标准,其中 Maker 费用为 ,Taker 费用为 。

前往我们的 TypeScript 文档 ,只需几步即可!

CosmWasm 是专为 Cosmos 生态系统 设计的新型智能合约平台。 你可以在 了解更多信息,或者参考 获取创建 CosmWasm 智能合约 的指南。

在这里 开始使用 CosmWasm 在 Injective 上构建你的项目。

此外,请务必查看我们的 。

安装 injectived
运行自己的节点
TypeScript 文档
Injective Discord server
Injective Developer Telegram channel
Injective Discord server
Injective Developer Telegram channel
TypeScript 文档
Cosmos SDK Gas
此处
Injective GitHub Releases page
使用 injectived
使用 injectived
使用 injectived
Tendermint 官方文档
Cosmos SDK
Tendermint 官方文档
injectived relay-provider-prices [providerName] [symbol:prices] [flags]
β=40β=40%β=40%β=40
rm=0.1rm=0.1%rm​=0.1%rm=0.1
rt=0.2rt=0.2%rt​=0.2%rt=0.2
chainFormat=value×10baseDecimals \text{chainFormat} = \text{value} \times 10^{\text{baseDecimals}} chainFormat=value×10baseDecimals
humanReadableFormat=value×10−baseDecimals \text{humanReadableFormat} = \text{value} \times 10^{-\text{baseDecimals}} humanReadableFormat=value×10−baseDecimals
您的首个智能合约
Injective 101
Injectived
Injective Explorer
Injective Local
Injective REST API
Injective TypeScript SDK
Injective API Reference
Real-time Status (Mainnet)
Real-time Status (Testnet)
Cosmovisor
CosmosSDK
Desig
DiscoverINJ
The Graph
NewMetric
Notifi
Pyth
Starship
SubQuery
Synternet
Wormhole
DAO DAO
CosmWasm101
cw20-adapter
cw-injective
injective-ts-examples
injective-ui
swap-contract
test-tube
Market min price tick size
Market min quantity tick size
Injective Oracle 模块
二元期权市场
Exchange 模块
快速启动您的 DEX
CosmWasm 官方文档
CosmWasm Book
TypeScript 文档

指南

本节包含一些简单的开发者指南,介绍如何使用 CosmWasm 在 Injective 上进行开发。

Injective

Injective Modules

CW20 适配器

背景

CW-20 是 CosmWasm 中的一种可替代代币规范, loosely 基于 ERC-20 规范。它允许在 CosmWasm 中创建和处理任意的可替代代币,指定用于创建、铸造、销毁和在账户之间转移这些代币的方法。适配器合约将确保只有授权的源 CW-20 合约可以铸造代币(以避免创建“伪造”代币)。

虽然 CW-20 标准相对成熟且完整,但这些代币仅存在于 CosmWasm 上下文中,并完全由发行合约管理(包括跟踪账户余额)。这意味着它们无法直接与 Injective 的原生模块交互(例如,无法通过 Injective 交易模块进行交易,或者在不涉及发行合约的情况下进行转移)。

考虑到上述情况,需要提供一个解决方案,作为 CW-20 和 Injective 银行模块之间的桥梁。

合约的工作流程如下:

  1. 注册一个新的 CW-20 代币。

  2. 将 X 数量的 CW-20 代币兑换为 Y 数量的 TokenFactory 代币(原始 CW-20 代币将由合约持有)。

  3. 将 Y 数量的 TF 代币兑换回 X 数量的 CW-20 代币(CW-20 代币被释放,TokenFactory 代币被销毁)。

Messages

RegisterCw20Contract { addr: Addr }

注册一个新的 CW-20 合约(地址),该合约将由适配器处理,并创建一个新的 TokenFactory 代币,格式为 factory/{adapter_contract}/{cw20_contract}。

ExecuteMsg::RegisterCw20Contract { addr } => execute_register::handle_register_msg(deps, env, info, addr)

pub fn handle_register_msg(
    deps: DepsMut<InjectiveQueryWrapper>,
    env: Env,
    info: MessageInfo,
    addr: Addr,
) -> Result<Response<InjectiveMsgWrapper>, ContractError> {
    if is_contract_registered(&deps, &addr) {
        return Err(ContractError::ContractAlreadyRegistered);
    }
    let required_funds = query_denom_creation_fee(&deps.querier)?;
    if info.funds.len() > required_funds.len() {
        return Err(ContractError::SuperfluousFundsProvided);
    }

    let mut provided_funds = info.funds.iter();

    for required_coin in &required_funds {
        let pf = provided_funds
           .find(|c| -> bool { c.denom == required_coin.denom })
           .ok_or(ContractError::NotEnoughBalanceToPayDenomCreationFee)?;

        match pf.amount.cmp(&required_coin.amount) {
            Ordering::Greater => return Err(ContractError::SuperfluousFundsProvided),
            Ordering::Less => return Err(ContractError::NotEnoughBalanceToPayDenomCreationFee),
            Ordering::Equal => {}
        }
    }

    let create_denom_msg = register_contract_and_get_message(deps, &env, &addr)?;
    Ok(Response::new().add_message(create_denom_msg))
}

Receive { sender: String, amount: Uint128, msg: Binary }

CW-20 接收者接口的实现。

必须仅由 CW-20 合约调用。

ExecuteMsg::Receive { sender, amount, msg: _ } => execute_receive::handle_on_received_cw20_funds_msg(deps, env, info, sender, amount)

pub fn handle_on_received_cw20_funds_msg(
    deps: DepsMut<InjectiveQueryWrapper>,
    env: Env,
    info: MessageInfo,
    recipient: String,
    amount: Uint128,
) -> Result<Response<InjectiveMsgWrapper>, ContractError> {
    if!info.funds.is_empty() {
        return Err(ContractError::SuperfluousFundsProvided);
    }
    let mut response = Response::new();
    let token_contract = info.sender;
    if!is_contract_registered(&deps, &token_contract) {
        ensure_sufficient_create_denom_balance(&deps, &env)?;
        response = response.add_message(register_contract_and_get_message(deps, &env, &token_contract)?);
    }
    let master = env.contract.address;

    let denom = get_denom(&master, &token_contract);
    let coins_to_mint = Coin::new(amount.u128(), denom);
    let mint_tf_tokens_message = create_mint_tokens_msg(master, coins_to_mint, recipient);

    Ok(response.add_message(mint_tf_tokens_message))
}

RedeemAndTransfer { recipient: Option<String> }

赎回附加的 TokenFactory 代币并将 CW-20 代币转移给接收者。如果未提供接收者,则代币将发送给消息发送者。

RedeemAndSend { recipient: String, submessage: Binary }

赎回附加的 TokenFactory 代币并将 CW-20 代币发送到接收者合约。调用者可以提供可选的子消息。

ExecuteMsg::RedeemAndTransfer { recipient } => execute_redeem::handle_redeem_msg(deps, env, info, recipient, None)

ExecuteMsg::RedeemAndSend { recipient, submsg } => execute_redeem::handle_redeem_msg(deps, env, info, Some(recipient), Some(submsg))

pub fn handle_redeem_msg(
    deps: DepsMut<InjectiveQueryWrapper>,
    env: Env,
    info: MessageInfo,
    recipient: Option<String>,
    submessage: Option<Binary>,
) -> Result<Response<InjectiveMsgWrapper>, ContractError> {
    let recipient = recipient.unwrap_or_else(|| info.sender.to_string());

    if info.funds.len() > 1 {
        return Err(ContractError::SuperfluousFundsProvided);
    }
    let tokens_to_exchange = info
       .funds
       .iter()
       .find_map(|c| -> Option<AdapterCoin> {
            match AdapterDenom::new(&c.denom) {
                Ok(denom) => Some(AdapterCoin { amount: c.amount, denom }),
                Err(_) => None,
            }
        })
       .ok_or(ContractError::NoRegisteredTokensProvided)?;

    let cw20_addr = tokens_to_exchange.denom.cw20_addr.clone();
    let is_contract_registered = CW20_CONTRACTS.contains(deps.storage, &tokens_to_exchange.denom.cw20_addr);
    if!is_contract_registered {
        return Err(ContractError::NoRegisteredTokensProvided);
    }

    let burn_tf_tokens_message = create_burn_tokens_msg(env.contract.address, tokens_to_exchange.as_coin());

    let cw20_message: WasmMsg = match submessage {
        None => WasmMsg::Execute {
            contract_addr: cw20_addr,
            msg: to_binary(&Cw20ExecuteMsg::Transfer {
                recipient,
                amount: tokens_to_exchange.amount,
            })?,
            funds: vec![],
        },
        Some(msg) => WasmMsg::Execute {
            contract_addr: cw20_addr,
            msg: to_binary(&Cw20ExecuteMsg::Send {
                contract: recipient,
                amount: tokens_to_exchange.amount,
                msg,
            })?,
            funds: vec![],
        },
    };
    Ok(Response::new().add_message(cw20_message).add_message(burn_tf_tokens_message))
}

UpdateMetadata { addr : Addr}

将查询 CW-20 地址(如果已注册)的元数据,并使用 TokenFactory 访问方法调用银行模块中的 setMetadata。

ExecuteMsg::UpdateMetadata { addr } => execute_metadata::handle_update_metadata(deps, env, addr)

pub fn handle_update_metadata(
    deps: DepsMut<InjectiveQueryWrapper>,
    env: Env,
    cw20_addr: Addr,
) -> Result<Response<InjectiveMsgWrapper>, ContractError> {
    let is_contract_registered = CW20_CONTRACTS.contains(deps.storage, cw20_addr.as_str());
    if!is_contract_registered {
        return Err(ContractError::ContractNotRegistered);
    }
    let token_metadata = fetch_cw20_metadata(&deps, cw20_addr.as_str())?;

    let denom = get_denom(&env.contract.address, &cw20_addr);
    let set_metadata_message = create_set_token_metadata_msg(denom, token_metadata.name, token_metadata.symbol, token_metadata.decimals);

    Ok(Response::new().add_message(set_metadata_message))
}

Queries
RegisteredContracts {}
Return a list of registered CW-20 contracts.

QueryMsg::RegisteredContracts {} => to_binary(&query::registered_contracts(deps)?)

pub fn registered_contracts(deps: Deps<InjectiveQueryWrapper>) -> StdResult<Vec<Addr>> {}

NewDenomFee {}
Returns a fee required to register a new tokenFactory denom.

QueryMsg::NewDenomFee {} => to_binary(&query::new_denom_fee(deps)?)

pub fn new_denom_fee(deps: Deps<InjectiveQueryWrapper>) -> StdResult<Uint128> {}

创建 UIs

我们通过 Injective CLI 与合约进行了交互,但对于大多数 dApp 用户来说,这并不是理想的方式。一个 Web UI 可以提供更好的体验!我们可以抽象掉复杂性,提供两个按钮—一个用于增加计数,一个用于重置计数,而不是通过 injectived 发送交易消息。

主网开发

本指南将帮助您了解在 Injective 主网(Mainnet)上部署和实例化 CosmWasm 智能合约的治理流程。

提交代码上传提案

在本节中,您将学习如何提交智能合约代码提案并对其进行投票。

Injective 网络的参与者可以提议智能合约部署,并在治理中投票使其生效。wasmd 授权设置由链上治理决定,这意味着合约的部署完全由治理决定。因此,治理提案是将合约上传到 Injective 主网的第一步。

以下是使用 injectived 启动治理提案将代码上传到链上的示例用法:

injectived tx wasm submit-proposal wasm-store artifacts/cw_controller.wasm
--title="Proposal Title" \
--summary="Proposal Summary" \
--instantiate-everybody true \
--broadcast-mode=sync \
--chain-id=injective-1 \
--node=https://sentry.tm.injective.network:443 \
--deposit=100000000000000000000inj \
--gas=20000000 \
--gas-prices=160000000inj \
--from [YOUR_KEY] \
--yes \
--output json

命令 injectived tx gov submit-proposal wasm-store 提交一个 wasm 二进制提案。如果提案获得治理批准,代码将被部署。

让我们了解两个关键的标志:instantiate-everybody 和 instantiate-only-address,这两个标志设置上传代码的实例化权限。默认情况下,任何人都可以实例化合约。

--instantiate-everybody boolean # Everybody can instantiate a contract from the code, optional
--instantiate-only-address string # Only this address can instantiate a contract instance from the code

合约实例化(无治理)

在大多数情况下,你不需要提交另一个治理提案来进行实例化。只需使用 injectived tx wasm instantiate 进行实例化即可。你只需要通过治理提案来上传合约。如果合约设置了 --instantiate-everybody 标志为 false,并且 --instantiate-only-address 标志设置为治理模块,才需要通过治理来实例化。--instantiate-everybody 的默认值为 true,在这种情况下,你可以通过 injectived tx wasm instantiate 无需许可地进行实例化。

injectived tx wasm instantiate [code_id_int64] [json_encoded_init_args] --label [text] --admin [address,optional] --amount [coins,optional]  [flags]
Flags:
  -a, --account-number uint      The account number of the signing account (offline mode only)
      --admin string             Address or key name of an admin
      --amount string            Coins to send to the contract during instantiation
      --aux                      Generate aux signer data instead of sending a tx
  -b, --broadcast-mode string    Transaction broadcasting mode (sync|async) (default "sync")
      --chain-id string          The network chain ID
      --dry-run                  ignore the --gas flag and perform a simulation of a transaction, but don't broadcast it (when enabled, the local Keybase is not accessible)
      --fee-granter string       Fee granter grants fees for the transaction
      --fee-payer string         Fee payer pays fees for the transaction instead of deducting from the signer
      --fees string              Fees to pay along with transaction; eg: 10uatom
      --from string              Name or address of private key with which to sign
      --gas string               gas limit to set per-transaction; set to "auto" to calculate sufficient gas automatically. Note: "auto" option doesn't always report accurate results. Set a valid coin value to adjust the result. Can be used instead of "fees". (default 200000)
      --gas-adjustment float     adjustment factor to be multiplied against the estimate returned by the tx simulation; if the gas limit is set manually this flag is ignored  (default 1)
      --gas-prices string        Gas prices in decimal format to determine the transaction fee (e.g. 0.1uatom)
      --generate-only            Build an unsigned transaction and write it to STDOUT (when enabled, the local Keybase only accessed when providing a key name)
  -h, --help                     help for instantiate
      --keyring-backend string   Select keyring's backend (os|file|kwallet|pass|test|memory) (default "os")
      --keyring-dir string       The client Keyring directory; if omitted, the default 'home' directory will be used
      --label string             A human-readable name for this contract in lists
      --ledger                   Use a connected Ledger device
      --no-admin                 You must set this explicitly if you don't want an admin
      --node string              <host>:<port> to tendermint rpc interface for this chain (default "tcp://localhost:26657")
      --note string              Note to add a description to the transaction (previously --memo)
      --offline                  Offline mode (does not allow any online functionality)
  -o, --output string            Output format (text|json) (default "json")
  -s, --sequence uint            The sequence number of the signing account (offline mode only)
      --sign-mode string         Choose sign mode (direct|amino-json|direct-aux), this is an advanced feature
      --timeout-height uint      Set a block timeout height to prevent the tx from being committed past a certain height
      --tip string               Tip is the amount that is going to be transferred to the fee payer on the target chain. This flag is only valid when used with --aux, and is ignored if the target chain didn't enable the TipDecorator
  -y, --yes                      Skip tx broadcasting prompt confirmation

一个 injectived tx wasm instantiate 的示例如下所示:

injectived tx wasm instantiate \
150 \
'{"bank": "inj1egl894wme0d4d029hlv3kuqs0mc9atep2s89h8"}' \
--label="LABEL" \
--from=inj17vytdwqczqz72j65saukplrktd4gyfme5agf6c \
--chain-id=injective-1 \
--yes \
--gas-prices 160000000inj \
--gas=10000000 \
--no-admin \
--node=https://sentry.tm.injective.network:443 \

合约实例化 (有治理)

如上所述,主网合约实例化权限取决于上传代码时使用的标志。默认情况下,设置为无需许可,正如我们在 Genesis wasmd Injective 设置中可以验证的那样:

"wasm": {
            "codes": [],
            "contracts": [],
            "gen_msgs": [],
            "params": {
                "code_upload_access": {
                    "address": "",
                    "permission": "Everybody"
                },
                "instantiate_default_permission": "Everybody"
            },
            "sequences": []
        }

然而,如果 --instantiate-everybody 标志设置为 false,那么合约实例化必须通过治理进行。

Injective 测试网默认是无需许可的,以便允许开发者轻松部署合约。

合约实例化提案

 injectived tx gov submit-proposal instantiate-contract [code_id_int64] [json_encoded_init_args] --label [text] --title [text] --description [text] --run-as [address] --admin [address,optional] --amount [coins,optional] [flags]
Flags:
  -a, --account-number uint      The account number of the signing account (offline mode only)
      --admin string             Address of an admin
      --amount string            Coins to send to the contract during instantiation
  -b, --broadcast-mode string    Transaction broadcasting mode (sync|async|block) (default "sync")
      --deposit string           Deposit of proposal
      --description string       Description of proposal
      --dry-run                  ignore the --gas flag and perform a simulation of a transaction, but dont broadcast it (when enabled, the local Keybase is not accessible)
      --fee-account string       Fee account pays fees for the transaction instead of deducting from the signer
      --fees string              Fees to pay along with transaction; eg: 10uatom
      --from string              Name or address of private key with which to sign
      --gas string               gas limit to set per-transaction; set to "auto" to calculate sufficient gas automatically (default 200000)
      --gas-adjustment float     adjustment factor to be multiplied against the estimate returned by the tx simulation; if the gas limit is set manually this flag is ignored  (default 1)
      --gas-prices string        Gas prices in decimal format to determine the transaction fee (e.g. 0.1uatom)
      --generate-only            Build an unsigned transaction and write it to STDOUT (when enabled, the local Keybase is not accessible)
  -h, --help                     help for instantiate-contract
      --keyring-backend string   Select keyrings backend (os|file|kwallet|pass|test|memory) (default "os")
      --keyring-dir string       The client Keyring directory; if omitted, the default 'home' directory will be used
      --label string             A human-readable name for this contract in lists
      --ledger                   Use a connected Ledger device
      --no-admin                 You must set this explicitly if you dont want an admin
      --node string              <host>:<port> to tendermint rpc interface for this chain (default "tcp://localhost:26657")
      --note string              Note to add a description to the transaction (previously --memo)
      --offline                  Offline mode (does not allow any online functionality
  -o, --output string            Output format (text|json) (default "json")
      --proposal string          Proposal file path (if this path is given, other proposal flags are ignored)
      --run-as string            The address that pays the init funds. It is the creator of the contract and passed to the contract as sender on proposal execution
  -s, --sequence uint            The sequence number of the signing account (offline mode only)
      --sign-mode string         Choose sign mode (direct|amino-json), this is an advanced feature
      --timeout-height uint      Set a block timeout height to prevent the tx from being committed past a certain height
      --title string             Title of proposal
      --type string              Permission of proposal, types: store-code/instantiate/migrate/update-admin/clear-admin/text/parameter_change/software_upgrade
  -y, --yes                      Skip tx broadcasting prompt confirmation

合约迁移

您的首个智能合约

在本节中,我们将说明如何设置环境以进行 CosmWasm 智能合约开发。

前置条件

此外,还需要安装 wasm32-unknown-unknown 目标以及 cargo-generate Rust 依赖包。

可以使用以下命令检查版本:

rustc --version
cargo --version
rustup target list --installed
# if wasm32 is not listed above, run this
rustup target add wasm32-unknown-unknown
# to install cargo-generate, run this
cargo install cargo-generate

目标

  • 创建并交互一个智能合约,该合约可以增加计数器的值,并将其重置为指定值。

  • 理解 CosmWasm 智能合约的基础知识,学习如何在 Injective 上部署合约,并使用 Injective 工具与其交互。

CosmWasm 合约基础知识

作为智能合约的开发者,你的任务是定义三个组成合约接口的函数:

  • instantiate():构造函数,在合约实例化时调用,用于提供初始状态。

  • execute():当用户希望调用智能合约上的方法时执行。

  • query():当用户希望从智能合约中获取数据时执行。

从模版开始

在你的工作目录中,通过运行以下命令,快速启动智能合约,并使用推荐的文件夹结构和构建选项:

cargo generate --git https://github.com/CosmWasm/cw-template.git --branch 1.0 --name my-first-contract
cd my-first-contract

这有助于你快速开始,通过提供智能合约的基本模板和结构。在 src/contract.rs 文件中,你会发现标准的 CosmWasm 入口函数 instantiate()、execute() 和 query() 已正确暴露并连接。

State

State 处理存储和访问智能合约数据的数据库状态。

起始模板具有以下基本状态,一个单例结构体 State,包含:

  • count,一个 32 位整数,execute() 消息将通过增加或重置该值进行交互。

  • owner,MsgInstantiateContract 的发送者地址,决定是否允许某些执行消息。

// src/state.rs
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

use cosmwasm_std::Addr;
use cw_storage_plus::Item;

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct State {
    pub count: i32,
    pub owner: Addr,
}

pub const STATE: Item<State> = Item::new("state");

Injective 智能合约能够通过 Injective 的原生 LevelDB(一个基于字节的键值存储)保持持久化状态。因此,任何你希望持久化的数据都应该分配一个唯一的键,用于索引和检索数据。

数据只能以原始字节形式持久化,因此任何结构或数据类型的概念必须通过一对序列化和反序列化函数来表达。例如,对象必须以字节形式存储,因此你需要提供一个将对象编码为字节以便保存到区块链上的函数,以及一个将字节解码回合约逻辑能够理解的数据类型的函数。字节表示的选择由你决定,只要它提供一个干净的双向映射。

请注意,State 结构体包含了 count 和 owner。此外,derive 属性被应用于自动实现一些有用的特性:

  • Serialize:提供序列化

  • Deserialize:提供反序列化

  • Clone:使结构体可复制

  • Debug:使结构体可以打印为字符串

  • PartialEq:提供相等性比较

  • JsonSchema:自动生成 JSON 架构

Addr 指的是一个可读的 Injective 地址,以 inj 为前缀,例如 inj1clw20s2uxeyxtam6f7m84vgae92s9eh7vygagt。

InstantiateMsg

你可以在 CosmWasm 的文档中了解更多关于 InstantiateMsg 的信息。

InstantiateMsg 是在用户通过 MsgInstantiateContract 在区块链上实例化合约时提供给合约的。它为合约提供了配置以及初始状态。

在 Injective 区块链上,合约代码的上传和合约的实例化被视为两个独立的事件,这与以太坊不同。这样做是为了允许一小部分经过审查的合约原型作为多个实例存在,这些实例共享相同的基础代码,但可以用不同的参数进行配置(想象一个标准的 ERC20 合约和多个使用其代码的代币)。

示例

对于你的合约,合约创建者需要在 JSON 消息中提供初始状态。我们可以在下面的消息定义中看到,消息包含一个参数 count,表示初始计数值。

{
  "count": 100
}

Message 定义

// src/msg.rs

use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct InstantiateMsg {
    pub count: i32,
}

合约逻辑

在 contract.rs 文件中,你将定义第一个入口函数 instantiate(),该函数用于实例化合约并接收 InstantiateMsg。

从消息中提取 count 并设置初始状态,其中:

  • count 赋值为消息中的 count。

  • owner 赋值为 MsgInstantiateContract 的发送者。

// src/contract.rs
#[cfg_attr(not(feature = "library"), entry_point)]
pub fn instantiate(
    deps: DepsMut,
    _env: Env,
    info: MessageInfo,
    msg: InstantiateMsg,
) -> Result<Response, ContractError> {
    let state = State {
        count: msg.count,
        owner: info.sender.clone(),
    };
    set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?;
    STATE.save(deps.storage, &state)?;

    Ok(Response::new()
        .add_attribute("method", "instantiate")
        .add_attribute("owner", info.sender)
        .add_attribute("count", msg.count.to_string()))
}

ExecuteMsg

  • Increment 没有输入参数,将 count 值增加 1。

  • Reset 接受一个 32 位整数作为参数,并将 count 值重置为该输入参数。

示例

Increment

任何用户都可以将当前 count 值增加 1。

{
  "increment": {}
}

Reset

只有 owner 可以将 count 重置为指定的数值。实现详情请参考以下逻辑。

{
  "reset": {
    "count": 5
  }
}

Message 定义

对于 ExecuteMsg,可以使用 enum 来对合约能够识别的不同类型的消息进行多路复用。

serde 属性会将枚举的键转换为蛇形(snake case)和小写(lower case),因此在 JSON 序列化和反序列化时,Increment 和 Reset 会被转换为 increment 和 reset。

// src/msg.rs

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum ExecuteMsg {
    Increment {},
    Reset { count: i32 },
}

逻辑

// src/contract.rs

#[cfg_attr(not(feature = "library"), entry_point)]
pub fn execute(
    deps: DepsMut,
    _env: Env,
    info: MessageInfo,
    msg: ExecuteMsg,
) -> Result<Response, ContractError> {
    match msg {
        ExecuteMsg::Increment {} => try_increment(deps),
        ExecuteMsg::Reset { count } => try_reset(deps, info, count),
    }
}

这是你的 execute() 方法,它使用 Rust 的模式匹配(pattern matching)来将接收到的 ExecuteMsg 路由到相应的处理逻辑。

根据接收到的消息,它会调用 try_increment() 或 try_reset() 方法进行处理。

pub fn try_increment(deps: DepsMut) -> Result<Response, ContractError> {
    STATE.update(deps.storage, |mut state| -> Result<_, ContractError> {
        state.count += 1;
        Ok(state)
    })?;

    Ok(Response::new().add_attribute("method", "try_increment"))
}

首先,它获取对存储的可变引用,以更新存储在键 state 位置的项。然后,它通过返回 Ok 结果并包含新的 state 来更新状态中的 count。最后,它返回 Ok 结果并附带 Response,以确认合约执行成功。

// src/contract.rs

pub fn try_reset(deps: DepsMut, info: MessageInfo, count: i32) -> Result<Response, ContractError> {
    STATE.update(deps.storage, |mut state| -> Result<_, ContractError> {
        if info.sender != state.owner {
            return Err(ContractError::Unauthorized {});
        }
        state.count = count;
        Ok(state)
    })?;
    Ok(Response::new().add_attribute("method", "reset"))
}

reset 的逻辑与 increment 类似,但不同之处在于:它首先检查消息发送者是否被允许调用 reset 方法(在本例中,必须是合约 owner)。

QueryMsg

实现详情请参考以下逻辑。

示例

该模板合约仅支持一种类型的 QueryMsg:

GetCount

请求:

{
  "get_count": {}
}

返回:

{
  "count": 5
}

Message 定义

为了在合约中支持数据查询,你需要定义查询消息格式(代表请求),并提供查询输出的结构——在这种情况下是 CountResponse。你必须这样做,因为 query() 会通过结构化的 JSON 将信息发送回用户,因此你需要定义响应的结构。有关更多信息,请参见 "生成 JSON 架构"。

将以下内容添加到你的 src/msg.rs 文件中:

// src/msg.rs
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum QueryMsg {
    // GetCount returns the current count as a json-encoded number
    GetCount {},
}

// Define a custom struct for each query response
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct CountResponse {
    pub count: i32,
}

逻辑

query() 的逻辑与 execute() 类似;然而,由于 query() 在没有最终用户发起交易的情况下被调用,因此省略了 env 参数,因为不需要该信息。

// src/contract.rs

#[cfg_attr(not(feature = "library"), entry_point)]
pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult<Binary> {
    match msg {
        QueryMsg::GetCount {} => to_binary(&query_count(deps)?),
    }
}

fn query_count(deps: Deps) -> StdResult<CountResponse> {
    let state = STATE.load(deps.storage)?;
    Ok(CountResponse { count: state.count })
}

单元测试

单元测试应该作为部署合约到链上的第一步保障。它们执行迅速,并且在失败时可以通过 RUST_BACKTRACE=1 标志提供有用的回溯信息:

cargo unit-test // run this with RUST_BACKTRACE=1 for helpful backtraces

构建合约

现在我们已经理解并测试了合约,可以运行以下命令来构建合约。这个命令将在我们进入下一步优化合约之前检查任何初步的错误。

cargo wasm

接下来,我们需要优化合约,以便为将代码上传到区块链做准备。

在 Docker 正在运行的情况下,运行以下命令将合约代码挂载到 /code 并优化输出(如果不想先进入目录,可以使用绝对路径替代 $(pwd)):

docker run --rm -v "$(pwd)":/code \
  --mount type=volume,source="$(basename "$(pwd)")_cache",target=/code/target \
  --mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \
  cosmwasm/rust-optimizer:0.12.12

如果你使用的是 ARM64 机器,应该使用为 ARM64 构建的 Docker 镜像:

docker run --rm -v "$(pwd)":/code \
  --mount type=volume,source="$(basename "$(pwd)")_cache",target=/code/target \
  --mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \
  cosmwasm/rust-optimizer-arm64:0.12.12

在运行命令时,你可能会收到 Unable to update registry 'crates-io' 错误。

尝试将以下行添加到合约目录中的 Cargo.toml 文件中,然后再次运行该命令:

[net]
git-fetch-with-cli = true

这会生成一个 artifacts 目录,其中包含 PROJECT_NAME.wasm 文件,以及 checksums.txt 文件,后者包含 Wasm 文件的 Sha256 哈希值。Wasm 文件是确定性编译的(在相同的 git 提交上运行相同 Docker 的任何人都应该获得相同的文件,并且具有相同的 Sha256 哈希值)。

安装 injectived

injectived 是命令行界面和daemon进程,连接到 Injective 并使你能够与 Injective 区块链进行交互。

另外,为了简化这个教程,已经准备好了一个 Docker 镜像。

执行此命令将使 Docker 容器无限期地执行。

docker run --name="injective-core-staging" \
-v=<directory_to_which_you_cloned_cw-template>/artifacts:/var/artifacts \
--entrypoint=sh public.ecr.aws/l9h3g6c6/injective-core:staging \
-c "tail -F anything"

Note: directory_to_which_you_cloned_cw-template 必须是一个绝对路径。你可以通过在 CosmWasm/cw-counter 目录中运行 pwd 命令轻松找到绝对路径。

打开一个新终端并进入 Docker 容器以初始化链:

docker exec -it injective-core-staging sh

让我们首先添加 jq 依赖,它将在后续步骤中使用:

# inside the "injective-core-staging" container
apk add jq

现在我们可以继续进行本地区块链初始化,并添加一个名为 testuser 的测试用户(当提示时使用 12345678 作为密码)。我们将仅使用该测试用户来生成一个新的私钥,稍后将在测试网中用于签名消息:

# inside the "injective-core-staging" container
injectived keys add testuser

输出

- name: testuser
  type: local
  address: inj1exjcp8pkvzqzsnwkzte87fmzhfftr99kd36jat
  pubkey: '{"@type":"/injective.crypto.v1beta1.ethsecp256k1.PubKey","key":"Aqi010PsKkFe9KwA45ajvrr53vfPy+5vgc3aHWWGdW6X"}'
  mnemonic: ""

**Important** write this mnemonic phrase in a safe place.
It is the only way to recover your account if you ever forget your password.

wash wise evil buffalo fiction quantum planet dial grape slam title salt dry and some more words that should be here

请花点时间记下地址,或者将其导出为环境变量,因为接下来你将需要用到它:

# inside the "injective-core-staging" container
export INJ_ADDRESS= <your inj address>

现在你已经成功在 Injective 测试网上创建了 testuser,并且在从测试水龙头请求测试网资金后,账户应该会有一些资金。

curl -X GET "https://sentry.testnet.lcd.injective.network/cosmos/bank/v1beta1/balances/<your_INJ_address>" -H "accept: application/json"

上传 Wasm 合约

# inside the "injective-core-staging" container, or from the contract directory if running injectived locally
yes 12345678 | injectived tx wasm store artifacts/my_first_contract.wasm \
--from=$(echo $INJ_ADDRESS) \
--chain-id="injective-888" \
--yes --fees=1000000000000000inj --gas=2000000 \
--node=https://testnet.sentry.tm.injective.network:443

输出:

code: 0
codespace: ""
data: ""
events: []
gas_used: "0"
gas_wanted: "0"
height: "0"
info: ""
logs: []
raw_log: '[]'
timestamp: ""
tx: null
txhash: 912458AA8E0D50A479C8CF0DD26196C49A65FCFBEEB67DF8A2EA22317B130E2C

有几种方法可以找到你刚刚存储的代码:

  1. 使用 injectived 查询交易信息。

要查询交易,请使用 txhash 并验证合约是否已成功部署。

injectived query tx 912458AA8E0D50A479C8CF0DD26196C49A65FCFBEEB67DF8A2EA22317B130E2C --node=https://testnet.sentry.tm.injective.network:443

仔细检查输出,我们可以看到上传合约的 code_id 为 290。

- events:
  - attributes:
    - key: access_config
      value: '{"permission":"Everybody","address":""}'
    - key: checksum
      value: '"+OdoniOsDJ1T9EqP2YxobCCwFAqNdtYA4sVGv7undY0="'
    - key: code_id
      value: '"290"'
    - key: creator
      value: '"inj1h3gepa4tszh66ee67he53jzmprsqc2l9npq3ty"'
    type: cosmwasm.wasm.v1.EventCodeStored
  - attributes:
    - key: action
      value: /cosmwasm.wasm.v1.MsgStoreCode
    - key: module
      value: wasm
    - key: sender
      value: inj1h3gepa4tszh66ee67he53jzmprsqc2l9npq3ty
    type: message
  - attributes:
    - key: code_id
      value: "290"
    type: store_code

让我们将 code_id 导出为环境变量——我们稍后在实例化合约时需要用到它。你也可以跳过这一步,稍后手动添加,但请记住这个 ID。

export CODE_ID= <code_id of your stored contract>

生成 JSON 架构

虽然 Wasm 调用 instantiate、execute 和 query 接受 JSON,但仅有这些信息不足以使用它们。我们需要将预期消息的架构暴露给客户端。

为了利用 JSON 架构的自动生成,你应该为每个需要架构的数据结构进行注册。

// examples/schema.rs

use std::env::current_dir;
use std::fs::create_dir_all;

use cosmwasm_schema::{export_schema, remove_schemas, schema_for};

use my_first_contract::msg::{CountResponse, HandleMsg, InitMsg, QueryMsg};
use my_first_contract::state::State;

fn main() {
    let mut out_dir = current_dir().unwrap();
    out_dir.push("schema");
    create_dir_all(&out_dir).unwrap();
    remove_schemas(&out_dir).unwrap();

    export_schema(&schema_for!(InstantiateMsg), &out_dir);
    export_schema(&schema_for!(ExecuteMsg), &out_dir);
    export_schema(&schema_for!(QueryMsg), &out_dir);
    export_schema(&schema_for!(State), &out_dir);
    export_schema(&schema_for!(CountResponse), &out_dir);
}

然后,架构可以通过以下命令生成:

cargo schema

这将生成 5 个文件,保存在 ./schema 目录中,分别对应合约接受的 3 种消息类型、查询响应消息和内部状态。这些文件采用标准的 JSON Schema 格式,可以被各种客户端工具使用,既可以自动生成编解码器,也可以根据定义的架构验证传入的 JSON。

实例化合约

现在我们已经将代码上传到 Injective,是时候实例化合约并与之交互了。

提醒:在 CosmWasm 中,合约代码的上传和合约的实例化被视为两个独立的事件。

INIT='{"count":99}'
yes 12345678 | injectived tx wasm instantiate $CODE_ID $INIT \
--label="CounterTestInstance" \
--from=$(echo $INJ_ADDRESS) \
--chain-id="injective-888" \
--yes --fees=1000000000000000inj \
--gas=2000000 \
--no-admin \
--node=https://testnet.sentry.tm.injective.network:443

输出:

code: 0
codespace: ""
data: ""
events: []
gas_used: "0"
gas_wanted: "0"
height: "0"
info: ""
logs: []
raw_log: '[]'
timestamp: ""
tx: null
txhash: 01804F525FE336A5502E3C84C7AE00269C7E0B3DC9AA1AB0DDE3BA62CF93BE1D

你可以通过以下方式找到合约地址和元数据:

  • 通过 CLI 查询

injectived query wasm contract inj1ady3s7whq30l4fx8sj3x6muv5mx4dfdlcpv8n7 --node=https://testnet.sentry.tm.injective.network:443

查询合约

如我们之前所知,我们唯一的 QueryMsg 是 get_count。

GET_COUNT_QUERY='{"get_count":{}}'
injectived query wasm contract-state smart inj1ady3s7whq30l4fx8sj3x6muv5mx4dfdlcpv8n7 "$GET_COUNT_QUERY" \
--node=https://testnet.sentry.tm.injective.network:443 \
--output json

输出:

{"data":{"count":99}}

我们看到 count 是 99,这是在实例化合约时设置的值。.

如果你查询相同的合约,可能会收到不同的响应,因为其他人可能已经与合约交互并增加或重置了 count。

执行合约

现在让我们通过增加计数器来与合约进行交互。

INCREMENT='{"increment":{}}'
yes 12345678 | injectived tx wasm execute inj1ady3s7whq30l4fx8sj3x6muv5mx4dfdlcpv8n7 "$INCREMENT" --from=$(echo $INJ_ADDRESS) \
--chain-id="injective-888" \
--yes --fees=1000000000000000inj --gas=2000000 \
--node=https://testnet.sentry.tm.injective.network:443 \
--output json

如果我们查询合约的 count,我们会看到:

{"data":{"count":100}}

是的,12345678 | 自动将密码传递给 injectived tx wasm execute 的输入,因此你无需手动输入密码。

充值计数器:

RESET='{"reset":{"count":999}}'
yes 12345678 | injectived tx wasm execute inj1ady3s7whq30l4fx8sj3x6muv5mx4dfdlcpv8n7 "$RESET" \
--from=$(echo $INJ_ADDRESS) \
--chain-id="injective-888" \
--yes --fees=1000000000000000inj --gas=2000000 \
--node=https://testnet.sentry.tm.injective.network:443 \
--output json

现在,如果我们再次查询合约,我们会看到 count 已经重置为提供的值:

{"data":{"count":999}}

Cosmos Messages

除了定义自定义智能合约逻辑外,CosmWasm 还允许合约与底层的 Cosmos SDK 功能进行交互。一个常见的用例是使用 Cosmos SDK 的银行模块从合约向指定地址发送 tokens。

示例: Bank Send

BankMsg::Send 消息允许合约将 tokens 转移到另一个地址。这在各种场景中都很有用,比如分发奖励或将资金返还给用户。

Note: 如果你想同时发送资金并执行另一个合约中的函数,不要使用 BankMsg::Send。相反,使用 WasmMsg::Execute 并设置相应的资金字段。

构造 Message

你可以在合约的 execute 函数中构造 BankMsg::Send 消息。此消息需要指定接收地址和要发送的金额。以下是如何构造该消息的示例:

use cosmwasm_std::{BankMsg, Coin, Response, MessageInfo};

pub fn try_send(
    info: MessageInfo,
    recipient_address: String,
    amount: Vec<Coin>,
) -> Result<Response, ContractError> {
    let send_message = BankMsg::Send {
        to_address: recipient_address,
        amount,
    };

    let response = Response::new().add_message(send_message);
    Ok(response)
}

在智能合约中的使用

在你的合约中,你可以向 ExecuteMsg 枚举添加一个新变体,以处理银行发送功能。例如:

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum ExecuteMsg {
    // ... other messages ...
    SendTokens { recipient: String, amount: Vec<Coin> },
}

然后,在 execute 函数中,你可以添加一个 case 来处理这个消息:

#[cfg_attr(not(feature = "library"), entry_point)]
pub fn execute(
    deps: DepsMut,
    env: Env,
    info: MessageInfo,
    msg: ExecuteMsg,
) -> Result<Response, ContractError> {
    match msg {
        // ... other message handling ...
        ExecuteMsg::SendTokens { recipient, amount } => try_send(info, recipient, amount),
    }
}

测试

恭喜你!你已经创建并与第一个 Injective 智能合约进行了交互,现在知道如何开始在 Injective 上进行 CosmWasm 开发。继续阅读了解如何创建 Web UI 的指南。

本地开发

前置条件

按照以下说明安装 Go、Rust 以及其他 CosmWasm 依赖项:

injectived

编译 CosmWasm 合约

非 ARM(非 Apple Silicon)设备:

对于 Apple Silicon 设备(M1、M2 等),请使用:

Docker 脚本会构建并优化仓库中的所有 CW 合约,编译后的合约位于 artifacts 目录下。现在,我们可以部署 cw20_base.wasm 合约(如果在 ARM 设备上编译,则使用 cw20_base-aarch64.wasm)。

将 CosmWasm 合约上传至区块链

输出:

然后,通过 txhash 查询交易,以验证合约是否成功部署。

输出:

Inspecting the output more closely, we can see the code_id of 1 for the contract

我们已经上传了合约代码,但仍需要实例化合约。

实例化合约

在实例化合约之前,让我们先看一下 CW-20 合约的 instantiate 函数签名。

值得注意的是,它包含 InstantiateMsg 参数,其中包括代币名称、符号、小数位数及其他详细信息。

实例化合约的第一步是选择一个地址来提供初始 CW20 代币分配。在本示例中,我们可以直接使用创世地址(genesis address),因为我们已经设置了密钥。但如果需要,也可以生成新的地址和密钥。

确保你选择的地址拥有对应的私钥,否则无法测试从该地址进行的代币转账。此外,所选地址必须是链上的有效地址(即该地址必须在过去某个时间点收到过资金),并且必须有余额用于支付执行合约时的 gas 费用。

要查找创世地址,请运行以下命令:

输出:

运行以下 CLI 命令,使用 code_id 1,并提供 JSON 编码的初始化参数(包含你选择的地址)以及一个标签(用于在列表中提供可读的合约名称),以实例化合约:

现在,可以通过以下方式获取已实例化合约的地址:

这将返回你在本地 Injective 网络上部署的 CW20 合约的详细信息。

输出:

查询合约

整个合约状态可以通过以下命令查询::

输出:

个别用户的代币余额也可以通过以下命令查询:

输出:

传送代币

然后,可以通过以下命令确认余额转账是否成功:

输出:

并确认接收方已收到资金:

输出:

测试网开发

以下是本地开发/部署和测试网开发/部署之间的主要区别:

  • 当您使用 injectived 时,您需要通过 --node 标志指定测试网 RPC,使用以下地址:--node=https://testnet.sentry.tm.injective.network:443。

  • 与使用 injective-1 作为 chainId 不同,您应该使用 injective-888,即 --chain-id="injective-888" 标志。

模块

模块是 Injective 区块链架构中的基础组件,每个模块都提供特定的功能。模块本质上是一个自包含的单元,具有明确定义的逻辑和服务,允许 Injective 高效地管理网络中的多种操作。

模块类似于构建区块链能力的基础组件,可以组合使用以扩展功能。这种方法减少了实现核心特性的阻力和技术复杂性,使开发更加简单。每个模块独立运行,但通过模块间消息传递进行通信,使 Injective 的架构具有模块化和适应性。这种设计使 Injective 能够持续演进,实现针对性的升级和功能扩展,而无需对整个系统进行大规模改动。

探索模块

拍卖(Auction)

摘要

拍卖模块(auction module) 会定期从 交易模块(exchange module) 获取由交易手续费累积而成的一篮子代币,并通过 公开的 English 拍卖 以 INJ 竞价的方式将其拍卖给出价最高的竞标者。拍卖的赢家将获得这篮子代币,而本次拍卖中的获胜 INJ 出价金额将被销毁(burn)。

目录

给部署地址白名单

  • 历史上,社区曾发现 CosmWasm 中存在漏洞,某些合约可能导致链停滞。因此,要求批准在本质上更加安全,直到该语言随着时间的发展和稳定化。

  • 治理可以防止未经审计和虚假的合约被部署到网络中,确保黑客攻击和诈骗的发生率较低。

  • 这为生态系统用户带来了更加精心筛选的体验,因为区块链不会被不必要的交易和合约填满。

然而,这一治理过程对于验证者和社区来说是非常耗时的。Injective 开发者还需要等待 4 天才能推出新特性,这影响了开发者体验和用户增长。 自 Altaris 链升级以来,现在可以请求将你的地址列入合约上传的白名单。

提交指南

为了考虑将地址列入白名单,强烈建议在治理提案中包含以下所有信息。验证者建议在投票之前,通过 Discord 联系基金会验证每个提案提交的信息。

  • 团队成员的身份是否为社区所知?

  • 团队是否已通过 Injective 基金会的 KYC/KYB 认证?

  • 该协议在 Injective 主网上线多久了?

  • 团队是否在其他生态系统中开发过应用?(提供详细信息)

投票指南

符合以下条件的用户将更有可能获得白名单权限:

  • 项目已完成 Injective 基金会的 KYC/KYB,且团队成员的身份为社区所知。例外:匿名开发者,且具有可信度和在其他生态系统中成功应用的经历。

  • 项目已在主网上线至少 1 个月,并且取得了显著的 TVL(总价值锁仓)/使用量。

运维指南

强烈建议使用多签钱包或 Ledger 钱包进行白名单上传。

创建您的 Swap 合约

快速开始

  1. 哪个地址应为手续费接收者? 由于交换合约下的订单是 Injective 交易模块中的订单,这意味着每个订单可以有一个手续费接收者,该接收者可以获得 40% 的交易手续费。通常,交易所 dApp 会将手续费接收者设置为自己的地址。

  2. 该合约应支持哪些代币? 合约中可用的每个代币必须定义一个交易路线。路线是指代币 A 要通过哪些市场才能换取代币 B。例如,如果你想支持 ATOM 和 INJ 之间的交换,则需要通过提供 ATOM/USDT 和 INJ/USDT 的市场 ID 来设置路线,这样它就知道 ATOM 和 INJ 之间的交换路线是 ATOM ⇔ USDT ⇔ INJ。 目前,合约只能支持以 USDT 报价的市场。

  3. 应该为该合约提供多少缓冲资金? 作为合约拥有者,你还需要为合约提供资金,当发生交换时,这些资金将被使用。缓冲资金由合约在下单时使用。如果用户想要交换大额资产或在流动性不足的市场中进行交换,则需要更多的缓冲资金。当合约的缓冲资金无法满足用户输入金额时,会发生错误。

目前,缓冲资金应仅为 USDT。

Messages

Instantiate

初始化合约状态,包含合约版本和配置详情。配置包括管理员地址和手续费接收者地址。

Execute

处理不同类型的交易和管理员功能:

  • SwapMinOutput: 以最小输出数量进行交换。

  • SwapExactOutput: 以精确输出数量进行交换。

  • SetRoute: 设置交换路线。

  • DeleteRoute: 删除交换路线。

  • UpdateConfig: 更新合约配置。

  • WithdrawSupportFunds: 从合约中提取支持资金。

Reply

处理来自其他合约或交易的回复。

Query

处理对合约的各种查询:

  • GetRoute: 获取特定的交换路径。

  • GetOutputQuantity: 获取给定输入数量的输出数量。

  • GetInputQuantity: 获取给定输出数量的输入数量。

  • GetAllRoutes: 获取所有可用的交换路径。

代码库

在本文档中,我们将解释 CW20 适配器合约,该合约允许将 CW-20 代币交换为 Injective 发行的原生代币(使用 TokenFactory 模块),反之亦然。关于 CW-20 适配器的 GitHub 仓库,请见。

关于创建用户界面以及引导选项的更全面文档可以在中找到。

例如,请查看。关于使用 Vue 和 开发前端的高级指南可以在该网站的中找到。对于 React 实现,请查看。 现在,与合约的交互变得和点击按钮、使用 MetaMask 签名一样简单(确保账户设置为 Ethereum Goerli Testnet,否则会收到链 ID 不匹配的错误)。

你可能会注意到,在尝试重置计数时,出现了“Unauthorized”(未经授权)错误消息。这是预期的行为!回顾,对于重置操作,只有合约拥有者才被允许重置计数。由于你没有实例化前端交互的确切合约,因此你没有重置计数所需的权限。

迁移是通过它可以替换或“升级”给定智能合约代码的过程。 在实例化合约时,有一个可选的管理员字段可以设置。如果该字段为空,合约将是不可变的。如果设置了管理员(指向外部账户或治理合约),该账户可以触发迁移。管理员还可以重新分配管理员角色,或者如果需要的话,使合约完全不可变。然而,值得注意的是,在从旧合约迁移到新合约时,新合约需要了解之前如何编码状态。 关于迁移的技术细节可以在 中找到更详细的描述。

在开始之前,请确保已安装 ,以及最新版本的 rustc 和 cargo。当前,我们在 Rust v1.58.1+ 版本上进行测试。

智能合约可以被视为的一个实例,其内部状态持久化存储在区块链上。用户可以通过发送 JSON 消息来触发状态变更,也可以通过格式化为 JSON 消息的请求来查询合约状态。这些 JSON 消息不同于 Injective 区块链消息,例如 MsgSend 和 MsgExecuteContract。

在我们的中,将实现一个 instantiate 方法、一个 query 方法和两个 execute 方法。

你可以在 CosmWasm 的中了解更多关于 CosmWasm 状态的信息。

幸运的是,CosmWasm 提供了实用的库,例如 ,它为数据容器(如 "singleton" 和 "bucket")提供了方便的高层抽象,自动提供常用类型(如结构体和 Rust 数字)的序列化和反序列化功能。此外, 库可以用于更高效的存储机制。

你可以在 CosmWasm 的中了解更多关于 ExecuteMsg 的信息。

ExecuteMsg 是一个 JSON 消息,通过 MsgExecuteContract 传递给 execute() 函数。与 InstantiateMsg 不同,ExecuteMsg 可以有多个不同类型的消息,以对应智能合约向用户暴露的不同功能。函数会对这些不同类型的消息进行分发,并调用相应的消息处理逻辑。

我们有两个 :Increment 和 Reset。

你可以在 CosmWasm 的文档中了解更多关于 的信息。

GetCount 没有参数,返回 count 的值。

你可以在 src/contract.rs 文件中找到的实现。

你可以阅读更多关于为的详细信息。

CosmWasm 提供了 ,一个优化编译器,可以生成小巧且一致的构建输出。使用该工具最简单的方法是使用已发布的 Docker 镜像——可以在查看最新的 x86 版本,或者在查看最新的 ARM 版本。

CosmWasm 不建议使用 ARM64 版本的编译器,因为它生成的 Wasm 构件与 Intel/AMD 版本不同。对于发布/生产环境,仅推荐使用 Intel/AMD 优化器构建的合约。有关详细信息,请参见 CosmWasm 的。

有关更多信息,请参见 。

如果你想通过 CLI 在本地与智能合约进行交互,你需要安装 injectived。你可以按照来进行安装。

如果你是从二进制文件安装 injectived,则无需使用 Docker 命令。在 部分,你可以找到与 Mainnet 和 Testnet 交互的正确 --node 信息。

你可以使用 为你最近生成的测试地址请求测试网资金。

为了确认,你可以在 中搜索你的地址以检查余额。

另外,你也可以通过或使用 curl 来验证:

现在是时候将你在之前步骤中编译的 .wasm 文件上传到 Injective 测试网了。请注意,主网的程序流程不同,需要通过进行。

在 中检查你的地址,查找与将代码存储到链上的交易相关的 txhash。交易类型应该是 MsgStoreCode。

你可以在 Injective 测试网上的 部分查看所有已存储的代码。

在 Injective 浏览器的中查找 TxHash,它很可能是最新的。

花点时间生成架构()并熟悉它,因为接下来你将需要用到它。

要实例化合约,请运行以下 CLI 命令,使用你在上一步中获得的 code_id,以及 和标签(该标签是该合约在人类可读列表中的名称)。

在中查看

查询 和 API

像其他智能合约函数一样,你应该添加单元测试,以确保你的银行发送功能按预期工作。这包括测试不同场景,例如发送不同数量的 tokens,并正确处理错误。 你可以使用 来运行包括本地 Injective 链的集成测试。

本指南将帮助你开始在本地运行的 Injective 网络上部署 cw20 智能合约。 我们将使用 CosmWasm 提供的 cw20-base 合约,这些合约是为在实际网络上生产使用而设计的。cw20-base 是一个兼容 cw20 的基础实现,可以导入到任何你想要构建的自定义合约中。它包含了一个简单但完整的 cw20 规范实现,并支持所有扩展。cw20-base 可以直接部署或被其他合约导入使用。

在开始之前,确保你已经安装了 ,并且 rustc 和 cargo 的版本是最新的。目前我们正在测试 Rust v1.58.1+ 版本。 你还需要安装 wasm32-unknown-unknown 和 cargo-generate Rust crate。 你可以通过以下命令检查版本:

确保你已经在本地安装了 injectived。你可以按照 指南将 injectived 和其他先决条件在本地运行起来。 安装完成后,你还需要启动一个。

在此步骤中,我们将获取所有 CW 生产模板合约,并使用 Docker 镜像(用于编译多个合约,称为 workspace-optimizer)进行编译—有关最新版本,请参见(x86)或(ARM)。这个过程可能需要一些时间和 CPU 计算能力。

访问

通过 CLI 查询合约信息元数据,访问

您可以使用我们的 (Testnet Faucet)为您的地址获取测试网资金。

您可以使用(Testnet Explorer)查询您的交易并获取更多详情。

您可以使用 查找已上传智能合约的 codeId 或查找已实例化的智能合约。

您可以阅读更多关于如何使用 injectived 来查询/发送交易到测试网的相关信息。 .

什么是模块?

在 Injective 主网上传合约需要治理批准。你可以通过指南中的教程来了解如何操作。 设置这种结构有多个原因:

允许在两个不同的代币之间进行即时交换。在后台,它使用原子订单在一个或多个现货市场中下达市场订单。

任何人都可以实例化交换合约的实例。此合约的一个版本已经上传到 Injective 主网,并可以在找到。 在实例化合约之前,作为合约拥有者,你需要回答以下三个问题:

swap contract 的完整 GitHub 仓库可以在找到。

此处
TypeScript 文档
计数器网站
Injective TS SDK
仓库
这里
合约逻辑
CosmWasm 迁移文档
rustup
单例对象
示例计数器合约
文档
cosmwasm_storage
cw-storage-plus
文档
execute()
ExecuteMsg
QueryMsg
查询消息
单元测试
生产环境准备 Wasm 字节码
rust-optimizer
这里
这里
说明
The Cargo Book
这里的安装指南
public endpoints section
Injective 测试水龙头
Injective 测试网浏览器
查询银行余额
治理提案
Injective 测试网浏览器
Code
Code列表
可以在这里查看
JSON 编码的初始化参数
测试网浏览器
ContractsByCode
ContractInfo
test-tube
rustc --version
cargo --version
rustup target list --installed
# if wasm32 is not listed above, run this
rustup target add wasm32-unknown-unknown
# to install cargo-generate, run this
cargo install cargo-generate
git clone https://github.com/CosmWasm/cw-plus
cd cw-plus
docker run --rm -v "$(pwd)":/code \
--mount type=volume,source="$(basename "$(pwd)")_cache",target=/code/target \
--mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \
cosmwasm/workspace-optimizer:0.12.12
docker run --rm -v "$(pwd)":/code \
--mount type=volume,source="$(basename "$(pwd)")_cache",target=/code/target \
--mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \
cosmwasm/workspace-optimizer-arm64:0.12.12
# inside the CosmWasm/cw-plus repo 
yes 12345678 | injectived tx wasm store artifacts/cw20_base.wasm --from=genesis --chain-id="injective-1" --yes --gas-prices=500000000inj --gas=20000000
code: 0
codespace: ""
data: ""
events: []
gas_used: "0"
gas_wanted: "0"
height: "0"
info: ""
logs: []
raw_log: '[]'
timestamp: ""
tx: null
txhash: 4CFB63A47570C4CFBE8E669273B26BEF6EAFF922C07480CA42180C52219CE784
injectived query tx 4CFB63A47570C4CFBE8E669273B26BEF6EAFF922C07480CA42180C52219CE784
code: 0
codespace: ""
data: 0A460A1E2F636F736D7761736D2E7761736D2E76312E4D736753746F7265436F64651224080112205F8201CF5E2D7E6C15DB11ADF03D62DDDDC92B8D4FAE98C4F3C1C37F378E15D9
events:
- attributes:
  - index: true
    key: YWNjX3NlcQ==
    value: aW5qMTByZHN4ZGdyOGw4czBndnU4cnluaHUyMm5ueGtmeXRnNThjd204LzE=
  type: tx
- attributes:
  - index: true
    key: c2lnbmF0dXJl
    value: R29McmoxaDBtelNWN085SUNScStLbDdCVmdocDB6aU5EQ0Jwc1dFS1I1TlNXZkR2V1ZJejF0TEpGb0ZwSzlhNkFIQVdSVkZRNjExYitwSHdpY04wN1FFPQ==
  type: tx
- attributes:
  - index: true
    key: c3BlbmRlcg==
    value: aW5qMTByZHN4ZGdyOGw4czBndnU4cnluaHUyMm5ueGtmeXRnNThjd204
  - index: true
    key: YW1vdW50
    value: MTAwMDAwMDAwMDAwMDAwMDBpbmo=
  type: coin_spent
- attributes:
  - index: true
    key: cmVjZWl2ZXI=
    value: aW5qMTd4cGZ2YWttMmFtZzk2MnlsczZmODR6M2tlbGw4YzVsNnM1eWU5
  - index: true
    key: YW1vdW50
    value: MTAwMDAwMDAwMDAwMDAwMDBpbmo=
  type: coin_received
- attributes:
  - index: true
    key: cmVjaXBpZW50
    value: aW5qMTd4cGZ2YWttMmFtZzk2MnlsczZmODR6M2tlbGw4YzVsNnM1eWU5
  - index: true
    key: c2VuZGVy
    value: aW5qMTByZHN4ZGdyOGw4czBndnU4cnluaHUyMm5ueGtmeXRnNThjd204
  - index: true
    key: YW1vdW50
    value: MTAwMDAwMDAwMDAwMDAwMDBpbmo=
  type: transfer
- attributes:
  - index: true
    key: c2VuZGVy
    value: aW5qMTByZHN4ZGdyOGw4czBndnU4cnluaHUyMm5ueGtmeXRnNThjd204
  type: message
- attributes:
  - index: true
    key: ZmVl
    value: MTAwMDAwMDAwMDAwMDAwMDBpbmo=
  - index: true
    key: ZmVlX3BheWVy
    value: aW5qMTByZHN4ZGdyOGw4czBndnU4cnluaHUyMm5ueGtmeXRnNThjd204
  type: tx
- attributes:
  - index: true
    key: YWN0aW9u
    value: L2Nvc213YXNtLndhc20udjEuTXNnU3RvcmVDb2Rl
  type: message
- attributes:
  - index: true
    key: bW9kdWxl
    value: d2FzbQ==
  - index: true
    key: c2VuZGVy
    value: aW5qMTByZHN4ZGdyOGw4czBndnU4cnluaHUyMm5ueGtmeXRnNThjd204
  type: message
- attributes:
  - index: true
    key: Y29kZV9jaGVja3N1bQ==
    value: NWY4MjAxY2Y1ZTJkN2U2YzE1ZGIxMWFkZjAzZDYyZGRkZGM5MmI4ZDRmYWU5OGM0ZjNjMWMzN2YzNzhlMTVkOQ==
  - index: true
    key: Y29kZV9pZA==
    value: MQ==
  type: store_code
- attributes:
  - index: true
    key: YWNjZXNzX2NvbmZpZw==
    value: eyJwZXJtaXNzaW9uIjoiRXZlcnlib2R5IiwiYWRkcmVzcyI6IiIsImFkZHJlc3NlcyI6W119
  - index: true
    key: Y2hlY2tzdW0=
    value: Ilg0SUJ6MTR0Zm13VjJ4R3Q4RDFpM2QzSks0MVBycGpFODhIRGZ6ZU9GZGs9Ig==
  - index: true
    key: Y29kZV9pZA==
    value: IjEi
  - index: true
    key: Y3JlYXRvcg==
    value: ImluajEwcmRzeGRncjhsOHMwZ3Z1OHJ5bmh1MjJubnhrZnl0ZzU4Y3dtOCI=
  type: cosmwasm.wasm.v1.EventCodeStored
gas_used: "2158920"
gas_wanted: "20000000"
height: "47"
info: ""
logs:
- events:
  - attributes:
    - key: access_config
      value: '{"permission":"Everybody","address":"","addresses":[]}'
    - key: checksum
      value: '"X4IBz14tfmwV2xGt8D1i3d3JK41PrpjE88HDfzeOFdk="'
    - key: code_id
      value: '"1"'
    - key: creator
      value: '"inj10rdsxdgr8l8s0gvu8rynhu22nnxkfytg58cwm8"'
    type: cosmwasm.wasm.v1.EventCodeStored
  - attributes:
    - key: action
      value: /cosmwasm.wasm.v1.MsgStoreCode
    - key: module
      value: wasm
    - key: sender
      value: inj10rdsxdgr8l8s0gvu8rynhu22nnxkfytg58cwm8
    type: message
  - attributes:
    - key: code_checksum
      value: 5f8201cf5e2d7e6c15db11adf03d62ddddc92b8d4fae98c4f3c1c37f378e15d9
    - key: code_id
      value: "1"
    type: store_code
  log: ""
  msg_index: 0
raw_log: '[{"events":[{"type":"cosmwasm.wasm.v1.EventCodeStored","attributes":[{"key":"access_config","value":"{\"permission\":\"Everybody\",\"address\":\"\",\"addresses\":[]}"},{"key":"checksum","value":"\"X4IBz14tfmwV2xGt8D1i3d3JK41PrpjE88HDfzeOFdk=\""},{"key":"code_id","value":"\"1\""},{"key":"creator","value":"\"inj10rdsxdgr8l8s0gvu8rynhu22nnxkfytg58cwm8\""}]},{"type":"message","attributes":[{"key":"action","value":"/cosmwasm.wasm.v1.MsgStoreCode"},{"key":"module","value":"wasm"},{"key":"sender","value":"inj10rdsxdgr8l8s0gvu8rynhu22nnxkfytg58cwm8"}]},{"type":"store_code","attributes":[{"key":"code_checksum","value":"5f8201cf5e2d7e6c15db11adf03d62ddddc92b8d4fae98c4f3c1c37f378e15d9"},{"key":"code_id","value":"1"}]}]}]'
timestamp: "2023-03-06T15:48:30Z"
tx:
  '@type': /cosmos.tx.v1beta1.Tx
  auth_info:
    fee:
      amount:
      - amount: "10000000000000000"
        denom: inj
      gas_limit: "20000000"
      granter: ""
      payer: ""
    signer_infos:
    - mode_info:
        single:
          mode: SIGN_MODE_DIRECT
      public_key:
        '@type': /injective.crypto.v1beta1.ethsecp256k1.PubKey
        key: Ay+cc/lvd4Mn4pbgFkN87vWDaCXuXjVJYJGsdhrD09vk
      sequence: "1"
  body:
    extension_options: []
    memo: ""
    messages:
    - '@type': /cosmwasm.wasm.v1.MsgStoreCode
      instantiate_permission: null
      sender: inj10rdsxdgr8l8s0gvu8rynhu22nnxkfytg58cwm8
      wasm_byte_code: YOUR_WASM_BYTE_HERE
    non_critical_extension_options: []
    timeout_height: "0"
  signatures:
  - GoLrj1h0mzSV7O9ICRq+Kl7BVghp0ziNDCBpsWEKR5NSWfDvWVIz1tLJFoFpK9a6AHAWRVFQ611b+pHwicN07QE=
txhash: 4CFB63A47570C4CFBE8E669273B26BEF6EAFF922C07480CA42180C52219CE784
logs:
- events:
  - attributes:
    - key: access_config
      value: '{"permission":"Everybody","address":"","addresses":[]}'
    - key: checksum
      value: '"X4IBz14tfmwV2xGt8D1i3d3JK41PrpjE88HDfzeOFdk="'
    - key: code_id
      value: '"1"'
    - key: creator
      value: '"inj10rdsxdgr8l8s0gvu8rynhu22nnxkfytg58cwm8"'
    type: cosmwasm.wasm.v1.EventCodeStored
  - attributes:
    - key: action
      value: /cosmwasm.wasm.v1.MsgStoreCode
    - key: module
      value: wasm
    - key: sender
      value: inj10rdsxdgr8l8s0gvu8rynhu22nnxkfytg58cwm8
    type: message
  - attributes:
    - key: code_checksum
      value: 5f8201cf5e2d7e6c15db11adf03d62ddddc92b8d4fae98c4f3c1c37f378e15d9
    - key: code_id
      value: "1"
    type: store_code
  log: ""
  msg_index: 0
#[cfg_attr(not(feature = "library"), entry_point)]
pub fn instantiate(
    mut deps: DepsMut,
    _env: Env,
    _info: MessageInfo,
    msg: InstantiateMsg,
) -> Result<Response, ContractError> {
#[derive(Serialize, Deserialize, JsonSchema, Debug, Clone, PartialEq)]
pub struct InstantiateMsg {
    pub name: String,
    pub symbol: String,
    pub decimals: u8,
    pub initial_balances: Vec<Cw20Coin>,
    pub mint: Option<MinterResponse>,
    pub marketing: Option<InstantiateMarketingInfo>,
}
yes 12345678 | injectived keys show genesis
- name: genesis
  type: local
  address: inj10cfy5e6qt2zy55q2w2ux2vuq862zcyf4fmfpj3
  pubkey: '{"@type":"/injective.crypto.v1beta1.ethsecp256k1.PubKey","key":"ArtVkg9feLXjD4p6XRtWxVpvJUDhrcqk/5XYLsQI4slb"}'
  mnemonic: ""
CODE_ID=1
INIT='{"name":"Albcoin","symbol":"ALB","decimals":6,"initial_balances":[{"address":"inj10cfy5e6qt2zy55q2w2ux2vuq862zcyf4fmfpj3","amount":"69420"}],"mint":{"minter":"inj10cfy5e6qt2zy55q2w2ux2vuq862zcyf4fmfpj3"},"marketing":{}}'
yes 12345678 | injectived tx wasm instantiate $CODE_ID $INIT --label="Albcoin Token" --from=genesis --chain-id="injective-1" --yes --gas-prices=500000000inj --gas=20000000 --no-admin
CONTRACT=$(injectived query wasm list-contract-by-code $CODE_ID --output json | jq -r '.contracts[-1]')
injectived query wasm contract $CONTRACT
injectived query wasm contract $CONTRACT
address: inj14hj2tavq8fpesdwxxcu44rty3hh90vhujaxlnz
contract_info:
  admin: ""
  code_id: "1"
  created:
    block_height: "95"
    tx_index: "0"
  creator: inj10rdsxdgr8l8s0gvu8rynhu22nnxkfytg58cwm8
  extension: null
  ibc_port_id: ""
  label: Albcoin Token
injectived query wasm contract-state all $CONTRACT
models:
- key: 000762616C616E6365696E6A31306366793565367174327A793535713277327578327675713836327A63796634666D66706A33
  value: IjY5NDIwIg==
- key: 636F6E74726163745F696E666F
  value: eyJjb250cmFjdCI6ImNyYXRlcy5pbzpjdzIwLWJhc2UiLCJ2ZXJzaW9uIjoiMS4wLjEifQ==
- key: 6D61726B6574696E675F696E666F
  value: eyJwcm9qZWN0IjpudWxsLCJkZXNjcmlwdGlvbiI6bnVsbCwibG9nbyI6bnVsbCwibWFya2V0aW5nIjpudWxsfQ==
- key: 746F6B656E5F696E666F
  value: eyJuYW1lIjoiQWxiY29pbiIsInN5bWJvbCI6IkFMQiIsImRlY2ltYWxzIjo2LCJ0b3RhbF9zdXBwbHkiOiI2OTQyMCIsIm1pbnQiOnsibWludGVyIjoiaW5qMTBjZnk1ZTZxdDJ6eTU1cTJ3MnV4MnZ1cTg2MnpjeWY0Zm1mcGozIiwiY2FwIjpudWxsfX0=
pagination:
  next_key: null
  total: "0"
BALANCE_QUERY='{"balance": {"address": "inj10cfy5e6qt2zy55q2w2ux2vuq862zcyf4fmfpj3"}}'
injectived query wasm contract-state smart $CONTRACT "$BALANCE_QUERY" --output json
{"data":{"balance":"69420"}}
TRANSFER='{"transfer":{"recipient":"inj1dzqd00lfd4y4qy2pxa0dsdwzfnmsu27hgttswz","amount":"420"}}'
yes 12345678 | injectived tx wasm execute $CONTRACT "$TRANSFER" --from genesis --chain-id="injective-1" --yes --gas-prices=500000000inj --gas=20000000
# first address balance query
BALANCE_QUERY='{"balance": {"address": "inj10cfy5e6qt2zy55q2w2ux2vuq862zcyf4fmfpj3"}}'
injectived query wasm contract-state smart $CONTRACT "$BALANCE_QUERY" --output json
{"data":{"balance":"69000"}}
# recipient's address balance query
BALANCE_QUERY='{"balance": {"address": "inj1dzqd00lfd4y4qy2pxa0dsdwzfnmsu27hgttswz"}}'
injectived query wasm contract-state smart $CONTRACT "$BALANCE_QUERY" --output json
{"data":{"balance":"420"}}
pub fn instantiate(
    deps: DepsMut<InjectiveQueryWrapper>,
    env: Env,
    info: MessageInfo,
    msg: InstantiateMsg,
) -> Result<Response<InjectiveMsgWrapper>, ContractError>
pub fn execute(
    deps: DepsMut<InjectiveQueryWrapper>,
    env: Env,
    info: MessageInfo,
    msg: ExecuteMsg,
) -> Result<Response<InjectiveMsgWrapper>, ContractError>
pub fn reply(
    deps: DepsMut<InjectiveQueryWrapper>,
    env: Env,
    msg: Reply,
) -> Result<Response<InjectiveMsgWrapper>, ContractError>
pub fn query(deps: Deps<InjectiveQueryWrapper>, env: Env, msg: QueryMsg) -> StdResult<Binary>
使用 injectived
主网开发

Injective

Core

参数

拍卖模块包含以下参数:

键
类型
示例

AuctionPeriod

int64

604800

MinNextBidIncrementRate

math.LegacyDec

"0.0025"

State

Params

Params 是一个模块级的配置结构,用于存储系统参数,并定义 拍卖模块 的整体运行方式。

  • Params: Paramsspace("auction") -> legacy_amino(params)

type Params struct {
	// auction_period_duration defines the auction period duration
	AuctionPeriod int64 
	// min_next_bid_increment_rate defines the minimum increment rate for new bids
	MinNextBidIncrementRate math.LegacyDec
}

LastBid

用于跟踪当前最高出价。

  • LastBid: 0x01 -> ProtocolBuffer(Bid)

type Bid struct {
	Bidder string                                  
	Amount sdk.Coin 
}

AuctionRound

当前拍卖轮次。

  • AuctionRound: 0x03 -> BigEndian(AuctionRound)

EndingTimeStamp

该值会与当前区块时间进行比较,以决定拍卖轮次的结算时间。当导出的链再次导入时,EndingTimeStamp 将更新为未来的下一个时间点。

  • EndingTimeStamp: 0x04 -> BigEndian(EndingTimestamp)

LastAuctionResult

用于跟踪最近一次拍卖的结果。

  • LastAuctionResult: 0x05 -> ProtocolBuffer(LastAuctionResult)

type LastAuctionResult struct {
    Winner string 
    Amount sdk.Coin 
    Round uint64 
}

消息

本节将介绍拍卖消息的处理流程及其对应的状态更新。

Msg/Bid

拍卖轮次中的代币篮子可通过 Msg/Bid 服务消息进行竞标。

// Bid defines a SDK message for placing a bid for an auction
message MsgBid {
  option (gogoproto.equal) = false;
  option (gogoproto.goproto_getters) = false;
  string sender = 1;
  // amount of the bid in INJ tokens
  cosmos.base.v1beta1.Coin bid_amount = 2 [(gogoproto.nullable) = false];
  // the current auction round being bid on
  uint64 round = 3;
}

如果满足以下条件,此服务消息预计会失败:

  • 轮次与当前拍卖轮次不相等

  • 出价金额未能超过之前最高出价金额至少 min_next_increment_rate 百分比

此服务消息会将 BidAmount 的 INJ 从发送方转移到拍卖模块,存储该出价,并退还给上一个竞标者的出价金额。

规范和合约集合
Go
Rust
rustup
安装 injectived
本地链实例
CosmWasm Rust 优化器
这里
这里
http://localhost:10337/swagger/#/Query/ContractsByCode
http://localhost:10337/swagger/#/Query/ContractInfo
Injective 测试网水龙头
Injective 测试网浏览器
Injective 测试网浏览器
​
状态
消息
End Block
事件
参数
swap contract
此处
这里

状态转换

本文档描述了以下状态转换操作:

  • 向交易模块账户存款

  • 从交易模块账户取款

  • 即时现货市场创建

  • 即时永续合约市场创建

  • 即时交割合约市场创建

  • 现货限价订单创建

  • 批量创建现货限价订单

  • 现货市价订单创建

  • 取消现货订单

  • 批量取消现货订单

  • 衍生品限价订单创建

  • 批量创建衍生品限价订单

  • 衍生品市价订单创建

  • 取消衍生品订单

  • 批量取消衍生品订单

  • 子账户之间转账

  • 向外部账户转账

  • 清算持仓

  • 增加持仓保证金

  • 现货市场参数更新提案

  • 交易模块启用提案

  • 现货市场创建提案

  • 永续合约市场创建提案

  • 交割合约市场创建提案

  • 衍生品市场参数更新提案

  • 交易奖励启动提案

  • 交易奖励更新提案

  • Begin-blocker 处理

  • End-blocker 处理

存入 Exchange 模块账户

存款操作由 MsgDeposit 执行,该消息包含 Sender、SubaccountId 和 Amount 字段。

注意:SubaccountId 为可选字段,如果未提供,则会根据 Sender 地址动态计算。

步骤

  1. 检查 msg.Amount 中指定的 denom 是否为银行供应中存在的有效 denom。

  2. 将代币从个人账户转入 Exchange 模块账户,若失败则回滚操作。

  3. 从 msg.SubaccountId 获取 subaccountID 的哈希类型,如果为零子账户,则使用 SdkAddressToSubaccountID 根据 msg.Sender 动态计算。

  4. 将 msg.Amount 递增至 subaccountID 的存款余额。

  5. 触发 EventSubaccountDeposit 事件,包含 msg.Sender、subaccountID 和 msg.Amount。

从 Exchange 模块账户提现

提现操作由 MsgWithdraw 执行,该消息包含 Sender、SubaccountId 和 Amount 字段。

注意:msg.ValidateBasic 函数会验证 msg.Sender 对 msg.SubaccountId 的所有权。

步骤

  1. 从 msg.SubaccountId 获取 subaccountID 的哈希类型。

  2. 检查 msg.Amount 中指定的 denom 是否为银行供应中存在的有效 denom。

  3. 从 subaccountID 中减少 msg.Amount,若失败则回滚操作。

  4. 将代币从 Exchange 模块账户发送至 msg.Sender。

  5. 触发 EventSubaccountWithdraw 事件,包含 subaccountID、msg.Sender 和 msg.Amount。

即时现货市场启动

即时现货市场启动操作由 MsgInstantSpotMarketLaunch 执行,该消息包含 Sender、Ticker、BaseDenom、QuoteDenom、MinPriceTickSize 和 MinQuantityTickSize 字段。

步骤

  1. 根据 msg.BaseDenom 和 msg.QuoteDenom 计算 marketID。

  2. 检查是否已存在相同的市场启动提案,如果已存在,则回滚操作。

  3. 使用 msg.Ticker、msg.BaseDenom、msg.QuoteDenom、msg.MinPriceTickSize、msg.MinQuantityTickSize 启动现货市场,若失败则回滚操作。

  4. 从 msg.Sender 向 Exchange 模块账户发送即时上市费用 (params.SpotMarketInstantListingFee)。

  5. 最后将即时上市费用发送至社区支出池。

即时永续市场启动

即时永续市场启动操作由 MsgInstantPerpetualMarketLaunch 执行,该消息包含 Sender、Ticker、QuoteDenom、OracleBase、OracleQuote、OracleScaleFactor、OracleType、MakerFeeRate、TakerFeeRate、InitialMarginRatio、MaintenanceMarginRatio、MinPriceTickSize 和 MinQuantityTickSize 字段。

步骤

  1. 根据 msg.Ticker、msg.QuoteDenom、msg.OracleBase、msg.OracleQuote 和 msg.OracleType 计算 marketID。

  2. 检查是否已存在相同的市场启动提案,如果已存在,则回滚操作。

  3. 从 msg.Sender 向 Exchange 模块账户发送即时上市费用 (params.DerivativeMarketInstantListingFee)。

  4. 使用 msg 对象中的必需参数启动永续市场,若失败则回滚操作。

  5. 最后将即时上市费用发送至社区支出池。

即时到期期货市场启动

即时到期期货市场启动操作由 MsgInstantExpiryFuturesMarketLaunch 执行,该消息包含 Sender、Ticker、QuoteDenom、OracleBase、OracleQuote、OracleScaleFactor、OracleType、Expiry、MakerFeeRate、TakerFeeRate、InitialMarginRatio、MaintenanceMarginRatio、MinPriceTickSize 和 MinQuantityTickSize 字段。

步骤

  1. 根据 msg.Ticker、msg.QuoteDenom、msg.OracleBase、msg.OracleQuote、msg.OracleType 和 msg.Expiry 计算 marketID。

  2. 检查是否已存在相同的市场启动提案,如果已存在,则回滚操作。

  3. 从 msg.Sender 向 Exchange 模块账户发送即时上市费用 (params.DerivativeMarketInstantListingFee)。

  4. 使用 msg 对象中的必需参数启动到期期货市场,若失败则回滚操作。

  5. 触发 EventExpiryFuturesMarketUpdate 事件,包含市场信息。

  6. 最后将即时上市费用发送至社区支出池。

现货限价订单创建

现货限价订单创建操作由 MsgCreateSpotLimitOrder 执行,该消息包含 Sender 和 Order 字段。

步骤

  1. 检查现货交易是否已启用,若未启用则回滚操作。

  2. 检查订单的价格和数量是否符合市场的最小数量和价格刻度要求。

  3. 递增子账户的 TradeNonce。

  4. 如果现货市场 ID 没有引用有效的现货市场,则拒绝该订单。

  5. 使用 TradeNonce 计算唯一的订单哈希。

  6. 如果子账户的可用存款不足以支付该订单所需的资金,则拒绝订单。

  7. 从可用余额中扣除订单所需的资金。

  8. 将订单存储在临时限价订单存储和临时市场指示器存储中。

注意:临时存储中的订单会在结束区块处理时执行,若未执行则存入长期存储。

批量创建现货限价订单

批量创建现货限价订单操作由 MsgBatchCreateSpotLimitOrders 执行,该消息包含 Sender 和 Orders 字段。

步骤

  1. 遍历 msg.Orders,并按照 MsgCreateSpotLimitOrder 中的步骤创建每个现货限价订单。

现货市场订单创建

现货市场订单创建操作由 MsgCreateSpotMarketOrder 执行,该消息包含 Sender 和 Order 字段。

步骤

  1. 检查现货交易是否已启用,若未启用则回滚操作。

  2. 检查订单的价格和数量是否符合市场的最小数量和价格刻度要求。

  3. 递增子账户的 TradeNonce。

  4. 如果现货市场 ID 没有引用有效的现货市场,则拒绝该订单。

  5. 使用 TradeNonce 计算唯一的订单哈希。

  6. 检查可用余额是否足以支持市场订单。

  7. 计算市场订单的最差可接受价格。

  8. 从存款的可用余额中扣除冻结的资金。

  9. 将订单存储在临时现货市场订单存储和临时市场指示器存储中。

取消现货订单

现货订单取消操作由 MsgCancelSpotOrder 执行,该消息包含 Sender、MarketId、SubaccountId 和 OrderHash 字段。

步骤

  1. 检查现货交易是否已启用,若未启用则回滚操作。

  2. 如果现货市场 ID 没有引用有效、挂起或已拆除的现货市场,则拒绝该操作。

  3. 检查通过 marketID、subaccountID 和 orderHash 是否存在现货限价订单。

  4. 将保证金冻结金额返还至可用余额。

  5. 递增可用余额的保证金冻结金额。

  6. 从 ordersStore 和 ordersIndexStore 中删除订单状态。

  7. 触发 EventCancelSpotOrder 事件,包含 marketID 和订单信息。

批量取消现货订单

批量取消现货订单操作由 MsgBatchCancelSpotOrders 执行,该消息包含 Sender 和 Data 字段。

步骤

  1. 遍历 msg.Data,并按照 MsgCancelSpotOrder 中的步骤取消每个现货订单。

衍生品限价订单创建

衍生品限价订单创建操作由 MsgCreateDerivativeLimitOrder 执行,该消息包含 Sender 和 Order 字段。

步骤

  1. 检查衍生品交易是否已启用,若未启用则回滚操作。

  2. 如果子账户已在该市场下已下单(注意:限价订单和市场订单不能同时存在),则拒绝该订单。

  3. 获取衍生品市场和通过 marketID 获取标记价格。

  4. 获取指定 marketID 和 subaccountID 的订单簿元数据(SubaccountOrderbookMetadata)。

  5. 确保限价订单有效:

    • 市场配置(市场 ID 和刻度大小)。

    • 子账户交易 nonce。

    • 子账户最大订单数量。

  6. 如果是减少仓位订单:

    • 确保存在有效的仓位,并且方向相反。

    • 如果订单会导致其他减少仓位订单失效,则拒绝该订单。

  7. 如果是限价订单:

    • 确保子账户的存款足以支持保证金冻结。

    • 如果订单方向与现有仓位相反且会导致其他减少仓位订单失效,则取消失效的减少仓位订单。

  8. 将订单存储在临时限价订单存储和临时市场指示器存储中。

批量创建衍生品限价订单

批量创建衍生品限价订单操作由 MsgBatchCreateDerivativeLimitOrders 执行,该消息包含 Sender 和 Orders 字段。

步骤

  1. 遍历 msg.Orders,并按照 MsgCreateDerivativeLimitOrder 中的步骤创建每个衍生品限价订单。

衍生品市价订单创建

衍生品市场订单创建操作由 MsgCreateDerivativeMarketOrder 执行,该消息包含 Sender 和 Order 字段。

步骤

  1. 检查衍生品交易是否已启用,若未启用则回滚操作。

  2. 检查即将下单的 SubaccountID 是否已有限价衍生品订单或市场订单,若有则拒绝该订单。(注意:永续市场不能同时下两个市场订单或同时存在限价/市场订单)

  3. 检查订单的价格和数量是否符合市场的最小数量和价格刻度要求。

  4. 递增子账户的 TradeNonce。

  5. 如果衍生品市场 ID 没有引用有效的衍生品市场,则拒绝该订单。

  6. 使用 TradeNonce 计算唯一的订单哈希。

  7. 检查市场订单的最差价格是否达到最优相反方向订单簿的价格。

  8. 检查订单/仓位的保证金金额。

如果是减少仓位订单 A. 检查该子账户在该市场的仓位是否为非空。 B. 检查该订单是否能够平仓。 C. 如果 position.quantity - AggregateReduceOnlyQuantity - order.quantity < 0,则拒绝该订单。 D. 如果是卖出仓位,将保证金冻结金额设置为零。

如果不是减少仓位订单 A. 检查可用余额是否足以支持市场订单。 B. 如果子账户的可用存款不足以支付该订单所需的资金,则拒绝订单。 C. 从存款的可用余额中扣除冻结的资金。 对于相反方向的仓位,如果 AggregateVanillaQuantity > position.quantity - AggregateReduceOnlyQuantity - order.FillableQuantity,则可能会使某些现有的减少仓位订单失效,或使新订单本身无效,需要进行相应操作。

  1. 将订单存储在临时衍生品市场订单存储和临时市场指示器存储中。

取消衍生品订单

衍生品订单取消操作由 MsgCancelDerivativeOrder 执行,该消息包含 Sender、MarketId、SubaccountId 和 OrderHash 字段。

步骤

  1. 检查衍生品交易是否已启用,若未启用则回滚操作。

  2. 如果衍生品市场 ID 没有引用有效的衍生品市场,则拒绝该操作。

  3. 检查通过 marketID、subaccountID 和 orderHash 是否存在有效的衍生品限价订单。

  4. 将保证金冻结金额返还至可用余额。

  5. 如果订单类型不允许取消,则跳过取消该限价订单。

  6. 从 ordersStore、ordersIndexStore 和 subaccountOrderStore 中删除订单状态。

  7. 更新子账户的订单簿元数据。

  8. 触发 EventCancelDerivativeOrder 事件,包含 marketID 和订单信息。

批量取消衍生品订单

批量取消衍生品订单操作由 MsgBatchCancelDerivativeOrders 执行,该消息包含 Sender 和 Data 字段。

步骤

  1. 遍历 msg.Data,并按照 MsgCancelDerivativeOrder 中的步骤取消每个衍生品订单。

批量订单更新

批量更新订单操作由 MsgBatchUpdateOrders 执行,该消息包含 Sender 和 Orders 字段。

步骤

  1. 根据指定的 subaccountID,取消所有在 SpotMarketIdsToCancelAll 和 DerivativeMarketIdsToCancelAll 中列出的市场 ID 中的订单。

  2. 遍历 msg.SpotOrdersToCancel,并按照 MsgCancelSpotOrder 中的步骤取消现货限价订单。如果取消失败,继续下一个订单。取消成功的结果在 MsgBatchUpdateOrdersResponse 中反映为 SpotCancelSuccess。

  3. 遍历 msg.DerivativeOrdersToCancel,并按照 MsgCancelDerivativeOrder 中的步骤取消衍生品限价订单。如果取消失败,继续下一个订单。取消成功的结果在 MsgBatchUpdateOrdersResponse 中反映为 DerivativeCancelSuccess。

  4. 遍历 msg.SpotOrdersToCreate,并按照 MsgCreateSpotOrder 中的步骤创建现货限价订单。如果创建失败,继续下一个订单。创建成功的结果在 MsgBatchUpdateOrdersResponse 中反映为 SpotOrderHashes。

  5. 遍历 msg.DerivativeOrdersToCreate,并按照 MsgCreateDerivativeOrder 中的步骤创建衍生品限价订单。如果创建失败,继续下一个订单。创建成功的结果在 MsgBatchUpdateOrdersResponse 中反映为 DerivativeOrderHashes。

子账户间转账

从子账户之间的转账操作由 MsgSubaccountTransfer 执行,该消息包含 Sender、SourceSubaccountId、DestinationSubaccountId 和 Amount 字段。

步骤

  1. 从 msg.SourceSubaccountId 提取 msg.Amount,如果失败则回滚交易。

  2. 增加 msg.DestinationSubaccountId 的存款余额,增加的金额为 msg.Amount。

  3. 触发 EventSubaccountBalanceTransfer 事件,包含 SrcSubaccountId、DstSubaccountId 和 msg.Amount。

备注:对于子账户之间的转账,不需要从银行模块实际转移代币,只需要更改相关记录即可。

转账到外部账户

外部账户转账由 MsgExternalTransfer 执行,该消息包含 Sender、SourceSubaccountId、DestinationSubaccountId 和 Amount 字段。

步骤

  1. 从 msg.SourceSubaccountId 提取 msg.Amount,如果失败则回滚交易。

  2. 增加 msg.DestinationSubaccountId 的存款余额,增加的金额为 msg.Amount。

  3. 触发 EventSubaccountBalanceTransfer 事件,包含 SrcSubaccountId、DstSubaccountId 和 msg.Amount。

备注:对于子账户转账,不需要从银行模块实际转移代币,只需要更改相关记录即可。

  1. 事件应区分子账户转账和外部转账。

  2. 子账户转账和外部转账没有区别,仍然需要保持不同的消息吗?

清算仓位

清算仓位由 MsgLiquidatePosition 执行,该消息包含 Sender、SubaccountId、MarketId 和 Order 字段。

步骤

  1. 检查衍生品交易是否已启用以清算衍生品市场中的仓位,若未启用则回滚交易。

  2. 如果衍生品市场 ID 没有引用有效的衍生品市场,则拒绝该操作。

  3. 根据 marketID 获取衍生品市场和标记价格(markPrice)。

  4. 获取指定 marketID 和 subaccountID 的仓位信息。

  5. 根据仓位信息计算清算价格(liquidationPrice)和破产价格(bankruptcyPrice)。

  6. 确定是进行清算还是销毁仓位,如果无法全部清算,则回滚。

  7. 取消仓位持有者在给定市场中创建的所有减仓限价单。

  8. 应用资金并更新仓位信息。

  9. 取消仓位持有者在给定市场中创建的所有市场订单。

  10. 检查并增加子账户的交易序列号(nonce),计算订单哈希。

  11. 计算清算订单的哈希值。

  12. 将清算订单存储到存储中。

  13. 通过匹配仓位和清算订单执行清算。

  14. 根据清算的支付结果处理:

  • 正向支付:

    • 将一半的支付金额发送给清算人(激励清算机器人)。

    • 将另一半的支付金额发送到保险基金(激励参与保险基金的用户)。

  • 负向支付 - 四个级别的资金回收步骤:

    1. 从交易者的可用余额中回收。

    2. 通过取消交易者的限价单,从交易者的锁仓余额中回收。

    3. 从保险基金中回收。

    4. 如果资金不足,暂停市场并将市场添加到存储中,以便在下一个区块中结算,见 BeginBlocker 规范。

  1. 如果市场是永续市场,则根据清算价格和数量更新 VWAP 数据。

  2. 如果清算订单中仍有剩余,取消订单并返回剩余部分。

增加仓位保证金

增加仓位保证金由 MsgIncreasePositionMargin 执行,该消息包含 Sender、SourceSubaccountId、DestinationSubaccountId、MarketId 和 Amount 字段。

步骤

  1. 检查衍生品交易是否已启用以增加衍生品市场的仓位保证金,若未启用则回滚交易。

  2. 如果衍生品市场 ID 没有引用有效的衍生品市场,则拒绝该操作。

  3. 获取 sourceSubaccountID 的存款信息。

  4. 如果 deposit.AvailableBalance 小于 msg.Amount,则回滚交易。

  5. 根据 marketID 和 destinationSubaccountID 获取仓位信息,如果仓位不存在,则回滚交易。

  6. 从 sourceSubaccountID 减少 msg.Amount 的存款金额。

  7. 增加仓位保证金 msg.Amount 并更新存储中的仓位信息。

交易所启用提案

市场类型的启用通过 ExchangeEnableProposal 执行,该提案包含 Title、Description 和 ExchangeType 字段。

步骤

  1. 对提案进行 ValidateBasic 验证。

  2. 如果 p.ExchangeType 是现货市场(spot market),则启用现货交易。

  3. 如果 p.ExchangeType 是衍生品市场(derivative market),则启用衍生品交易。

现货市场启动提案

现货市场启动由 SpotMarketLaunchProposal 执行,该提案包含 Title、Description、Ticker、BaseDenom、QuoteDenom、MinPriceTickSize 和 MinQuantityTickSize 字段。

步骤

  1. 对提案进行 ValidateBasic 验证。

  2. 验证 BaseDenom 和 QuoteDenom 是否有效。

  3. 验证基于 msg.BaseDenom 和 msg.QuoteDenom 是否已有相同市场存在。

  4. 根据交易模块参数计算 RelayerFeeShareRate。注意:对于 INJ 货币,清算者分成比例设置为 100%。

  5. 保存现货市场,包含计算的 ticker、baseDenom、quoteDenom、exchangeParams.DefaultSpotMakerFeeRate、exchangeParams.DefaultSpotTakerFeeRate、relayerFeeShareRate、minPriceTickSize、minQuantityTickSize、marketID 和 MarketStatus_Active。

永续市场启动提案

永续市场启动由 PerpetualMarketLaunchProposal 执行,该提案包含 Title、Description、Ticker、QuoteDenom、OracleBase、OracleQuote、OracleScaleFactor、OracleType、MakerFeeRate、TakerFeeRate、InitialMarginRatio、MaintenanceMarginRatio、MinPriceTickSize 和 MinQuantityTickSize 字段。

步骤

  1. 对提案进行 ValidateBasic 验证。

  2. 验证 quoteDenom 是否有效。

  3. 根据 ticker、quoteDenom、oracleBase、oracleQuote 和 oracleType 计算 marketID。

  4. 验证指定的 marketID 是否已存在有效或无效的永续市场。

  5. 尝试通过 oracleBase、oracleQuote、oracleScaleFactor 和 oracleType 获取衍生品市场价格,以检查价格预言机。

  6. 验证 marketID 是否存在保险基金。

  7. 根据交易模块参数计算 defaultFundingInterval、nextFundingTimestamp 和 relayerFeeShareRate。

  8. 执行 SetDerivativeMarketWithInfo 将市场信息、市场信息和资金对象存储到存储中。

到期期货市场启动提案

到期期货市场启动由 ExpiryFuturesMarketLaunchProposal 执行,该提案包含 Title、Description、Ticker、QuoteDenom、OracleBase、OracleQuote、OracleScaleFactor、OracleType、Expiry、MakerFeeRate、TakerFeeRate、InitialMarginRatio、MaintenanceMarginRatio、MinPriceTickSize 和 MinQuantityTickSize 字段。

步骤

  1. 对提案进行 ValidateBasic 验证。

  2. 验证 quoteDenom 是否有效。

  3. 根据 p.Ticker、p.QuoteDenom、p.OracleBase、p.OracleQuote、p.OracleType 和 p.Expiry 计算 marketID。

  4. 验证指定的 marketID 是否已存在有效或无效的到期期货市场。

  5. 如果到期时间已经过去(即 ctx.BlockTime() 已经超过),则回滚。

  6. 尝试通过 oracleBase、oracleQuote、oracleScaleFactor 和 oracleType 获取衍生品市场价格,以检查价格预言机。

  7. 验证 marketID 是否存在保险基金。

  8. 根据交易模块参数计算 RelayerFeeShareRate。注意:对于 INJ 货币,清算者分成比例设置为 100%。

  9. 执行 SetDerivativeMarketWithInfo 将市场信息和市场信息对象存储到存储中。注意:TwapStartTimestamp 设置为到期时间减去 30 分钟的秒数。

现货市场参数更新提案

现货市场参数更新由 SpotMarketParamUpdateProposal 执行,该提案包含 Title、Description、MarketId、MakerFeeRate、TakerFeeRate、RelayerFeeShareRate、MinPriceTickSize、MinQuantityTickSize 和 Status 字段。

步骤

  1. 对提案进行 ValidateBasic 验证。

  2. 根据 p.MarketId 获取现货市场,如果不存在,则回滚。

  3. 如果参数不为空,则重置 MakerFeeRate、TakerFeeRate、RelayerFeeShareRate、MinPriceTickSize、MinQuantityTickSize 和 Status,如果为空,则保持不变。

  4. 验证 MakerFeeRate 是否大于 TakerFeeRate。

衍生品市场参数更新提案

衍生品市场参数更新由 DerivativeMarketParamUpdateProposal 处理,包含标题、描述、市场ID、初始保证金比例、维持保证金比例、做市商费用率、交易者费用率、分销商费用分成比例、最小价格刻度、最小数量刻度和状态。

步骤:

  1. 对提案进行基本验证。

  2. 通过 p.MarketId 验证衍生品市场是否存在,如果不存在,回滚交易。

  3. 如果不为空,则重置初始保证金比例、维持保证金比例、做市商费用率、交易者费用率、分销商费用分成比例、最小价格刻度、最小数量刻度和状态的参数;如果为空,保持原值。

  4. 验证做市商费用率大于交易者费用率。

  5. 验证初始保证金比例大于维持保证金比例。

  6. 调度衍生品市场参数更新,并在 Endblocker 上进行最终更新 - 注意:这是由于衍生品市场参数更新的订单更新,需确保此过程中不会发生异常。

交易奖励活动启动提案

步骤

  1. 对提案进行基本验证(ValidateBasic)。

  2. 不允许存在已启动的活动。

  3. 活动开始时间戳必须是未来时间。

  4. 活动的报价币种必须存在。

  5. 所有开始时间戳必须匹配持续时间。

  6. 设置活动数据(奖励池、信息、市场资格和市场积分乘数)。

  7. 触发 EventTradingRewardCampaignUpdate 事件。

交易奖励活动更新提案

步骤

  1. 对提案进行基本验证(ValidateBasic)。

  2. 所有在CampaignRewardPoolsUpdates中的StartTimestamp必须匹配现有活动。

  3. CampaignDurationSeconds不能修改,但必须与当前活动匹配。

  4. CampaignRewardPoolsUpdates不能修改当前活动,可以包含nil值来删除奖励池。

  5. 来自CampaignRewardPoolsAdditions的活动开始时间戳必须在未来。

  6. 所有活动的quote denoms必须存在。

  7. 删除当前活动的数据(信息、市场资格和市场点数乘数)。

  8. 设置活动数据(信息、市场资格和市场点数乘数)。

  9. 设置奖励池更新。

  10. 设置奖励池添加。

  11. 触发EventTradingRewardCampaignUpdate事件。

费率折扣计划提案

步骤

  1. 验证提案的基本信息

  2. 如果当前的费率折扣计划存在,则删除它以及市场资格

  3. 定义的报价币种必须存在

  4. 如果需要重启费率周期(例如桶数、桶持续时间或报价币种发生变化),则删除所有账户费率桶并重启周期

  5. 将第一个已付费桶的时间戳设置为当前区块时间

  6. 设置新的费率折扣计划,并删除它以及市场资格

  7. 设置新的市场资格

现货市场概念

定义

在现货市场中,AAA/BBB的交易对中,AAA是基础资产,BBB是报价资产。 例如,在ETH/USDT市场中:

  • ETH是基础资产

  • USDT是报价资产

现货市场的价格指的是购买一个单位ETH(基础资产)所需的USDT(报价资产)数量。对于所有现货市场,费用总是以报价资产支付,例如USDT。

借方与贷方

  • 借方金额是指从账户中提取的资产数量。

  • 贷方金额是指存入账户的资产数量。

退款 在我们的系统中,退款是指增加账户可用余额的操作。这种资金的释放发生在账户的负担解除时(例如取消限价单、将订单的应付费用调整为做市费用、使用较少的保证金来资助市场订单等)。

限价买单

限价买单旨在以指定数量的ETH(基础资产)交换指定数量 * 价格金额的USDT(报价资产),并支付相应的费用,这些费用取决于限价单是否作为做市单(maker order)或吃单(taker order)执行。

限价卖单

限价卖单旨在以指定数量的ETH(基础资产)交换数量 * 价格金额的USDT(报价资产),减去相应的费用,这些费用取决于限价单是否作为做市单(maker order)或吃单(taker order)执行。

市价买单

市场买单旨在以指定的最差价格(该价格位于当前卖价或其附近)购买指定数量的ETH(基础资产),并使用相应账户的报价资产余额(USDT)作为抵押(包含费用)。

因此,每个市场买单隐含地具有一个可接受的最高价格,因为如果市场单的成交价格超出该上限,将因资金不足而无法执行。

市价卖单

市场卖单旨在以指定的最差价格(该价格位于当前买价或其附近)卖出指定数量的ETH(基础资产),并换取市场上可用的任意数量的报价资产(USDT)。

因此,每个市场卖单隐含地具有一个零价格限制。

订单类型

  • BUY (1): 标准买单,可按当前市场价格或设定的限价购买资产。

  • SELL (2): 标准卖单,可按当前市场价格或设定的限价卖出资产。

  • STOP_BUY (3): 现货市场不支持此订单类型。

  • STOP_SELL (4): 现货市场不支持此订单类型。

  • TAKE_BUY (5): 现货市场不支持此订单类型。

  • TAKE_SELL (6): 现货市场不支持此订单类型。

  • BUY_PO (7): 仅挂单买单。此订单类型确保订单只会被添加到订单簿,而不会与已有订单匹配。它保证用户为市场的“做市商”(maker),而非“吃单者”(taker)。

  • SELL_PO (8): 仅挂单卖单。与BUY_PO类似,此订单确保卖单仅向订单簿提供流动性,而不会与已有订单匹配。

  • BUY_ATOMIC (9): 原子买单,该市场单会立即执行,绕过频繁批次拍卖(FBA)。此类型适用于需要即时执行交易的智能合约,并支付较高的费用,该费用由全球交易所参数定义。

  • SELL_ATOMIC (10): 原子卖单,类似于BUY_ATOMIC,会立即按当前市场价格执行,绕过FBA。

市场数据要求

除订单簿数据外,只要我们的链支持获取逐笔交易数据的基本能力,就可以通过聚合计算获得大多数必要的高阶数据,包括:

  • OHLCV 数据

  • 账户交易历史

  • 市场统计数据

现货市场生命周期

基于治理的现货市场创建

市场的创建方式有两种:通过 MsgInstantSpotMarketLaunch 创建市场,需支付额外费用,无需治理批准。或者通过 MsgSpotMarketLaunchProposal 提交提案,经治理批准后创建市场。

基于上架费用的现货市场创建

允许任何人通过燃烧预设的 SpotMarketInstantListingFee (以INJ计)来创建自己选择的现货市场,无需治理批准。 不过,我们仍然需要检查该币种是否有效。

现货市场状态更新

现货市场可以处于四种不同的状态:

  • 活动状态

  • 暂停状态

  • 挂起状态

  • 销毁状态

活动状态 如果现货市场处于活动状态,则可以接受订单和交易。

暂停状态 如果现货市场处于暂停状态,它将不再接受订单和交易,也不允许用户在该市场上进行任何操作(如取消订单)。

挂起状态 如果现货市场处于挂起状态,它将不再接受订单和交易,只允许交易者取消其订单。

销毁状态

当市场进入销毁状态时,所有未完成的订单将被取消。

市场状态转换

有三个状态转换对应以下状态变化:

  • 激活操作:从暂停或挂起状态 → 活动状态

  • 暂停操作:从活动或挂起状态 → 暂停状态

  • 挂起操作:从活动或暂停状态 → 挂起状态

  • 销毁操作:从暂停或挂起状态 → 销毁状态

现货市场参数更新

现货市场有以下参数:

  • SpotMarketInstantListingFee

  • DefaultSpotMakerFeeRate

  • DefaultSpotTakerFeeRate

衍生品市场概念

定义

在使用线性合约的衍生品市场中(与反向合约不同),一个标的为 AAA/BBB 的合约提供了对基础资产 AAA 的敞口,并使用报价货币 BBB 作为保证金和结算货币。对于每个合约,报价单位是 BBB 货币单位的 AAA 价格,例如 ETH 的 USDT 价格。

  • 名义价值(Notional) - 头寸的名义价值为:notional = quantity * price。

  • 退款(Refunds) - 在我们的清算系统中,退款指的是增加账户可用余额的操作。当账户上的负担解除时,资金将被释放(例如,取消限价订单、将订单的应付费用减少为做市商费用、使用较少的保证金来资助市场订单等)。

永久市场交易生命周期

永久市场创建

市场首先通过以下两种方式之一创建:

通过 MsgInstantPerpetualMarketLaunch 或 MsgInstantExpiryFuturesMarketLaunch 的即时启动功能创建市场,这种方式通过支付额外费用创建市场,不需要治理批准。

或者通过治理的正常方式创建市场,即通过 MsgPerpetualMarketLaunchProposal 或 MsgExpiryFuturesMarketLaunchProposal 提交提案。

余额管理

将资金存入交易所

交易者可以通过发送 MsgDeposit 将资金(例如 USDT)存入交易所,这会将币种从 Cosmos-SDK 银行模块转移到交易者在交易所模块中的子账户存款。

存入一定数量的币种将增加交易者子账户存款的 AvailableBalance 和 TotalBalance,增幅为存入的金额。

从交易所提取资金

交易者可以通过发送 MsgWithdraw 从交易所提取资金,这将从交易者在交易所模块中的子账户转移币种。

提取要求:提取一定数量的币种将减少交易者子账户存款的 AvailableBalance 和 TotalBalance,减少的金额为提取的数量。注意:提取金额必须小于或等于 AvailableBalance。

在子账户之间转移资金

交易者可以通过发送 MsgSubaccountTransfer 在自己的子账户之间转移资金,该操作会将币种从一个子账户存款转移到另一个同样由交易者拥有的子账户。

子账户转移与正常提取的要求相同。

转移资金到其他交易所账户

交易者可以通过发送 MsgExternalTransfer 将资金转移到外部账户,该操作会将资金从交易者的子账户转移到另一个第三方账户。

外部资金转移与正常提取的要求相同。

订单管理

下限价单

Note that it is possible for an order to be partially matched and for the remaining unmatched portion to be added to the orderbook.

交易者可以通过发送 MsgCreateDerivativeLimitOrder 发布限价买单或卖单。提交后,订单可以:

  • 在 Endblocker 批量拍卖中立即(完全或部分)与订单簿上的其他对立未成交订单进行撮合,从而为用户建立一个头寸。

  • 被添加到订单簿。

注意,订单可能会部分匹配,剩余未匹配的部分会被添加到订单簿中。

下市价单

交易者可以通过发送 MsgCreateDerivativeMarketOrder 发布市价买单或卖单。提交后,市价单将与订单簿上的其他对立未成交订单在 Endblocker 批量拍卖中执行,从而为用户建立一个头寸。

取消限价单

用户通过发送 MsgCancelDerivativeOrder 来取消限价买单或卖单,这将把用户的限价单从订单簿中移除。

增加头寸保证金

用户可以通过发送 MsgIncreasePositionMargin 来增加头寸的保证金。

平掉破产头寸

如果头寸的维持保证金比例被突破,第三方可以通过发送 MsgLiquidatePosition 来平掉任何用户的头寸。

初始保证金要求

这是新建头寸时保证金与订单名义价值和标记价格的比例要求。额外的标记价格要求旨在减少交易价格和标记价格在短时间内过度偏离时的强平风险。根据初始保证金比例,订单必须满足以下两个要求:

  1. 保证金要求: 保证金必须满足: Margin ≥ InitialMarginRatio * Price * Quantity 例如,在最大杠杆为 20 倍的市场中,初始保证金比例为 0.05。任何新建头寸的保证金必须至少为其名义价值的 0.05。

  2. 标记价格要求: 保证金必须满足标记价格要求: Margin ≥ Quantity * (InitialMarginRatio * MarkPrice - PNL)

其中,PNL 是头寸的预期盈亏,如果在当前标记价格下平仓的话。通过解方程得到标记价格的计算公式:

  • 买单:MarkPrice ≥ (Margin - Price * Quantity) / ((InitialMarginRatio - 1) * Quantity)

  • 卖单:MarkPrice ≤ (Margin + Price * Quantity) / ((InitialMarginRatio + 1) * Quantity)

维持保证金要求

在一个活跃仓位的生命周期中,如果未满足以下保证金要求,该仓位将面临平仓。(注意:为了简化符号表示,且不失一般性,我们假设所考虑的仓位没有任何融资)。

  • 对于多头:保证金 >= 数量 * 维持保证金比例 * 标记价格 - (标记价格 - 进场价格)

  • 对于空头:保证金 >= 数量 * 维持保证金比例 * 标记价格 - (进场价格 - 标记价格)

平仓支付

当您的仓位低于维持保证金比例时,任何人都可以平仓。链上的操作是自动创建一个与仓位大小相同的仅减仓市场订单。该市场订单的最差价格定义为无穷大或0,这意味着它将在订单簿中可用的任何价格上进行匹配。 执行仅减仓市场订单的支付不会直接给仓位所有者。相反,剩余资金的一部分将转移到清算机器人,另一部分将转移到保险基金。资金的分配比例由交易所参数中的LiquidatorRewardShareRate定义。如果仓位的支付为负,即仓位的负PNL大于其保证金,则保险基金将覆盖缺失的资金。 还需注意,清算会在一个区块内立即执行,而不会等到其他订单匹配发生后。

资金支付

资金支付仅适用于永续市场,作为一种将交易价格与标记价格对齐的机制。它指的是在每个资金周期结束时(例如每小时),多头和空头之间交换的定期支付。当资金利率为正时,多头支付空头;当资金利率为负时,空头支付多头。

  • 仓位大小 = 仓位数量 * 标记价格

  • 资金支付 = 仓位大小 * 每小时资金利率(HFR)

  • HFR = Cap((TWAP((SyntheticVWAPExecutionPrice - 标记价格) / 标记价格) + 日利率) * 1/24)

  • SyntheticVWAPExecutionPrice = (Price_A * Volume_A + Price_B * Volume_B + Price_C * Volume_C) / (Volume_A + Volume_B + Volume_C)

    • A 是市场买入批次执行

    • B 是市场卖出批次执行

    • C 是限价匹配批次执行

资金支付通过修改累计资金(CumulativeFunding)值来应用于整个市场。每个仓位存储当前的累计资金作为累计资金入口(CumulativeFundingEntry)。后续的资金支付仅在仓位变动时应用,并可以通过以下公式计算:

  • 资金支付

    • 对于多头:FundingPayment ← PositionQuantity * (CumulativeFunding - CumulativeFundingEntry)

    • 对于空头:FundingPayment ← PositionQuantity * (CumulativeFundingEntry - CumulativeFunding)

  • Margin' ← Margin + FundingPayment

  • CumulativeFundingEntry' ← CumulativeFunding

永续市场交易规范

仓位

交易者的仓位记录了交易者进入衍生品合约时的条件,定义如下:

仓位定义:

  • 数量

  • 进场价格

  • 保证金

  • 持仓数量

  • 累计资金入口

举例说明,考虑以下在ETH/USDT市场的仓位:

  • 数量 = -2

  • 进场价格 = 2200

  • 保证金 = 800

  • 持仓数量 = 1

  • 累计资金入口 = 4838123

该仓位代表在ETH/USDT市场中做空2张合约,保证金为800 USDT,进场价格为2200。持仓数量代表交易者的反向订单的仓位数量。累计资金入口代表该仓位上次更新时的累计资金值。

仓位对冲: 当一个新的普通订单与现有仓位的子账户匹配时,新仓位将是将现有仓位与新普通订单对冲后的结果。一个匹配的普通订单会产生一个仓位变化,定义为成交数量(FillQuantity)、成交保证金(FillMargin)和结算价格(ClearingPrice)。

将仓位变化应用于同方向的仓位:

  • 进场价格' ← (数量 * 进场价格 + 成交数量 * 结算价格) / (数量 + 成交数量)

  • 数量' ← 数量 + 成交数量

  • 保证金' ← 保证金 + 成交保证金

将仓位变化应用于反向仓位:

  • 进场价格 - 无变化

  • 数量' ← 数量 - 成交数量

  • 保证金' ← 保证金 * (数量 - 成交数量) / 数量

限价买单

限价买单是指以指定的价格购买一定数量的衍生品合约,并提供一定数量的保证金作为抵押。

限价卖单

限价卖单是指以指定的价格卖出一定数量的衍生品合约,并提供一定数量的保证金作为抵押。

匹配的仓位将扣除费用,这些费用取决于限价单是否作为做市单(maker order)或吃单(taker order)执行。

市价买单

市场买单是指以指定的最差价格购买一定数量的衍生品合约,并使用子账户的可用余额作为保证金抵押。

市场订单的处理器(Handler)和结束区块执行(EndBlocker)与限价买单(立即匹配的情况)的执行在概念上是相同的,因为交易者提供的保证金隐含地设定了一个最大价格限制,这一限制是由于初始的最低保证金要求所决定的。

市价卖单

市场卖单是指以指定的最差价格卖出一定数量的衍生品合约,并使用子账户的可用余额作为保证金抵押。

市场订单的处理器(Handler)和结束区块执行(EndBlocker)与限价卖单(立即匹配的情况)的执行在概念上是相同的,因为交易者提供的保证金隐含地设定了一个最低价格限制,这一限制是由于初始的最低保证金要求所决定的。

订单类型

  • BUY (1): 标准买单,按当前市场价格或设定的限价价格购买资产。

  • SELL (2): 标准卖单,按当前市场价格或设定的限价价格卖出资产。

  • STOP_BUY (3): 止损买单,当预言机价格达到或超过指定的触发价格时,转换为常规买

  • STOP_SELL (4): 止损卖单,当预言机价格降至或低于指定的触发价格时,转换为常规卖单。

  • TAKE_BUY (5): 止盈买单,当预言机价格达到或跌至指定的触发价格时,转换为常规买单。

  • TAKE_SELL (6): 止盈卖单,当预言机价格达到或超过指定的触发价格时,转换为常规卖单。

  • BUY_PO (7): 仅挂单买单。此订单类型确保订单只会被加入到订单簿中,而不会与已有订单匹配。它保证您将成为市场的“做市商”,而不是“吃单者”。

  • SELL_PO (8): 仅挂单卖单。与BUY_PO类似,确保您的卖单只会为订单簿提供流动性,而不会与已有订单匹配。

  • BUY_ATOMIC (9): 原子买单是一种市场单,立即执行,绕过频繁批次拍卖(FBA)。此类型适用于需要立即执行交易的智能合约。支付较高的费用,该费用在全球交易所参数中定义。

  • SELL_ATOMIC (10): 原子卖单类似于BUY_ATOMIC,它立即在当前市场价格下执行,绕过FBA。

仅减仓订单(卖出仓位)

限价买入仅减仓订单

限价买入仅减仓订单旨在通过指定数量的ETH(基础货币)减少现有的多头暴露。平仓时的支付将扣除相应的费用。

限价卖出仅减仓订单

限价卖出仅减仓订单旨在通过指定数量的ETH(基础货币)减少现有的空头暴露。平仓时的支付将扣除相应的费用。

交易所(Exchange)

摘要

交易模块是 Injective 链的核心,支持完全去中心化的现货和衍生品交易。 它是链上的必备模块,与拍卖、保险、预言机和 Peggy 模块紧密集成。 交易协议使交易者能够在任意现货和衍生品市场上创建和交易。订单簿管理、交易执行、订单匹配和结算的整个过程都通过交易模块中编码的逻辑在链上进行。 交易模块支持在两种类型的市场上进行代币交换:

  • 衍生品市场:包括永续掉期市场或期货市场。

  • 现货市场

目录

Injective Test Tube

快速入门

use cosmwasm_std::Coin;
use injective_test_tube::InjectiveTestApp;

// create new injective appchain instance.
let app = InjectiveTestApp::new();

// create new account with initial funds
let accs = app
    .init_accounts(
        &[
            Coin::new(1_000_000_000_000, "usdt"),
            Coin::new(1_000_000_000_000, "inj"),
        ],
        2,
    )
    .unwrap();

let admin = &accs[0];
let new_admin = &accs[1];

现在我们已经有了 appchain 实例和一些具有初始余额的账户,这些账户可以与 appchain 进行交互。这并不运行 Docker 实例或启动外部进程,它只是将 appchain 的代码作为库加载,以创建一个内存中的实例。 请注意,init_accounts 是一个便捷函数,它创建多个具有相同初始余额的账户。如果你只想创建一个账户,可以改为使用 init_account。

use cosmwasm_std::Coin;
use injective_test_tube::InjectiveTestApp;

let app = InjectiveTestApp::new();

let account = app.init_account(&[
    Coin::new(1_000_000_000_000, "usdt"),
    Coin::new(1_000_000_000_000, "inj"),
]);

现在,如果我们想测试一个 CosmWasm 合约,我们需要:

  1. 构建 wasm 文件

  2. 存储代码

  3. 实例化合约

然后,我们就可以开始与合约进行交互了。让我们一步步来操作。

use cosmwasm_std::Coin;
use cw1_whitelist::msg::{InstantiateMsg}; // for instantiating cw1_whitelist contract
use injective_test_tube::{Account, Module, InjectiveTestApp, Wasm};

let app = InjectiveTestApp::new();
let accs = app
    .init_accounts(
        &[
            Coin::new(1_000_000_000_000, "usdt"),
            Coin::new(1_000_000_000_000, "inj"),
        ],
        2,
    )
    .unwrap();
let admin = &accs[0];
let new_admin = &accs[1];

// ============= NEW CODE ================

// `Wasm` is the module we use to interact with cosmwasm related logic on the appchain
// it implements `Module` trait which you will see more later.
let wasm = Wasm::new(&app);

// Load compiled wasm bytecode
let wasm_byte_code = std::fs::read("./test_artifacts/cw1_whitelist.wasm").unwrap();
let code_id = wasm
    .store_code(&wasm_byte_code, None, admin)
    .unwrap()
    .data
    .code_id;
use cosmwasm_std::Coin;
use cw1_whitelist::msg::{InstantiateMsg, QueryMsg, AdminListResponse};
use injective_test_tube::{Account, Module, InjectiveTestApp, Wasm};

let app = InjectiveTestApp::new();
let accs = app
    .init_accounts(
        &[
            Coin::new(1_000_000_000_000, "usdt"),
            Coin::new(1_000_000_000_000, "inj"),
        ],
        2,
    )
    .unwrap();
let admin = &accs[0];
let new_admin = &accs[1];

let wasm = Wasm::new(&app);


let wasm_byte_code = std::fs::read("./test_artifacts/cw1_whitelist.wasm").unwrap();
let code_id = wasm
    .store_code(&wasm_byte_code, None, admin)
    .unwrap()
    .data
    .code_id;

// ============= NEW CODE ================

// instantiate contract with initial admin and make admin list mutable
let init_admins = vec![admin.address()];
let contract_addr = wasm
    .instantiate(
        code_id,
        &InstantiateMsg {
            admins: init_admins.clone(),
            mutable: true,
        },
        None, // contract admin used for migration, not the same as cw1_whitelist admin
        Some("Test label"), // contract label
        &[], // funds
        admin, // signer
    )
    .unwrap()
    .data
    .address;

// query contract state to check if contract instantiation works properly
let admin_list = wasm
    .query::<QueryMsg, AdminListResponse>(&contract_addr, &QueryMsg::AdminList {})
    .unwrap();

assert_eq!(admin_list.admins, init_admins);
assert!(admin_list.mutable);

现在,让我们执行合约并验证合约的状态是否正确更新。

use cosmwasm_std::Coin;
use cw1_whitelist::msg::{InstantiateMsg, QueryMsg, ExecuteMsg, AdminListResponse};
use injective_test_tube::{Account, Module, InjectiveTestApp, Wasm};

let app = InjectiveTestApp::new();
let accs = app
    .init_accounts(
        &[
            Coin::new(1_000_000_000_000, "usdt"),
            Coin::new(1_000_000_000_000, "inj"),
        ],
        2,
    )
    .unwrap();
let admin = &accs[0];
let new_admin = &accs[1];

let wasm = Wasm::new(&app);


let wasm_byte_code = std::fs::read("./test_artifacts/cw1_whitelist.wasm").unwrap();
let code_id = wasm
    .store_code(&wasm_byte_code, None, admin)
    .unwrap()
    .data
    .code_id;

// instantiate contract with initial admin and make admin list mutable
let init_admins = vec![admin.address()];
let contract_addr = wasm
    .instantiate(
        code_id,
        &InstantiateMsg {
            admins: init_admins.clone(),
            mutable: true,
        },
        None, // contract admin used for migration, not the same as cw1_whitelist admin
        Some("Test label"), // contract label
        &[], // funds
        admin, // signer
    )
    .unwrap()
    .data
    .address;

let admin_list = wasm
    .query::<QueryMsg, AdminListResponse>(&contract_addr, &QueryMsg::AdminList {})
    .unwrap();

assert_eq!(admin_list.admins, init_admins);
assert!(admin_list.mutable);

// ============= NEW CODE ================

// update admin list and rechec the state
let new_admins = vec![new_admin.address()];
wasm.execute::<ExecuteMsg>(
    &contract_addr,
    &ExecuteMsg::UpdateAdmins {
        admins: new_admins.clone(),
    },
    &[],
    admin,
)
.unwrap();

let admin_list = wasm
    .query::<QueryMsg, AdminListResponse>(&contract_addr, &QueryMsg::AdminList {})
    .unwrap();

assert_eq!(admin_list.admins, new_admins);
assert!(admin_list.mutable);

调试

使用模块包装器

在某些情况下,你可能想直接与 appchain 的逻辑进行交互,以设置环境或查询 appchain 的状态。模块包装器提供了方便的函数来与 appchain 的模块进行交互。 让我们尝试与 Exchange 模块进行交互:

use cosmwasm_std::{Addr, Coin};
use injective_std::types::injective::exchange::v1beta1::{
    MarketStatus, MsgInstantSpotMarketLaunch,
    QuerySpotMarketsRequest, QuerySpotMarketsResponse, SpotMarket,
};
use injective_test_tube::{Account, Exchange, InjectiveTestApp};
use test_tube_inj::Module;

let app = InjectiveTestApp::new();
let signer = app
    .init_account(&[
        Coin::new(10_000_000_000_000_000_000_000u128, "inj"),
        Coin::new(100_000_000_000_000_000_000u128, "usdt"),
    ])
    .unwrap();
let trader = app
    .init_account(&[
        Coin::new(10_000_000_000_000_000_000_000u128, "inj"),
        Coin::new(100_000_000_000_000_000_000u128, "usdt"),
    ])
    .unwrap();
let exchange = Exchange::new(&app);

exchange
    .instant_spot_market_launch(
        MsgInstantSpotMarketLaunch {
            sender: signer.address(),
            ticker: "INJ/USDT".to_owned(),
            base_denom: "inj".to_owned(),
            quote_denom: "usdt".to_owned(),
            min_price_tick_size: "10000".to_owned(),
            min_quantity_tick_size: "100000".to_owned(),
        },
        &signer,
    )
    .unwrap();

exchange
    .instant_spot_market_launch(
        MsgInstantSpotMarketLaunch {
            sender: signer.address(),
            ticker: "INJ/USDT".to_owned(),
            base_denom: "inj".to_owned(),
            quote_denom: "usdt".to_owned(),
            min_price_tick_size: "10000".to_owned(),
            min_quantity_tick_size: "100000".to_owned(),
        },
        &signer,
    )
    .unwrap_err();

app.increase_time(1u64);

let spot_markets = exchange
    .query_spot_markets(&QuerySpotMarketsRequest {
        status: "Active".to_owned(),
        market_ids: vec![],
    })
    .unwrap();

let expected_response = QuerySpotMarketsResponse {
    markets: vec![SpotMarket {
        ticker: "INJ/USDT".to_string(),
        base_denom: "inj".to_string(),
        quote_denom: "usdt".to_string(),
        maker_fee_rate: "-100000000000000".to_string(),
        taker_fee_rate: "1000000000000000".to_string(),
        relayer_fee_share_rate: "400000000000000000".to_string(),
        market_id: "0xd5a22be807011d5e42d5b77da3f417e22676efae494109cd01c242ad46630115"
            .to_string(),
        status: MarketStatus::Active.into(),
        min_price_tick_size: "10000".to_string(),
        min_quantity_tick_size: "100000".to_string(),
    }],
};
assert_eq!(spot_markets, expected_response);

版本管理

injective-test-tube 的版本由其依赖项 injective 和 test-tube 的版本以及自身的更改决定。版本表示为 A.B.C 格式,其中:

  • A 是 injective 的主版本号,

  • B 是 test-tube 的次版本号,

  • C 是 injective-test-tube 本身的修补版本号。

当发布新的 injective 版本且包含破坏性更改时,如果 test-tube 也有破坏性更改,我们也会发布这些更改,并递增 injective-test-tube 的主版本号。这样,可以明确新版本的 injective-test-tube 不向后兼容之前的版本。

当向 injective-test-tube 添加向后兼容的新功能时,将递增次版本号。

当修复 bug 或进行其他 injective-test-tube 特定且向后兼容的更改时,将递增修补版本号。

请查阅升级指南以了解如何升级包,以应对破坏性更改。

需要注意的是,我们跟踪包的版本与依赖项的版本是独立的。

EndBlock

拍卖结算

给定拍卖轮次的结算发生在 blockTime ≥ EndingTimeStamp 时。如果在此期间有非零的 INJ 出价(即存在 LastBid),则会执行以下流程:

  1. 获胜的 INJ 出价金额将被销毁。

  2. 拍卖模块持有的代币篮子将转移给获胜竞标者。

  3. LastAuctionResult 会写入状态,并触发 EventAuctionResult 事件。

  4. LastBid 被清除。

  5. AuctionRound 递增 1,EndingTimestamp 递增 AuctionPeriod。

  6. 累积的交易手续费将从交易模块转移到拍卖模块,为即将到来的新一轮拍卖做准备。

  7. 如果该轮拍卖未有任何有效出价,现有的代币篮子将被转入下一轮拍卖,并与新的累积手续费篮子合并。

状态

Genesis 状态定义了模块的初始状态,用于设置模块。

Params

Params 是一个模块级配置,用于存储系统参数并定义交易所模块的整体功能。该配置可以通过治理使用由治理模块原生支持的参数更新提案进行修改。 它定义了用于现货市场和衍生品市场的默认费用对象,以及衍生品市场的资金参数和即时上市费用。 用于交易所模块参数存储的 Protobuf 接口。

Balance

Balance 用于管理账户余额。该模块将整个余额存储在模块账户中,而每个账户的余额则作为记录进行管理。 余额对象按 subaccount_id 和 denom 存储。

SubaccountNonce

SubaccountNonce 用于表示唯一的订单哈希。

Order

有多个结构用于将订单存储到存储中。

SpotMarket

SpotMarket 是用于存储现货市场所需的所有信息和状态的结构。现货市场通过市场的哈希值存储,以便高效查询市场。

SpotOrderBook

SpotOrderBook 是用于存储特定市场的现货限价订单的结构。创建了两个对象,一个用于买单,另一个用于卖单。

DerivativeMarket

DerivativeMarket 是用于存储衍生品市场所需的所有信息和状态的结构。衍生品市场通过市场的哈希值存储,以便高效查询市场。

DerivativeOrderBook

DerivativeOrderBook 是用于存储特定市场的衍生品限价订单的结构。为买单和卖单分别创建两个对象。

DerivativePosition

DerivativePosition 是用于存储特定市场上子账户的衍生品头寸的结构。 注意:衍生品订单代表意图,而头寸代表持有。

ExpiryFuturesMarketInfo

ExpiryFuturesMarketInfo 是用于存储到期期货市场信息的结构。它通过市场的 ID 存储。

PerpetualMarketInfo

PerpetualMarketInfo 是用于存储永续市场信息的结构。

PerpetualMarketFunding

PerpetualMarketFunding 是用于管理永续市场资金信息的结构。

Trading Rewards

CampaignRewardPool

CampaignRewardPool 是一个结构,用于获取即将到来的交易奖励池。

TradingRewardCampaignInfo

TradingRewardCampaignInfo 是一个结构,用于获取交易奖励活动的信息。

FeeDiscountProposal

FeeDiscountProposal 是一个结构,用于提议新的费用折扣计划和持续时间。

DerivativeMarketSettlementInfo

DerivativeMarketSettlementInfo 是一个结构,用于管理计划结算的衍生品市场。

TradeLog

交易日志通过事件发出,以跟踪交易历史。

Enums

枚举用于描述订单类型、执行类型和市场状态。

BeginBlock

1. 处理每小时资金支付

  1. 检查第一个接收资金支付的市场。如果第一个市场尚未到期接收资金(资金时间戳尚未达到),则跳过所有资金支付。

  2. 否则逐一检查每个市场:

    1. 如果资金时间戳尚未到期,跳过该市场。

    2. 计算资金,公式为 twap + hourlyInterestRate,其中 $\mathrm{twap = \frac{cumulativePrice}{timeInterval * 24}}$,其中 $\mathrm{timeInterval = lastTimestamp - startingTimestamp}$。cumulativePrice 是通过每笔交易计算的加权时间差:$\mathrm{\frac{VWAP - markPrice}{markPrice} * timeElapsed}$。

    3. 如果需要,将资金限制在 HourlyFundingRateCap 定义的最大值。

    4. 设置下一个资金时间戳。

    5. 发出 EventPerpetualMarketFundingUpdate 事件。

2. 处理计划结算的市场

对于待结算的每个市场:

  1. 使用零结算费用和当前标记价格结算市场。

    1. 运行社会化损失。这将计算所有市场中缺少的资金总额,然后按比例减少每个盈利头寸的支付。例如,一个市场缺少100 USDT的资金,且有10个相同数量的盈利头寸,则每个头寸的支付将减少10 USDT。

    2. 所有头寸都将被强制平仓。

  2. 从存储中删除。

3. 处理到期的期货市场

对于每个到期的期货市场,从第一个到期的市场开始迭代:

  1. 如果市场是过早的,停止迭代。

  2. 如果市场已禁用,从存储中删除市场并继续下一个市场。

  3. 从预言机获取该市场的累积价格。

  4. 如果市场开始成熟,存储该市场的 startingCumulativePrice。

  5. 如果市场已成熟,计算结算价格为 $\mathrm{twap = (currentCumulativePrice - startingCumulativePrice) / twapWindow}$,并将其添加到待结算市场列表中。 使用定义的结算费用和结算价格结算所有成熟的市场。该过程与之前的结算过程相同(见上文)。注意,社会化损失是一个可选步骤。在常规情况下,市场不需要任何社会化损失。

  6. 从存储中删除所有已结算的市场。

4. 处理交易奖励

  1. 检查当前交易奖励活动是否已结束。

  2. 如果活动已结束,向符合条件的交易员分发奖励代币。

    1. 计算每种奖励币种的可用奖励,公式为 min(campaignRewardTokens, communityPoolRewardTokens)。

    2. 根据交易份额计算每个交易员的奖励,公式为 accountPoints * totalReward / totalTradingRewards。

    3. 从社区池中向交易员发送奖励代币。

    4. 重置所有账户的交易奖励积分和总积分。

    5. 删除当前活动的结束时间戳。

  3. 如果启动新活动,设置下一个当前活动结束时间戳为 CurrentCampaignStartTimestamp + CampaignDurationSeconds。

  4. 如果没有进行中的活动,也没有启动新活动,从存储中删除活动信息、市场资格和市场倍数。

5. 处理费用折扣桶

  • 如果最旧的桶的结束时间戳早于 block.timestamp - bucketCount * bucketDuration:

    • 修剪最旧的桶。

    • 遍历所有的 bucketStartTimestamp + account → FeesPaidAmount:

      • 从每个账户的 totalPastBucketFeesPaidAmount 中减去 FeesPaidAmount。

      • 删除账户的 account → {tier, TTL timestamp}。注意,技术上这对于正确性并不是必须的,因为我们会在 Endblocker 中检查 TTL 时间戳,但这是一种状态修剪策略。

    • 更新 CurrBucketStartTimestamp ← CurrBucketStartTimestamp + BucketDuration。

消息

Msg/Deposit

MsgDeposit 定义了一个 SDK 消息,用于将代币从发送者的银行余额转入子账户的交易所存款。

字段描述

  • Sender 字段描述了进行存款的地址。

  • SubaccountId 描述了接收存款的子账户 ID。

  • Amount 指定了存款的金额。

Msg/Withdraw

MsgWithdraw 定义了一个 SDK 消息,用于从子账户的存款中提取币到用户的银行余额。

字段描述:

  • Sender:描述发起提取操作的地址。

  • SubaccountId:描述接收提取的子账户ID。

  • Amount:指定提取的金额。

Msg/InstantSpotMarketLaunch

MsgInstantSpotMarketLaunch 定义了一个 SDK 消息,用于通过支付上市费用在没有治理的情况下创建一个新的现货市场。费用将发送到社区支出池。

字段描述

  • Sender 字段描述了此消息的创建者。

  • Ticker 描述了现货市场的交易代码。

  • BaseDenom 指定了用作基础货币的币种类型。

  • QuoteDenom 指定了用作报价货币的币种类型。

  • MinPriceTickSize 定义了订单价格的最小价格跳动。

  • MinQuantityTickSize 定义了订单数量的最小数量跳动。

Msg/InstantPerpetualMarketLaunch

MsgInstantPerpetualMarketLaunch 定义了一个 SDK 消息,用于通过支付上市费用来创建一个新的永久期货市场,无需治理。费用将发送到社区支出池。

字段描述

  • Sender 字段描述了此消息的创建者。

  • Ticker 字段描述了衍生品市场的交易代码。

  • QuoteDenom 字段描述了用于基础货币的币种类型。

  • OracleBase 字段描述了预言机的基础货币。

  • OracleQuote 字段描述了预言机的报价货币。

  • OracleScaleFactor 字段描述了预言机价格的缩放因子。

  • OracleType 字段描述了预言机的类型。

  • MakerFeeRate 字段描述了衍生品市场上做市商的交易费率。

  • TakerFeeRate 字段描述了衍生品市场上吃单者的交易费率。

  • InitialMarginRatio 字段描述了衍生品市场的初始保证金比例。

  • MaintenanceMarginRatio 字段描述了衍生品市场的维持保证金比例。

  • MinPriceTickSize 字段描述了订单价格和保证金的最小刻度。

  • MinQuantityTickSize 字段描述了订单数量的最小刻度。

Msg/InstantExpiryFuturesMarketLaunch

MsgInstantExpiryFuturesMarketLaunch 定义了一个 SDK 消息,用于通过支付上市费用创建一个新的到期期货市场,无需治理。费用将发送到社区支出池。

字段描述

  • Sender:描述此消息的创建者。

  • Ticker:描述衍生品市场的标记符。

  • QuoteDenom:描述用作报价货币的币种类型。

  • OracleBase:描述预言机基础货币。

  • OracleQuote:描述预言机报价货币。

  • OracleScaleFactor:描述预言机价格的缩放因子。

  • OracleType:描述预言机类型。

  • Expiry:描述市场的到期时间。

  • MakerFeeRate:描述衍生品市场上做市商的交易费用率。

  • TakerFeeRate:描述衍生品市场上交易者的交易费用率。

  • InitialMarginRatio:描述衍生品市场的初始保证金比率。

  • MaintenanceMarginRatio:描述衍生品市场的维持保证金比率。

  • MinPriceTickSize:描述订单价格和保证金的最小刻度大小。

Msg/CreateSpotLimitOrder

MsgCreateSpotLimitOrder 定义了一个 SDK 消息,用于创建一个新的现货限价订单。

字段描述

  • Sender 字段描述了此消息的创建者。

  • Order 字段描述了订单信息。

Msg/BatchCreateSpotLimitOrders

MsgBatchCreateSpotLimitOrders 定义了一个 SDK 消息,用于创建一批新的现货限价订单。

字段描述

  • Sender 字段描述了此消息的创建者。

  • Order 字段描述了订单信息。

Msg/CreateSpotMarketOrder

MsgCreateSpotMarketOrder 定义了一个 SDK 消息,用于创建一个新的现货市场订单。

字段描述

  • Sender 字段描述了此消息的创建者。

  • Order 字段描述了订单信息。

Msg/CancelSpotOrder

MsgCancelSpotOrder 定义了一个消息,用于取消一个现货订单。

字段描述

  • Sender 字段描述了此消息的创建者。

  • MarketId 字段描述了订单所在市场的 ID。

  • SubaccountId 字段描述了下单的子账户 ID。

  • OrderHash 字段描述了订单的哈希值。

Msg/BatchCancelSpotOrders

MsgBatchCancelSpotOrders 定义了批量取消现货订单的消息。

字段描述

  • Sender 字段描述了该消息的创建者。

  • Data 字段描述了要取消的订单。

Msg/CreateDerivativeLimitOrder

MsgCreateDerivativeLimitOrder 定义了创建衍生品限价订单的消息。

字段描述

  • Sender 字段描述了消息的创建者。

  • Order 字段描述了订单的详细信息。

Batch creation of derivative limit orders

MsgBatchCreateDerivativeLimitOrders 描述了批量创建衍生品限价订单的消息。

字段描述

  • Sender 字段描述了消息的创建者。

  • Order 字段描述了订单的详细信息。

Msg/CreateDerivativeMarketOrder

MsgCreateDerivativeMarketOrder 是用于创建衍生品市场订单的消息。

字段描述

  • Sender 字段描述了消息的创建者。

  • Order 字段描述了订单的详细信息。

Msg/CancelDerivativeOrder

MsgCancelDerivativeOrder 是用于取消衍生品订单的消息。

字段描述

  • Sender 字段描述此消息的创建者。

  • MarketId 字段描述订单所在市场的 ID。

  • SubaccountId 字段描述下单的子账户 ID。

  • OrderHash 字段描述订单的哈希值。

Msg/BatchCancelDerivativeOrders

MsgBatchCancelDerivativeOrders 是一个批量取消衍生品订单的消息。

字段描述

  • Sender 字段描述此消息的创建者。

  • Data 字段描述要取消的订单。

Msg/SubaccountTransfer

MsgSubaccountTransfer 是一条用于在子账户之间转移余额的消息。

字段描述

  • Sender 字段描述消息的创建者。

  • SourceSubaccountId 字段描述发送币的源子账户。

  • DestinationSubaccountId 字段描述接收币的目标子账户。

  • Amount 字段描述要发送的币的数量。

Msg/ExternalTransfer

MsgExternalTransfer 是一条将余额从源账户转移到外部子账户的消息。

字段描述

  • Sender 字段描述消息的创建者。

  • SourceSubaccountId 字段描述发送币的源子账户。

  • DestinationSubaccountId 字段描述接收币的目标子账户。

  • Amount 字段描述要发送的币的数量。

Msg/LiquidatePosition

MsgLiquidatePosition 描述了一条清算账户持仓的消息。

字段描述

  • Sender 字段描述消息的创建者。

  • SubaccountId 字段描述接收清算金额的子账户。

  • MarketId 字段描述持仓所在的市场。

  • Order 字段描述订单信息。

Msg/IncreasePositionMargin

MsgIncreasePositionMargin 描述了一条增加账户保证金的消息。

字段描述

  • Sender 字段描述消息的创建者。

  • SourceSubaccountId 字段描述发送余额的源子账户。

  • DestinationSubaccountId 字段描述接收余额的目标子账户。

  • MarketId 字段描述持仓所在的市场。

  • Amount 字段描述增加的金额。

Msg/BatchUpdateOrders

MsgBatchUpdateOrders 允许原子级别地取消和创建现货及衍生品限价订单,并引入了一种新的订单取消模式。执行时,首先进行订单取消(如果有),然后进行订单创建(如果有)。

字段描述

  • Sender 字段描述消息的创建者。

  • SubaccountId 字段描述发送者的子账户 ID。

  • SpotMarketIdsToCancelAll 字段描述发送者希望取消所有未结订单的现货市场 ID 列表。

  • DerivativeMarketIdsToCancelAll 字段描述发送者希望取消所有未结订单的衍生品市场 ID 列表。

  • SpotOrdersToCancel 字段描述发送者希望取消的具体现货订单。

  • DerivativeOrdersToCancel 字段描述发送者希望取消的具体衍生品订单。

  • SpotOrdersToCreate 字段描述发送者希望创建的现货订单。

  • DerivativeOrdersToCreate 字段描述发送者希望创建的衍生品订单。

事件

交易模块会触发以下事件:

其他概念

并发友好的市场订单清算价格算法

  1. 在所有市场中并发匹配所有可匹配的订单(详细信息见订单匹配)。

  • 中间结果是清算价格和已匹配订单的列表,包括它们的成交数量。

  • 最终结果是所有新事件的临时缓存,以及所有位置、订单、子账户存款、交易奖励积分和已支付费用的更改。

  1. 等待所有市场的执行并持久化所有数据。

注意:除了执行结算外,设计还必须考虑市场数据传播的需求,以供链下消费。

原子市场订单执行

一个常见的请求是,基于Cosmwasm构建的新应用程序希望在订单执行时收到通知。在常规的订单执行流程中,这是不可能的,因为频繁批量拍卖(FBA)是在EndBlocker内执行的。为了绕过FBA,介绍了一种新的原子市场订单类型。为了能够即时执行这种原子市场订单,需要额外的交易费用。计算原子市场订单费用时,市场的taker费用会乘以市场类型的AtomicMarketOrderFeeMultiplier。

  • SpotAtomicMarketOrderFeeMultiplier

  • DerivativeAtomicMarketOrderFeeMultiplier

  • BinaryOptionsAtomicMarketOrderFeeMultiplier

这些乘数由全球交易所参数定义。此外,交易所参数还定义了AtomicMarketOrderAccessLevel,指定执行原子市场订单所需的最低访问级别。

交易奖励

治理通过批准一个 TradingRewardCampaignLaunchProposal 提交,该提案指定:

  • 第一个活动的开始时间戳

  • TradingRewardCampaignInfo,其中指定:

    • 活动持续时间(秒)

    • 接受的交易手续费报价货币符号

    • 可选的市场特定的加成信息

    • 排除的市场ID,这些市场的交易将无法获得奖

  • CampaignRewardPools,指定每个连续活动的最大时期奖励,这构成了每个活动的交易奖励池

在每个活动期间,交易所将记录每个交易者从所有合格市场获得的累计交易奖励积分(如果适用,加成会被应用),即报价货币匹配且不在排除列表中的市场。

在每个活动结束时,即活动开始时间戳 + 活动持续时间经过后,每个交易者将根据其在该活动时期的交易奖励积分,按比例获得交易奖励池的份额。

活动不会自动续期。如果在 CampaignRewardPools 中没有定义额外的活动,则交易奖励活动将结束。

费用折扣

治理通过批准一个 FeeDiscountProposal 提交,该提案定义了一个费用折扣计划,其中指定了折扣阶梯,每个阶梯都规定了如果交易者满足指定的最低 INJ 持仓量,并且在指定的时间段内(桶数 * 桶持续时间秒数,通常为 30 天)完成了至少指定交易量(基于指定的报价货币符号),交易者将获得的做市商和吃单者折扣率。该计划还指定了一个排除的市场ID列表,这些市场的交易量将不计入交易量贡献。

  • 在被接受的报价货币符号列表中的现货市场,基础资产和报价资产都属于该列表的市场将不会获得奖励(例如,USDC/USDT 现货市场)。

  • 在具有负做市商费用的市场中,做市商的成交不会为交易者提供任何费用折扣。

  • 如果费用折扣提案通过的时间少于 30 天,即提案创建以来还没有经过 BucketCount * BucketDuration(30 天),那么费用交易量要求将被忽略,以避免对立即入驻的做市商进行不公平的惩罚。

内部的交易量存储在桶中,通常有 30 个桶,每个桶持续 24 小时。当某个桶超过 30 天时,它将被移除。此外,为了提高性能,会缓存一个账户的费用折扣阶梯,该缓存每 24 小时更新一次。

事件

拍卖模块会触发以下事件:

处理器(Handlers)

Msg/Bid

EndBlocker

二元期权市场概念

概念

二元期权市场与其他市场不同,它没有基础资产,报价通常使用USDT(未来可能会添加其他报价资产)。二元期权市场的交易对通常遵循类似 UFC-KHABIB-TKO-09082022 的命名规则。通常,二元期权市场用于对体育赛事进行投注,但也可以用于对任何事件结果进行投注。所有市场的价格区间在 $0.00 到 $1.00 之间,用户可以提交从 $0.01 到 $0.99 的订单($0.00 和 $1.00 分别表示事件未发生或已发生的结束条件)。订单中提交的价格本质上是对给定事件(市场)发生的假设概率。

对于所有二元期权市场,费用始终以报价资产(如 USDT)支付。

这类市场没有杠杆,因为用户在一个零和市场中相互交易。由此隐含了另一个要求:如果一方认为事件将发生(YES方),且当前市场的概率为P(即当前市场价格为P),那么反方(NO方)应该确信该事件不会发生,概率为(1-P)。因此,如果YES方以价格P购买Q份合约,他需要将QP的余额作为保证金,而反方(卖方)则需要将Q(1-P)的报价余额作为保证金。

示例:

Alice以$0.20的价格购买1份合约(以$0.20作为保证金)对抗Bob,Bob以$0.20的价格卖出1份合约(以$0.80作为保证金),为双方创建了头寸。

  • 如果市场最终以$1结算,Alice将赢得$0.80,Bob将赢得$0.20,如果市场结算为$0,则结果相反。

预言机

二元期权市场与提供者预言机类型紧密关联,允许一个治理注册的提供者在其子类型下中继价格数据,支持添加任意新的价格数据源,而无需额外的治理批准。每个二元期权市场由以下预言机参数组成:

  • 预言机符号(例如:UFC-KHABIB-TKO-09082022)

  • 预言机提供者(例如:frontrunner)

  • 预言机类型(必须为provider类型)

  • 预言机规模因子(例如:如果报价计价单位是USDT,则为6)

预言机的主要目标是发布事件的最终结果。该最终价格将以该确切价格结算市场。这个价格通常为0或1,反映二元结果。

此外,市场也可以在(0, 1)价格区间内的任何价格结算。如果预言机发布的settlement_price介于0和1之间,所有头寸将在settlement_price(例如:0.42)处结算。如果预言机价格超过1,结算价格将四舍五入为1。

预言机还可以发布最终价格为-1,这将作为触发所有当前市场头寸退款并销毁市场的标志价格。如果在结算之前没有任何预言机更新,则默认使用-1触发所有头寸的退款。

关于预言机提供者类型的进一步文档可以在预言机模块文档中找到。

注册预言机提供者

要注册您的预言机提供者,您需要提交一个 GrantProviderPrivilegeProposal 治理提案。这个提案将注册您的预言机提供者,并允许您的地址中继价格数据。

一旦提案通过,您的提供者将被注册,您将能够中继您的价格数据(如下例所示)。

市场生命周期

市场创建

二元期权市场可以通过即时启动(通过 MsgInstantBinaryOptionsMarketLaunch)或通过治理(通过 BinaryOptionsMarketLaunchProposal)创建。 该市场可以选择配置一个市场管理员,管理员具有触发结算、更改市场状态以及修改给定市场的到期时间和结算时间戳的能力。如果市场没有指定管理员,则市场参数只能通过治理进行修改,结算程序将完全基于相关的预言机提供者价格数据。

市场状态转换

二元期权市场在 Injective 上可以有三种状态:激活、到期或拆除。市场创建后,市场处于激活状态,这表示个人可以开始交易。 特别地,二元期权市场还具有一个特征 ExpirationTimestamp,它指定了市场交易活动停止的截止时间,以及一个 SettlementTimestamp,它指定了结算发生的截止时间(必须在到期之后)。

  • Active = 交易开启

  • Expired = 交易关闭,未完成的订单被取消,仓位不变

  • Demolished = 仓位已结算/退款(取决于结算情况),市场被拆除

二元期权市场状态转换的性质如下:

市场结算

结算市场可以通过以下两种方式之一实现:

  1. 使用为特定市场注册的提供者oracle。一旦提供者oracle获得了传递价格的权限(上面已解释),具有该权限的地址可以使用MsgRelayProviderPrices消息为特定价格信息源传递价格。

  1. 使用MsgAdminUpdateBinaryOptionsMarket,允许市场的管理员(创建者)直接向市场提交结算价格。

injective-test-tube 是一个 CosmWasm 与 Injective 集成的测试库,与 cw-multi-test 不同,它允许你将 CosmWasm 合约与链的实际逻辑进行测试,而不是使用模拟。 dev 分支依赖于当前私有的仓库,但你可以使用发布的版本。请参阅 以获取功能和更新信息。

为了演示 injective-test-tube 的工作原理,我们将使用一个简单的示例合约:来自 cw-plus 的 。 以下是如何设置测试的步骤:

需要注意的是,在此示例中,它从 发布中加载 wasm 字节码,以便进行简单演示。你可能需要运行 cargo wasm,并在 target/wasm32-unknown-unknown/release/<contract_name>.wasm 中找到你的 wasm 文件。

在你的合约代码中,如果你想进行调试,可以使用 ,它会将调试消息打印到标准输出。wasmd 默认禁用了这个功能,但 InjectiveTestApp 允许输出到标准输出,这样你就可以在运行测试时调试你的智能合约。

更多示例可以在目录中找到。

交易所的 在每个区块的开始运行,作为我们定义顺序中的最后一个模块。

在本节中,我们描述了交易所消息的处理过程以及相应的状态更新。每个消息所创建/修改的状态对象都在部分进行了定义。

我们应用范式来利用并发进行高效的数据处理。

类型
属性键(Attribute Key)
Attribute Value
累心
属性键(Attribute Key)
Attribute Value
状态改变
工作流

结算价格选项在上面的部分中解释。

衍生品市场概念
现货市场概念
其他概念
状态
状态转换
消息
提案
Begin Block
End Block
事件
参数
MsgPrivilegedExecuteContract
CHANGELOG
cw-whitelist
cw-plus release
deps.api.debug(..)
模块
// GenesisState defines the exchange module's genesis state.
type GenesisState struct {
	// params defines all the parameters of related to exchange.
	Params Params
	// accounts is an array containing the genesis trade pairs
	SpotMarkets []*SpotMarket
	// accounts is an array containing the genesis derivative markets
	DerivativeMarkets []*DerivativeMarket
	// spot_orderbook defines the spot exchange limit orderbook active at genesis.
	SpotOrderbook []SpotOrderBook
	// derivative_orderbook defines the derivative exchange limit orderbook active at genesis.
	DerivativeOrderbook []DerivativeOrderBook
	// balances defines the exchange users balances active at genesis.
	Balances []Balance
	// positions defines the exchange derivative positions at genesis
	Positions []DerivativePosition
	// subaccount_trade_nonces defines the subaccount trade nonces for the subaccounts at genesis
	SubaccountTradeNonces []SubaccountNonce
	// expiry_futures_market_info defines the market info for the expiry futures markets at genesis
	ExpiryFuturesMarketInfoState []ExpiryFuturesMarketInfoState
	// perpetual_market_info defines the market info for the perpetual derivative markets at genesis
	PerpetualMarketInfo []PerpetualMarketInfo
	// perpetual_market_funding_state defines the funding state for the perpetual derivative markets at genesis
	PerpetualMarketFundingState []PerpetualMarketFundingState
	// derivative_market_settlement_scheduled defines the scheduled markets for settlement at genesis
	DerivativeMarketSettlementScheduled []DerivativeMarketSettlementInfo
	// sets spot markets as enabled
	IsSpotExchangeEnabled               bool
	// sets derivative markets as enabled
	IsDerivativesExchangeEnabled        bool
	// the current trading reward campaign info
	TradingRewardCampaignInfo           *TradingRewardCampaignInfo
	// the current and upcoming trading reward campaign pools
	TradingRewardPoolCampaignSchedule   []*CampaignRewardPool
	// the current and upcoming trading reward account points
	TradingRewardCampaignAccountPoints  []*TradingRewardCampaignAccountPoints
	// the current and upcoming trading reward campaign pending pools
	PendingTradingRewardPoolCampaignSchedule []*CampaignRewardPool
	// the pending trading reward account points
	PendingTradingRewardCampaignAccountPoints []*TradingRewardCampaignAccountPendingPoints
	// the fee discount schedule
	FeeDiscountSchedule                 *FeeDiscountSchedule
	// the cached fee discount account tiers with TTL
	FeeDiscountAccountTierTtl           []*FeeDiscountAccountTierTTL
	// the fee discount paid by accounts in all buckets
	FeeDiscountBucketFeesPaidAccounts   []*FeeDiscountBucketFeesPaidAccounts
	// sets the first fee cycle as finished
	IsFirstFeeCycleFinished             bool
}
type Params struct {
	// spot_market_instant_listing_fee defines the expedited fee in INJ required to create a spot market by bypassing governance
	SpotMarketInstantListingFee types.Coin
	// derivative_market_instant_listing_fee defines the expedited fee in INJ required to create a derivative market by bypassing governance
	DerivativeMarketInstantListingFee types.Coin
	// default_spot_maker_fee defines the default exchange trade fee for makers on a spot market
	DefaultSpotMakerFeeRate math.LegacyDec
	// default_spot_taker_fee_rate defines the default exchange trade fee rate for takers on a new spot market
	DefaultSpotTakerFeeRate math.LegacyDec
	// default_derivative_maker_fee defines the default exchange trade fee for makers on a new derivative market
	DefaultDerivativeMakerFeeRate math.LegacyDec
	// default_derivative_taker_fee defines the default exchange trade fee for takers on a new derivative market
	DefaultDerivativeTakerFeeRate math.LegacyDec
	// default_initial_margin_ratio defines the default initial margin ratio on a new derivative market
	DefaultInitialMarginRatio math.LegacyDec
	// default_maintenance_margin_ratio defines the default maintenance margin ratio on a new derivative market
	DefaultMaintenanceMarginRatio math.LegacyDec
	// default_funding_interval defines the default funding interval on a derivative market
	DefaultFundingInterval int64
	// funding_multiple defines the timestamp multiple that the funding timestamp should be a multiple of
	FundingMultiple int64
	// relayer_fee_share_rate defines the trade fee share percentage that goes to relayers
	RelayerFeeShareRate math.LegacyDec
	// default_hourly_funding_rate_cap defines the default maximum absolute value of the hourly funding rate
	DefaultHourlyFundingRateCap math.LegacyDec
	// hourly_interest_rate defines the hourly interest rate
	DefaultHourlyInterestRate math.LegacyDec
	// max_derivative_order_side_count defines the maximum number of derivative active orders a subaccount can have for a given orderbook side
	MaxDerivativeOrderSideCount uint32
	// inj_reward_staked_requirement_threshold defines the threshold on INJ rewards after which one also needs staked INJ to receive more
	InjRewardStakedRequirementThreshold github_com_cosmos_cosmos_sdk_types.Int
	// the trading_rewards_vesting_duration defines the vesting times for trading rewards
	TradingRewardsVestingDuration int64
}
message Balance {
	SubaccountId string
	Denom        string
	Deposits     *Deposit
}

// An subaccount's deposit for a given base currency
type Deposit struct {
	AvailableBalance math.LegacyDec
	TotalBalance     math.LegacyDec
}

type SubaccountDeposit {
	SubaccountId []byte
	Deposit      *Deposit
}
type SubaccountNonce struct {
	SubaccountId         string
	SubaccountTradeNonce SubaccountTradeNonce
}
type OrderInfo struct {
	// bytes32 subaccount ID that created the order
	SubaccountId string
	// address fee_recipient address that will receive fees for the order
	FeeRecipient string
	// price of the order
	Price math.LegacyDec
	// quantity of the order
	Quantity math.LegacyDec
}

type SubaccountOrderbookMetadata struct {
	VanillaLimitOrderCount    uint32
	ReduceOnlyLimitOrderCount uint32
	// AggregateReduceOnlyQuantity is the aggregate fillable quantity of the subaccount's reduce-only limit orders in the given direction.
	AggregateReduceOnlyQuantity math.LegacyDec
	// AggregateVanillaQuantity is the aggregate fillable quantity of the subaccount's vanilla limit orders in the given direction.
	AggregateVanillaQuantity math.LegacyDec
}

type SubaccountOrder struct {
	// price of the order
	Price math.LegacyDec
	// the amount of the quantity remaining fillable
	Quantity     math.LegacyDec
	IsReduceOnly bool
    Cid          string
}

type MarketOrderIndicator struct {
	// market_id represents the unique ID of the market
	MarketId string
	IsBuy    bool
}
// An object describing trade pair of two assets.
type SpotMarket struct {
	// A name of the pair in format AAA/BBB, where AAA is base asset, BBB is quote asset.
	Ticker string
	// Coin denom used for the base asset
	BaseDenom string
	// Coin used for the quote asset
	QuoteDenom string
	// maker_fee_rate defines the fee percentage makers pay when trading
	MakerFeeRate math.LegacyDec
	// taker_fee_rate defines the fee percentage takers pay when trading
	TakerFeeRate math.LegacyDec
	// relayer_fee_share_rate defines the percentage of the transaction fee shared with the relayer in a derivative market
	RelayerFeeShareRate math.LegacyDec
	// Unique market ID.
	MarketId string
	// Status of the market
	Status MarketStatus
	// min_price_tick_size defines the minimum tick size that the price required for orders in the market
	MinPriceTickSize math.LegacyDec
	// min_quantity_tick_size defines the minimum tick size of the quantity required for orders in the market
	MinQuantityTickSize math.LegacyDec
}
// Spot Exchange Limit Orderbook
type SpotOrderBook struct {
	MarketId  string
	IsBuySide bool
	Orders    []*SpotLimitOrder
}

type SpotOrder struct {
	// market_id represents the unique ID of the market
	MarketId string
	// order_info contains the information of the order
	OrderInfo OrderInfo
	// order types
	OrderType OrderType
	// trigger_price is the trigger price used by stop/take orders
	TriggerPrice *math.LegacyDec
}

// A valid Spot limit order with Metadata.
type SpotLimitOrder struct {
	// order_info contains the information of the order
	OrderInfo OrderInfo
	// order types
	OrderType OrderType
	// the amount of the quantity remaining fillable
	Fillable math.LegacyDec
	// trigger_price is the trigger price used by stop/take orders
	TriggerPrice *math.LegacyDec
	OrderHash    []byte
}

// A valid Spot market order with Metadata.
type SpotMarketOrder struct {
	// order_info contains the information of the order
	OrderInfo   OrderInfo
	BalanceHold math.LegacyDec
	OrderHash   []byte
}
// An object describing a derivative market in the Injective Futures Protocol.
type DerivativeMarket struct {
	// Ticker for the derivative contract.
	Ticker string
	// Oracle base currency
	OracleBase string
	// Oracle quote currency
	OracleQuote string
	// Oracle type
	OracleType types1.OracleType
	// Scale factor for oracle prices.
	OracleScaleFactor uint32
	// Address of the quote currency denomination for the derivative contract
	QuoteDenom string
	// Unique market ID.
	MarketId string
	// initial_margin_ratio defines the initial margin ratio of a derivative market
	InitialMarginRatio math.LegacyDec
	// maintenance_margin_ratio defines the maintenance margin ratio of a derivative market
	MaintenanceMarginRatio math.LegacyDec
	// maker_fee_rate defines the maker fee rate of a derivative market
	MakerFeeRate math.LegacyDec
	// taker_fee_rate defines the taker fee rate of a derivative market
	TakerFeeRate math.LegacyDec
	// relayer_fee_share_rate defines the percentage of the transaction fee shared with the relayer in a derivative market
	RelayerFeeShareRate math.LegacyDec
	// true if the market is a perpetual market. false if the market is an expiry futures market
	IsPerpetual bool
	// Status of the market
	Status MarketStatus
	// min_price_tick_size defines the minimum tick size that the price and margin required for orders in the market
	MinPriceTickSize math.LegacyDec
	// min_quantity_tick_size defines the minimum tick size of the quantity required for orders in the market
	MinQuantityTickSize math.LegacyDec
}
// Spot Exchange Limit Orderbook
type DerivativeOrderBook struct {
	MarketId  string
	IsBuySide bool
	Orders    []*DerivativeLimitOrder
}

type DerivativeOrder struct {
	// market_id represents the unique ID of the market
	MarketId string
	// order_info contains the information of the order
	OrderInfo OrderInfo
	// order types
	OrderType OrderType
	// margin is the margin used by the limit order
	Margin math.LegacyDec
	// trigger_price is the trigger price used by stop/take orders
	TriggerPrice *math.LegacyDec
}

// A valid Derivative limit order with Metadata.
type DerivativeLimitOrder struct {
	// order_info contains the information of the order
	OrderInfo OrderInfo
	// order types
	OrderType OrderType
	// margin is the margin used by the limit order
	Margin math.LegacyDec
	// the amount of the quantity remaining fillable
	Fillable math.LegacyDec
	// trigger_price is the trigger price used by stop/take orders
	TriggerPrice *math.LegacyDec
	OrderHash    []byte
}

// A valid Derivative market order with Metadata.
type DerivativeMarketOrder struct {
	// order_info contains the information of the order
	OrderInfo OrderInfo
	// order types
	OrderType  OrderType
	Margin     math.LegacyDec
	MarginHold math.LegacyDec
	// trigger_price is the trigger price used by stop/take orders
	TriggerPrice *math.LegacyDec
	OrderHash    []byte
}

type DerivativeMarketOrderCancel struct {
	MarketOrder    *DerivativeMarketOrder
	CancelQuantity math.LegacyDec
}
type Position struct {
	IsLong                 bool
	Quantity               math.LegacyDec
	EntryPrice             math.LegacyDec
	Margin                 math.LegacyDec
	CumulativeFundingEntry math.LegacyDec
}

type PositionDelta struct {
	IsLong            bool
	ExecutionQuantity math.LegacyDec
	ExecutionMargin   math.LegacyDec
	ExecutionPrice    math.LegacyDec
}

type DerivativePosition struct {
	SubaccountId string
	MarketId     string
	Position     *Position
}

type SubaccountPosition struct {
	Position     *Position
	SubaccountId []byte
}
type ExpiryFuturesMarketInfo struct {
	// market ID.
	MarketId string
	// expiration_timestamp defines the expiration time for a time expiry futures market.
	ExpirationTimestamp int64
	// expiration_twap_start_timestamp defines the start time of the TWAP calculation window
	TwapStartTimestamp int64
	// expiration_twap_start_price_cumulative defines the cumulative price for the start of the TWAP window
	ExpirationTwapStartPriceCumulative math.LegacyDec
	// settlement_price defines the settlement price for a time expiry futures market.
	SettlementPrice math.LegacyDec
}
type PerpetualMarketInfo struct {
	// market ID.
	MarketId string
	// hourly_funding_rate_cap defines the maximum absolute value of the hourly funding rate
	HourlyFundingRateCap math.LegacyDec
	// hourly_interest_rate defines the hourly interest rate
	HourlyInterestRate math.LegacyDec
	// next_funding_timestamp defines the next funding timestamp in seconds of a perpetual market
	NextFundingTimestamp int64
	// funding_interval defines the next funding interval in seconds of a perpetual market.
	FundingInterval int64
}
type PerpetualMarketFunding struct {
	// cumulative_funding defines the cumulative funding of a perpetual market.
	CumulativeFunding math.LegacyDec
	// cumulative_price defines the cumulative price for the current hour up to the last timestamp
	CumulativePrice math.LegacyDec
	LastTimestamp   int64
}
type CampaignRewardPool struct {
	StartTimestamp int64
	// max_campaign_rewards are the maximum reward amounts to be disbursed at the end of the campaign
	MaxCampaignRewards sdk.Coins
}
type TradingRewardCampaignInfo struct {
	// number of seconds of the duration of each campaign
	CampaignDurationSeconds int64
	// the trading fee quote denoms which will be counted for the rewards
	QuoteDenoms []string
	// the optional boost info for markets
	TradingRewardBoostInfo *TradingRewardCampaignBoostInfo
	// the marketIDs which are disqualified from being rewarded
	DisqualifiedMarketIds []string
}

type TradingRewardCampaignBoostInfo struct {
	BoostedSpotMarketIds        []string
	SpotMarketMultipliers       []PointsMultiplier
	BoostedDerivativeMarketIds  []string
	DerivativeMarketMultipliers []PointsMultiplier
}

type PointsMultiplier struct {
	MakerPointsMultiplier math.LegacyDec
	TakerPointsMultiplier math.LegacyDec
}
type FeeDiscountSchedule struct {
	// the bucket count, e.g., 30
	BucketCount    uint64
	// the bucket duration, e.g., 1 day
	BucketDuration int64
	// the trading fee quote denoms which will be counted for the fee paid contribution
	QuoteDenoms []string
	// the fee discount tiers
	TierInfos []*FeeDiscountTierInfo
	// the marketIDs which are disqualified from contributing to the fee paid amount
	DisqualifiedMarketIds []string
}

type FeeDiscountTierInfo struct {
  MakerDiscountRate math.LegacyDec
  TakerDiscountRate math.LegacyDec
  StakedAmount      math.Int
  FeePaidAmount     math.LegacyDec
}
type DerivativeMarketSettlementInfo struct {
	// market ID.
	MarketId string
	// settlement_price defines the settlement price
	SettlementPrice math.LegacyDec
	// starting_deficit defines starting deficit
	StartingDeficit math.LegacyDec
}
type TradeLog struct {
	Quantity math.LegacyDec
	Price    math.LegacyDec
	// bytes32 subaccount ID that executed the trade
	SubaccountId []byte
	Fee          math.LegacyDec
	OrderHash    []byte
}

type DerivativeTradeLog struct {
	SubaccountId  []byte
	PositionDelta *PositionDelta
	Payout        math.LegacyDec
	Fee           math.LegacyDec
	OrderHash     []byte
}
enum OrderType {
  UNSPECIFIED = 0 [(gogoproto.enumvalue_customname) = "UNSPECIFIED"];
  BUY = 1 [(gogoproto.enumvalue_customname) = "BUY"];
  SELL = 2 [(gogoproto.enumvalue_customname) = "SELL"];
  STOP_BUY = 3 [(gogoproto.enumvalue_customname) = "STOP_BUY"];
  STOP_SELL = 4 [(gogoproto.enumvalue_customname) = "STOP_SELL"];
  TAKE_BUY = 5 [(gogoproto.enumvalue_customname) = "TAKE_BUY"];
  TAKE_SELL = 6 [(gogoproto.enumvalue_customname) = "TAKE_SELL"];
  BUY_PO = 7 [(gogoproto.enumvalue_customname) = "BUY_PO"];
  SELL_PO = 8 [(gogoproto.enumvalue_customname) = "SELL_PO"];
  BUY_ATOMIC = 9 [ (gogoproto.enumvalue_customname) = "BUY_ATOMIC" ];
  SELL_ATOMIC = 10 [ (gogoproto.enumvalue_customname) = "SELL_ATOMIC" ];
}

enum MarketStatus {
  Unspecified = 0;
  Active = 1;
  Paused = 2;
  Suspended = 3;
  Demolished = 4;
  Expired = 5;
}

enum ExecutionType {
  UnspecifiedExecutionType = 0;
  Market = 1;
  LimitFill = 2;
  LimitMatchRestingOrder = 3;
  LimitMatchNewOrder = 4;
}
bucket count 5 and with 100 sec duration

120 220 320 420 520          220 320 420 520 620
 |   |   |   |   |   |  -->   |   |   |   |   |   |
   1   2   3   4   5            1   2   3   4   5

Current block.timestamp of 621:
621 - 5*100 = 121
120 is older than 121, so prune the last bucket and create a new bucket.
type MsgDeposit struct {
	Sender        string
	// (Optional) bytes32 subaccount ID to deposit funds into. If empty, the coin will be deposited to the sender's default
	// subaccount address.
	SubaccountId string
	Amount       types.Coin
}
type MsgWithdraw struct {
	Sender       string
	// bytes32 subaccount ID to withdraw funds from
	SubaccountId string
	Amount       types.Coin
}
type MsgInstantSpotMarketLaunch struct {
	Sender              string
	Ticker              string
	BaseDenom           string
	QuoteDenom          string
	MinPriceTickSize    math.LegacyDec
	MinQuantityTickSize math.LegacyDec
    MinNotional         math.LegacyDec
}
type MsgInstantPerpetualMarketLaunch struct {
	Sender                  string
	Ticker                  string
	QuoteDenom              string
	OracleBase              string
	OracleQuote             string
	OracleScaleFactor       uint32
	OracleType              types1.OracleType
	MakerFeeRate            math.LegacyDec
	TakerFeeRate            math.LegacyDec
	InitialMarginRatio      math.LegacyDec
	MaintenanceMarginRatio  math.LegacyDec
	MinPriceTickSize        math.LegacyDec
	MinQuantityTickSize     math.LegacyDec
    MinNotional             math.LegacyDec
}
type MsgInstantExpiryFuturesMarketLaunch struct {
	Sender                  string
	Ticker                  string
	QuoteDenom              string
	OracleBase              string
	OracleQuote             string
	OracleType              types1.OracleType
	OracleScaleFactor       uint32
	Expiry                  int64
	MakerFeeRate            math.LegacyDec
	TakerFeeRate            math.LegacyDec
	InitialMarginRatio      math.LegacyDec
	MaintenanceMarginRatio  math.LegacyDec
	MinPriceTickSize        math.LegacyDec
	MinQuantityTickSize     math.LegacyDec
    MinNotional             math.LegacyDec
}
type MsgCreateSpotLimitOrder struct {
	Sender string
	Order  SpotOrder
}
type MsgBatchCreateSpotLimitOrders struct {
	Sender string
	Orders []SpotOrder
}
type MsgCreateSpotMarketOrder struct {
	Sender string
	Order  SpotOrder
}
type MsgCancelSpotOrder struct {
	Sender       string
	MarketId     string
	SubaccountId string
	OrderHash    string
    Cid          string
}
type MsgBatchCancelSpotOrders struct {
	Sender string
	Data   []OrderData
}
type MsgCreateDerivativeLimitOrder struct {
	Sender string
	Order  DerivativeOrder
}
type MsgBatchCreateDerivativeLimitOrders struct {
	Sender string
	Orders []DerivativeOrder
}
// A Cosmos-SDK MsgCreateDerivativeMarketOrder
type MsgCreateDerivativeMarketOrder struct {
	Sender string
	Order  DerivativeOrder
}
type MsgCancelDerivativeOrder struct {
	Sender       string
	MarketId     string
	SubaccountId string
	OrderHash    string
    OrderMask    int32
    Cid          string
}
type MsgBatchCancelDerivativeOrders struct {
	Sender string
	Data   []OrderData
}
type MsgSubaccountTransfer struct {
	Sender                  string
	SourceSubaccountId      string
	DestinationSubaccountId string
	Amount                  types.Coin
}
type MsgExternalTransfer struct {
	Sender                  string
	SourceSubaccountId      string
	DestinationSubaccountId string
	Amount                  types.Coin
}
type MsgLiquidatePosition struct {
	Sender       string
	SubaccountId string
	MarketId     string
	// optional order to provide for liquidation
	Order        *DerivativeOrder
}
// A Cosmos-SDK MsgIncreasePositionMargin
type MsgIncreasePositionMargin struct {
	Sender                  string
	SourceSubaccountId      string
	DestinationSubaccountId string
	MarketId                string
	// amount defines the amount of margin to add to the position
	Amount                  math.LegacyDec
}
// A Cosmos-SDK MsgBatchUpdateOrders
// SubaccountId only used for the spot_market_ids_to_cancel_all and derivative_market_ids_to_cancel_all.
type MsgBatchUpdateOrders struct {
	Sender                          string
	SubaccountId                    string
	SpotMarketIdsToCancelAll        []string
	DerivativeMarketIdsToCancelAll  []string
	SpotOrdersToCancel              []OrderData
	DerivativeOrdersToCancel        []OrderData
	SpotOrdersToCreate              []SpotOrder
	DerivativeOrdersToCreate        []DerivativeOrder
}
message EventBatchSpotExecution {
  string market_id = 1;
  bool is_buy = 2;
  ExecutionType executionType = 3;
  repeated TradeLog trades = 4;
}

message EventBatchDerivativeExecution {
  string market_id = 1;
  bool is_buy = 2;
  bool is_liquidation = 3;
  // nil for time expiry futures
  string cumulative_funding = 4 [
    (gogoproto.customtype) = "cosmossdk.io/math.LegacyDec",
    (gogoproto.nullable) = true
  ];
  ExecutionType executionType = 5;
  repeated DerivativeTradeLog trades = 6;
}

message EventLostFundsFromLiquidation {
  string market_id = 1;
  bytes subaccount_id = 2;
  string lost_funds_from_available_during_payout = 3 [
    (gogoproto.customtype) = "cosmossdk.io/math.LegacyDec",
    (gogoproto.nullable) = false
  ];
  string lost_funds_from_order_cancels = 4 [
    (gogoproto.customtype) = "cosmossdk.io/math.LegacyDec",
    (gogoproto.nullable) = false
  ];
}

message EventBatchDerivativePosition {
  string market_id = 1;
  repeated SubaccountPosition positions = 2;
}

message EventDerivativeMarketPaused {
  string market_id = 1;
  string settle_price = 2;
  string total_missing_funds = 3;
  string missing_funds_rate = 4;
}

message EventBinaryOptionsMarketUpdate {
  BinaryOptionsMarket market = 1 [
    (gogoproto.nullable) = false
  ];
}

message EventNewSpotOrders {
  string market_id = 1;
  repeated SpotLimitOrder buy_orders = 2;
  repeated SpotLimitOrder sell_orders = 3;
}

message EventNewDerivativeOrders {
  string market_id = 1;
  repeated DerivativeLimitOrder buy_orders = 2;
  repeated DerivativeLimitOrder sell_orders = 3;
}

message EventCancelSpotOrder {
  string market_id = 1;
  SpotLimitOrder order = 2 [
    (gogoproto.nullable) = false
  ];
}

message EventSpotMarketUpdate {
  SpotMarket market = 1 [
    (gogoproto.nullable) = false
  ];
}

message EventPerpetualMarketUpdate {
  DerivativeMarket market = 1 [
    (gogoproto.nullable) = false
  ];
  PerpetualMarketInfo perpetual_market_info = 2[
    (gogoproto.nullable) = true
  ];
  PerpetualMarketFunding funding = 3[
    (gogoproto.nullable) = true
  ];
}

message EventExpiryFuturesMarketUpdate {
  DerivativeMarket market = 1 [
    (gogoproto.nullable) = false
  ];
  ExpiryFuturesMarketInfo expiry_futures_market_info = 3[
    (gogoproto.nullable) = true
  ];
}

message EventPerpetualMarketFundingUpdate {
  string market_id = 1;
  PerpetualMarketFunding funding = 2[
    (gogoproto.nullable) = false
  ];
  bool is_hourly_funding = 3;
  string funding_rate = 4 [
    (gogoproto.customtype) = "cosmossdk.io/math.LegacyDec",
    (gogoproto.nullable) = true
  ];
  string mark_price = 5 [
    (gogoproto.customtype) = "cosmossdk.io/math.LegacyDec",
    (gogoproto.nullable) = true
  ];
}

message EventSubaccountDeposit {
  string src_address = 1;
  bytes subaccount_id = 2;
  cosmos.base.v1beta1.Coin amount = 3 [(gogoproto.nullable) = false];
}

message EventSubaccountWithdraw {
  bytes subaccount_id = 1;
  string dst_address = 2;
  cosmos.base.v1beta1.Coin amount = 3 [(gogoproto.nullable) = false];
}

message EventSubaccountBalanceTransfer {
  string src_subaccount_id = 1;
  string dst_subaccount_id = 2;
  cosmos.base.v1beta1.Coin amount = 3 [(gogoproto.nullable) = false];
}

message EventBatchDepositUpdate {
  repeated DepositUpdate deposit_updates = 1;
}

message EventCancelDerivativeOrder {
  string market_id = 1;
  bool isLimitCancel = 2;
  DerivativeLimitOrder limit_order = 3 [
    (gogoproto.nullable) = true
  ];
  DerivativeMarketOrderCancel market_order_cancel = 4 [
    (gogoproto.nullable) = true
  ];
}

message EventFeeDiscountSchedule {
  FeeDiscountSchedule schedule = 1;
}

message EventTradingRewardCampaignUpdate {
  TradingRewardCampaignInfo campaign_info = 1;
  repeated CampaignRewardPool campaign_reward_pools = 2;
}

message EventTradingRewardDistribution {
  repeated AccountRewards account_rewards = 1;
}
const (
	AtomicMarketOrderAccessLevel_Nobody                         AtomicMarketOrderAccessLevel = 0
	AtomicMarketOrderAccessLevel_BeginBlockerSmartContractsOnly AtomicMarketOrderAccessLevel = 1
	AtomicMarketOrderAccessLevel_SmartContractsOnly             AtomicMarketOrderAccessLevel = 2
	AtomicMarketOrderAccessLevel_Everyone                       AtomicMarketOrderAccessLevel = 3
)

EventBid

Bidder

EventBid

Amount

EventBid

Round

EventAuctionResult

Winner

EventAuctionResult

Amount

EventAuctionResult

Round

type GrantProviderPrivilegeProposal struct {
	Title       string   
	Description string   
	Provider    string    // the name of the provider, should be specific to you
	Relayers    []string  // addresses which will be able to relay prices 
}

Active → Expired

到期是市场标准工作流程的一部分。市场的交易立即停止,所有未完成的订单将被取消。此时,市场可以立即由管理员或预言机强制结算,或者在达到 SettlementTimestamp 时,使用最新的预言机价格自然结算。

Expired → Demolished (Settlement)

所有头寸将根据强制结算或自然结算的价格进行结算。市场将无法再进行交易或重新激活。对于自然结算,在 SettlementTimestamp 时间到达时,记录并使用最后的预言机价格进行结算。对于“强制结算”,管理员应发布包含结算价格的 MarketUpdate 消息,该价格应设置在 [0, 1] 的价格区间内。

Active/Expired → Demolished (Refund)

所有头寸将被退款。该市场将无法再进行交易或重新激活。管理员应发布 MarketUpdate 消息,并将结算价格设置为 -1。

// MsgRelayProviderPrices defines a SDK message for setting a price through the provider oracle.
type MsgRelayProviderPrices struct {
	Sender   string                        
	Provider string                        
	Symbols  []string                      
	Prices   []cosmossdk_io_math.LegacyDec 
}
type MsgAdminUpdateBinaryOptionsMarket struct {
  // new price at which market will be settled
  SettlementPrice *Dec 
  // expiration timestamp
  ExpirationTimestamp int64
  // expiration timestamp
  SettlementTimestamp int64
  // Status of the market
  Status MarketStatus
}

// Where Status can be one of these options
enum MarketStatus {
  Unspecified = 0;
  Active = 1;
  Paused = 2;
  Demolished = 3;
  Expired = 4;
} 

状态转换

状态转换

本文件描述了与以下事项相关的状态转换操作:

  • 创建保险基金

  • 承保保险基金

  • 向保险基金请求赎回

  • 自动处理到期的赎回请求

创建保险基金

参数描述:Sender 字段描述了保险基金的创建者。Ticker、QuoteDenom、OracleBase、OracleQuote、OracleType、Expiry 字段描述了与保险基金相关的衍生品市场信息。InitialDeposit 字段描述了投入保险基金的初始存款金额。

步骤

  1. 获取保险基金的 MarketId——注意,市场可能尚未在交易所上线,但这不是问题。

  2. 确保与该 MarketId 关联的保险基金不存在。

  3. 确保初始存款金额不为零。

  4. 获取唯一的 shareDenom——当请求保险基金创建的 share denom 或当承保一个余额为零且总 share denom 供应量不为零的保险基金时,会递增 share denom。

  5. 从创建者账户向保险基金模块账户发送币。

  6. 使用 DefaultRedemptionNoticePeriodDuration 和提供的参数创建保险基金对象。

  7. 将基金对象的余额设置为初始存款金额。

  8. 向创建者账户铸造 InsuranceFundInitialSupply (10^18) 的 shareDenom 代币。

  9. 将保险基金对象保存到存储中。

  10. 在 BankKeeper 中注册新创建的保险基金 shareDenom 元数据。

承保保险基金

参数描述:Sender 字段描述了保险基金的承保人。MarketId 字段描述了与保险基金关联的衍生品市场 ID。Deposit 字段描述了要添加到保险基金的存款金额。

步骤

  1. 确保与该 MarketId 关联的保险基金存在。

  2. 从发送者账户向模块账户发送承保代币。

  3. 根据与 MarketId 关联的保险基金的状态进行操作。 A. 当余额和 ShareDenomSupply 为零时

    • 向发送者铸造 InsuranceFundInitialSupply (10^18)。

    • 设置余额为存款金额。

    • 设置 ShareDenomSupply 为 InsuranceFundInitialSupply。

    B. 当余额为零且 ShareDenomSupply 不为零时

    • 更改保险基金的 ShareDenom,重新开始新的保险基金。

    • 在银行 Keeper 中注册新创建的 ShareDenom。

    • 向发送者铸造 InsuranceFundInitialSupply (10^18)。

    • 设置余额为存款金额。

    • 设置 ShareDenomSupply 为 InsuranceFundInitialSupply。

    C. 当余额不为零且 ShareDenomSupply 为零时

    • 向发送者铸造 InsuranceFundInitialSupply (10^18)。

    • 增加余额为存款金额。

    • 设置 ShareDenomSupply 为 InsuranceFundInitialSupply。

    D. 当余额和 ShareDenomSupply 都不为零时——正常情况

    • 增加余额为存款金额。

    • 向发送者铸造 prev_ShareDenomSupply * deposit_amount / prev_Balance 数量的 ShareDenom。

    • 增加 ShareDenomSupply 与铸造数量。

  4. 将保险基金对象保存到存储中

向保险基金请求赎回

参数描述: Sender 字段描述了保险基金的赎回请求者。MarketId 字段描述了与保险基金关联的衍生品市场 ID。Amount 字段描述了要赎回的份额代币数量。

步骤

  1. 确保与 MarketId 关联的保险基金存在。

  2. 将 ShareDenom 发送至模块账户。

  3. 获取新的赎回计划 ID。

  4. 根据保险基金的赎回通知期持续时间和当前区块时间计算 ClaimTime。

  5. 计算用于存储待处理赎回(赎回计划)的键。

  6. 创建包含详细信息的赎回计划对象。

衍生品市场清算事件中的保险基金操作

步骤

  1. 交易模块从保险 Keeper 中查找相关的保险基金。

  2. 如果 missingFund 为正值,则通过 WithdrawFromInsuranceFund 从保险基金提取相应金额。

  3. 如果 missingFund 为负值,则通过 DepositIntoInsuranceFund 向保险基金存入相应金额。

待处理赎回的自动处理

步骤

按 ClaimTime 排序遍历所有已到期的赎回请求,并执行以下操作:

  1. 如果 ClaimTime 晚于当前区块时间,则提前中断处理。

  2. 确保赎回计划对应的保险基金存在。

  3. 根据份额数量计算赎回金额:shareAmt * fund.Balance / fund.TotalShare。

  4. 从模块账户向赎回者账户发送计算出的赎回金额。

  5. 燃烧在赎回计划创建时发送至模块账户的份额代币。

  6. 删除赎回计划对象。

  7. 将保险基金的余额减少赎回金额。

  8. 将更新后的保险基金对象存储至存储中。

钩子(Hooks)

其他模块可以注册操作,以便在保险基金发生特定事件时执行。这些事件可以注册为在交易事件之前(Before)或之后(After)执行(依据钩子名称)。

可在交易模块中注册以下钩子: 注意:钩子当前不可用,交易模块会直接调用保险 Keeper 的函数。

衍生品市场清算事件的处理步骤

  1. 交易模块从保险 Keeper 中查找相关的保险基金。

  2. 如果 missingFund 为正值,则通过 WithdrawFromInsuranceFund 从保险基金提取相应金额。

  3. 如果 missingFund 为负值,则通过 DepositIntoInsuranceFund 向保险基金存入相应金额。

状态

Params

Params 是一个模块级的配置结构,用于存储系统参数并定义保险模块的整体功能。

  • Params: Paramsspace("insurance") -> legacy_amino(params)


type Params struct {
	// default_redemption_notice_period_duration defines the default minimum notice period duration that must pass after an underwriter sends
	// a redemption request before the underwriter can claim his tokens
	DefaultRedemptionNoticePeriodDuration time.Duration 
}

Insurance Types

InsuranceFund 定义了按市场划分的所有保险基金信息。


type InsuranceFund struct {
	// deposit denomination for the given insurance fund
	DepositDenom string 
	// insurance fund pool token denomination for the given insurance fund
	InsurancePoolTokenDenom string 
	// redemption_notice_period_duration defines the minimum notice period duration that must pass after an underwriter sends
	// a redemption request before the underwriter can claim his tokens
	RedemptionNoticePeriodDuration time.Duration 
	// balance of fund
	Balance math.Int 
	// total share tokens minted
	TotalShare math.Int 
	// marketID of the derivative market
	MarketId string 
	// ticker of the derivative market
	MarketTicker string 
	// Oracle base currency of the derivative market
	OracleBase string 
	// Oracle quote currency of the derivative market
	OracleQuote string 
	// Oracle type of the derivative market
	OracleType types.OracleType 
    // Expiration time of the derivative market. Should be -1 for perpetual markets.
	Expiry int64
}

RedemptionSchedule 定义了用户的赎回计划——赎回不会立即执行,而是每个市场都有一个指定的赎回通知期持续时间(redemption_notice_period_duration)。

type RedemptionSchedule struct {
	// id of redemption schedule
	Id uint64 
	// marketId of redemption schedule
	MarketId string
	// address of the redeemer
	Redeemer string
	// the time after which the redemption can be claimed
	ClaimableRedemptionTime time.Time 
  // the insurance_pool_token amount to redeem
	RedemptionAmount sdk.Coin
}

此外,我们引入了 next_share_denom_id 和 next_redemption_schedule_id 来管理来自不同用户的保险基金份额代币的 denom 和赎回计划。

// GenesisState defines the insurance module's genesis state.
type GenesisState struct {
	// params defines all the parameters of related to insurance.
	Params                   Params               
	InsuranceFunds           []InsuranceFund      
	RedemptionSchedule       []RedemptionSchedule 
	NextShareDenomId         uint64               
	NextRedemptionScheduleId uint64               
}

Pending Redemptions

待处理赎回对象用于存储有关赎回请求的所有信息,并在期限过后自动进行赎回。

参数

交易模块包含以下参数:

键
类型
示例

SpotMarketInstantListingFee

sdk.Coin

100inj

DerivativeMarketInstantListingFee

sdk.Coin

1000inj

DefaultSpotMakerFeeRate

math.LegacyDec

0.1%

DefaultSpotTakerFeeRate

math.LegacyDec

0.2%

DefaultDerivativeMakerFeeRate

math.LegacyDec

0.1%

DefaultDerivativeTakerFeeRate

math.LegacyDec

0.2%

DefaultInitialMarginRatio

math.LegacyDec

5%

DefaultMaintenanceMarginRatio

math.LegacyDec

2%

DefaultFundingInterval

int64

3600

FundingMultiple

int64

3600

RelayerFeeShareRate

math.LegacyDec

40%

DefaultHourlyFundingRateCap

math.LegacyDec

0.0625%

DefaultHourlyInterestRate

math.LegacyDec

0.000416666%

MaxDerivativeOrderSideCount

int64

20

InjRewardStakedRequirementThreshold

sdk.Coin

25inj

TradingRewardsVestingDuration

int64

1209600

LiquidatorRewardShareRate

math.LegacyDec

0.05%

BinaryOptionsMarketInstantListingFee

sdk.Coin

10inj

AtomicMarketOrderAccessLevel

string

SmartContractsOnly

SpotAtomicMarketOrderFeeMultiplier

math.LegacyDec

2x

DerivativeAtomicMarketOrderFeeMultiplier

math.LegacyDec

2x

BinaryOptionsAtomicMarketOrderFeeMultiplier

math.LegacyDec

2x

MinimalProtocolFeeRate

math.LegacyDec

0.00001%

IsInstantDerivativeMarketLaunchEnabled

bool

false

MsgPrivilegedExecuteContract

MsgPrivilegedExecuteContract 定义了一个方法,用于从交易模块执行具有特权能力的 Cosmwasm 合约。

type MsgPrivilegedExecuteContract struct {
	Sender string
	// funds defines the user's bank coins used to fund the execution (e.g. 100inj).
	Funds github_com_cosmos_cosmos_sdk_types.Coins
	// contract_address defines the contract address to execute
	ContractAddress string
	// data defines the call data used when executing the contract
	Data string
}

字段描述

  • Sender:描述此消息的创建者。

  • Funds:定义用户用于资助执行的银行币(例如 100inj)。

  • ContractAddress:定义要执行的合约地址。

  • Data:定义执行合约时使用的调用数据,详细信息请见下文。

合约接口 如果您希望在合约中启用特权操作,必须实现以下执行方法:

InjectiveExec {
    origin: String,
    name: String,
    args: MyArgs,
}
  • origin 字段是发送特权操作的用户地址。您不需要自己设置此字段,它将由交易模块设置。

  • name 字段是特权操作的名称。您可以根据需要定义这些名称。

  • args 字段是特权操作的参数。您可以根据需要定义这些参数。

在 Golang 中,Data 字符串的完整定义是:

type ExecutionData struct {
	Origin string      `json:"origin"`
	Name   string      `json:"name"`
	MyArgs   interface{} `json:"args"`
}

用户可以通过发送带有以下数据的 MsgPrivilegedExecuteContract 来调用特权操作:

{
	sender: "inj...",
	funds: "1000000000000000000inj",
	contract_address: "inj...",
	data: {
		origin: "inj...",
		name: "my_privileged_action",
		args: {
			...
		}
	}
}

支持的特权操作

目前支持两种特权操作:

type PrivilegedAction struct {
	SyntheticTrade   *SyntheticTradeAction `json:"synthetic_trade"`
	PositionTransfer *PositionTransfer     `json:"position_transfer"`
}

这些特权操作必须设置在 Cosmwasm 响应数据字段内,例如:

let privileged_action = PrivilegedAction {
    synthetic_trade: None,
    position_transfer: position_transfer_action,
};
response = response.set_data(to_binary(&privileged_action)?);

PositionTransfer

头寸转移允许合约将衍生头寸从其自身子账户转移到用户的子账户。该头寸不得处于强平状态。仅接收方支付接单商交易手续费,该手续费从其余额中扣除。 目前仅支持从合约的子账户转移到用户的子账户。

type PositionTransfer struct {
    MarketID                common.Hash `json:"market_id"`
    SourceSubaccountID      common.Hash `json:"source_subaccount_id"`
    DestinationSubaccountID common.Hash `json:"destination_subaccount_id"`
    Quantity                math.LegacyDec     `json:"quantity"`
}

SyntheticTrade

合成交易允许合约代表用户在衍生品市场执行合成交易。这不涉及订单簿,仅为纯粹的合成交易。接单商交易手续费仍然适用。子账户 ID 必须设置为合约的子账户 ID 和用户的子账户 ID。

type SyntheticTradeAction struct {
	UserTrades     []*SyntheticTrade `json:"user_trades"`
	ContractTrades []*SyntheticTrade `json:"contract_trades"`
}

type SyntheticTrade struct {
	MarketID     common.Hash `json:"market_id"`
	SubaccountID common.Hash `json:"subaccount_id"`
	IsBuy        bool        `json:"is_buy"`
	Quantity     math.LegacyDec     `json:"quantity"`
	Price        math.LegacyDec     `json:"price"`
	Margin       math.LegacyDec     `json:"margin"`
}

提案

Proposal/SpotMarketParamUpdate

SpotMarketParamUpdateProposal 定义了一条提议更新现货市场参数的 SDK 消息。

type SpotMarketParamUpdateProposal struct {
	Title                string
	Description          string
	MarketId             string
	MakerFeeRate         *math.LegacyDec
	TakerFeeRate         *math.LegacyDec
	RelayerFeeShareRate  *math.LegacyDec
	MinPriceTickSize     *math.LegacyDec
	MinQuantityTickSize  *math.LegacyDec
    MinNotional          *math.LegacyDec
	Status               MarketStatus
}

字段描述

  • Title 字段描述提案的标题。

  • Description 字段描述提案的描述。

  • MarketId 字段描述要更改参数的市场 ID。

  • MakerFeeRate 字段描述做市商的目标费用率。

  • TakerFeeRate 字段描述吃单者的目标费用率。

  • RelayerFeeShareRate 字段描述中继商费用分成率。

  • MinPriceTickSize 字段定义订单价格的最小刻度。

  • MinQuantityTickSize 字段定义订单数量的最小刻度。

  • MinNotional 定义订单的最小名义价值。

  • Status 字段描述市场的目标状态。

Proposal/ExchangeEnable

ExchangeEnableProposal 定义了一条提议启用特定交易类型的消息。

type ExchangeEnableProposal struct {
	Title        string
	Description  string
	ExchangeType ExchangeType
}

字段描述

  • Title 字段描述提案的标题。

  • Description 字段描述提案的描述。

  • ExchangeType 字段描述交易类型,现货或衍生品。

Proposal/BatchExchangeModification

BatchExchangeModificationProposal 定义了一条在交易模块中批量处理多个提案的消息。

type BatchExchangeModificationProposal struct {
	Title                                string
	Description                          string
	SpotMarketParamUpdateProposal        []*SpotMarketParamUpdateProposal
	DerivativeMarketParamUpdateProposal  []*DerivativeMarketParamUpdateProposal
	SpotMarketLaunchProposal             []*SpotMarketLaunchProposal
	PerpetualMarketLaunchProposal        []*PerpetualMarketLaunchProposal
	ExpiryFuturesMarketLaunchProposal    []*ExpiryFuturesMarketLaunchProposal
	TradingRewardCampaignUpdateProposal  *TradingRewardCampaignUpdateProposal
}

字段描述

  • Title 字段描述提案的标题。

  • Description 字段描述提案的描述。

  • SpotMarketParamUpdateProposal 字段描述 SpotMarketParamUpdateProposal。

  • DerivativeMarketParamUpdateProposal 字段描述 DerivativeMarketParamUpdateProposal。

  • SpotMarketLaunchProposal 字段描述 SpotMarketLaunchProposal。

  • PerpetualMarketLaunchProposal 字段描述 PerpetualMarketLaunchProposal。

  • ExpiryFuturesMarketLaunchProposal 字段描述 ExpiryFuturesMarketLaunchProposal。

  • TradingRewardCampaignUpdateProposal 字段描述 TradingRewardCampaignUpdateProposal。

Proposal/SpotMarketLaunch

SpotMarketLaunchProposal 定义了一条通过治理提议新现货市场的 SDK 消息。

type SpotMarketLaunchProposal struct {
	Title                string
	Description          string
	Ticker               string
	BaseDenom            string
	QuoteDenom           string
	MinPriceTickSize     math.LegacyDec
	MinQuantityTickSize  math.LegacyDec
    MinNotional          math.LegacyDec
	MakerFeeRate         math.LegacyDec
	TakerFeeRate         math.LegacyDec
}

字段描述

  • Title 字段描述提案的标题。

  • Description字段描述提案的描述。

  • Ticker 字段描述现货市场的交易对符号。

  • BaseDenom 字段指定用作基础货币的币种类型。

  • QuoteDenom 字段指定用作报价货币的币种类型。

  • MinPriceTickSize 字段定义订单价格的最小刻度。

  • MinQuantityTickSize 字段定义订单数量的最小刻度。

  • MakerFeeRate 字段描述衍生品市场上做市商的交易费用率。

  • TakerFeeRate 字段描述衍生品市场上吃单者的交易费用率。

Proposal/PerpetualMarketLaunch

PerpetualMarketLaunchProposal 定义了一条通过治理提议新永久期货市场的 SDK 消息。

type PerpetualMarketLaunchProposal struct {
	Title                   string
	Description             string
	Ticker                  string
	QuoteDenom              string
	OracleBase              string
	OracleQuote             string
	OracleScaleFactor       uint32
	OracleType              types1.OracleType
	InitialMarginRatio      math.LegacyDec
	MaintenanceMarginRatio  math.LegacyDec
	MakerFeeRate            math.LegacyDec
	TakerFeeRate            math.LegacyDec
	MinPriceTickSize        math.LegacyDec
	MinQuantityTickSize     math.LegacyDec
}

字段描述

  • Title 字段描述提案的标题。

  • Description 字段描述提案的描述。

  • Ticker 字段描述衍生品市场的交易对符号。

  • QuoteDenom 字段描述用作基础货币的币种类型。

  • OracleBase 字段描述预言机的基础货币。

  • OracleQuote 字段描述预言机的报价货币。

  • OracleScaleFactor 字段描述预言机价格的缩放因子。

  • OracleType 字段描述预言机的类型。

  • MakerFeeRate 字段描述衍生品市场上做市商的交易费用率。

  • TakerFeeRate 字段描述衍生品市场上吃单者的交易费用率。

  • InitialMarginRatio 字段描述衍生品市场的初始保证金比例。

  • MaintenanceMarginRatio 字段描述衍生品市场的维持保证金比例。

  • MinPriceTickSize 字段描述订单价格和保证金的最小刻度。

  • MinQuantityTickSize 字段描述订单数量的最小刻度。

Expiry futures market launch proposal

// ExpiryFuturesMarketLaunchProposal defines an SDK message for proposing a new expiry futures market through governance
type ExpiryFuturesMarketLaunchProposal struct {
	Title                      string
	Description                string
	// Ticker for the derivative market.
	Ticker                     string
	// type of coin to use as the quote currency
	QuoteDenom                 string
	// Oracle base currency
	OracleBase                 string
	// Oracle quote currency
	OracleQuote                string
	// Scale factor for oracle prices.
	OracleScaleFactor          uint32
	// Oracle type
	OracleType                 types1.OracleType
	// Expiration time of the market
	Expiry                     int64
	// initial_margin_ratio defines the initial margin ratio for the derivative market
	InitialMarginRatio         math.LegacyDec
	// maintenance_margin_ratio defines the maintenance margin ratio for the derivative market
	MaintenanceMarginRatio     math.LegacyDec
	// maker_fee_rate defines the exchange trade fee for makers for the derivative market
	MakerFeeRate               math.LegacyDec
	// taker_fee_rate defines the exchange trade fee for takers for the derivative market
	TakerFeeRate               math.LegacyDec
	// min_price_tick_size defines the minimum tick size of the order's price and margin
	MinPriceTickSize           math.LegacyDec
	// min_quantity_tick_size defines the minimum tick size of the order's quantity
	MinQuantityTickSize        math.LegacyDec
    // min_notional defines the minimum notional (in quote asset) required for orders in the market
    MinNotional                math.LegacyDec
}

字段描述

  • Title 字段描述提案的标题。

  • Description 字段描述提案的描述。

  • Ticker 字段描述衍生品市场的交易对符号。

  • QuoteDenom 字段描述用作报价货币的币种类型。

  • OracleBase 字段描述预言机的基础货币。

  • OracleQuote 字段描述预言机的报价货币。

  • OracleScaleFactor 字段描述预言机价格的缩放因子。

  • OracleType 字段描述预言机的类型。

  • Expiry 字段描述市场的到期时间。

  • MakerFeeRate 字段描述衍生品市场上做市商的交易费用率。

  • TakerFeeRate字段描述衍生品市场上吃单者的交易费用率。

  • InitialMarginRatio 字段描述衍生品市场的初始保证金比例。

  • MaintenanceMarginRatio 字段描述衍生品市场的维持保证金比例。

  • MinPriceTickSize 字段描述订单价格和保证金的最小刻度。

  • MinQuantityTickSize 字段描述订单数量的最小刻度。

Binary options market launch proposal

type BinaryOptionsMarketLaunchProposal struct {
	Title       string
	Description string
	// Ticker for the derivative contract.
	Ticker string
	// Oracle symbol
	OracleSymbol string
	// Oracle Provider
	OracleProvider string
	// Oracle type
	OracleType types1.OracleType
	// Scale factor for oracle prices.
	OracleScaleFactor uint32
	// expiration timestamp
	ExpirationTimestamp int64
	// expiration timestamp
	SettlementTimestamp int64
	// admin of the market
	Admin string
	// Address of the quote currency denomination for the binary options contract
	QuoteDenom string
	// maker_fee_rate defines the maker fee rate of a binary options market
	MakerFeeRate math.LegacyDec
	// taker_fee_rate defines the taker fee rate of a derivative market
	TakerFeeRate math.LegacyDec
	// min_price_tick_size defines the minimum tick size that the price and margin required for orders in the market
	MinPriceTickSize math.LegacyDec
	// min_quantity_tick_size defines the minimum tick size of the quantity required for orders in the market
	MinQuantityTickSize math.LegacyDec
}

Binary options market param update

type BinaryOptionsMarketParamUpdateProposal struct {
	Title       string
	Description string
	MarketId    string
	// maker_fee_rate defines the exchange trade fee for makers for the derivative market
	MakerFeeRate *math.LegacyDec
	// taker_fee_rate defines the exchange trade fee for takers for the derivative market
	TakerFeeRate *math.LegacyDec
	// relayer_fee_share_rate defines the relayer fee share rate for the derivative market
	RelayerFeeShareRate *math.LegacyDec
	// min_price_tick_size defines the minimum tick size of the order's price and margin
	MinPriceTickSize *math.LegacyDec
	// min_quantity_tick_size defines the minimum tick size of the order's quantity
	MinQuantityTickSize *math.LegacyDec
    // min_notional defines the minimum notional for orders
    MinNotional *math.LegacyDec
	// expiration timestamp
	ExpirationTimestamp int64
	// expiration timestamp
	SettlementTimestamp int64
	// new price at which market will be settled
	SettlementPrice *math.LegacyDec
	// admin of the market
	Admin        string
	Status       MarketStatus
	OracleParams *ProviderOracleParams
}

Proposal/DerivativeMarketParamUpdate

type OracleParams struct {
    // Oracle base currency
    OracleBase        string
    // Oracle quote currency
    OracleQuote       string
    // Scale factor for oracle prices.
    OracleScaleFactor uint32
    // Oracle type
    OracleType        types1.OracleType
}

type DerivativeMarketParamUpdateProposal struct {
	Title                  string
	Description            string
	MarketId               string
	InitialMarginRatio     *math.LegacyDec
	MaintenanceMarginRatio *math.LegacyDec
	MakerFeeRate           *math.LegacyDec
	TakerFeeRate           *math.LegacyDec
	RelayerFeeShareRate    *math.LegacyDec
	MinPriceTickSize       *math.LegacyDec
	MinQuantityTickSize    *math.LegacyDec
    MinNotional            *math.LegacyDec
	HourlyInterestRate     *math.LegacyDec
	HourlyFundingRateCap   *math.LegacyDec
	Status                 MarketStatus
	OracleParams           *OracleParams
}

字段描述

  • Title 字段描述提案的标题。

  • Description 字段描述提案的描述。

  • MarketId 字段描述要更改参数的市场 ID。

  • InitialMarginRatio 字段描述目标初始保证金比例。

  • MaintenanceMarginRatio 字段描述目标维持保证金比例。

  • MakerFeeRate 字段描述目标做市商费用率。

  • TakerFeeRate 字段描述目标吃单者费用率。

  • RelayerFeeShareRate 字段描述中继商费用分成率。

  • MinPriceTickSize字段定义订单价格的最小刻度。

  • MinQuantityTickSize 字段定义订单数量的最小刻度。

  • Status 字段描述市场的目标状态。

  • OracleParams 字段描述新的预言机参数。

Proposal/TradingRewardCampaignLaunch

TradingRewardCampaignLaunchProposal 定义了一条提议启动新交易奖励活动的 SDK 消息。

type TradingRewardCampaignLaunchProposal struct {
	Title               string
	Description         string
	CampaignInfo        *TradingRewardCampaignInfo
	CampaignRewardPools []*CampaignRewardPool
}

字段描述

  • Title 字段描述提案的标题。

  • Description 字段描述提案的描述。

  • CampaignInfo 字段描述活动信息。

  • CampaignRewardPools 字段描述奖励池信息。

Proposal/TradingRewardCampaignUpdate

TradingRewardCampaignUpdateProposal 定义了一条提议更新现有交易奖励活动的 SDK 消息。

type TradingRewardCampaignUpdateProposal struct {
	Title                        string
	Description                  string
	CampaignInfo                 *TradingRewardCampaignInfo
	CampaignRewardPoolsAdditions []*CampaignRewardPool
	CampaignRewardPoolsUpdates   []*CampaignRewardPool
}

字段描述

  • Title 字段描述提案的标题。

  • Description 字段描述提案的描述。

  • CampaignRewardPoolsAdditions 字段描述奖励池新增信息。

  • CampaignRewardPoolsUpdates 字段描述奖励池更新信息。

Proposal/FeeDiscount

FeeDiscountProposal 定义了一条提议启动或更新费用折扣计划的 SDK 消息。

type FeeDiscountProposal struct {
	Title          string
	Description    string
	Schedule       *FeeDiscountSchedule
}

字段描述

  • Title 字段描述提案的标题。

  • Description 字段描述提案的描述。

  • Schedule 字段描述费用折扣计划。

Proposal/TradingRewardPendingPointsUpdate

TradingRewardPendingPointsUpdateProposal 定义了一条在归属期间更新特定地址奖励积分的 SDK 消息。

type TradingRewardPendingPointsUpdateProposal struct {
	Title                  string
	Description            string
	PendingPoolTimestamp   int64
	RewardPointUpdates     *[]RewardPointUpdate
}

字段描述

  • Title 字段描述提案的标题。

  • Description 字段描述提案的描述。

  • PendingPoolTimestamp 字段描述待处理池的时间戳。

  • RewardPointUpdates 描述 RewardPointUpdate.

参数

保险模块包含以下参数:

键
类型
示例

default_redemption_notice_period_duration

time.Duration

time.Hour * 24 * 14

事件

保险模块会触发以下事件:

Handlers

MsgCreateInsuranceFund

类型
属性键
属性值

injective.insurance.v1beta1.EventInsuranceFundUpdate

fund

{fundJSON}

MsgUnderwrite

类型
属性键
属性值

injective.insurance.v1beta1.EventInsuranceFundUpdate

fund

{fundJSON}

MsgRequestRedemption

类型
属性键
属性值

injective.insurance.v1beta1.EventRequestRedemption

schedule

{scheduleJSON}

EndBlocker

类型
属性键
属性值

injective.insurance.v1beta1.EventInsuranceFundUpdate

fund

{fundJSON}

injective.insurance.v1beta1.EventWithdrawRedemption

schedule

{scheduleJSON}

injective.insurance.v1beta1.EventWithdrawRedemption

redeem_coin

{redeemCoin}

BeginBlocker
状态转换
拆分-应用-合并
预言机

钩子(Hooks)

其他模块可以注册操作,以便在ocr模块内发生特定事件时执行。以下钩子可以在ocr中注册:

  • AfterSetFeedConfig(ctx sdk.Context, feedConfig *FeedConfig)

    • 在feed配置创建或更新后调用

  • AfterTransmit(ctx sdk.Context, feedId string, answer math.LegacyDec, timestamp int64)

    • 在信息传输时调用

  • AfterFundFeedRewardPool(ctx sdk.Context, feedId string, newPoolAmount sdk.Coin)

    • 在feed奖励池更新时调用

注意:oracle模块接受AfterTransmit钩子,用于在传输时存储累计价格。

改进

精度损失边界情况处理

保险基金份额代币当前采用 10¹⁸ 的小数精度。由于用户存款金额可能存在较大差异,并且未来份额代币的价格可能会大幅升高或降低,因此可能会出现潜在问题。

保险(Insurance)

摘要

本文规定了 Injective 链的保险模块。 该模块为 Injective 链的交易模块中的衍生品市场提供保险资金,以支持更高杠杆交易。从高层次来看,每个衍生品市场的保险资金由一个无许可的承保人群体提供资助,每个承保人拥有对保险基金中基础资产的按比例索赔权(通过保险基金份额代币表示)。 当其对应的衍生品市场中的头寸被强平并且有正的权益时,保险基金会增长,因为在强平时,正的权益的一半将被发送到保险基金。当一个负权益的头寸被强平时(即头寸已超过破产),保险基金将被用于覆盖缺失的权益。

目录

EndBlock

在每个 EndBlock 处,已到期的赎回请求将被自动处理,具体流程如下:赎回对应的保险基金份额代币将被销毁。按比例计算的报价货币金额将从保险模块提取至承保人的账户余额。

消息

Msg/CreateInsuranceFund

MsgCreateInsuranceFund 定义了用于为衍生品市场创建保险基金的消息。

// MsgCreateInsuranceFund a message to create an insurance fund for a derivative market.
message MsgCreateInsuranceFund {
  option (gogoproto.equal) = false;
  option (gogoproto.goproto_getters) = false;
  // Creator of the insurance fund.
  string sender = 1;
  // Ticker for the derivative market.
  string ticker = 2;
  // Coin denom to use for the market quote denom
  string quote_denom = 3;
  // Oracle base currency
  string oracle_base = 4;
  // Oracle quote currency
  string oracle_quote = 5;
  // Oracle type
  injective.oracle.v1beta1.OracleType oracle_type = 6;
  // Expiration time of the market. Should be -1 for perpetual markets.
  int64 expiry = 7;
  // Initial deposit of the insurance fund
  cosmos.base.v1beta1.Coin initial_deposit = 8 [(gogoproto.nullable) = false];
}

字段描述

  • Sender 字段描述了保险基金的创建者。

  • Ticker、QuoteDenom、OracleBase、OracleQuote、OracleType、Expiry 字段描述了与保险基金对应的衍生品市场信息。

  • InitialDeposit 指定了用于承保保险基金的初始存款金额。

免责声明:在创建保险基金时,基金本身将保留一小部分份额(1%)(协议拥有的流动性)。建议首次认购金额为 1 美元。

此功能的背后动机是避免在承保基金时出现潜在的舍入问题。例如,如果没有协议拥有的流动性,原始基金创建者如果取出大部分份额,仅留下少量份额,份额代币的价值可能会与原始价值大幅偏离。下一个承保者将不得不提供更大金额的存款,尽管他们获得的份额数量相同。

Msg/Underwrite

MsgUnderwrite 定义了用于承保保险基金的消息。

// MsgUnderwrite defines a message for depositing coins to underwrite an insurance fund
message MsgUnderwrite {
  option (gogoproto.equal) = false;
  option (gogoproto.goproto_getters) = false;
  // Address of the underwriter.
  string sender = 1;
  // MarketID of the insurance fund.
  string market_id = 2;
  // Amount of quote_denom to underwrite the insurance fund.
  cosmos.base.v1beta1.Coin deposit = 3 [(gogoproto.nullable) = false];
}

字段描述

  • Sender 字段描述了保险基金的承保人。

  • MarketId 字段描述了与保险基金关联的衍生品市场 ID。

  • Deposit 字段描述了要添加到保险基金的存款金额。

Msg/RequestRedemption

MsgRequestRedemption 定义了用于向保险基金请求赎回的消息。

// MsgRequestRedemption defines a message for requesting a redemption of the sender's insurance fund tokens
message MsgRequestRedemption {
  option (gogoproto.equal) = false;
  option (gogoproto.goproto_getters) = false;
  // Address of the underwriter requesting a redemption.
  string sender = 1;
  // MarketID of the insurance fund.
  string market_id = 2;
  // Insurance fund share token amount to be redeemed.
  cosmos.base.v1beta1.Coin amount = 3 [(gogoproto.nullable) = false];
}

字段描述

  • Sender 字段描述了保险基金的赎回请求者。

  • MarketId 字段描述了与保险基金关联的衍生品市场 ID。

  • Amount 字段描述了要赎回的份额代币数量。

OCR

摘要

OCR 模块用于将 Chainlink 的 OCR(Off-Chain Report,链下报告)信息存储到链上存储。

Feed 配置由模块管理员管理,报告由传输者(Transmitters)和观察者(Observers)提交至链上。传输者和观察者在由治理配置的链上以 LINK 代币获得奖励。

在存储 Feed 信息时,模块提供钩子,供 Oracle 模块用于计算期货市场的累积价格。

目录

概念

OCR 模块用于由验证成员将 Chainlink 的 OCR(Off-Chain Report)信息存储到链上。

链下报告由 N 个节点(Oracles)组成,这些节点从外部来源收集数据。报告在 Oracles 之间以 P2P 方式交换,以获取签名批准。OCR 模块在链上识别一部分节点(Transmitters),这些节点必须将报告提交至模块,第一个成功提交至链上的 Transmitter 可额外获得奖励以覆盖 Gas 费用,其他 Transmitters 不会获得额外奖励。所有参与该轮的 Oracles 都会获得报酬。OCR 模块存储报告中的中位值。

OCR 术语

协议定期将 Oracle 报告发送到 OCR 模块。报告协议由三个组件组成:Pacemaker、报告生成和传输。

Pacemaker Pacemaker 驱动报告生成过程,该过程以纪元(Epoch)为单位进行。每个纪元都有一个指定的领导者,Pacemaker 会将启动报告生成协议的任务交给该领导者。如果领导者未能及时生成有效报告,Pacemaker 会中止当前的报告生成并启动一个新的纪元。

报告生成 在给定的纪元中,报告生成协议进入多个回合,在这些回合中收集观测数据,并在满足条件(如心跳和偏差)时生成已签名的 Oracle 报告。回合由领导节点控制,领导节点控制回合的频率,收集观测数据并生成报告。

传输 传输协议随后将生成的报告传输到 OCR 模块。

链下 OCR 集成

  • 提供与 Injective 进行通信的手段,使用 sdk-go。

  • 从模块中读取数据,例如已批准的 Oracle 列表。

  • 将报告提交为 Msg(实现 ContractTransmitter)。

  • 实现 OffchainConfigDigester。

  • 实现 OnchainKeyring,用于生成将在目标链模块上有效的签名。

  • 实现 ContractConfigTracker,用于跟踪链模块配置的变化(政府批准)。

备注:

  • 报告按 Epoch-Round 格式进行时间戳标记。

  • OCR 模块验证报告中 Oracle 的签名。

  • OCR 模块记录对报告做出贡献的 Oracles,以便进行支付。

  • OCR 模块存储观测数据的中位值。

  • OCR 模块为第一个提交 Msg 的用户提供额外奖励。

集成概览

  • 80 个加密货币/USD 配对(例如 ETH/USD、BTC/USD)

  • 17 个稳定币配对(例如 USDT/USD、USDC/USD)

  • 73 个 ETH 配对(例如 LINK/ETH)

  • 17 个外汇配对(例如 GBP/USD、CNY/USD)

在 Injective 上的衍生品市场指定了以下 Oracle 参数:

  • oracleBase(例如 BTC)

  • oracleQuote(例如 USDT)

  • oracleType(例如 Chainlink)

因此,对于 Injective 上的 BTC/USDT 衍生品市场,oracleBase 将是 BTC/USD,oracleQuote 将是 USDT/USD,oracleType 将是 Chainlink。市场价格将通过将 BTC/USD 价格除以 USDT/USD 价格来获得,从而得出 BTC/USDT 价格。

提案

SetConfigProposal

SetConfigProposal 是由治理设置 Feed 配置的提案。

步骤:

  1. 验证提案的基本信息。

  2. 确保模块的 LinkDenom 与提案中的 LinkDenom 相同。

  3. 从 ctx.ChainID 设置 p.Config.OnchainConfig.ChainId。

  4. 为 feedId 设置 Feed 配置。

  5. 为 Config.Transmitters 设置 Feed 传输和观测计数。

SetBatchConfigProposal

SetBatchConfigProposal 是由治理一次性设置多个 Feed 配置的提案。

消息

在本节中,我们描述了 OCR 消息的处理过程及其对状态的相应更新。

Msg/CreateFeed

MsgCreateFeed 是用于创建 Feed 配置的消息,它是一个受限制的消息,仅能由模块管理员执行。

步骤:

  1. 确保发送者是模块管理员。

  2. 确保 msg.Config.OnchainConfig.LinkDenom 与模块参数中的 LinkDenom 匹配。

  3. 从 ctx.ChainID 设置 OnchainConfig.ChainId。

  4. 确保相同 FeedId 的 FeedConfig 不存在。

  5. 将最新的 EpochAndRound 设置为 (0, 0)。

  6. 设置给定 FeedId 的 Feed 配置。

  7. 将 Feed 传输计数和观测计数设置为 1。

Msg/UpdateFeed

MsgCreateFeed 是用于更新 Feed 配置的消息,它是一个受限制的消息,仅能由 Feed 管理员或 Feed 计费管理员执行。

步骤:

  1. 根据 FeedId 获取之前的 Feed 配置,并确保其存在。

  2. 确保发送者是 Feed 管理员或 Feed 计费管理员。

  3. 确保计费管理员未更改签名者、传输者和 Feed 管理员。

  4. 处理之前的 Feed 配置的奖励支付。

  5. 删除之前的 Feed 传输和观测计数。

  6. 将最新的 EpochAndRound 设置为 (0, 0)。

  7. 如果设置了,则更新签名者、传输者、LinkPerObservation、LinkPerTransmission、LinkDenom、FeedAdmin 和 BillingAdmin。

Msg/Transmit

MsgTransmit 是用于传输特定 Feed 的报告的消息。在广播该消息时,必须有足够的观察者签名才能被接受。

步骤:

  1. 获取 feedId 的 epoch 和 round。

  2. 通过检查 msg.Epoch 和 msg.Round 确保报告不是过期的。

  3. 从 feedId 获取 Feed 配置和配置信息。

  4. 检查 msg.ConfigDigest 是否等于 Feed 配置信息的最新配置摘要。

  5. 检查传输者是否是 FeedConfig 中配置的有效传输者。

  6. 保存传输者的报告。

  7. 触发传输事件。

  8. 验证签名及签名数量。

  9. 增加 Feed 的观测和传输计数。

Msg/FundFeedRewardPool

MsgFundFeedRewardPool 是一条消息,用于向 Feed 奖励池中添加资金,这些资金将分配给传输者和观察者。

步骤:

  1. 获取 feedId 的之前奖励池金额。

  2. 如果之前的金额为空,则将池金额初始化为零。

  3. 确保之前的金额 denom 与存款 denom 相同(如果存在)。

  4. 从账户向模块账户(OCR 模块)发送币。

  5. 使用金额字段增加更新奖励池金额。

  6. 如果设置了钩子,则调用 AfterFundFeedRewardPool 钩子。

Msg/WithdrawFeedRewardPool

MsgFundFeedRewardPool 是一条消息,用于从 Feed 奖励池中提取资金,仅限 Feed 管理员或计费管理员执行。

步骤:

  1. 获取 feedId 的 Feed 配置。

  2. 确保 msg.Sender 是 Feed 管理员或计费管理员。

  3. 为 Feed 处理奖励。

  4. 从模块账户中提取指定金额 msg.Amount。

Msg/SetPayees

MsgSetPayees 是一条消息,用于为传输者设置支付方,仅限 Feed 管理员执行。一旦设置,只有支付方才能更改。

步骤:

  1. 获取 feedId 的 Feed 配置,并确保 Feed 配置存在。

  2. 确保 msg.Sender 是 Feed 管理员。

  3. 遍历 msg.Transmitters,

    1. 确保传输者已经设置了支付方。

    2. 为传输者设置支付方。

Msg/TransferPayeeship

MsgTransferPayeeship 是一条消息,用于转移特定传输者的 Feed 支付权。在执行后,将创建一个待处理的支付权对象。

步骤:

  1. 获取 feedId 的 Feed 配置,并确保 Feed 配置存在。

  2. 确保 msg.Sender 是当前的支付方。

  3. 检查之前的待处理支付权转移记录,确保之前的支付权转移不会冲突。

  4. 设置支付权转移记录。

Msg/AcceptPayeeship

MsgTransferPayeeship 是一条消息,用于接受特定传输者的 Feed 支付权。

步骤:

  1. 获取 feedId 的 Feed 配置,并确保 Feed 配置存在。

  2. 获取 msg.Transmitter 和 feedId 的待处理支付权转移记录。

  3. 重置 feedId 和传输者的支付方。

事件

ocr模块会发出以下事件:

处理器

MsgCreateFeed

MsgUpdateFeed

MsgTransmit

MsgFundFeedRewardPool

MsgWithdrawFeedRewardPool

MsgSetPayees

MsgTransferPayeeship

MsgAcceptPayeeship

提案

SetConfigProposal

SetBatchConfigProposal

BeginBlocker

EndBlock

  • 阶段 0:确定当前区块中所有在支持费用折扣的市场中下单的账户的费用折扣。

  • 阶段 1:并行处理所有市场订单

    • 现货市场和衍生品市场订单

    • 市场订单将在区块开始时根据挂单簿执行

    • 请注意,市场订单可能会由于后续的预言机更新或限价单取消而在 EndBlocker 中失效。

  • 阶段 2:将市场订单执行结果持久化到存储

    • 现货市场

      • 持久化现货市场订单执行数据

      • 发出相关事件

        • EventBatchSpotExecution

    • 衍生品市场

      • 持久化衍生品市场订单执行数据

      • 发出相关事件

        • EventBatchDerivativeExecution

        • EventCancelDerivativeOrder

  • 阶段 3:并行处理所有限价单

    • 匹配的现货和衍生品限价单

    • 限价单将以频繁批量拍卖模式执行,以确保公平的匹配价格,详细信息请见下文。

    • 请注意,普通限价单可能会由于后续的预言机更新而在 EndBlocker 中失效,而减仓限价单可能会由于后续到来的订单翻转头寸而在 EndBlocker 中失效。

  • 阶段 4:将限价单匹配执行结果和新限价单持久化到存储

    • 现货市场

      • 持久化现货市场匹配执行数据

      • 发出相关事件

        • EventNewSpotOrders

        • EventBatchSpotExecution

    • 衍生品市场

      • 持久化衍生品市场匹配执行数据

      • 发出相关事件

        • EventNewDerivativeOrders

        • EventBatchDerivativeExecution

        • EventCancelDerivativeOrder

  • 阶段 5:持久化永续市场资金信息

  • 阶段 6:持久化交易奖励总额和账户积分

  • 阶段 7:持久化新的费用折扣数据

    • 即新的已支付费用和新的账户等级

  • 阶段 8:处理现货市场参数更新(如果有)

  • 阶段 9:处理衍生品市场参数更新(如果有)

  • 阶段 10:发出存款和头寸更新事件

订单匹配:频繁批量拍卖(FBA)

  • 同样,限价单也会以统一的清算价格执行。新的限价单与挂单簿结合,并且只要存在负价差(spread),订单就会被匹配。

  • 清算价格可以是以下几种:

    a. 如果最后一个匹配的订单跨越了价格差(spread)方向,则为最佳买/卖订单;b. 如果是衍生品市场,且标记价格位于最后一个匹配订单之间,则为标记价格;c. 如果没有以上条件,则为中间价格(mid price)。

单笔交易计算

  • 对于符合条件的市场,计算手续费折扣:

    • 手续费折扣作为退款应用,并记录支付的手续费贡献。

    • 中继商费用在应用手续费折扣后计算。

  • 对于符合条件的市场,计算交易奖励积分贡献:

    • 获取做市商和接单商的FeePaidMultiplier。

    • 计算交易奖励积分贡献。

    • 交易奖励积分基于折扣后的交易手续费。

  • 计算手续费退款(或收费)。订单匹配后可能有几种原因导致手续费退款:

    1. 这是一个未匹配或部分匹配的限价单,这意味着它将变成一个休息限价单,并且从接单商费用转变为做市商费用。退款为UnmatchedQuantity * (TakerFeeRate - MakerFeeRate)。请注意,对于负的做市商手续费,我们退还UnmatchedQuantity * TakerFeeRate。

    2. 应用了手续费折扣。我们退还原始手续费与折扣后支付的手续费之间的差额。

    3. 订单以更好的价格匹配,从而导致不同的费用。

      1. 对于买单,更好的价格意味着更低的价格,因此手续费较低。我们退还手续费价格差额。

      2. 对于卖单,更好的价格意味着更高的价格,因此手续费较高。我们收取手续费价格差额。

更多详细信息可参考部分的待处理赎回的自动提现。

本节描述了交易消息的处理流程及其对状态的相应更新。每条消息创建或修改的状态对象均在“”部分定义。

Chainlink 有多个,包括:

类型
属性键
属性值
类型
属性键
属性值
类型
属性键
属性值
类型
属性键
属性值
类型
属性键
属性值
类型
属性键
属性值
类型
属性键
属性值
类型
属性键
属性值
类型
属性键
属性值
类型
属性键
属性值

交易所的 在每个区块结束时运行,按照我们定义的顺序,在治理和质押模块之后,佩吉、拍卖和保险模块之前。特别需要注意的是,治理模块的 EndBlocker 必须在交易所模块的 EndBlocker 之前运行。

FBA(频繁批量拍卖)的目标是防止任何前置交易()。这一目标通过在给定区块内计算一个单一的清算价格来实现,所有匹配的订单都会按照该价格进行清算。

市场订单首先根据区块开始时的挂单簿进行填充。当挂单按其各自的订单价格被执行时,市场订单将以统一的清算价格执行,采用与限价单相同的机制。举个例子,关于市场订单在 FBA 模式下的匹配,详情见 。

关于限价单在 FBA 模式下的匹配示例,请查看 。

您可以在找到相关代码示例。请查看主分支以获取最新的链上代码。

状态
状态转换
消息
End Block
事件
参数
改进
状态转换
状态
概念
状态
消息
提案
Begin-Block
钩子(Hooks)
事件
参数
价格数据 Feed
message SetConfigProposal {
  option (gogoproto.equal) = false;
  option (gogoproto.goproto_getters) = false;

  string title = 1;
  string description = 2;
  FeedConfig config = 3;
}
message SetBatchConfigProposal {
  option (gogoproto.equal) = false;
  option (gogoproto.goproto_getters) = false;

  string title = 1;
  string description = 2;
  // signers ith element is address ith oracle uses to sign a report
  repeated string signers = 3;
  // transmitters ith element is address ith oracle uses to transmit a report via the transmit method
  repeated string transmitters = 4;
  // Native denom for LINK coin in the bank keeper
  string link_denom = 5;
  repeated FeedProperties feed_properties = 6;
}
message MsgCreateFeed {
  option (gogoproto.equal) = false;
  option (gogoproto.goproto_getters) = false;
  string sender = 1;
  FeedConfig config = 2;
}
message MsgUpdateFeed {
  option (gogoproto.equal) = false;
  option (gogoproto.goproto_getters) = false;
  string sender = 1;
  // feed_id is an unique ID for the target of this config
  string feed_id = 2;
  // signers ith element is address ith oracle uses to sign a report
  repeated string signers = 3;
  // transmitters ith element is address ith oracle uses to transmit a report via the transmit method
  repeated string transmitters = 4;
  // Fixed LINK reward for each observer
  string link_per_observation = 5[
    (gogoproto.customtype) = "cosmossdk.io/math.Int",
    (gogoproto.nullable) = true
  ];
  // Fixed LINK reward for transmitter
  string link_per_transmission = 6[
    (gogoproto.customtype) = "cosmossdk.io/math.Int",
    (gogoproto.nullable) = true
  ];
  // Native denom for LINK coin in the bank keeper
  string link_denom = 7;
  // feed administrator
  string feed_admin = 8;
  // feed billing administrator
  string billing_admin = 9;
}
message MsgTransmit {
  option (gogoproto.equal) = false;
  option (gogoproto.goproto_getters) = false;
  // Address of the transmitter
  string transmitter = 1;
  bytes config_digest = 2;
  string feed_id = 3;
  uint64 epoch = 4;
  uint64 round = 5;
  bytes extra_hash = 6;
  Report report = 7;
  repeated bytes signatures = 8;
}
message MsgFundFeedRewardPool {
  option (gogoproto.equal) = false;
  option (gogoproto.goproto_getters) = false;

  string sender = 1;
  string feed_id = 2;
  cosmos.base.v1beta1.Coin amount = 3 [(gogoproto.nullable) = false];
}
message MsgWithdrawFeedRewardPool {
  option (gogoproto.equal) = false;
  option (gogoproto.goproto_getters) = false;

  string sender = 1;
  string feed_id = 2;
  cosmos.base.v1beta1.Coin amount = 3 [(gogoproto.nullable) = false];
}
message MsgSetPayees {
  option (gogoproto.equal) = false;
  option (gogoproto.goproto_getters) = false;

  string sender = 1;
  string feed_id = 2;
  // addresses oracles use to transmit the reports
  repeated string transmitters = 3;
  // addresses of payees corresponding to list of transmitters
  repeated string payees = 4;
}
message MsgTransferPayeeship {
  option (gogoproto.equal) = false;
  option (gogoproto.goproto_getters) = false;

  // transmitter address of oracle whose payee is changing
  string sender = 1;
  string transmitter = 2;
  string feed_id = 3;
  // new payee address
  string proposed = 4;
}
message MsgAcceptPayeeship {
  option (gogoproto.equal) = false;
  option (gogoproto.goproto_getters) = false;
  // new payee address
  string payee = 1;
  // transmitter address of oracle whose payee is changing
  string transmitter = 2;
  string feed_id = 3;
}

message

action

MsgCreateFeed

message

sender

{sender}

message

action

MsgUpdateFeed

message

sender

{sender}

EventNewTransmission

FeedId

{FeedId}

EventNewTransmission

AggregatorRoundId

{AggregatorRoundId}

EventNewTransmission

Answer

{Answer}

EventNewTransmission

Transmitter

{Transmitter}

EventNewTransmission

ObservationsTimestamp

{ObservationsTimestamp}

EventNewTransmission

Observations

{Observations}

EventNewTransmission

Observers

{Observers}

EventNewTransmission

ConfigDigest

{ConfigDigest}

EventNewTransmission

EpochAndRound

{EpochAndRound}

EventTransmitted

ConfigDigest

{ConfigDigest}

EventTransmitted

Epoch

{Epoch}

message

action

MsgTransmit

message

sender

{sender}

message

action

MsgFundFeedRewardPool

message

sender

{sender}

message

action

MsgWithdrawFeedRewardPool

message

sender

{sender}

message

action

MsgSetPayees

message

sender

{sender}

message

action

MsgTransferPayeeship

message

sender

{sender}

message

action

MsgAcceptPayeeship

message

sender

{sender}

EventConfigSet

ConfigDigest

{ConfigDigest}

EventConfigSet

PreviousConfigBlockNumber

{PreviousConfigBlockNumber}

EventConfigSet

Config

{Config}

EventConfigSet

ConfigInfo

{ConfigInfo}

EventConfigSet[]

ConfigDigest

{ConfigDigest}

EventConfigSet[]

PreviousConfigBlockNumber

{PreviousConfigBlockNumber}

EventConfigSet[]

Config

{Config}

EventConfigSet[]

ConfigInfo

{ConfigInfo}

事件

oracle模块会触发以下事件:

Band

message SetBandPriceEvent {
  string relayer = 1;
  string symbol = 2;
  string price = 3 [(gogoproto.customtype) = "cosmossdk.io/math.LegacyDec", (gogoproto.nullable) = false];
  uint64 resolve_time = 4;
  uint64 request_id = 5;
}

message SetBandIBCPriceEvent {
  string relayer = 1;
  repeated string symbols = 2;
  repeated string prices = 3 [(gogoproto.customtype) = "cosmossdk.io/math.LegacyDec", (gogoproto.nullable) = false];
  uint64 resolve_time = 4;
  uint64 request_id = 5;
  int64 client_id = 6;
}

message EventBandIBCAckSuccess {
  string ack_result = 1;
  int64 client_id = 2;
}

message EventBandIBCAckError {
  string ack_error = 1;
  int64 client_id = 2;
}

message EventBandIBCResponseTimeout {
  int64 client_id = 1;
}

Chainlink

message SetChainlinkPriceEvent {
  string feed_id = 1;
  string answer = 2 [(gogoproto.customtype) = "cosmossdk.io/math.LegacyDec", (gogoproto.nullable) = false];
  uint64 timestamp = 3;
}

Coinbase

message SetCoinbasePriceEvent {
  string symbol = 1;
  string price = 2 [(gogoproto.customtype) = "cosmossdk.io/math.LegacyDec", (gogoproto.nullable) = false];
  uint64 timestamp = 3;
}

Provider

message SetProviderPriceEvent {
  string provider = 1;
  string relayer = 2;
  string symbol = 3;
  string price = 4 [(gogoproto.customtype) = "cosmossdk.io/math.LegacyDec", (gogoproto.nullable) = false];
}

Pricefeed

message SetPriceFeedPriceEvent {
  string relayer = 1;

  string base = 2;
  string quote = 3;

  // price defines the price of the oracle base and quote
  string price = 4 [(gogoproto.customtype) = "cosmossdk.io/math.LegacyDec", (gogoproto.nullable) = false];
}

Pyth

message EventSetPythPrices {
  repeated PythPriceState prices = 1;
}

Stork

message EventSetStorkPrices {
  repeated StorkPriceState prices = 1;
}

BeginBlock

在每个BeginBlock时,它会检查是否到了支付间隔时间,如果到了,它会为所有的feed处理支付。

步骤

  • 确保是支付间隔的BeginBlock 。

  • 在遍历所有feed配置, 处理奖励支付。

参数

ocr模块包含以下参数:

键
类型
示例

LinkDenom

string

link

PayoutBlockInterval

uint64

100

ModuleAdmin

string

{address}

Keeper

oracle模块当前提供了三个不同的导出keeper接口,这些接口可以传递给需要读取价格源的其他模块。模块应使用最小权限的接口,以提供其所需的功能。

Oracle模块 ViewKeeper

oracle模块的ViewKeeper提供了获取任何支持的oracle类型和oracle对的价格数据以及累计价格数据的功能。

type ViewKeeper interface {
    GetPrice(ctx sdk.Context, oracletype types.OracleType, base string, quote string) *math.LegacyDec // Returns the price for a given pair for a given oracle type.
    GetCumulativePrice(ctx sdk.Context, oracleType types.OracleType, base string, quote string) *math.LegacyDec // Returns the cumulative price for a given pair for a given oracle type.
}

注意,GetPrice 对于Coinbase oracle返回的是5分钟的时间加权平均价格(TWAP)。

Band

BandKeeper 提供了创建/修改/读取/删除BandPricefeed和BandRelayer的功能。

type BandKeeper interface {
    GetBandPriceState(ctx sdk.Context, symbol string) *types.BandPriceState
    SetBandPriceState(ctx sdk.Context, symbol string, priceState types.BandPriceState)
    GetAllBandPriceStates(ctx sdk.Context) []types.BandPriceState
    GetBandReferencePrice(ctx sdk.Context, base string, quote string) *math.LegacyDec
    IsBandRelayer(ctx sdk.Context, relayer sdk.AccAddress) bool
    GetAllBandRelayers(ctx sdk.Context) []string
    SetBandRelayer(ctx sdk.Context, relayer sdk.AccAddress)
    DeleteBandRelayer(ctx sdk.Context, relayer sdk.AccAddress)
}

Band IBC

BandIBCKeeper 提供了创建/修改/读取/删除BandIBCOracleRequest、BandIBCPriceState、BandIBCLatestClientID和BandIBCCallDataRecord的功能。

type BandIBCKeeper interface {
	SetBandIBCOracleRequest(ctx sdk.Context, req types.BandOracleRequest)
	GetBandIBCOracleRequest(ctx sdk.Context) *types.BandOracleRequest
	DeleteBandIBCOracleRequest(ctx sdk.Context, requestID uint64)
	GetAllBandIBCOracleRequests(ctx sdk.Context) []*types.BandOracleRequest

	GetBandIBCPriceState(ctx sdk.Context, symbol string) *types.BandPriceState
	SetBandIBCPriceState(ctx sdk.Context, symbol string, priceState types.BandPriceState)
	GetAllBandIBCPriceStates(ctx sdk.Context) []types.BandPriceState
	GetBandIBCReferencePrice(ctx sdk.Context, base string, quote string) *math.LegacyDec

	GetBandIBCLatestClientID(ctx sdk.Context) uint64
	SetBandIBCLatestClientID(ctx sdk.Context, clientID uint64)
	SetBandIBCCallDataRecord(ctx sdk.Context, clientID uint64, bandIBCCallDataRecord []byte)
	GetBandIBCCallDataRecord(ctx sdk.Context, clientID uint64) *types.CalldataRecord
}

Coinbase

CoinbaseKeeper 提供了创建、修改和读取CoinbasePricefeed数据的功能。

type CoinbaseKeeper interface {
    GetCoinbasePrice(ctx sdk.Context, base string, quote string) *math.LegacyDec
    HasCoinbasePriceState(ctx sdk.Context, key string) bool
    GetCoinbasePriceState(ctx sdk.Context, key string) *types.CoinbasePriceState
    SetCoinbasePriceState(ctx sdk.Context, priceData *types.CoinbasePriceState) error
    GetAllCoinbasePriceStates(ctx sdk.Context) []*types.CoinbasePriceState
}

GetCoinbasePrice 返回基于Coinbase提供的CoinbasePriceState.Timestamp值的5分钟时间加权平均价格(TWAP)。

PriceFeeder

PriceFeederKeeper 提供了创建/修改/读取/删除PriceFeedPrice和PriceFeedRelayer的功能。

type PriceFeederKeeper interface {
    IsPriceFeedRelayer(ctx sdk.Context, oracleBase, oracleQuote string, relayer sdk.AccAddress) bool
    GetAllPriceFeedStates(ctx sdk.Context) []*types.PriceFeedState
    GetAllPriceFeedRelayers(ctx sdk.Context, baseQuoteHash common.Hash) []string
    SetPriceFeedRelayer(ctx sdk.Context, oracleBase, oracleQuote string, relayer sdk.AccAddress)
    SetPriceFeedRelayerFromBaseQuoteHash(ctx sdk.Context, baseQuoteHash common.Hash, relayer sdk.AccAddress)
    DeletePriceFeedRelayer(ctx sdk.Context, oracleBase, oracleQuote string, relayer sdk.AccAddress)
    HasPriceFeedInfo(ctx sdk.Context, priceFeedInfo *types.PriceFeedInfo) bool
    GetPriceFeedInfo(ctx sdk.Context, baseQuoteHash common.Hash) *types.PriceFeedInfo
    SetPriceFeedInfo(ctx sdk.Context, priceFeedInfo *types.PriceFeedInfo)
    GetPriceFeedPriceState(ctx sdk.Context, base string, quote string) *types.PriceState
    SetPriceFeedPriceState(ctx sdk.Context, oracleBase, oracleQuote string, priceState *types.PriceState)
    GetPriceFeedPrice(ctx sdk.Context, base string, quote string) *math.LegacyDec
}

Stork

StorkKeeper 提供了创建/修改/读取StorkPricefeed和StorkPublishers数据的功能。

type StorkKeeper interface {
	GetStorkPrice(ctx sdk.Context, base string, quote string) *math.LegacyDec
	IsStorkPublisher(ctx sdk.Context, address string) bool
	SetStorkPublisher(ctx sdk.Context, address string)
	DeleteStorkPublisher(ctx sdk.Context, address string)
	GetAllStorkPublishers(ctx sdk.Context) []string

	SetStorkPriceState(ctx sdk.Context, priceData *types.StorkPriceState)
	GetStorkPriceState(ctx sdk.Context, symbol string) types.StorkPriceState
	GetAllStorkPriceStates(ctx sdk.Context) []*types.StorkPriceState
}

GetStorkPrice 返回StorkPriceState的价格(value)。

状态

Genesis 状态定义了模块的初始状态,用于设置模块。

// GenesisState defines the OCR module's genesis state.
type GenesisState struct {
	// params defines all the parameters of related to OCR.
	Params Params 
	// feed_configs stores all of the supported OCR feeds
	FeedConfigs []*FeedConfig
	// latest_epoch_and_rounds stores the latest epoch and round for each feedId
	LatestEpochAndRounds []*FeedEpochAndRound
	// feed_transmissions stores the last transmission for each feed
	FeedTransmissions []*FeedTransmission
	// latest_aggregator_round_ids stores the latest aggregator round ID for each feedId
	LatestAggregatorRoundIds []*FeedLatestAggregatorRoundIDs
	// reward_pools stores the reward pools
	RewardPools []*RewardPool
	// feed_observation_counts stores the feed observation counts
	FeedObservationCounts []*FeedCounts
	// feed_transmission_counts stores the feed transmission counts
	FeedTransmissionCounts []*FeedCounts
	// pending_payeeships stores the pending payeeships
	PendingPayeeships []*PendingPayeeship
}

Params

Params 是一个模块级别的配置,用于存储系统参数并定义 OCR 模块的整体功能。该模块可以通过治理使用参数更新提案进行修改,治理模块原生支持此功能。

OCR 模块参数存储的结构体。

type Params struct {
	// Native denom for LINK coin in the bank keeper
	LinkDenom string
	// The block number interval at which payouts are made
	PayoutBlockInterval uint64
	// The admin for the OCR module
	ModuleAdmin string
}

FeedConfig

FeedConfig 用于管理 Feed 的配置,每个 Feed 存在一个对应的 FeedConfig。

type FeedConfig struct {
	// signers ith element is address ith oracle uses to sign a report
	Signers []string
	// transmitters ith element is address ith oracle uses to transmit a report via the transmit method
	Transmitters []string
	// f maximum number of faulty/dishonest oracles the protocol can tolerate while still working correctly
	F uint32
	// onchain_config contains properties relevant only for the Cosmos module.
	OnchainConfig *OnchainConfig
	// offchain_config_version version of the serialization format used for "offchain_config" parameter
	OffchainConfigVersion uint64
	// offchain_config serialized data used by oracles to configure their offchain operation
	OffchainConfig []byte
}

FeedConfigInfo

FeedConfigInfo 用于存储每次传输事件中需要更频繁更新的信息。

type FeedConfigInfo struct {
	LatestConfigDigest []byte
	F                  uint32
	N                  uint32
	// config_count ordinal number of this config setting among all config settings
	ConfigCount             uint64
	LatestConfigBlockNumber int64
}

Transmission

Transmission 是用于在存储中保存传输信息的单元。

// Transmission records the median answer from the transmit transaction at
// time timestamp
type Transmission struct {
	Answer                math.LegacyDec
	ObservationsTimestamp int64
	TransmissionTimestamp int64
}

Report

Report 是用于在存储中保存报告信息的单元。

type Report struct {
	ObservationsTimestamp int64
	Observers             []byte
	Observations          []math.LegacyDec
}

ReportToSign 保存需要由观察者签名的信息。

type ReportToSign struct {
	ConfigDigest []byte 
	Epoch        uint64
	Round        uint64 
	ExtraHash    []byte
	// Opaque report
	Report []byte
}

OnchainConfig

OnchainConfig 保存需要在链上管理的 Feed 配置的相关配置。

type OnchainConfig struct {
	// chain_id the ID of the Cosmos chain itself.
	ChainId string
	// feed_id is an unique ID for the target of this config
	FeedId string
	// lowest answer the median of a report is allowed to be
	MinAnswer math.LegacyDec
	// highest answer the median of a report is allowed to be
	MaxAnswer math.LegacyDec
	// Fixed LINK reward for each observer
	LinkPerObservation math.Int
	// Fixed LINK reward for transmitter
	LinkPerTransmission math.Int
	// Native denom for LINK coin in the bank keeper
	LinkDenom string
	// Enables unique reports
	UniqueReports bool
	// short human-readable description of observable this feed's answers pertain to
	Description string
	// feed administrator
	FeedAdmin string
	// feed billing administrator
	BillingAdmin string
}

ContractConfig

ContractConfig 保存与合同相关的配置,用于存储 OCR。

type ContractConfig struct {
	// config_count ordinal number of this config setting among all config settings
	ConfigCount uint64
	// signers ith element is address ith oracle uses to sign a report
	Signers []string 
	// transmitters ith element is address ith oracle uses to transmit a report via the transmit method
	Transmitters []string
	// f maximum number of faulty/dishonest oracles the protocol can tolerate while still working correctly
	F uint32
	// onchain_config serialized config that is relevant only for the module.
	OnchainConfig []byte
	// offchain_config_version version of the serialization format used for "offchain_config" parameter
	OffchainConfigVersion uint64
	// offchain_config serialized data used by oracles to configure their offchain operation
	OffchainConfig []byte
}

FeedProperties

FeedProperties 是用于通过 ID 存储 Feed 属性的单元。

type FeedProperties struct {
	// feed_id is an unique ID for the target of this config
	FeedId string
	// f maximum number of faulty/dishonest oracles the protocol can tolerate while still working correctly
	F uint32
	// offchain_config_version version of the serialization format used for "offchain_config" parameter
	OffchainConfigVersion uint64
	// offchain_config serialized data used by oracles to configure their offchain operation
	OffchainConfig []byte
	// lowest answer the median of a report is allowed to be
	MinAnswer math.LegacyDec
	// highest answer the median of a report is allowed to be
	MaxAnswer math.LegacyDec
	// Fixed LINK reward for each observer
	LinkPerObservation math.Int
	// Fixed LINK reward for transmitter
	LinkPerTransmission math.Int
	// Enables unique reports
	UniqueReports bool
	// short human-readable description of observable this feed's answers pertain to
	Description string
}

PendingPayeeship

PendingPayeeship 是一个记录,当某人将支付权委托给另一个地址时存储此记录。当提议的支付方接受后,该记录将被移除。

type PendingPayeeship struct {
	FeedId        string
	Transmitter   string
	ProposedPayee string
}

改进

扩展对其他oracle提供者的支持,包括Chainlink、Razor、DIA、API3、UMA、Pyth以及通过IBC获取的Band oracle数据。 在上述提供者中,正在开发通过IBC集成Band和Pyth oracle数据,以及通过OCR(链下报告)机制集成Chainlink oracle数据。

EndBlocker
Front-Running
API 文档
API 文档
此处

EndBlock

在每个区块结束时,将对模块的状态执行以下操作:

1. 处罚

验证者处罚

当验证者未能对通过 SignedValsetsWindow 的验证者集合更新(valset update)进行签名时,将受到惩罚。换句话说,如果验证者未能在预配置的时间内为验证者集合更新提供确认,他们将因其抵押的 SlashFractionValset 部分被罚没,并立即被监禁(jailed)。

批量处罚

当验证者未能对通过 SignedBatchesWindow 的交易批次(batch)进行签名时,将受到惩罚。换句话说,如果验证者未能在预配置的时间内为交易批次提供确认,他们将因其抵押的 SlashFractionBatch 部分被罚没,并立即被监禁(jailed)。

2. 取消超时的交易批次

任何仍在 Outgoing Batch 池中且其 BatchTimeout(指定的以太坊区块高度,交易批次应在此高度之前执行)已超时的交易批次将被从池中移除,相关的提款请求将重新插入到 Outgoing Tx 池中。

3. 创建新的验证者集合更新

在以下情况下,将自动创建新的验证者集合更新(Validator Set update):

  1. 验证者权力变化超过 5%:当最新验证者集合与当前验证者集合之间的权力差异(power diff)超过 5% 时。

  2. 验证者开始解绑:当某个验证者开始解绑(unbonding)时。

新的验证者集合最终将被中继(relayed)至以太坊上的 Peggy 合约。

4. 修剪旧验证者集

已通过 SignedValsetsWindow 的先前观察到的验证者集合(valsets)将从状态中移除。

5. 证明处理

处理当前正在投票的所有证明(attestations)(针对特定事件的声明集合)。每个证明依次处理,以确保每个 Peggy 合约事件都被处理。在处理完每个证明后,模块的 lastObservedEventNonce 和 lastObservedEthereumBlockHeight 将被更新。

根据证明中的声明类型,执行以下操作:

  • MsgDepositClaim:为接收者地址铸造/解锁存入的代币。

  • MsgWithdrawClaim:从 Outgoing 池中移除相应的交易批次,并取消任何先前的批次。

  • MsgValsetUpdatedClaim:更新模块的 LastObservedValset。

  • MsgERC20DeployedClaim:验证新的代币元数据并将其注册到模块的状态中(denom <-> token_contract)。

6. 清理已处理的证明

之前处理过的证明(高度早于 lastObservedEthereumBlockHeight)会从模块状态中移除。

状态

Params

oracle模块参数:

message Params {
  option (gogoproto.equal) = true;

  string pyth_contract = 1;
}

PriceState

PriceState是一个通用类型,用于管理所有oracle类型的累计价格、最新价格及其时间戳。

message PriceState {
    string price = 1 [(gogoproto.customtype) = "cosmossdk.io/math.LegacyDec", (gogoproto.nullable) = false];
    
    string cumulative_price = 2 [(gogoproto.customtype) = "cosmossdk.io/math.LegacyDec", (gogoproto.nullable) = false];
    
    int64 timestamp = 3;
}

其中:

  • Price 表示标准化的小数价格

  • CumulativePrice 表示自oracle价格源创建以来的累计价格

  • Timestamp 表示价格状态被中继的区块时间

公式:

$\mathrm{TWAP = \frac{CumulativePrice_2 - CumulativePrice_1}{Timestamp_2 - Timestamp_1}}$

Band

给定符号的Band价格数据表示和存储如下:

  • BandPriceState: 0x01 | []byte(symbol) -> ProtocolBuffer(BandPriceState)

message BandPriceState {
    string symbol = 1;
    string rate = 2 [(gogoproto.customtype) = "cosmossdk.io/math.Int", (gogoproto.nullable) = false];
    uint64 resolve_time = 3;
    uint64 request_ID = 4;
    PriceState price_state = 5 [(gogoproto.nullable) = false];
}

请注意,Rate 是从Band链获取的符号的原始USD汇率,经过1e9的缩放(例如,1.42的价格为1420000000),而PriceState 存储的是标准化的小数价格(例如,1.42)。

Band中继者通过其地址存储如下:

  • BandRelayer: 0x02 | RelayerAddr -> []byte{}

Band IBC

本节描述了通过IBC连接到Band链以维护价格的所有状态管理。

  • LatestClientID 用于管理Band IBC数据包的唯一clientID。当发送价格请求数据包到bandchain时,它会增加1。

  • LatestClientID: 0x32 -> 格式化(LatestClientID)

  • LatestRequestID 用于管理唯一的BandIBCOracleRequests。在创建新的BandIBCOracleRequest时,值会增加1。

  • LatestRequestID: 0x36 -> 格式化(LatestRequestID)

给定符号的Band IBC价格数据存储如下:

  • BandPriceState: 0x31 | []byte(symbol) -> ProtocolBuffer(BandPriceState)

message BandPriceState {
  string symbol = 1;
  string rate = 2 [(gogoproto.customtype) = "cosmossdk.io/math.Int", (gogoproto.nullable) = false];
  uint64 resolve_time = 3;
  uint64 request_ID = 4;
  PriceState price_state = 5 [(gogoproto.nullable) = false];
}
  • 当发送价格请求数据包到bandchain时,BandIBCCallDataRecord 存储如下

    • CalldataRecord: 0x33 | []byte(ClientId) -> ProtocolBuffer(CalldataRecord)

message CalldataRecord {
  uint64 client_id = 1;
  bytes calldata = 2;
}
  • 当治理配置oracle请求发送时,BandIBCOracleRequest 存储如下:

    • BandOracleRequest: 0x34 | []byte(RequestId) -> ProtocolBuffer(BandOracleRequest)

message BandOracleRequest {
  // Unique Identifier for band ibc oracle request
  uint64 request_id = 1;

  // OracleScriptID is the unique identifier of the oracle script to be executed.
  int64 oracle_script_id = 2;

  // Symbols is the list of symbols to prepare in the calldata
  repeated string symbols = 3;

  // AskCount is the number of validators that are requested to respond to this
  // oracle request. Higher value means more security, at a higher gas cost.
  uint64 ask_count = 4;

  // MinCount is the minimum number of validators necessary for the request to
  // proceed to the execution phase. Higher value means more security, at the
  // cost of liveness.
  uint64 min_count = 5;

  // FeeLimit is the maximum tokens that will be paid to all data source providers.
  repeated cosmos.base.v1beta1.Coin fee_limit = 6 [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"];

  // PrepareGas is amount of gas to pay to prepare raw requests
  uint64 prepare_gas = 7;
  // ExecuteGas is amount of gas to reserve for executing
  uint64 execute_gas = 8;
}
  • BandIBCParams 存储如下,并由治理配置:

  • BandIBCParams: 0x35 -> ProtocolBuffer(BandIBCParams)

BandIBCParams 包含与band链的IBC连接信息。

message BandIBCParams {
  // true if Band IBC should be enabled
  bool band_ibc_enabled = 1;
  // block request interval to send Band IBC prices
  int64 ibc_request_interval = 2;
  // band IBC source channel
  string ibc_source_channel = 3;
  // band IBC version
  string ibc_version = 4;
  // band IBC portID
  string ibc_port_id = 5;
}

注意:

  • BandIbcEnabled 描述了band IBC连接的状态

  • IbcSourceChannel、IbcVersion、IbcPortId 是IBC连接所需的常见参数

  • IbcRequestInterval 描述了在Injective链的BeginBlock时自动触发的价格获取请求间隔

Coinbase

给定符号("key")的Coinbase价格数据表示和存储如下:

  • CoinbasePriceState: 0x21 | []byte(key) -> CoinbasePriceState

message CoinbasePriceState {
  // kind should always be "prices"
  string kind = 1;
  // timestamp of the when the price was signed by coinbase
  uint64 timestamp = 2;
  // the symbol of the price, e.g. BTC
  string key = 3;
  // the value of the price scaled by 1e6
  uint64 value = 4;
  // the price state
  PriceState price_state = 5 [(gogoproto.nullable) = false];
}

请注意,Value 是从Coinbase获取的原始USD价格数据,经过1e6的缩放(例如,1.42的价格为1420000),而PriceState 存储的是标准化的小数价格(例如,1.42)。

Pricefeed

给定基础报价对的价格源数据表示和存储如下:

  • PriceFeedInfo: 0x11 + Keccak256Hash(base + quote) -> PriceFeedInfo

message PriceFeedInfo {
  string base = 1;
  string quote = 2;
}
  • PriceFeedPriceState: 0x12 + Keccak256Hash(base + quote) -> PriceFeedPriceState

message PriceFeedState {
  string base = 1;
  string quote = 2;
  PriceState price_state = 3;
  repeated string relayers = 4;
}
  • PriceFeedRelayer: 0x13 + Keccak256Hash(base + quote) + relayerAddr -> relayerAddr

Provider

提供者价格源数据表示和存储如下:

  • ProviderInfo: 0x61 + provider + @@@ -> ProviderInfo

message ProviderInfo {
  string provider = 1;
  repeated string relayers = 2;
}
  • ProviderIndex: 0x62 + relayerAddress -> provider

  • ProviderPrices: 0x63 + provider + @@@ + symbol -> ProviderPriceState

message ProviderPriceState {
  string symbol = 1;
  PriceState state = 2;
}

Pyth

Pyth价格数据表示和存储如下:

  • PythPriceState: 0x71 + priceID -> PythPriceState

message PythPriceState {
  bytes price_id = 1;
  string ema_price = 2 [(gogoproto.customtype) = "cosmossdk.io/math.LegacyDec", (gogoproto.nullable) = false];
  string ema_conf = 3 [(gogoproto.customtype) = "cosmossdk.io/math.LegacyDec", (gogoproto.nullable) = false];
  string conf = 4 [(gogoproto.customtype) = "cosmossdk.io/math.LegacyDec", (gogoproto.nullable) = false];
  uint64 publish_time = 5;
  PriceState price_state = 6 [(gogoproto.nullable) = false];
}

Stork

Stork价格数据表示和存储如下:

  • StorkPriceState: 0x81 + symbol -> PythPriceState

message StorkPriceState {
  // timestamp of the when the price was signed by stork
  uint64 timestamp = 1;
  // the symbol of the price, e.g. BTC
  string symbol = 2;
  // the value of the price scaled by 1e18
  string value = 3 [
    (gogoproto.customtype) = "cosmossdk.io/math.LegacyDec",
    (gogoproto.nullable) = false
  ];
  // the price state
  PriceState price_state = 5 [ (gogoproto.nullable) = false ];
}

Stork发布者数据表示和存储如下:

  • Publisher: 0x82 + stork_publisher -> publisher

string stork_publisher

提案

GrantProviderPrivilegeProposal

可以通过GrantBandOraclePrivilegeProposal将oracle提供者权限授予您的账户。在治理提案通过后,您将能够使用您的提供者中继价格源数据。

// Grant Privileges
message GrantProviderPrivilegeProposal {
  option (amino.name) = "oracle/GrantProviderPrivilegeProposal";
  option (gogoproto.equal) = false;
  option (gogoproto.goproto_getters) = false;

  option (cosmos_proto.implements_interface) = "cosmos.gov.v1beta1.Content";

  string title = 1;
  string description = 2;
  string provider = 3;
  repeated string relayers = 4;
}

您可以根据以下示例提交您的提案:

injectived tx oracle grant-provider-privilege-proposal YOUR_PROVIDER \
  YOUR_ADDRESS_HERE \
  --title="TITLE OF THE PROPOSAL" \
  --description="Registering PROVIDER as an oracle provider" \
  --chain-id=injective-888 \
  --from=local_key \
  --node=https://testnet.sentry.tm.injective.network:443 \
  --gas-prices=160000000inj \
  --gas=20000000 \
  --deposit="40000000000000000000inj"

为了成功通过测试网的提案,YOUR_DEPOSIT 应略低于 min_deposit 值(例如,40000000000000000000inj)。之后,您应该联系Injective开发团队。开发团队将补充您的存款至 min_deposit 并为您的提案投票。

RevokeProviderPrivilegeProposal

可以通过RevokeProviderPrivilegeProposal撤销您账户的oracle提供者权限。

// Revoke Privileges
message RevokeProviderPrivilegeProposal {
  option (amino.name) = "oracle/RevokeProviderPrivilegeProposal";
  option (gogoproto.equal) = false;
  option (gogoproto.goproto_getters) = false;

  option (cosmos_proto.implements_interface) = "cosmos.gov.v1beta1.Content";

  string title = 1;
  string description = 2;
  string provider = 3;
  repeated string relayers = 5;
}

GrantBandOraclePrivilegeProposal

可以通过GrantBandOraclePrivilegeProposal将Band Oracle权限授予Band提供者的中继者账户。

// Grant Privileges
message GrantBandOraclePrivilegeProposal {
    option (gogoproto.equal) = false;
    option (gogoproto.goproto_getters) = false;

    string title = 1;
    string description = 2;
    repeated string relayers = 3;
}

RevokeBandOraclePrivilegeProposal

可以通过RevokeBandOraclePrivilegeProposal从Band提供者的中继者账户撤销Band Oracle权限。

// Revoke Privileges
message RevokeBandOraclePrivilegeProposal {
    option (gogoproto.equal) = false;
    option (gogoproto.goproto_getters) = false;

    string title = 1;
    string description = 2;
    repeated string relayers = 3;
}

GrantPriceFeederPrivilegeProposal

可以通过GrantPriceFeederPrivilegeProposal将给定基础报价对的价格源权限授予中继者。

// Grant Privileges
message GrantPriceFeederPrivilegeProposal {
    option (gogoproto.equal) = false;
    option (gogoproto.goproto_getters) = false;

    string title = 1;
    string description = 2;
    string base = 3;
    string quote = 4;
    repeated string relayers = 5;
}

RevokePriceFeederPrivilegeProposal

可以通过RevokePriceFeederPrivilegeProposal从中继者账户撤销价格源权限。

// Revoke Privileges
message RevokePriceFeederPrivilegeProposal {
    option (gogoproto.equal) = false;
    option (gogoproto.goproto_getters) = false;

    string title = 1;
    string description = 2;
    string base = 3;
    string quote = 4;
    repeated string relayers = 5;
}

AuthorizeBandOracleRequestProposal

该提案用于将一个Band oracle请求添加到列表中。当提案被接受时,Injective链将从bandchain获取更多的价格信息。

message AuthorizeBandOracleRequestProposal {
    option (gogoproto.equal) = false;
    option (gogoproto.goproto_getters) = false;

    string title = 1;
    string description = 2;
    BandOracleRequest request = 3 [(gogoproto.nullable) = false];
}

UpdateBandOracleRequestProposal

该提案用于删除或更新请求。当DeleteRequestId不为零时,它将删除具有该ID的请求并完成其执行。当DeleteRequestId为零时,它将使用UpdateOracleRequest.RequestId更新该ID的请求为UpdateOracleRequest。

message UpdateBandOracleRequestProposal {
    option (gogoproto.equal) = false;
    option (gogoproto.goproto_getters) = false;

    string title = 1;
    string description = 2;
    uint64 delete_request_id = 3;
    BandOracleRequest update_oracle_request = 4;
}

EnableBandIBCProposal

该提案用于启用Band链和Injective链之间的IBC连接。当提案被批准时,它将更新BandIBCParams为提案中配置的新值。

message EnableBandIBCProposal {
    option (gogoproto.equal) = false;
    option (gogoproto.goproto_getters) = false;

    string title = 1;
    string description = 2;

    BandIBCParams band_ibc_params = 3 [(gogoproto.nullable) = false];
}

GrantStorkPublisherPrivilegeProposal

可以通过GrantStorkPublisherPrivilegeProposal从发布者授予Stork发布者权限。

// Grant Privileges
message GrantStorkPublisherPrivilegeProposal {
  option (amino.name) = "oracle/GrantStorkPublisherPrivilegeProposal";
  option (gogoproto.equal) = false;
  option (gogoproto.goproto_getters) = false;

  option (cosmos_proto.implements_interface) = "cosmos.gov.v1beta1.Content";

  string title = 1;
  string description = 2;

  repeated string stork_publishers = 3;
}

RevokeStorkPublisherPrivilegeProposal

可以通过RevokeStorkPublisherPrivilegeProposal从发布者撤销Stork发布者权限。

// Revoke Privileges
message RevokeStorkPublisherPrivilegeProposal {
  option (amino.name) = "oracle/RevokeStorkPublisherPrivilegeProposal";
  option (gogoproto.equal) = false;
  option (gogoproto.goproto_getters) = false;

  option (cosmos_proto.implements_interface) = "cosmos.gov.v1beta1.Content";

  string title = 1;
  string description = 2;

  repeated string stork_publishers = 3;
}

预言机(Oracle)

摘要

本规范指定了oracle模块,该模块主要由交易模块使用,以获取外部价格数据。

工作流

  1. 新的价格源提供者必须首先通过治理提案获得oracle权限,该提案授予一组中继者权限。唯一的例外是Coinbase价格oracle,因为任何人都可以发送Coinbase价格更新,因为这些更新已经由Coinbase oracle私钥独占签名。 示例授权提案:GrantBandOraclePrivilegeProposal、GrantPriceFeederPrivilegeProposal

  2. 一旦治理提案获得批准,指定的中继者可以通过发送特定于其oracle类型的中继消息来发布oracle数据。 示例中继消息:MsgRelayBandRates、MsgRelayPriceFeedPrice、MsgRelayCoinbaseMessages等

  3. 收到中继消息后,oracle模块会检查中继者账户是否具有授权权限,并将最新的价格数据持久化到状态中。

  4. 其他Cosmos-SDK模块可以通过查询oracle模块来获取特定提供者的最新价格数据。

注意:如果出现任何不一致,价格源权限可以通过治理撤销。 示例撤销提案:RevokeBandOraclePrivilegeProposal、RevokePriceFeederPrivilegeProposal等

Band IBC集成流程

Cosmos SDK区块链可以通过IBC进行相互交互,而Injective支持通过IBC从BandChain获取价格源数据。

  1. 为了通过IBC与BandChain的oracle通信,Injective Chain必须首先使用中继者初始化与BandChain上oracle模块的通信通道。

  2. 一旦建立连接,将生成一对通道标识符——一个用于Injective Chain,一个用于Band。通道标识符用于Injective Chain将外发的oracle请求数据包路由到Band。同样,Band的oracle模块在发送oracle响应时也使用该通道标识符。

  3. 在设置好通信通道后,为了启用Band IBC集成,应该通过治理提案EnableBandIBCProposal。

  4. 然后,应该通过AuthorizeBandOracleRequestProposal和UpdateBandOracleRequestProposal确定要通过IBC获取的价格列表。

  5. 一旦启用BandIBC,链会定期发送价格请求IBC数据包(OracleRequestPacketData)到bandchain,而bandchain会通过IBC数据包(OracleResponsePacketData)返回价格。 Band链会在足够数量的数据提供者确认后提供价格,并且在发送请求后获取价格需要一定时间。为了在配置的间隔之前请求价格,任何用户都可以广播MsgRequestBandIBCRates消息,这会立即执行。

目录

Peggy

摘要

peggy模块使Injective链能够支持一个可信的、链上双向ERC-20代币桥接到以太坊。在该系统中,以太坊上的ERC-20代币持有者可以将他们的ERC-20代币转换为Injective链上的Cosmos原生币,反之亦然。 这个去中心化的桥接由Injective链的验证者进行安全保障和操作。

目录

组件

  1. Peggy 模块(Injective Chain)

    1. Oracle(监测Peggy合约事件并向Peggy模块发送声明)

    2. EthSigner(对Valset和批量确认进行签名并发送至Peggy模块)

    3. Batch Requester(向Peggy模块发送批量代币提取请求)

    4. Valset Relayer(向Peggy合约提交验证人集合更新)

    5. Batch Relayer(向Peggy合约提交批量代币提取)

此外,Injective Chain验证人除了运行injectived节点以签署区块外,还必须运行peggo orchestrator,以在Ethereum上的Peggy智能合约与Injective Chain上的Peggy模块之间中继数据。

Peggo 功能

  1. 在Ethereum上维护Injective Chain验证人集合的最新检查点

  2. 从Ethereum向Injective Chain转移ERC-20代币

  3. 从Injective Chain向Ethereum转移锚定代币

工作流

概念概述

回顾一下,每个Operator负责维护两个安全进程:

  • 一个完全同步的Injective链验证节点(injectived进程)

  • 与两个网络交互的协调器服务(peggo orchestrator进程)。隐式地,还需要一个完全同步的以太坊节点的RPC端点(请参阅peggo .env示例)

这两个实体共同完成三件事:

  1. 将代币资产从以太坊转移到Injective

  2. 将代币资产从Injective转移到以太坊

  3. 保持Peggy.sol合约与Injective上的活动验证者集同步

即使不成为验证者,也可以运行peggo。当配置为使用与验证者地址无关的地址时,peggo会自动以“中继者模式”运行。在这种模式下,只有两件事可以发生:

  • 可以在Injective上创建新的代币批次

  • 已确认的验证者集/批次可以被转发到以太坊

资产类型

以太坊原生资产

所有Operators都运行他们的peggo进程,这些进程提交MsgDepositClaim消息,描述它们观察到的存款。一旦超过66%的所有投票权提交了该特定存款的声明,代表代币就会被铸造并发行到发送者请求的Injective链地址。

这些代表代币的面额前缀为peggy,后接ERC-20代币的十六进制地址,例如peggy0xdac17f958d2ee523a2206206994597c13d831ec7。

原生 Cosmos SDK 资产

此端点没有权限限制,因此由验证者和Peggy桥的用户来声明任何给定的ERC-20代币作为某个资产的表示。

peggo协调者会观察到这个事件,并决定Cosmos资产是否已被准确表示(正确的小数位,正确的名称,没有预先存在的表示)。如果是这样,ERC-20合约地址将被采用并存储为该Cosmos资产在以太坊上的最终表示。

协调器(Peggo)子进程

peggo协调器进程由四个子进程组成,这些子进程在精确的时间间隔内并发运行(循环)。它们分别是:

  1. Relayer:将已确认的验证者集更新和代币批次提交到以太坊上的Peggy合约。

  2. Batch Creator:观察Injective上的(新)提款,并根据其类型和配置的PEGGO_MIN_BATCH_FEE_USD值决定哪些提款需要打包。

Batch Creator

Batch Creator 的目的是仅在 Injective 端创建代币批次。相关的 Peggy 模块 RPC 没有权限限制,因此任何人都可以创建批次。 当用户想要从 Injective 提取资产到以太坊时,他们会发送一条特殊消息到 Injective(MsgSendToEth),该消息将他们的提取请求添加到 Peggy Tx Pool 中。Batch Creator 会持续查询池中的提取请求(按代币类型),并在潜在批次满足配置的 PEGGO_MIN_BATCH_FEE_USD 值时(参见 .env 示例),向 Injective 发出 MsgRequestBatch。 在接收端,所有与请求中的代币类型匹配的池中提取请求将从 Outgoing Tx Pool 中移动,作为一个单独的批次放入 Outgoing Batch Pool 中。

Signer

Oracle

  • TransactionBatchExecutedEvent - 表示一个代币批次(提取)已成功转发到以太坊的事件

  • ValsetUpdatedEvent - 表示验证器集更新已成功转发到以太坊的事件

  • SendToInjectiveEvent - 表示已启动新的 Injective 存款的事件

  • ERC20DeployedEvent - 表示新 Cosmos 代币已在以太坊上注册的事件

Injective 的 Oracle 实现忽略以太坊上的最后 12 个区块,以确保区块最终性。实际上,这意味着最新的事件会在发生后的 2-3 分钟后被观察到。

Relayer

Relayer 将验证器集更新(或代币批次)及其确认打包成以太坊交易,并发送到 Peggy 合约。 请注意,这些消息的费用会根据以太坊 gas 价格的剧烈波动而变化,因此一个单独的批次可能需要消耗超过一百万的 gas。我们为 relayer 奖励做出的一个重要设计决策是始终在以太坊链上发放奖励。这有其缺点,尤其是在验证器集更新奖励的情况下可能会出现一些奇怪的行为。 但其优点是显而易见的,因为以太坊上的消息会支付给 msg.sender,任何现有的机器人都会在以太坊生态系统中接收并尝试提交这些消息。这使得转发市场更加竞争激烈,减少了类似秘密团体的行为。

端到端生命周期

本文档描述了 Peggy 桥接的端到端生命周期。

Peggy 智能合约部署

为了部署 Peggy 合约,必须先知道原生链(Injective 链)的验证器集。在部署 Peggy 合约套件(Peggy 实现、代理合约和 ProxyAdmin 合约)时,必须使用验证器集初始化 Peggy 合约(即代理合约)。在初始化过程中,合约会触发一个 ValsetUpdatedEvent 事件。 代理合约用于升级 Peggy 实现合约,这对于在初始阶段修复错误和进行潜在改进是必要的。它是一个简单的包装器或“代理”,用户直接与之交互,并负责将事务转发到 Peggy 实现合约,后者包含实际的逻辑。需要理解的关键概念是,实施合约可以被替换,但代理(访问点)永远不会改变。 ProxyAdmin 是 Peggy 代理的中央管理员,简化了管理工作。它控制着升级功能和所有权转移。ProxyAdmin 合约本身有一个内置的过期时间,一旦过期,便会阻止未来对 Peggy 实现合约的升级。 接下来,应更新以下 Peggy 初始化参数:

  • bridge_ethereum_address 为 Peggy 代理合约地址

  • bridge_contract_start_height 为 Peggy 代理合约部署时的区块高度

这完成了 Peggy 桥接的引导过程,链可以开始运行。之后,Operators 应该启动他们的 peggo 进程,并最终观察到最初的 ValsetUpdatedEvent 在 Injective 上被确认。

在以太坊上更新 Injective 链验证者集

验证器集 是一系列以太坊地址,附带规范化的权重,用于表示 Peggy 合约中以太坊上的 Injective 验证器集(Valset)。通过以下机制,Peggy 合约与 Injective 链的验证器集保持同步:

  1. 在 Injective 上创建新的验证器集:当以下任意情况发生时,Injective 链上会自动创建新的验证器集:

  • 当前验证器集的累计权重与上一个记录的验证器集相比,差值超过 5%

  • 某个验证器开始从验证器集解绑

  1. 在 Injective 上确认验证器集:每个 Operator 负责确认在 Injective 上创建的验证器集更新。Signer 进程通过 MsgConfirmValset 发送这些确认,方法是让验证器的委托以太坊密钥对验证器集数据的压缩表示签名。Peggy 模块验证签名的有效性,并将其持久化到状态中。

  2. 更新 Peggy 合约中的验证器集:在 2/3+1 多数验证器提交其确认后,Relayer 通过调用 updateValset 向 Peggy 合约提交新的验证器集数据。Peggy 合约随后验证数据、更新验证器集检查点、将验证器集奖励转给发送者,并触发 ValsetUpdatedEvent。

  3. 在 Injective 上确认 ValsetUpdatedEvent:Oracle 见证以太坊上的 ValsetUpdatedEvent,并发送 MsgValsetUpdatedClaim,通知 Peggy 模块验证器集已在以太坊上更新。

  4. 在 Injective 上修剪验证器集:一旦 2/3 多数的验证器对某个 ValsetUpdateEvent 提交了声明,所有先前的验证器集将从 Peggy 模块的状态中修剪掉。


将ERC-20代币从以太坊转移到Injective

ERC-20代币通过以下机制从以太坊转移到Injective:

  1. 将ERC-20代币存入Peggy合约:用户通过调用Peggy合约中的sendToInjective函数发起将ERC-20代币从以太坊转移到Injective的操作,向Peggy合约存入代币并触发SendToInjectiveEvent事件。存入的代币将保持锁定状态,直到在未来某个不确定的时刻被提取。该事件包含代币的数量和类型,以及接收资金的Injective链上的目标地址。

  2. 确认存款:每个Oracle见证SendToInjectiveEvent并发送MsgDepositClaim,该消息包含存款信息,发送到Peggy模块。

  3. 在Injective上铸造代币:一旦大多数验证者确认存款声明,存款将被处理。

    • 如果资产来源于以太坊,代币将在Injective链上被铸造,并转移到目标接收者的地址。

    • 如果资产来源于Cosmos-SDK,代币将被解锁并转移到目标接收者的地址。


将代币从Injective提取到以太坊

  1. 请求从Injective提取:用户可以通过向Peggy模块发送MsgSendToEth交易来发起从Injective链到以太坊的资产转移。

    1. 如果资产是以太坊原生资产,表示的代币将被销毁。

    2. 如果资产是Cosmos SDK原生资产,代币将在模块中被锁定。随后,提取请求将被加入到Outgoing Tx Pool中。

  2. 批次创建:批次创建者观察待处理的提取池。批次创建者(或任何外部第三方)通过向Injective链发送MsgRequestBatch请求为指定的代币创建批次。Peggy模块将匹配的代币类型的提取请求收集到一个批次中,并将其放入Outgoing Batch Pool。

  3. 批次确认:当检测到存在Outgoing Batch时,签名者使用其以太坊密钥对该批次进行签名,并向Peggy模块提交MsgConfirmBatch交易。

  4. 将批次提交到Peggy合约:一旦大多数验证者确认了该批次,转发者将调用Peggy合约上的submitBatch函数,提交该批次及其确认信息。Peggy合约验证签名,更新批次检查点,处理批次中的ERC-20提取请求,将批次费用转移到交易发送者,并触发TransactionBatchExecutedEvent事件。

  5. 发送提取声明到Injective:Oracles见证TransactionBatchExecutedEvent事件,并向Peggy模块发送包含提取信息的MsgWithdrawClaim。

  6. 修剪批次:一旦大多数验证者提交了他们的MsgWithdrawClaim,该批次将被删除,所有之前的批次也将被取消。取消的批次中的提取请求将被移回Outgoing Tx Pool。

注意:虽然批次处理显著降低了单次提取的费用,但这也带来了延迟和实现复杂度。如果用户希望快速提取,他们将需要支付更高的费用。然而,这个费用将大致与非批次系统中每次提取所需的费用相同。

处罚

安全考虑

验证者集合是实际拥有质押的密钥集合,这些密钥会因双重签名或其他不当行为而被处罚。我们通常将链的安全性视为验证者集合的安全性。每条链的情况不同,但这是我们的黄金标准。即使是IBC,其安全性也仅限于参与的两个验证者集合中的最小值。

Eth桥中继是与main injective daemon 一起运行的二进制文件,由验证者集合管理。它纯粹作为代码组织的一部分,负责签署以太坊交易,并观察以太坊上的事件并将其引入 Injective 链状态。它使用以太坊密钥签署发送到以太坊的交易,并使用 Injective 链账户密钥签署来自以太坊的事件。我们可以对任何由验证者集合运行的Eth签名者签署的错误消息添加处罚条件,并能够提供与验证者集合相同的安全性,只是由不同的模块检测恶意证据并决定处罚的程度。如果我们能证明由验证者集合的任何Eth签名者签署的交易是非法或恶意的,那么我们可以在 Injective 链侧进行处罚,并可能提供验证者集合的100%安全性。请注意,这还可以访问3周的解锁期,即使他们立即解锁,也允许证据进行处罚。

以下是我们在Peggy中使用的各种削减条件。

PEGGYSLASH-01:签署虚假的验证者集合或交易批次证据

该惩罚条件旨在阻止验证者对从未存在于 Injective Chain 上的验证者集合(validator set)和随机数(nonce)进行签名。其通过一种证据机制实现,任何人都可以提交一条消息,其中包含验证者对伪造的验证者集合的签名。此机制旨在实现以下效果:如果一个验证者联盟意图提交伪造的验证者集合,其中一名叛变者可能导致整个联盟的验证者受到惩罚(slashed)。

实现注意事项:

该惩罚条件最复杂的部分在于确定某个验证者集合是否从未存在于 Injective 上。为了节省空间,我们需要清理旧的验证者集合。我们可以在键值存储(KV store)中维护一个验证者集合哈希到布尔值的映射,并利用该映射来检查某个验证者集合是否曾经存在。这种方法比存储整个验证者集合更高效,但其增长仍然是无界的。或许可以使用其他加密方法来减少该映射的大小。此外,可以考虑从该映射中删除非常旧的条目,但任何删除操作都会降低该惩罚条件的威慑力。

当前实现的版本存储了所有过去事件的哈希映射,这比存储完整的批次或验证者集合更小,并且不需要频繁访问。一个可能但尚未实现的效率优化是,在一定时间后从该列表中移除哈希值。但这需要为每个哈希存储更多的元数据。

目前,中继器(relayer)尚未实现自动证据提交功能。当签名在以太坊上可见时,再进行惩罚已经无法防止桥接劫持或资金盗窃。此外,由于执行此操作需要验证者集合中 66% 的多数同意,控制多数的一方可能会直接拒绝提交证据。该惩罚条件最常见的应用场景是破坏试图控制桥接的验证者联盟,通过增加他们之间的信任难度,使其难以协调实施此类盗窃行为。

盗窃行为将涉及交换可惩罚的以太坊签名,这为联盟中的任何叛变者手动提交此类消息提供了可能性。

目前,该功能在状态中实现为一个不断增长的哈希数组。

PEGGYSLASH-02: 未能签署交易批次

当验证者在交易批次(transaction batch)由 Peggy 模块创建后的 SignedBatchesWindow 时间窗口内未能对其进行签名时,将触发该惩罚条件。此机制旨在防止以下两种不良情况的发生:

  1. 验证者未运行正确的二进制文件 验证者可能未在其系统中运行正确的二进制文件,导致其无法履行签名职责。

  2. 验证者联盟拒绝签名 超过 1/3 的验证者可能解除绑定(unbond)并拒绝签署更新,从而导致任何交易批次都无法获得足够的签名以提交至 Peggy 以太坊合约。

PEGGYSLASH-03: 未能签署验证者集合更新

当验证者未能对由 Peggy 模块生成的验证者集合更新(validator set update)进行签名时,将触发该惩罚条件。此机制旨在防止以下两种不良情况的发生:

  1. 验证者未运行正确的二进制文件 验证者可能未在其系统中运行正确的二进制文件,导致其无法履行签名职责。

  2. 验证者联盟拒绝签名 超过 1/3 的验证者可能解除绑定(unbond)并拒绝签署更新,从而导致任何验证者集合更新都无法获得足够的签名以提交至 Peggy 以太坊合约。如果他们阻止验证者集合更新的时间超过 Injective Chain 的解绑周期(unbonding period),他们将不再因提交伪造的验证者集合更新和交易批次(PEGGYSLASH-01 和 PEGGYSLASH-03)而受到惩罚。

为了应对第二种情况,PEGGYSLASH-03 还需要惩罚那些已停止验证但仍处于解绑周期内的验证者,惩罚时间最长可达 UnbondSlashingValsetsWindow 区块。这意味着当验证者离开验证者集合时,他们需要至少保持其设备运行 UnbondSlashingValsetsWindow 个区块。这对于 Injective Chain 来说是不常见的,可能不会被验证者接受。

当前 UnbondSlashingValsetsWindow 的值为 25,000 个区块,大约为 12-14 小时。我们根据以下逻辑确定这是一个安全的值:只要每个离开验证者集合的验证者至少签署一个不包含他们的验证者集合更新,就能保证中继器(relayer)能够生成一系列验证者集合更新,将链上的当前状态转换为最新状态。

需要注意的是,如果能够在共识代码内部执行以太坊签名,PEGGYSLASH-02 和 PEGGYSLASH-03 都可以被取消而不会降低安全性。这是 Tendermint 的一个较为有限的功能增强,但将显著减少 Peggy 的惩罚风险。

PEGGYSLASH-04: 提交错误的以太坊预言机声明(当前已禁用)

以太坊预言机代码(目前主要集中在 attestation.go 文件中)是 Peggy 模块的关键组成部分。它使 Peggy 模块能够了解以太坊上发生的事件,例如存款(deposits)和已执行的交易批次(executed batches)。PEGGYSLASH-03 旨在惩罚那些提交了从未在以太坊上发生的事件的声明的验证者。

实现注意事项

我们判断一个事件是否在以太坊上发生的唯一方式是通过以太坊事件预言机本身。因此,为了实现此惩罚条件,我们会惩罚那些在与 >2/3 验证者观察到的事件具有相同随机数(nonce)的情况下提交了不同事件声明的验证者。

尽管初衷良好,但该惩罚条件可能不适用于大多数 Peggy 应用场景。这是因为它将安装 Peggy 的 Injective Chain 的正常运行与以太坊链的正常运行绑定在一起。如果以太坊链发生严重分叉,不同验证者可能会在同一事件随机数下看到不同的事件,并因此受到惩罚,尽管他们并无过错。大规模的不公平惩罚将对 Injective Chain 的社会结构造成严重破坏。

PEGGYSLASH-04 的必要性分析

该惩罚条件的实际效用在于,如果 >2/3 的验证者形成一个联盟,在某个随机数下提交虚假事件声明,联盟中的部分成员可能会叛变并在同一随机数下提交真实事件声明。如果有足够多的联盟成员叛变,使得真实事件被观察到,那么剩余的联盟成员将因此惩罚条件受到惩罚。然而,这通常需要超过一半的联盟成员叛变。

如果没有足够多的联盟成员叛变,则两个事件都不会被观察到,以太坊预言机将停止运行。这种情况比 PEGGYSLASH-04 实际被触发的场景更为可能。

此外,在联盟成功的情况下,PEGGYSLASH-04 可能会针对诚实验证者触发。这可能使正在形成的联盟更容易威胁那些不愿加入的验证者。

PEGGYSLASH-05: 未能提交以太坊预言机声明(当前已禁用)

PEGGYSLASH-05 与 PEGGYSLASH-04 类似,但其针对的是未提交已被观察到的预言机声明(oracle claim)的验证者。与 PEGGYSLASH-04 不同,PEGGYSLASH-05 旨在惩罚那些完全停止参与预言机机制的验证者。

实现注意事项

遗憾的是,PEGGYSLASH-05 与 PEGGYSLASH-04 存在相同的缺点,即它将 Injective Chain 的正确运行与以太坊链的运行绑定在一起。此外,它可能无法有效激励正确的行为。为了避免触发 PEGGYSLASH-05,验证者只需复制接近被观察到的声明即可。虽然可以通过提交-揭示机制(commit-reveal scheme)来防止这种复制行为,但“懒惰的验证者”仍然可以轻松使用公共的以太坊全节点或区块浏览器,这对安全性影响类似。因此,PEGGYSLASH-05 的实际效用可能非常有限。

PEGGYSLASH-05 还引入了显著的风险,主要围绕以太坊链的分叉问题。例如,最近 OpenEthereum 未能正确处理柏林硬分叉(Berlin hardfork),由此导致的节点“故障”对自动化工具完全不可检测。节点并未崩溃,因此无需重启,区块仍在生成,尽管速度极慢。如果这种情况发生在 Peggy 运行且 PEGGYSLASH-05 激活的情况下,将导致这些验证者被从集合中移除,可能会引发链的混乱时刻,因为数十个验证者因几乎无过错而被移除。

在没有 PEGGYSLASH-04 和 PEGGYSLASH-05 的情况下,以太坊事件预言机仅在 >2/3 的验证者自愿提交正确声明时继续运行。尽管反对 PEGGYSLASH-04 和 PEGGYSLASH-05 的论点很有说服力,但我们必须决定是否能够接受这一事实。或者,我们必须接受 Injective Chain 可能因以太坊生成的因素而完全停止的可能性。

请注意,CumulativePrice 值遵循设定的约定,并用于允许模块计算两个任意区块时间间隔(t1,t2)之间的时间加权平均价格(TWAP)。

关于Coinbase价格oracle的更多详细信息可以在以及这篇解释性中找到。

BandIBCParams 的详细信息可以在中查看。

智能合约(Ethereum)

(链下中继器,又称orchestrator)

任何来自以太坊的资产,只要实现了ERC-20标准,都可以通过调用sendToInjective函数在合约中从以太坊转移到Injective,该函数将代币从发送者的余额转移到Peggy合约。

来自Cosmos SDK链的本地资产(例如ATOM)必须首先在以太坊上表示,才能进行桥接。为此,允许任何人通过调用deployERC20函数来创建一个表示Cosmos资产的新的ERC-20代币。

当以太坊上的用户调用deployERC20时,他们会传递描述所需资产的参数。使用ERC-20工厂部署实际的ERC-20合约,并将新代币的全部余额的所有权赋予Peggy合约本身,然后触发ERC20DeployedEvent事件。

Signer:使用操作员的以太坊密钥签署新的验证者集更新和代币批次,并通过提交。

Oracle:观察以太坊事件并将其作为发送到Injective。

Signer 的职责是提供确认,证明一个操作员(Orchestrator)参与了桥接活动。如果未能提供这些确认,将导致该操作员的验证节点遭受惩罚。换句话说,这个过程必须始终运行在验证节点上。 任何在 Injective->Ethereum 流水线中移动的负载(如验证器集更新/代币批次)都需要验证器签名才能成功转发到以太坊。某些在 Peggy 合约上的调用接受一组签名,并与合约中的验证器集进行比对。Orchestrators 使用他们的委托以太坊地址来生成这些签名:这是操作员在初始设置时决定的以太坊地址()。这个地址代表了该验证器在以太坊区块链上的身份,并将作为多签名成员添加,拥有尽可能接近 Injective 链投票权的加权投票权。 每当 Signer 发现 Peggy 模块中存在未确认的验证器集更新(代币批次)时,它会发出 MsgConfirmValset(MsgConfirmBatch)作为证明,证明该操作验证器在桥接活动中是活跃的。

监控以太坊网络中涉及 Peggy 合约的新事件。 每个由合约触发的事件都有一个唯一的事件 nonce。这个 nonce 值对于协调 Orchestrators 正确观察合约活动至关重要,并确保 Injective 通过 Claims 进行确认。多个相同 nonce 的 claims 组成一个 Attestation,当大多数(2/3)的 Orchestrators 观察到某个事件时,其特定的逻辑会在 Injective 上执行。 如果 2/3 的验证器无法就单个 Attestation 达成一致,oracle 将被暂停。这意味着在一些验证器改变他们的投票之前,不会有新的事件从以太坊转发过来。对此没有 slashing 条件,详细理由在 中列出。 Peggy.sol 触发了 4 种类型的事件:

验证器惩罚:在配置的时间窗口(SignedValsetsWindow)内未提供确认的验证器将面临惩罚。有关验证器惩罚的更多信息,请参阅 。

批次处罚:验证者负责确认批次,如果未能完成此操作,将受到处罚。有关批次处罚的更多信息,请参阅。

Uniswap V2 Oracle
Coinbase API文档
博客文章
状态
状态
Keeper
消息
提案
事件
// This call allows anyone to submit evidence that a
// validator has signed a valset, batch, or logic call that never
// existed. Subject contains the batch, valset, or logic call.
type MsgSubmitBadSignatureEvidence struct {
	Subject   *types1.Any 
	Signature string      
	Sender    string      
}

消息

MsgRelayBandRates

授权的Band中继者可以通过MsgRelayBandRates消息中继多个符号的价格源数据。注册的处理程序遍历MsgRelayBandRates中存在的所有符号,并为每个符号创建/更新BandPriceState。

message MsgRelayBandRates {
  string relayer = 1;
  repeated string symbols = 2;
  repeated uint64 rates = 3;
  repeated uint64 resolve_times = 4;
  repeated uint64 requestIDs = 5;
}

如果中继者不是授权的Band中继者,此消息预期会失败。

MsgRelayCoinbaseMessages

Coinbase提供者的中继者可以使用MsgRelayCoinbaseMessages消息发送价格数据。 每个Coinbase消息通过Coinbase oracle地址0xfCEAdAFab14d46e20144F48824d0C09B1a03F2BC提供的签名进行身份验证,从而允许任何人提交MsgRelayCoinbaseMessages。

message MsgRelayCoinbaseMessages {
  option (gogoproto.equal) = false;
  option (gogoproto.goproto_getters) = false;
  string sender = 1;

  repeated bytes messages = 2;
  repeated bytes signatures = 3;
}

如果签名验证失败,或者提交的时间戳比最后一次提交的Coinbase价格更旧,则此消息预期会失败。

MsgRelayPriceFeedPrice

价格源提供者的中继者可以使用MsgRelayPriceFeedPrice消息发送价格源数据。

// MsgRelayPriceFeedPrice defines a SDK message for setting a price through the pricefeed oracle.
message MsgRelayPriceFeedPrice {
  option (gogoproto.equal) = false;
  option (gogoproto.goproto_getters) = false;
  string sender = 1;

  repeated string base = 2;
  repeated string quote = 3;

  // price defines the price of the oracle base and quote
  repeated string price = 4 [
    (gogoproto.customtype) = "cosmossdk.io/math.LegacyDec",
    (gogoproto.nullable) = false
  ];
}

如果中继者(发送者)不是给定基础报价对的授权价格源中继者,或者价格大于10000000,则此消息预期会失败。

MsgRequestBandIBCRates

MsgRequestBandIBCRates 是一条消息,用于即时广播请求到bandchain。// MsgRequestBandIBCRates defines a SDK message for requesting data from BandChain using IBC.
message MsgRequestBandIBCRates {
  option (gogoproto.equal) = false;
  option (gogoproto.goproto_getters) = false;

  string sender = 1;
  uint64 request_id = 2;

}

任何人都可以广播此消息,无需特定授权。处理程序会检查BandIbcEnabled标志是否为true,如果是,则继续发送请求。

MsgRelayPythPrices

MsgRelayPythPrices 是一条消息,用于将Pyth合约的价格中继到oracle模块。

// MsgRelayPythPrices defines a SDK message for updating Pyth prices
message MsgRelayPythPrices {
  option (gogoproto.equal) = false;
  option (gogoproto.goproto_getters) = false;

  string sender = 1;
  repeated PriceAttestation price_attestations = 2;
}

message PriceAttestation {
  string product_id = 1;
  bytes price_id = 2;
  int64 price = 3;
  uint64 conf = 4;
  int32 expo = 5;
  int64 ema_price = 6;
  uint64 ema_conf = 7;
  PythStatus status = 8;
  uint32 num_publishers = 9;
  uint32 max_num_publishers = 10;
  int64 attestation_time = 11;
  int64 publish_time = 12;
}

enum PythStatus {
  // The price feed is not currently updating for an unknown reason.
  Unknown = 0;
  // The price feed is updating as expected.
  Trading = 1;
  // The price feed is not currently updating because trading in the product has been halted.
  Halted = 2;
  // The price feed is not currently updating because an auction is setting the price.
  Auction = 3;
}

如果中继者(发送者)与oracle模块参数中定义的Pyth合约地址不匹配,则此消息预期会失败。

MsgRelayStorkPrices

MsgRelayStorkPrices 是一条消息,用于将Stork合约的价格中继到oracle模块。

// MsgRelayStorkPrices defines a SDK message for relaying price message from Stork API.
message MsgRelayStorkPrices {
  option (gogoproto.equal) = false;
  option (gogoproto.goproto_getters) = false;
  option (cosmos.msg.v1.signer) = "sender";

  string sender = 1;
  repeated AssetPair asset_pairs = 2;
}

message AssetPair {
  string asset_id = 1;
  repeated SignedPriceOfAssetPair signed_prices = 2;
}

message SignedPriceOfAssetPair {
  string publisher_key = 1;
  uint64 timestamp = 2;
  string price = 3 [
    (gogoproto.customtype) = "cosmossdk.io/math.LegacyDec",
    (gogoproto.nullable) = false
  ];
  bytes signature = 4;
}

如果发生以下情况,则此消息预期会失败:

  • 中继者(发送者)不是授权的oracle发布者,或者assetId在提供的资产对中不是唯一的

  • 对SignedPriceOfAssetPair的ECDSA签名验证失败

  • 时间戳之间的差值超过MaxStorkTimestampIntervalNano(500毫秒)

MsgRelayProviderPrices

特定提供者的中继者可以使用MsgRelayProviderPrices消息发送价格源数据。

// MsgRelayProviderPrice defines a SDK message for setting a price through the provider oracle.
message MsgRelayProviderPrices {
  option (amino.name) = "oracle/MsgRelayProviderPrices";
  option (gogoproto.equal) = false;
  option (gogoproto.goproto_getters) = false;
  option (cosmos.msg.v1.signer) = "sender";

  string sender = 1;
  string provider = 2;
  repeated string symbols = 3;
  repeated string prices = 4 [
    (gogoproto.customtype) = "cosmossdk.io/math.LegacyDec",
    (gogoproto.nullable) = false
  ];
}

如果中继者(发送者)不是给定基础报价对的授权价格源中继者,或者价格大于10000000,则此消息预期会失败。

事件

peggy 模块会触发以下事件:

EndBlocker

EventAttestationObserved

类型
属性键
属性值

int32

attestation_type

{attestation_type}

string

bridge_contract

{bridge_contract_address}

uint64

bridge_chain_id

{bridge_chain_id}

[]byte

attestation_id

{attestation_id}

uint64

nonce

{event_nonce}

EventValidatorSlash

类型
属性键
属性值

string

reason

{reason_for_slashing}

int64

power

{validator_power}

string

consensus_address

{consensus_addr}

string

operator_address

{operator_addr}

string

moniker

{validator_moniker}

Handler

EventSetOrchestratorAddresses

类型
属性键
属性值

string

validator_address

{validator_addr}

string

orchestrator_address

{orchestrator_addr}

string

operator_eth_address

{eth_addr}

EventSendToEth

类型
属性键
属性值

message

outgoing_tx_id

{tx_id}

string

sender

{sender_addr}

string

receiver

{dest_addr}

sdk.Coin

amount

{token_amount}

sdk.Coin

bridge_fee

{token_amount}

EventBridgeWithdrawCanceled

类型
属性键
属性值

withdrawal_cancelled

bridge_contract

{bridge_contract}

withdrawal_cancelled

bridge_chain_id

{bridge_chain_id}

EventOutgoingBatch

类型
属性键
属性值

string

denom

{token_denom}

string

orchestrator_address

{orch_addr}

uint64

batch_nonce

{batch_nonce}

uint64

batch_timeout

{block_height}

[]uint64

batch_tx_ids

{ids}

EventOutgoingBatchCanceled

类型
属性键
属性值

string

bridge_contract

{bridge_contract}

uint64

bridge_chain_id

{bridge_chain_id}

uint64

batch_id

{id}

uint64

nonce

{nonce}

EventValsetConfirm

类型
属性键
属性值

uint64

valset_nonce

{nonce}

string

orchestrator_address

{prch_addr}

EventConfirmBatch

类型
属性键
属性值

uint64

batch_nonce

{nonce}

string

orchestrator_address

{orch_addr}

EventDepositClaim

类型
属性键
属性值

uint64

event_nonce

{event_nonce}

uint64

event_height

{event_height}

[]byte

attestation_id

{attestation_key}

string

ethereum_sender

{sender_addr}

string

cosmos_receiver

{receiver_addr}

string

token_contract

{contract_addr}

sdk.Int

amount

{token_amount}

string

orchestrator_address

{orch_addr}

string

data

{custom_data}

EventWithdrawClaim

类型
属性键
属性值

uint64

event_nonce

{event_nonce{

uint64

event_height

{event_height}

[]byte

attestation_id

{attestation_key}

uint64

batch_nonce

{batch_nonce}

string

token_contract

{contract_addr}

string

orchestrator_address

{orch_addr}

EventERC20DeployedClaim

类型
属性键
属性值

uint64

event_nonce

{event_nonce}

uint64

event_height

{event_height}

[]byte

attestation_id

{attestation_key}

string

cosmos_denom

{token_denom}

string

token_contract

{token_conntract_addr}

string

name

{token_name}

string

symbol

{token_symbol}

uint64

decimals

{token_decimals}

string

orchestrator_address

{orch_addr}

EventValsetUpdateClaim

类型
属性键
属性值

uint64

event_nonce

{event_nonce}

uint64

event_height

{event_height}

[]byte

attestation_id

{attestation_key}

uint64

valset_nonce

{valset_nonce}

[]*BridgeValidator

valset_members

{array_of_validators}

sdk.Int

reward_amount

{amount}

string

reward_token

{contract_addr}

string

orchestrator_address

{orch_addr}

定义
工作流
状态
消息
处罚
End-Block
事件
参数
Peggy
Peggo
Peggy.sol
Peggy合约
Peggy.sol
消息
声明
SetOrchestratorAddress
slashing 规范
valset slashing
批次处罚

消息

User Messages

SendToEth

当用户希望将资金提取回以太坊时,发送到Injective。提交的金额会立即从用户的余额中扣除。该提取请求作为types.OutgoingTransferTx被添加到外发交易池中,直到它被包含在一个批次中。

type MsgSendToEth struct {
	Sender    string    // sender's Injective address
	EthDest   string    // receiver's Ethereum address
	Amount    types.Coin    // amount of tokens to bridge
	BridgeFee types.Coin    // additional fee for bridge relayers. Must be of same token type as Amount
}

CancelSendToEth

此消息允许用户取消尚未批量处理的特定提取请求。用户余额将被退还(金额 + 桥接费用)。

type MsgCancelSendToEth struct {
	TransactionId uint64    // unique tx nonce of the withdrawal
	Sender        string    // original sender of the withdrawal
}

SubmitBadSignatureEvidence

此调用允许任何人提交证据,证明某个验证者签署了一个从未存在的验证者集合或批次。主体包含该批次或验证者集合。

type MsgSubmitBadSignatureEvidence struct {
	Subject   *types1.Any 
	Signature string      
	Sender    string      
}

Batch Creator Messages

这些消息由Peggy的批次创建子过程发送。

RequestBatch

每当某个批次创建者发现汇集的提取请求,且在批量处理后满足其最低批次费用(PEGGO_MIN_BATCH_FEE_USD)时,会发送此消息。接收到此消息后,Peggy模块会收集所有请求的代币标识符的提取请求,创建一个唯一的代币批次(types.OutgoingTxBatch),并将其放入外发批次池中。已批量处理的提取请求无法通过MsgCancelSendToEth取消。

type MsgRequestBatch struct {
	Orchestrator string // orchestrator address interested in creating the batch. Not permissioned.  
	Denom        string // the specific token whose withdrawals will be batched together
}

Oracle Messages

这些消息由Peggy的Oracle子过程发送。

DepositClaim

当Peggy合约触发SendToInjectiveEvent事件时,发送到Injective。每当用户从以太坊向Injective进行单独存款时,就会发生这种情况。

type MsgDepositClaim struct {
	EventNonce     uint64   // unique nonce of the event                                
	BlockHeight    uint64   // Ethereum block height at which the event was emitted                                
	TokenContract  string   // contract address of the ERC20 token                                 
	Amount         sdkmath.Int  // amount of deposited tokens 
	EthereumSender string   // sender's Ethereum address                                 
	CosmosReceiver string   // receiver's Injective address                                 
	Orchestrator   string   // address of the Orchestrator which observed the event                               
}

WithdrawClaim

当Peggy合约触发TransactionBatchExecutedEvent事件时,发送到Injective。这发生在转发者成功调用合约上的submitBatch函数以完成一批提取请求时。

type MsgWithdrawClaim struct {
	EventNonce    uint64    // unique nonce of the event
	BlockHeight   uint64    // Ethereum block height at which the event was emitted
	BatchNonce    uint64    // nonce of the batch executed on Ethereum
	TokenContract string    // contract address of the ERC20 token
	Orchestrator  string    // address of the Orchestrator which observed the event
}

ValsetUpdatedClaim

当Peggy合约触发ValsetUpdatedEvent事件时,发送到Injective。这发生在转发者成功调用合约上的updateValset函数以更新以太坊上的验证者集合时。


type MsgValsetUpdatedClaim struct {
	EventNonce   uint64 // unique nonce of the event                      
	ValsetNonce  uint64 // nonce of the valset                           
	BlockHeight  uint64 // Ethereum block height at which the event was emitted                           
	Members      []*BridgeValidator // members of the Validator Set               
	RewardAmount sdkmath.Int // Reward for relaying the valset update 
	RewardToken  string // reward token contract address                                 
	Orchestrator string // address of the Orchestrator which observed the event                                 
}

ERC20DeployedClaim

当Peggy合约触发ERC20DeployedEvent事件时,发送到Injective。这发生在合约上调用deployERC20方法以发行一个新的符合桥接条件的代币资产时。

type MsgERC20DeployedClaim struct {
	EventNonce    uint64    // unique nonce of the event
	BlockHeight   uint64    // Ethereum block height at which the event was emitted
	CosmosDenom   string    // denom of the token
	TokenContract string    // contract address of the token
	Name          string    // name of the token
	Symbol        string    // symbol of the token
	Decimals      uint64    // number of decimals the token has
	Orchestrator  string    // address of the Orchestrator which observed the event
}

Signer Messages

这些消息由Peggy的签名者子过程发送。

ConfirmBatch

当签名者发现一个批次尚未由协调器(验证者)签署时,它会使用其委托的以太坊密钥构造签名,并将确认信息发送到Injective。验证者最终必须为已创建的批次提供确认,否则他们将被处罚。

type MsgConfirmBatch struct {
	Nonce         uint64    // nonce of the batch 
	TokenContract string    // contract address of batch token
	EthSigner     string    // Validator's delegated Ethereum address (previously registered)
	Orchestrator  string    // address of the Orchestrator confirming the batch
	Signature     string    // Validator's signature of the batch
}

ValsetConfirm

当签名者发现一个验证者集合更新尚未由协调器(验证者)签署时,它会使用其委托的以太坊密钥构造签名,并将确认信息发送到Injective。验证者最终必须为已创建的验证者集合更新提供确认,否则他们将被处罚。

type MsgValsetConfirm struct {
	Nonce        uint64 // nonce of the valset 
	Orchestrator string // address of the Orchestrator confirming the valset
	EthAddress   string // Validator's delegated Ethereum address (previously registered)
	Signature    string // Validator's signature of the valset
}

Relayer Messages

转发者不会向Injective发送任何消息,而是构造包含Injective数据的以太坊交易,通过submitBatch和updateValset方法更新Peggy合约。

Validator Messages

这些是直接使用验证者的消息密钥发送的消息。

SetOrchestratorAddresses

由管理验证者节点的操作员发送到Injective。在能够启动他们的协调器(peggo)进程之前,他们必须注册一个选定的以太坊地址,以代表其验证者在以太坊上的身份。可选地,可以提供一个额外的Injective地址(Orchestrator字段)以代表该验证者在桥接过程中的身份(peggo)。如果省略,则默认为验证者的自身地址。

type MsgSetOrchestratorAddresses struct {
	Sender       string // address of the Injective validator
	Orchestrator string // optional Injective address to represent the Validator in the bridging process (Defaults to Sender if left empty)
	EthAddress   string // the Sender's (Validator) delegated Ethereum address
}

此消息设置协调器的委托密钥。

状态

该文档列出了Peggy模块读取/写入其状态的所有数据,以键值对(KV对)的形式。

Module Params

键
值
类型
编码

[]byte{0x4}

Module params

types.Params

Protobuf encoded

Validator Info

Ethereum Address by Validator

存储由验证者账户地址索引的委托以太坊地址。

键
值
类型
编码

[]byte{0x1} + []byte(validatorAddr)

Ethereum address

common.Address

Protobuf encoded

Validator by Ethereum Address

存储由委托以太坊地址索引的验证者账户地址。

键
值
类型
编码

[]byte{0xfb} + []byte(ethAddress)

Validator address

sdk.ValAddress

Protobuf encoded

OrchestratorValidator

当验证者希望将其投票权委托给另一个密钥时,值使用协调器地址作为键进行存储。

键
值
类型
编码

[]byte{0xe8} + []byte(AccAddress)

Orchestrator address assigned by a validator

[]byte

Protobuf encoded

Valset

这是桥接的验证者集合。由Peggy模块在EndBlocker期间自动创建。 以两种方式存储,第一种是带高度,第二种是没有高度(不安全)。没有高度的方式用于测试以及状态的导出和导入。

type Valset struct {
	Nonce        uint64                               
	Members      []*BridgeValidator                   
	Height       uint64                               
	RewardAmount math.Int 
	RewardToken string
}
键
值
类型
编码

[]byte{0x2} + nonce (big endian encoded)

Validator set

types.Valset

Protobuf encoded

SlashedValsetNonce

最新的验证者集合处罚计数器。用于跟踪需要被处罚的验证者集合以及已经被处罚的验证者集合。

键
值
类型
编码

[]byte{0xf5}

Nonce

uint64

encoded via big endian

ValsetNonce

最新验证者集合的计数器。每次新的验证者集合更新时,都会更新该计数器。

键
值
类型
编码

[]byte{0xf6}

Nonce

uint64

encoded via big endian

Valset Confirmation

键
值
类型
编码

[]byte{0x3} + (nonce + []byte(AccAddress)

Validator Confirmation

types.MsgValsetConfirm

Protobuf encoded

Batch Confirmation

键
值
类型
编码

[]byte{0xe1} + []byte(tokenContract) + nonce + []byte(AccAddress)

Validator Batch Confirmation

types.MsgConfirmBatch

Protobuf encoded

OutgoingTransferTx

用户的提取请求被汇集到Peggy交易池中,准备由批次创建者稍后批量处理。 每个提取请求都通过一个唯一的计数器进行索引,该计数器由Peggy模块在接收到提取请求时设置。

type OutgoingTransferTx struct {
	Id          uint64     
	Sender      string     
	DestAddress string     
	Erc20Token  *ERC20Token 
	Erc20Fee    *ERC20Token 
}
键
值
类型
编码

[]byte{0x7} + []byte("lastTxPoolId")

nonce of outgoing withdrawal

uint64

Big endian encoded

LastTXPoolID

每个由Injective接收到的提取请求的单调递增值。

键
值
类型
编码

[]byte{0x6} + []byte("lastTxPoolId")

Last used withdrawal ID

uint64

Big endian encoded

OutgoingTxBatch

type OutgoingTxBatch struct {
	BatchNonce    uint64               
	BatchTimeout  uint64               
	Transactions  []*OutgoingTransferTx 
	TokenContract string                
	Block         uint64               
}
键
值
类型
编码

[]byte{0xa} + []byte(tokenContract) + nonce (big endian encoded)

A batch of outgoing transactions

types.OutgoingTxBatch

Protobuf encoded

[]byte{0xb} + block (big endian encoded)

A batch of outgoing transactions

types.OutgoingTxBatch

Protobuf encoded

LastOutgoingBatchID

每个由批次创建者在Injective上创建的批次的单调递增值。

键
值
类型
编码

[]byte{0x7} + []byte("lastBatchId")

Last used batch ID

uint64

Big endian encoded

SlashedBlockHeight

表示最新的处罚区块高度。始终只存储一个值。

键
值
类型
编码

[]byte{0xf7}

Latest height a batch slashing occurred

uint64

Big endian encoded

LastUnbondingBlockHeight

表示验证者开始从验证者集合中解除绑定的最新区块高度。用于确定处罚条件。

键
值
类型
编码

[]byte{0xf8}

Latest height at which a Validator started unbonding

uint64

Big endian encoded

TokenContract & Denom

一个来自计数链的代币标识符将来自一个合约。代币合约和代币标识符以两种方式存储。 首先,代币标识符作为键,值为代币合约。 其次,合约作为键,值为代币合约所代表的代币标识符。

键
值
类型
编码

[]byte{0xf3} + []byte(denom)

Token contract address

[]byte

stored in byte format

[]byte{0xf4} + []byte(tokenContract)

Token denom

[]byte

stored in byte format

LastObservedValset

此条目表示最后一次成功中继到以太坊的验证者集合(Valset)。在ValsetUpdatedEvent的确认在Injective上处理后进行更新。

键
值
类型
编码

[]byte{0xfa}

Last observed Valset on Ethereum

types.Valset

Protobuf encoded

LastEventNonce

这是在以太坊上最后一次观察到的事件的计数器。当调用TryAttestation()时设置此值。此存储中始终只持有一个值。

键
值
类型
编码

[]byte{0xf2}

Last observed event nonce

uint64

Big endian encoded

LastObservedEthereumHeight

这是在以太坊上最后一次观察到的事件的区块高度。此存储中始终只会存储一个值。

键
值
类型
编码

[]byte{0xf9}

Last observed Ethereum Height

uint64

Protobuf encoded

LastEventByValidator

这是来自特定验证者的最后一个观察到的以太坊事件。每次关联的协调器发送事件声明时都会更新。

type LastClaimEvent struct {
    EthereumEventNonce  uint64 
    EthereumEventHeight uint64 
}
键
值
类型
编码

[]byte{0xf1} + []byte(validator address)

Last observed event by some Validator

types.LastClaimEvent

Protobuf encoded

Attestation

确认是多个声明的汇总,随着更多的投票(声明)到来,最终被所有协调器观察到。一旦被观察到,该声明的特定逻辑将被执行。 每个确认都绑定到一个唯一的事件计数器(由Peggy合约生成),并且必须按顺序处理。这是一个正确性问题,如果事务顺序被打乱,则可能会发生交易重放攻击。

type Attestation struct {
	Observed bool       
	Votes    []string   
	Height   uint64     
	Claim    *types.Any 
}
键
值
类型
编码

[]byte{0x5} + event nonce (big endian encoded) + []byte(claimHash)

Attestation of occurred events/claims

types.Attestation

Protobuf encoded

PastEthSignatureCheckpoint

一个计算出的哈希值,表明某个验证者集合/代币批次在Injective上确实存在。此检查点也存在于Peggy合约中。在每次新的验证者集合更新和代币批次创建时都会更新。

键
值
类型
编码

[]byte{0x1b}

Last created checkpoint hash on Injective

gethcommon.Hash

store in byte format

EthereumBlacklist

已知的恶意以太坊地址列表,这些地址被禁止使用桥接。

键
值
类型
编码

[]byte{0x1c} + []byte(ethereum address)

Empty []byte slice

gethcommon.Hash

stored in byte format]

这是Peggy消息类型的参考文档。有关代码参考和精确参数,请参阅。

这些是最终用户通过Injective链Peggy模块发送的消息。有关整个存款和提取过程的更详细概述,请参阅。

Params是一个模块范围的配置结构,存储参数并定义Peggy模块的整体功能。每个参数的详细规范可以在找到。

特定验证者集合的签名者确认。请参阅。

特定代币批次的签名者确认。请参阅。

OutgoingTxBatch表示同一代币类型的提取请求集合。在每次成功的MsgRequestBatch后创建。 以两种方式存储,第一种是带高度,第二种是没有高度(不安全)。没有高度的方式用于测试以及状态的导出和导入。目前, 被硬编码为只接受单一代币类型的批次,并且仅以该代币类型支付奖励。

proto定义
工作流程
Parameters部分
Oracle消息
Oracle消息
Peggy.sol

钱包

学习如何在Injective上创建钱包并查看Injective支持的钱包

代币标准

了解在 Injective 上不同的代币标准

交易

学习如何在Injecitve上准备,签名和提交交易

创建钱包

学习如何创建一个injective钱包

桥接

学习如何将您的资产桥接到Injective

发布代币

学习如何发布一个您自己的代币

发布市场

学习如何在Injective上启动交易对

获取 INJ

学习如何获取INJ

博客

访问我们的博客获取更多指南

Injective Bridge
Add Custom ERC-20 Token
Add Smart Contract Address
Bridging Completion
List on Injective
Specify Ticker
Select Quote Denom

安装

学习如何安装 injectived

使用

学习如何使用 injectived

指令

了解 injectived 的进阶指令

injectived

使用 CLI 与 Injective 交互

Injective TS SDK

使用TypeScript和Injective交互

Injective Go SDK

使用Go与Injective交互

Injective Python SDK

使用Python与Injective交互

Injective CW SDK

使用 CW与Injective交互

The Graph

使用Graph与Injective交互

交易所

学习如何构建像Helix一样的交易所

Cosmwasm

学习如何使用 CosmWasm 构建任何类型的 dApp

原生模块

了解更多关于Injective原生模块提供的功能

img.png
img.png
img.png
img.png

快速入门

开始您的 Injective 之旅

您是用户?

学习如何创建您的钱包

您是开发者?

学习如何在Injective上构建

您是交易员?

学习如何开始在Injective上交易

您是验证者?

学习如何运行哨兵节点和验证者节点

更多帮助?

加入 加入

定义

本文档旨在从技术角度提供关于Peggy(Injective的以太坊桥接)的概述,并深入探讨其操作逻辑。Peggy是基于Injective构建的自定义Cosmos SDK模块的名称,也是以太坊合约(Peggy.sol)的名称,它们构成了桥接的两端。通过一个名为Peggo的中介过程连接,用户可以安全地在网络之间转移代币资产。 如有改进建议,请提交GitHub issue。

关键定义

用词很重要,我们追求术语的清晰,以便在思考和沟通中保持清晰。为了帮助更好地理解,以下是一些关键定义:

  • Operator - 这是控制和操作验证者和协调者进程的个人(或团队)。

  • Validator - 这是Injective链的验证节点(例如:injectived进程)。

  • Validator Set - Injective链的(活动)验证者集(Valset),以及根据其股份权重确定的投票权。每个验证者都与一个以太坊地址关联,以便在以太坊网络上进行表示。

  • Orchestrator (Peggo) - 一个链下进程(peggo),在Injective和以太坊之间充当中介角色。协调者负责保持桥接在线,并需要活动的端点以完全同步Injective(以太坊)节点。

  • Peggy module - Peggy合约的对应Cosmos模块。除了提供资产桥接服务外,它还会随着时间推移自动反映在活动的验证者集上。更新随后通过Peggo应用到以太坊。

  • Peggy Contract - 以太坊合约,持有所有ERC-20代币。它还使用委托密钥和规范化的权重,维护Injective链验证者集的压缩检查点表示。

  • Delegate Keys - 当操作员首次设置协调器时,他们会在Injective上注册其验证者地址与以太坊地址。相应的密钥用于签署消息,并在以太坊上代表该验证者。可选地,还可以提供一个代理的Injective链账户密钥,以代表验证者签署Injective消息(例如,声明)。

  • Peggy Tx pool (withdrawals) - 当用户希望将其资产从Injective转移到以太坊时,他们的个人交易会与其他相同资产的交易一起被放入池中。

  • Peggy Batch pool - 汇聚的提款会被协调者批量处理,进行签名并最终转发到以太坊。这些批次会保存在这个池中。

  • Claim - 一个由协调者签署的证明,表明某个事件发生在Peggy合约中。

  • Attestation - 来自Peggy合约的某个事件随机数的声明的集合。在大多数协调者对一个声明进行确认后,事件将在Injective上被承认并执行。

  • Majority - Injective网络的多数,2/3 + 1的验证者。

  • Deposit - 从以太坊到Injective发起的资产转移。

  • Withdrawal - 从Injective到以太坊发起的资产转移(在Peggy Tx池中)。

  • Batch - 包含相同代币类型的提款批次(在Peggy Batch池中)。

Injective Discord
Developer Telegram
Cover
Cover
Cover
Cover
Cover
Cover
Cover
Cover
Cover
Cover
Cover
Cover
Cover
Cover
Cover
Cover
Cover
Cover
Cover
Cover
Cover
Cover
Cover
Cover
Cover
Cover
Cover