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 上的原生代币,也可以是从 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 原生钱包签署交易的过程包括:
将交易转换为 EIP712 类型数据(TypedData),
使用 Ethereum 原生钱包签署 EIP712 类型数据,
将交易打包成原生 Cosmos 交易(包括签名),并将交易广播到区块链上。
这个过程对最终用户是透明的。如果您之前使用过 Ethereum 原生钱包,用户体验将是相同的。
Injective 支持与 Cosmos 和 IBC 兼容的主流钱包,包括:
现在也有一些由中心化交易所(CEX)开发的钱包支持 Injective。如果您是这些 CEX 的活跃用户,使用它们的钱包可以提供更流畅的 Web3 体验。目前,支持 Injective 的 CEX 钱包包括:
目前 是唯一的 Injective 原生钱包。这样的钱包专门构建用于与更广泛的 Injective 生态系统协同工作。
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 是一个高性能、可互操作的 Layer 1 区块链,专为构建顶级 Web3 金融应用而设计。
Injective 是专为金融而构建的区块链。
Injective 是唯一一个允许开发者使用预构建且可定制模块 来创建其他网络上无法实现的动态应用程序的区块链。结合对其核心架构的优化和增强的跨链互操作性,Injective 提供了一个高性能网络,准备高效且安全地将全球金融系统引入链上
Injective 提供了多种不同的代币标准,供在创建 dApp 时使用。在本文件中,我们将介绍不同类型的代币,以及使用每种代币的建议和指南。
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 和本地银行模块转换的适配器合约地址。
我们将在本文件后续部分提供关于这些 denom 类型的更多细节。 在了解如何获取 denom 元数据。
INJ 的完全多功能性通过一系列协同运作的机制得以实现。基于 Injective 对供应动态的创新方法,该资产通过一个精心设计的系统展现出通缩特性,旨在将 INJ 从流通中移除。这个过程通过 Injective 的新颖销毁拍卖系统得以实现,该系统有效地减少了总供应量。
销毁拍卖定期举行,邀请参与者竞标一篮子代币,这些代币来自参与应用程序生成的部分收入以及个人用户的直接贡献。拍卖以英式拍卖方式进行,竞标使用 INJ。最高出价者将在拍卖结束时获得整个代币篮子。获胜的 INJ 出价将被销毁,从总供应量中移除。 销毁拍卖由 Injective 的两个原生模块实现:交易和拍卖。这些模块是 Injective 核心功能的一部分,作为即插即用的金融原语,供任何在 Injective 上构建的项目使用。
交易所模块是 Injective 与其他区块链的主要区别之一。这个技术工具为 Injective 上的共享流动性环境提供支持,并推动销毁拍卖的运作。订单簿管理、交易执行、订单匹配和结算的整个过程都通过该模块的逻辑在链上完成。 销毁拍卖的关键设计特性是为使用交易模块的应用程序内置的收入共享结构。在此结构中,部分累积的收入被分配给拍卖模块,以便纳入当前的销毁拍卖事件,而剩余部分则由使用该模块的应用程序保留,用于支持其交易服务。
拍卖模块为销毁拍卖的运作提供了两个关键服务:代币收集和拍卖协调。对于代币收集,该模块定期从交易模块收集代币,将它们汇集到一个拍卖基金中。值得注意的是,拍卖基金还接收来自未使用交易模块但选择参与的应用程序的代币,以及来自个人用户的贡献。拍卖过程本身涉及多个由拍卖模块管理的任务,包括协调竞标过程、确定赢家、交付获胜资产以及销毁获胜的 INJ 出价。
将您的 INJ 给验证者,开始赚取奖励。
一旦您质押了 INJ,奖励就会开始累积。您可以在 的质押部分监控您的奖励。一旦获得足够的奖励,您可以随时提取它们。
INJ 2.0 于 2023 年发布,使得任何应用程序都可以向拍卖基金贡献,不仅限于使用交易模块的应用程序。Injective 于 2024 年 4 月发布的 INJ 销毁升级扩展了该功能的访问权限,允许个人用户进行贡献。因此,任何项目或用户都可以直接向 Injective 销毁拍卖贡献,这反过来可以提高销毁拍卖的整体价值和效果。 销毁拍卖每周举行,结束时间为 UTC-4:00 9:00。参与者可以通过 Injective Hub 或直接与区块链交互进行参与。 和 提供了销毁拍卖至今销毁的总 INJ 的实时跟踪。
您可以通过按照以下步骤使用您首选的钱包创建一个 Injective 钱包: .
您可以在 CLI 环境中使用创建一个 Injective 钱包用于开发目的。您可以使用injectived keys
.
您还可以在以下页面了解更多关于使用 injectived
进行密钥管理的信息
CW20 代币标准提供了一个框架,用于无权限创建和管理可替代代币,其结构更接近于。如上所述,推荐使用 TokenFactory 标准,因为它与 Cosmos SDK 原生集成,但如果您出于某种原因希望使用 CW20 标准,您可以通过 将 CW20 代币转换为 TokenFactory 代币,反之亦然。有关 CW20 标准的更多信息,请参阅其正式规范 。
TokenFactory 代币是原生集成到 Cosmos SDK 的银行模块中的代币。它们的命名格式为 factory/{creatorAddress}/{subdenom}
。由于代币是按创建者地址命名空间进行命名的,这使得代币铸造无需权限,因为不需要解决名称冲突。
这种集成提供了对所有资产总供应量的跟踪和查询支持,不像 CW20 标准那样需要直接查询智能合约。因此,推荐使用 TokenFactory 标准。例如,像 Helix 或 Mito 这样的产品是基于 Injective 交换模块构建的,它们专门使用银行代币。TokenFactory 代币可以通过 injectived CLI 或智能合约创建。通过 Wormhole 桥接到 Injective 的代币也是 TokenFactory 代币。
本节介绍 Injective 内置的账户系统。
Injective 默认提供 3 种主要类型的地址和公钥(PubKeys):
账户地址和密钥:用于标识用户(例如消息的发送者),使用 eth_secp256k1 曲线生成。
验证者操作员地址和密钥:用于标识验证者的操作员,使用 eth_secp256k1 曲线生成。
共识节点地址和密钥:用于标识参与共识的验证者节点,使用 ed25519 曲线生成。
账户地址
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
压缩公钥:
您可以使用 Cosmos CLI 或 REST 客户端查询账户地址。
下面是如何从私钥和/或助记词派生 Injective 账户的示例:
下面是如何从私钥派生公钥的示例:
INJ 是驱动 Injective 及其更广泛生态系统的本地资产。INJ 的每个组成部分都是精心设计的,旨在培养一个繁荣的 Web3 生态系统。作为区块链的本地资产,INJ 在促进 Injective 上的各种操作中发挥着核心作用。作为 Injective 定制的 Tendermint 权益证明(PoS)共识框架的重要组成部分,INJ 对于通过质押保障网络安全至关重要。此外,INJ 还作为 Injective 的治理代币,并作为更广泛 Injective 生态系统中的交换媒介。值得注意的是,INJ 通过利用核心 Injective 模块,通过创新的销毁机制和动态供应机制,打造了通缩特性,从而与其他 PoS 链上的本地资产区别开来。
这与以太坊的单位相匹配:
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 生态系统中的关键作用。
INJ 被用于社区主导的治理,涵盖链上所有参数的管理。Injective 具有独特的智能合约上传权限层,这意味着要在主网部署智能合约,必须经过质押者社区的投票批准。这使得社区能够直接管理 Injective 的所有参数。
在治理方面,INJ 用于创建提案,并在活跃提案上进行基于代币权重的投票。为防止垃圾提案,Injective 要求提案必须达到最低存款要求(以 INJ 支付)才能进入投票阶段。这一存款门槛可以由提案人独自承担,或由其他用户共同出资补足。如果在最大存款期限内未达到最低存款要求,提案将自动被拒绝,且存款将被销毁(烧毁)。此外,如果提案在投票期结束后未能通过,提案存款也将被烧毁。
提案投票在预设的投票周期内进行,该周期由治理机制设定,并适用于所有治理投票。在投票过程中,只有已质押的 INJ 才有资格参与投票,因此仅限验证者和委托者可以对活跃提案投票。投票权重基于代币数量计算,即 1 INJ 等于 1 票。委托者无需主动参与治理即可保持其委托者身份,但他们可以选择直接对提案投票。如果委托者未投票,他们的投票权将在该特定投票事件中自动继承给他们所委托的验证者。
INJ 可用于治理链上的所有方面,包括:
拍卖(Auction)模块参数
交易(Exchange)模块的自定义提案和参数
保险(Insurance)模块参数
预言机(Oracle)模块的自定义提案
Peggy 模块参数
Wasmx 模块参数
软件升级
INJ 作为默认资产,用于在区块链上促进各方之间的商品和服务交易。常见示例包括支付交易费用(Gas 费)、购买/出售 NFT、支付交易手续费或将资产作为抵押品存入。尽管大多数商品和服务可以用任何资产计价,但在 Injective 上产生的所有交易费用均以 INJ 支付。此外,所有利用 Injective 共享流动性层的应用程序(通过交易模块)所产生的协议收入,最终都会以 INJ 形式累积。
交易协议实施了全球最低交易费用:做市商(Maker)为 0.1%,吃单者(Taker)为 0.2%。作为激励机制,旨在鼓励交易 dApp 在交易协议上引入交易活动,向共享订单簿提交订单的交易 dApp 将获得其促成订单所产生交易费用的 40% 作为奖励。
INJ 可以作为稳定币的替代品,用作 Injective 衍生品市场的保证金和担保品。在一些衍生品市场中,INJ 还可以作为保险池质押的担保品,质押者可以在锁仓的代币上赚取利息。
要了解更多关于在 Injective 上创建代币的信息,请查看。要阅读更多关于 TokenFactory 标准的信息,请查看。
Injective 定义了其自定义的账户类型,使用 Ethereum 的 ECDSA secp256k1 曲线来生成密钥。这符合 规范,适用于完整的 路径。Injective 账户的根 HD 路径为 m/44'/60'/0'/0
。
请参阅 文档,获取有关账户 API 的完整文档。
INJ 使用 作为基础单位,以保持与以太坊的对等性。
Cosmos-SDK 模块参数,包括 , , , , , , 和 模块
完整的治理流程详情可在查看。
剩余的 60% 交易费用将进行链上回购和销毁事件,其中所有交易费用的总额将被打包,并通过拍卖出售给最高竞标者,竞标者支付 INJ。该拍卖所得的 INJ 将被销毁,从而减少总的 INJ 供应量。 有关拍卖机制的更多细节可以在找到。
将资产桥接到Injective生态系统非常简单,支持23个以上的网络,并且还在不断增长。在这里,您将找到关于如何通过以太坊、Solana、IBC以及使用Wormhole协议桥接资产的逐步操作指南,使您能够轻松连接到Injective生态系统。
在本文中,我们将解释如何在Injective上启动一个代币。 在Injective上启动代币有两种选择:桥接现有代币或创建新代币。
在Injective上启动代币的最简单方法是通过桥接来自Injective支持的互操作网络中的现有资产。你可以参考桥接中的指南,了解如何将资产从其他网络桥接到Injective。 一旦桥接过程完成,代币将在Injective上创建,你就可以使用它来发布市场。
你也可以使用TokenFactory
模块在Injective上创建一个新代币。有多种方式可以实现这一目标。
一旦您安装了 injectived
并添加了密钥,您可以使用 CLI 启动您的代币:
创建一个 TokenFactory
denom
创建工厂 denom 的费用为 0.1 INJ
。
代币按创建者地址命名空间进行管理,以实现无需许可并避免名称冲突。在上述示例中,subdenom 是 ak
,但 denom 的命名方式将是 factory/{creator address}/{subdenom}
。
提交代币元数据
要使您的代币在 Injective dApp 上可见,您必须提交其元数据。
这条指令参数如下:
铸造代币
一旦您创建了代币并提交了代币元数据,就可以开始铸造您的代币了。
此命令将铸造1个代币,假设您的代币有6个小数位。
销毁代币
代币的管理者,也可以销毁代币。
更换管理者
一旦铸造了初始供应量,建议将管理员更改为null
地址,以确保代币的供应量无法被篡改。再次强调,代币的管理员可以随时铸造和销毁供应量。NEW_ADDRESS
,如上所述,在大多数情况下应设置为 inj1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqe2hm49
。
create_new_denom_msg
目的:创建一个消息,用于通过 tokenfactory 模块创建一个新的denomination。
参数:
sender
:发起创建的账户地址。
subdenom
:新代币的sub-denomination标识符。
返回:一个包装在 InjectiveMsgWrapper
中的 CosmosMsg
,准备发送到 Injective 区块链。
示例:
create_set_token_metadata_msg
目的:创建一个消息,用于设置或更新代币的元数据。
参数:
denom
:代币的denomination标识符。
name
:代币的完整名称。
symbol
:代币的符号。
decimals
:代币使用的十进制位数。
返回:一个包装在 InjectiveMsgWrapper
中的 CosmosMsg
,准备发送到 Injective 区块链。
示例:
create_mint_tokens_msg
目的:创建一个消息,用于铸造新代币。该代币必须是 TokenFactory 代币,且发送者必须是代币管理员。
参数:
sender
:发起铸币操作的账户地址。
amount
:要铸造的代币数量。
mint_to
:接收新铸造代币的地址。
返回:一个包装在 InjectiveMsgWrapper
中的 CosmosMsg
,准备发送到 Injective 区块链。
示例:
create_burn_tokens_msg
目的:创建一个消息,用于销毁代币。该代币必须是 TokenFactory 代币,且发送者必须是代币管理员。
参数:
sender
:发起销毁操作的账户地址。
amount
:要销毁的代币数量。
返回:一个包装在 InjectiveMsgWrapper
中的 CosmosMsg
,准备发送到 Injective 区块链。
示例:
web 应用程序让你能够轻松创建和管理代币,在Injective的上创建市场等。
web 应用程序让你能够轻松创建和管理代币,在 Injective 的上创建市场,启动空投等功能。
类似于上述,你可以使用 来创建、管理和列出你的代币,以及其他一些有用的功能。
你可以学些更多关于发布代币的内容在 。
要通过智能合约以编程方式创建和管理银行代币,可以使用以下在 包中找到的消息:
Gas代表执行特定操作所需的计算工作量。
Injective利用gas的概念来跟踪在执行过程中操作的资源使用情况。Injective上的操作表示对区块链存储的读写操作。
在消息执行过程中,会计算并向用户收取Fees。这个Fees是根据消息执行中消耗的所有gas的总和来计算的:
Gas用于确保操作完成时不会消耗过多的计算力,并且能够防止恶意用户对网络进行垃圾信息攻击。
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
在交易执行之前执行基本检查。这些检查通常包括签名验证、交易字段验证、交易费用等。
当用户想要与Injective进行交互并进行状态变更时,他们会创建交易。一旦交易创建完成,它需要由与发起该状态变更的账户关联的私钥签名。签名完成后,交易会被广播到Injective网络。
在广播并通过所有验证(包括签名验证、值验证等)后,交易会被包含在一个区块中,并通过共识过程经过网络的批准。
简单来说,消息是向Injective提供的关于期望状态变更的指令。消息是特定模块的对象,用于触发属于其模块范围内的状态转换。每个交易必须至少包含一条消息。
除了消息,每个交易还有一个上下文。上下文包括费用(fees)、账户详情(accountDetails)、备注(memo)、签名(signatures)等信息。
我们想要广播到Injective的每个交易都有相同的流程。这个流程包括三个步骤:准备、签名和广播交易。当交易被包含在区块中时,使用消息指定的状态变化将在Injective上应用。
在 Injective 上启动交易对快速、简单,而且最重要的是,无需许可!
以下教程假设交易对是以从以太坊桥接的 ERC-20 代币作为基础资产,并与 INJ 作为报价资产配对。
对于 Injective 原生代币,请跳过桥接部分,直接进入第 6 步。
使用代币地址桥接 ERC-20 代币,您可能需要在可信的来源如 CoinGecko 上验证该地址。
复制并粘贴正确的合约地址,然后点击“add”。
现在输入您希望桥接的ERC-20代币的数量,点击“approve”,确认交易,然后点击“review”,确认交易并等待。
一旦批准支出和存款交易在以太坊区块链上得到确认,您将看到桥接交易的进度。一旦交易在Injective上确认,您的桥接ERC-20代币将会出现在您的Injective钱包中。(注意,如果您在源链使用了MetaMask,默认情况下,您的桥接代币将会发送到与您的MetaMask关联的INJ地址。您可以通过点击第4步开始时收款地址旁边的锁定图标来更改此设置。)
从第一个下拉菜单中选择“instant spot market launch”,并指定一个交易对。在本例中,我们使用 PEPE/INJ。然后从下拉菜单中选择基础代币。然而,请注意,可能会有多个代币使用相同的交易对名称。请始终确保匹配正确的代币地址。在本例中,由于代币是通过Peggy桥接完成的,地址将是以 peggy 开头,后面跟随ERC-20合约地址。
现在选择正确的报价代币,在本例中是 INJ。(注意,如果您希望将代币与 USDT 配对,请确保选择“正确”的 USDT 地址,即 peggy 后跟 USDT 的 ERC-20 合约地址。)最后,指定最小价格刻度和最小数量刻度。由于 PEPE/INJ 将以不到一美分的价格交易,因此最小刻度已相应设置。
前置阅读->
有关 Cosmos SDK 中 gas 的更多信息,可以在找到。
此外,多个消息可以打包在同一交易中。每个模块可用的消息可以在中找到。
发布市场的前置条件是
导航到 页面,开始使用 Peggy 桥接将您选择的 ERC-20 代币从以太坊桥接到 Injective。
桥接交易完成后,您可以通过导航到,未经许可地在Injective上列出该代币。
一个 denom
是 Injective 的Bank
模块中如何表示代币的方式。这些资产可以用于交易、在交易模块上创建新市场、参与拍卖、转移到其他地址等。
对于开发者和交易者来说,最大的痛点之一就是获取这些 denoms
的元数据。元数据包括小数位数(decimals
)、符号(symbol
)、名称(name
)等。
本指南将展示如何直接从 injective-lists
仓库获取 denom
元数据并将其映射到您的 denom
。您还可以使用这种方法来映射现货和衍生品市场的 denom's
元数据。
元数据每 30 分钟自动从链上获取新 denoms
,并重新生成 json
文件。
一旦您获得了 JSON 文件,您需要将元数据与特定的 denom
映射。
该元数据的接口如下:
假设您获取了某个地址的Bank
余额(如下例所示,使用 TypeScript),您可以轻松地将其映射到上述 JSON 文件中的元数据。
现在,您的银行余额包含了所有您需要的元数据(包括小数位(decimals
)、符号(symbol
)、名称(namename
)、logo 等)。
与银行余额类似,您可以使用相同的方法将现货市场中的代币 denom
映射到其元数据。
与Bank
余额类似,您可以使用相同的方法将衍生品市场中的代币 denom
映射到其元数据。
injective-lists
是一个公开的仓库,保存了 Injective 上所有代币的元数据。它是该信息的最新和最可靠来源。您可以通过为此仓库创建一个 PR 来提交您的代币信息。确保正确指定字段,特别是 "denom
" 字段(请阅读),该字段应根据代币标准使用相应的 ibc
、peggy
和 factory
前缀。
您可以访问 文件夹,基于环境下载元数据。
查看此表以了解支持injectived
CLI运行的平台:
macOS (M1/ARM)
❌
✅
✅
macOS (Intel)
❌
✅
✅
Windows (x86_64)
❌
✅
❌
Windows (ARM)
❌
✅
❌
Linux (x86_64)
✅
✅
✅
Linux (ARM)
❌
✅
✅
此 ZIP 文件将包含以下文件:
injectived - Injective daemon 兼 CLI
peggo - Injective 以太坊桥接中继 daemon
libwasmvm.x86_64.so - WASM 虚拟机支持文件
注意:部署和实例化智能合约时不需要 peggo
,它是为验证者准备的。
确认您的版本与以下输出匹配(如果有更新版本,您的输出可能会略有不同):
以下命令将启动一个包含 injectived CLI
的容器:
以下命令将从源代码构建 injectived CLI
:
这将把 injectived CLI
安装到您的 Go 路径中。
本节描述了 injectived
提供的命令,这是连接运行中的 injectived
进程(节点)的命令行界面。
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
语法
例子
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 在同一进程中运行。
语法
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
创建一个测试网,指定目录的数量,并为每个目录填充必要的文件。
语法
例子
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 版本。
语法
目前,唯一支持运行预构建 injectived CLI
的平台是 Linux x86_64。预构建的二进制文件可在 . 页面上获取。
继续阅读 ,了解如何使用 injectived CLI
与 Injective 区块链交互。
这兼容大多数平台及 arm64 / x86_64 架构。
继续前往 ,了解如何使用 injectived CLI
与 Injective 区块链交互。
(提交哈希可能会有所不同,因为开源存储库是与预构建版本分开发布的)。
继续阅读 ,了解如何使用 injectived CLI
与 Injective 区块链交互。
将一个创世账户添加到 genesis.json
文件中。有关 genesis.json
的更多信息,请参阅加入测试网或指南。
收集创世交易并将其输出到 genesis.json
文件中。有关 genesis.json
的更多信息,请参阅加入测试网或指南。
将创世交易添加到 genesis.json
文件中。有关 genesis.json
的更多信息,请参阅加入测试网或指南。
将源创世数据迁移到目标版本,并打印到标准输出(STDOUT)。有关 genesis.json
的更多信息,请参阅加入测试网或指南。
验证默认位置或指定位置的创世文件。有关创世文件的更多信息,请参阅加入测试网或指南。
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 上找到开发者支持。
关于公共端点列表, 查阅 公共端点
关于私有节点服务列表, 查阅 高级端点
Injective 的口袋备忘单。使用这个术语表来了解与 Injective 相关的术语。
参与共识并获得奖励的验证者。
通过某些验证者向委托人分发的额外奖励,独立于质押奖励。空投通常由 Injective 生态系统中的应用程序发放,以增加可见性。
一种用户寻求利用市场间价格差异的过程。套利者通常在一个市场购买资产,并在另一个市场以更高的价格出售它们。
一个不可更改的交易账本,复制在一组独立计算机系统的网络中。
存储在区块链上的信息组。每个区块包含经验证并由验证者签名的交易。
参与共识的活跃集中的验证者。绑定的验证者可以获得奖励。
当用户将 INJ 委托或绑定到验证者,以获得质押奖励时的过程。即使被绑定,验证者也没有委托人 INJ 的所有权。委托、绑定和质押通常指代相同的过程。
资产的永久销毁。Injective 在每次燃烧拍卖后销毁 INJ。
每周举办的一项活动,社区成员可以使用 INJ 竞标 Injective 收集到的 60% 的交易费用。获胜者竞标使用的 INJ 将被销毁。
验证者在将其余奖励分配给委托人之前所保留的质押奖励的百分比。验证者的收入完全依赖于这一佣金。验证者设置自己的佣金率。
专门用于资助社区项目的基金。任何社区成员都可以提出治理提案,使用社区池中的代币。如果提案通过,资金将按照提案中指定的方式支出。
Injective 用来支持链上智能合约的库。更多信息,请查看 CosmWasm 文档。
去中心化应用。建立在去中心化平台上的应用
分布式拒绝服务攻击。攻击者通过向网络发送大量流量或请求来扰乱服务。
去中心化金融。从传统金融向无需金融中介的系统转变。
当用户或委托人将他们的 INJ 添加到验证者的质押中以交换奖励时。委托的 INJ 会被绑定到验证者。验证者永远没有委托人 INJ 的所有权。委托、绑定和质押通常指代相同的过程。
将 INJ 委托、绑定或质押到验证者以获得奖励的用户。委托、绑定和质押通常指代相同的过程。
开发网络。一个独立于主网的网络,允许用户测试新功能或产品,而不会干扰主要网络。
Injective 用于链上订单匹配的模型。与大多数中心化交易所提供的加密衍生品和传统金融市场使用的连续双向拍卖(CDA)模型相比,该模型更高效地利用资本。FBA 模型还消除了前置交易的可能性。
连接到 Injective 主网并能够验证交易和与 Injective 交互的计算机。所有活跃的验证者都运行完整节点
附加到所有交易上的计算费用,用于防止垃圾交易攻击。验证者设置最低的 gas 费用,并拒绝低于该阈值的交易。
治理是允许用户和验证者对 Injective 进行更改的民主过程。社区成员提交、投票并实施提案。每一单位质押的 INJ 等于一票。
对 Injective 协议的更改或新增的书面提交。提案的主题可以从社区池支出、软件更改、参数更改或任何与 Injective 相关的更改。
跨链通信技术(Inter-Blockchain Communication)。使不同区块链之间能够相互交互。IBC 允许在不同区块链之间进行资产交易和转账。
Injective 的原生代币。
与 Injective 节点交互的命令行接口。 有关更多信息,请查看 injectived
指南。
Injective 的官方源代码。 有关更多信息,请参阅 Injective 核心模块。
Injective 的平台,用于钱包、治理、质押和 INJ 销毁拍卖。 要了解 Injective Hub 的功能,请访问 Injective Hub 指南。
不在活跃集合中的验证者。这些验证者不参与共识,也不赚取奖励。
表现不当的验证者会被监禁或在一段时间内被排除在活跃集合之外。
通过包含、排除和改变区块中交易顺序,从区块生产中提取的最大价值,超过了标准的区块奖励和 gas 费用。
Injective 是抗 MEV 的。
Injective 核心中的一个模块,表示 Injective 的特定功能。 访问 Injective 核心模块规范了解更多信息。
一个使 Injective 能够访问外部实时数据的第三方服务。通常,这是喂价。
代币的集合。供应池表示市场中的代币总供应量。
区块链使用的一种验证方法,根据验证者持有的代币数量来选择提议区块的验证者。
使选举有效所需的最少投票数。必须有 33% 的所有质押 INJ 投票才能达到法定人数。如果投票期结束前未达到法定人数,提案将失败,且提案人的押金将被销毁。
当委托人希望将他们的绑定 INJ 转移到另一个验证者时。重新委托 INJ 是即时的,不需要 21 天的解锁期。
从费用中产生的收入,给予验证者并分发给委托者。
验证者将 INJ 绑定到自己身上的数量。也称为自我绑定。
对表现不当的验证者的惩罚。验证者在被削减时会失去一部分质押的代币。
在交易开始和结束时资产价格之间的差异。
绑定到验证者的 INJ 数量。
当用户将他们的 INJ 委托或绑定到活跃的验证者以获得奖励时。绑定的 INJ 会增加验证者的质押量。验证者将其质押作为参与共识过程的抵押。质押量更大的验证者被选中参与的频率更高。验证者因参与而获得质押奖励。如果验证者表现不当,质押量可能会被削减。验证者永远不会拥有委托者的 INJ,即使在质押过程中。
有关质押的更多信息,请访问质押页面。
Injective 的区块链网络,所有交易都在此进行。
主网的一个测试版本。测试网不使用真实资产。您可以使用测试网熟悉交易和整体网络。
委托人绑定的 INJ 总量,包括自我绑定的 INJ。
不在活跃集合中的验证者。这些验证者不参与共识,也不赚取奖励。一些未绑定的验证者可能会被监禁。
从活跃集合过渡到非活跃集合的验证者。未绑定的验证者不参与共识,也不赚取奖励。解锁过程需要 21 天。
可以自由交易且未绑定到任何验证者的 INJ。
当委托人决定从验证者那里解除委托其 INJ 时。此过程需要 21 天。在此期间不会获得任何奖励。一旦执行,无法停止此操作。
正在从绑定状态过渡到未绑定状态的 INJ。未绑定的 INJ 不能自由交易。解除绑定的过程需要 21 天。在此期间不会获得任何奖励。一旦执行,无法停止此操作。
当委托人不再希望将其 INJ 绑定到某个验证者时。此过程需要 21 天。在此期间不会获得任何奖励。一旦执行,无法停止此操作。
验证者在给定时间段内的活跃时间。活跃时间较低的验证者可能会被削减
负责验证区块链上的交易的 Injective 区块链矿工。验证者运行称为全节点的程序,使他们能够参与共识、验证区块、参与治理并获得奖励。只有活跃集合中的验证者才能参与共识。
验证者总质押量的度量。质押量较大的验证者更常被选中提议区块。验证者的权重也是其在治理中投票权重的度量。
在本文档中,我们将概述一些示例,展示如何在不同格式和衍生路径之间转换地址。
正如我们在前面的 钱包章节中提到的,Injective 地址与 Ethereum 地址是兼容的。你可以轻松地在这两种格式之间进行转换。
你可以通过使用 @injectivelabs/sdk-ts
包中的工具函数轻松地在 Injective 地址和 Ethereum 地址之间进行转换:
由于 Injective 使用的派生路径不同于默认的 Cosmos 派生路径,因此你需要账户的 publicKey 才能将 Cosmos publicAddress 转换为 Injective 地址。
假设您想在测试网上提交一个提案。由于提案的投票时间较短,我们建议将提案的存款金额调低,以避免提案直接进入投票阶段。基本上,存款金额应略低于 min_deposit
值。
提交提案后,您应该联系团队:
这是 GrantProviderPrivilegeProposal
的示例:
以下页面介绍了如何使用 injectived
,这是一个连接到 Injective 的命令行界面。您可以使用 injectived
与 Injective 区块链交互,例如上传智能合约、查询数据、管理质押活动、处理治理提案等。
请调整您的命令以正确使用主目录。
如果从 Docker 运行,您需要将主目录挂载到容器中。
使用 Docker 化的 CLI 添加密钥很简单。
以下是该命令的解析:
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
目录相同。
你可以运行以下命令列出所有密钥:
在访问 Injective 区块链之前,你需要运行一个节点。你可以选择运行自己的完整节点,或者连接到其他人的节点。
要查询状态并发送交易,你必须连接到一个节点,它是整个网络的访问入口。你可以选择运行自己的完整节点,或者连接到其他人的节点。
要设置 RPC 端点,你可以使用以下命令:
现在试着查询状态:
关于使用 injectived 的通用信息
, 运行:
要获取有关特定 injectived
命令的更多信息,可以在命令后附加 -h
或 --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
是一个非常小的数额。
以下步骤被执行:
生成一个包含 MsgSend
(x/bank
的 MsgSend
)的交易,并在控制台打印生成的交易。
请求用户确认是否从 $MY_WALLET
账户发送交易。
从密钥存储(keyring)中获取 $MY_WALLET
,这可以实现是因为在前面的步骤中已设置 CLI 的密钥存储。
使用密钥存储中的账户对生成的交易进行签名。
将已签名的交易广播到网络,这可以实现是因为 CLI 连接到了 Injective 公共节点的 RPC 端点。
CLI 将所有必要的步骤整合在一起,提供了简洁易用的用户体验。但也可以分别运行每个步骤来完成交易。
生成交易可以通过在任何交易命令后添加 --generate-only
标志来简单完成,例如:
这将把未签名的交易以 JSON 格式输出到控制台。我们还可以通过在上述命令后添加 > unsigned_tx.json
将未签名的交易保存到文件中(以便在签名者之间更方便地传递)。
使用 CLI 签名交易需要将未签名的交易保存到文件中。假设未签名的交易保存在当前目录中的一个名为 unsigned_tx.json
的文件中(参考前一段了解如何操作)。然后,只需运行以下命令:
此命令将解码未签名的交易,并使用我们在密钥存储中已经设置的 MY_WALLET 的密钥以 SIGN_MODE_DIRECT
模式对其进行签名。签名后的交易将以 JSON 格式输出到控制台,像上面一样,我们可以通过在命令后添加 > 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 个签名者,我们将运行:
广播交易是通过以下命令完成的:
你可以选择性地传递 --broadcast-mode
标志来指定从节点接收哪种响应:
block
:CLI 等待交易被包含在区块中。
sync
:CLI 仅等待 CheckTx
执行响应,手动查询交易结果以确保它已被包含。
async
:CLI 立即返回(交易可能失败)- 不要使用。
要查询交易结果,你可以使用以下命令:
有时候配置可能没有正确设置。你可以通过在命令行中添加以下内容来强制使用正确的节点 RPC 端点。在与他人分享命令时,建议在命令行中明确设置所有标志(如 chain-id
、node
、keyring-backend
等)。
什么是预言机提供方?预言机提供方是一种预言机类型,允许外部参与者向 Injective 链传输价格数据。这些外部参与者被称为提供方(Providers)。提供方用于标识每个外部参与者,并且所有提供的价格数据都存储在该特定提供方名下。这一机制使 Injective 能够创建自定义价格数据,从而支持 Injective 上创新且高级的市场。
开发者需要首先在 Oracle Provider 类型下注册自己的提供方。这可以通过提交 GrantProviderPrivilegeProposal
治理提案来完成。一旦提案通过,提供方即被注册,随后可以向链上传输价格数据。可以在 CLI 环境中使用 injectived
命令行工具执行 (grant-provider-privilege-proposal [providerName] [relayers] --title [title] --description [desc] [flags]
) 或者,也可以使用 Injective 提供的 SDK 之一来构造消息并将其广播至链上。
Note: GrantProviderPrivilegeProposal 的 relayers 指定了被列入白名单的地址,这些地址将被授权向 Injective 提交价格数据。
一旦提案通过,relayers 便可使用 MsgRelayProviderPrices
在其 Oracle Provider Type 的 provider namespace 内,为指定的 base/quote 交易对提交价格数据。
可以通过 CLI 环境使用 injectived
进行操作:
也可以使用 Injective 提供的 SDK 来构造消息并广播至链上。
最终,这些价格数据可用于创建 衍生品市场(Derivative Markets)。
加入 并寻找相关的频道
加入 .
一种由验证者或矿工使用的系统,用于确认区块链中的每个交易区块是正确的。Injective 使用 Tendermint 共识框架。验证者通过参与共识获得奖励。更多信息,请访问 网站。
Injective 区块链构建的开源框架。更多信息,请查看 文档。
Injective 使用的共识框架。首先,验证者提议一个新区块。其他验证者对该区块进行两轮投票。如果区块在两轮投票中都获得超过三分之二的支持票,它将被添加到区块链中。验证者通过区块的交易费用获得奖励。提议者获得额外奖励。每个验证者的提议是根据他们的权重来选择的。有关更多信息,请查看 。
更多示例可以在 中找到。
加入 并寻找相关的频道
加入 .
请参阅 获取更多信息。如果您已成功安装 injectived
,您应该能够运行以下命令:
仅适用于高级用户。对于大多数用户,建议连接到公共节点。
前置阅读
本节旨在为用户提供指南,帮助他们在 Injective 上启动和维护预言机提供方(Oracle Provider)。这些预言机可用于多种用途,例如永续合约市场、到期期货市场、等。
为 DeFi 优化
⇾ 专用模块 ⇾ 低延迟基础设施 ⇾ 更快的开发速度 ⇾ 更强的功能能力 ⇾ 解锁全新潜力
商业级可扩展性
⇾ 25,000+ TPS(每秒交易数) ⇾ 650ms 区块时间 ⇾ 即时最终确认 ⇾ 抵抗 MEV(最大可提取价值)攻击 ⇾ 每笔交易成本仅 $0.0003
高度互操作性
⇾ 原生互操作性,支持 23+ 网络,包括 Ethereum 和 Solana ⇾ IBC 支持,连接 110+ 条链
直观的开发者体验
⇾ 原生集成的执行层
⇾ WASM + EVM ⇾ 由 Rust、Golang 和 Solidity 提供支持
本节包含一些简单的开发者指南,介绍如何使用 CosmWasm 在 Injective 上进行开发。
在这里 开始使用 CosmWasm 在 Injective 上构建你的项目。您的首个智能合约
历史上,社区曾发现 CosmWasm 中存在漏洞,某些合约可能导致链停滞。因此,要求批准在本质上更加安全,直到该语言随着时间的发展和稳定化。
治理可以防止未经审计和虚假的合约被部署到网络中,确保黑客攻击和诈骗的发生率较低。
这为生态系统用户带来了更加精心筛选的体验,因为区块链不会被不必要的交易和合约填满。
然而,这一治理过程对于验证者和社区来说是非常耗时的。Injective 开发者还需要等待 4 天才能推出新特性,这影响了开发者体验和用户增长。 自 Altaris 链升级以来,现在可以请求将你的地址列入合约上传的白名单。
为了考虑将地址列入白名单,强烈建议在治理提案中包含以下所有信息。验证者建议在投票之前,通过 Discord 联系基金会验证每个提案提交的信息。
团队成员的身份是否为社区所知?
团队是否已通过 Injective 基金会的 KYC/KYB 认证?
该协议在 Injective 主网上线多久了?
团队是否在其他生态系统中开发过应用?(提供详细信息)
符合以下条件的用户将更有可能获得白名单权限:
项目已完成 Injective 基金会的 KYC/KYB,且团队成员的身份为社区所知。例外:匿名开发者,且具有可信度和在其他生态系统中成功应用的经历。
项目已在主网上线至少 1 个月,并且取得了显著的 TVL(总价值锁仓)/使用量。
强烈建议使用多签钱包或 Ledger 钱包进行白名单上传。
本指南将帮助您了解在 Injective 主网(Mainnet)上部署和实例化 CosmWasm 智能合约的治理流程。
在本节中,您将学习如何提交智能合约代码提案并对其进行投票。
Injective 网络的参与者可以提议智能合约部署,并在治理中投票使其生效。wasmd 授权设置由链上治理决定,这意味着合约的部署完全由治理决定。因此,治理提案是将合约上传到 Injective 主网的第一步。
以下是使用 injectived
启动治理提案将代码上传到链上的示例用法:
命令 injectived tx gov submit-proposal wasm-store
提交一个 wasm 二进制提案。如果提案获得治理批准,代码将被部署。
让我们了解两个关键的标志:instantiate-everybody
和 instantiate-only-address
,这两个标志设置上传代码的实例化权限。默认情况下,任何人都可以实例化合约。
在大多数情况下,你不需要提交另一个治理提案来进行实例化。只需使用 injectived tx wasm instantiate 进行实例化即可。你只需要通过治理提案来上传合约。如果合约设置了 --instantiate-everybody
标志为 false
,并且 --instantiate-only-address
标志设置为治理模块,才需要通过治理来实例化。--instantiate-everybody
的默认值为 true
,在这种情况下,你可以通过 injectived tx wasm instantiate 无需许可地进行实例化。
一个 injectived tx wasm instantiate 的示例如下所示:
如上所述,主网合约实例化权限取决于上传代码时使用的标志。默认情况下,设置为无需许可,正如我们在 Genesis wasmd Injective 设置中可以验证的那样:
然而,如果 --instantiate-everybody
标志设置为 false
,那么合约实例化必须通过治理进行。
按照以下说明安装 Go、Rust 以及其他 CosmWasm 依赖项:
非 ARM(非 Apple Silicon)设备:
对于 Apple Silicon 设备(M1、M2 等),请使用:
Docker 脚本会构建并优化仓库中的所有 CW 合约,编译后的合约位于 artifacts
目录下。现在,我们可以部署 cw20_base.wasm
合约(如果在 ARM 设备上编译,则使用 cw20_base-aarch64.wasm
)。
输出:
然后,通过 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"
标志。
在本节中,我们将说明如何设置环境以进行 CosmWasm 智能合约开发。
此外,还需要安装 wasm32-unknown-unknown
目标以及 cargo-generate
Rust 依赖包。
可以使用以下命令检查版本:
创建并交互一个智能合约,该合约可以增加计数器的值,并将其重置为指定值。
理解 CosmWasm 智能合约的基础知识,学习如何在 Injective 上部署合约,并使用 Injective 工具与其交互。
作为智能合约的开发者,你的任务是定义三个组成合约接口的函数:
instantiate()
:构造函数,在合约实例化时调用,用于提供初始状态。
execute()
:当用户希望调用智能合约上的方法时执行。
query()
:当用户希望从智能合约中获取数据时执行。
在你的工作目录中,通过运行以下命令,快速启动智能合约,并使用推荐的文件夹结构和构建选项:
这有助于你快速开始,通过提供智能合约的基本模板和结构。在 src/contract.rs
文件中,你会发现标准的 CosmWasm 入口函数 instantiate()
、execute()
和 query()
已正确暴露并连接。
State 处理存储和访问智能合约数据的数据库状态。
起始模板具有以下基本状态,一个单例结构体 State
,包含:
count
,一个 32 位整数,execute()
消息将通过增加或重置该值进行交互。
owner
,MsgInstantiateContract
的发送者地址,决定是否允许某些执行消息。
Injective 智能合约能够通过 Injective 的原生 LevelDB(一个基于字节的键值存储)保持持久化状态。因此,任何你希望持久化的数据都应该分配一个唯一的键,用于索引和检索数据。
数据只能以原始字节形式持久化,因此任何结构或数据类型的概念必须通过一对序列化和反序列化函数来表达。例如,对象必须以字节形式存储,因此你需要提供一个将对象编码为字节以便保存到区块链上的函数,以及一个将字节解码回合约逻辑能够理解的数据类型的函数。字节表示的选择由你决定,只要它提供一个干净的双向映射。
请注意,State
结构体包含了 count
和 owner
。此外,derive
属性被应用于自动实现一些有用的特性:
Serialize
:提供序列化
Deserialize
:提供反序列化
Clone
:使结构体可复制
Debug
:使结构体可以打印为字符串
PartialEq
:提供相等性比较
JsonSchema
:自动生成 JSON 架构
Addr
指的是一个可读的 Injective 地址,以 inj
为前缀,例如 inj1clw20s2uxeyxtam6f7m84vgae92s9eh7vygagt
。
InstantiateMsg
是在用户通过 MsgInstantiateContract
在区块链上实例化合约时提供给合约的。它为合约提供了配置以及初始状态。
在 Injective 区块链上,合约代码的上传和合约的实例化被视为两个独立的事件,这与以太坊不同。这样做是为了允许一小部分经过审查的合约原型作为多个实例存在,这些实例共享相同的基础代码,但可以用不同的参数进行配置(想象一个标准的 ERC20 合约和多个使用其代码的代币)。
对于你的合约,合约创建者需要在 JSON 消息中提供初始状态。我们可以在下面的消息定义中看到,消息包含一个参数 count
,表示初始计数值。
在 contract.rs
文件中,你将定义第一个入口函数 instantiate()
,该函数用于实例化合约并接收 InstantiateMsg
。
从消息中提取 count
并设置初始状态,其中:
count
赋值为消息中的 count
。
owner
赋值为 MsgInstantiateContract
的发送者。
Increment
没有输入参数,将 count
值增加 1。
Reset
接受一个 32 位整数作为参数,并将 count
值重置为该输入参数。
Increment
任何用户都可以将当前 count
值增加 1。
Reset
只有 owner
可以将 count
重置为指定的数值。实现详情请参考以下逻辑。
对于 ExecuteMsg
,可以使用 enum
来对合约能够识别的不同类型的消息进行多路复用。
serde
属性会将枚举的键转换为蛇形(snake case)和小写(lower case),因此在 JSON 序列化和反序列化时,Increment
和 Reset
会被转换为 increment
和 reset
。
这是你的 execute()
方法,它使用 Rust 的模式匹配(pattern matching)来将接收到的 ExecuteMsg
路由到相应的处理逻辑。
根据接收到的消息,它会调用 try_increment()
或 try_reset()
方法进行处理。
首先,它获取对存储的可变引用,以更新存储在键 state
位置的项。然后,它通过返回 Ok
结果并包含新的 state
来更新状态中的 count
。最后,它返回 Ok
结果并附带 Response
,以确认合约执行成功。
reset
的逻辑与 increment
类似,但不同之处在于:它首先检查消息发送者是否被允许调用 reset
方法(在本例中,必须是合约 owner
)。
实现详情请参考以下逻辑。
该模板合约仅支持一种类型的 QueryMsg
:
GetCount
请求:
返回:
为了在合约中支持数据查询,你需要定义查询消息格式(代表请求),并提供查询输出的结构——在这种情况下是 CountResponse
。你必须这样做,因为 query()
会通过结构化的 JSON 将信息发送回用户,因此你需要定义响应的结构。有关更多信息,请参见 "生成 JSON 架构"。
将以下内容添加到你的 src/msg.rs
文件中:
query()
的逻辑与 execute()
类似;然而,由于 query()
在没有最终用户发起交易的情况下被调用,因此省略了 env
参数,因为不需要该信息。
单元测试应该作为部署合约到链上的第一步保障。它们执行迅速,并且在失败时可以通过 RUST_BACKTRACE=1
标志提供有用的回溯信息:
现在我们已经理解并测试了合约,可以运行以下命令来构建合约。这个命令将在我们进入下一步优化合约之前检查任何初步的错误。
接下来,我们需要优化合约,以便为将代码上传到区块链做准备。
在 Docker 正在运行的情况下,运行以下命令将合约代码挂载到 /code
并优化输出(如果不想先进入目录,可以使用绝对路径替代 $(pwd)
):
如果你使用的是 ARM64 机器,应该使用为 ARM64 构建的 Docker 镜像:
在运行命令时,你可能会收到 Unable to update registry 'crates-io'
错误。
尝试将以下行添加到合约目录中的 Cargo.toml
文件中,然后再次运行该命令:
这会生成一个 artifacts
目录,其中包含 PROJECT_NAME.wasm
文件,以及 checksums.txt
文件,后者包含 Wasm 文件的 Sha256 哈希值。Wasm 文件是确定性编译的(在相同的 git 提交上运行相同 Docker 的任何人都应该获得相同的文件,并且具有相同的 Sha256 哈希值)。
injectived
injectived
是命令行界面和daemon进程,连接到 Injective 并使你能够与 Injective 区块链进行交互。
另外,为了简化这个教程,已经准备好了一个 Docker 镜像。
执行此命令将使 Docker 容器无限期地执行。
Note: directory_to_which_you_cloned_cw-template
必须是一个绝对路径。你可以通过在 CosmWasm/cw-counter
目录中运行 pwd
命令轻松找到绝对路径。
打开一个新终端并进入 Docker 容器以初始化链:
让我们首先添加 jq
依赖,它将在后续步骤中使用:
现在我们可以继续进行本地区块链初始化,并添加一个名为 testuser
的测试用户(当提示时使用 12345678
作为密码)。我们将仅使用该测试用户来生成一个新的私钥,稍后将在测试网中用于签名消息:
输出
请花点时间记下地址,或者将其导出为环境变量,因为接下来你将需要用到它:
现在你已经成功在 Injective 测试网上创建了 testuser
,并且在从测试水龙头请求测试网资金后,账户应该会有一些资金。
输出:
要查询交易,请使用 txhash
并验证合约是否已成功部署。
仔细检查输出,我们可以看到上传合约的 code_id
为 290。
让我们将 code_id
导出为环境变量——我们稍后在实例化合约时需要用到它。你也可以跳过这一步,稍后手动添加,但请记住这个 ID。
虽然 Wasm 调用 instantiate
、execute
和 query
接受 JSON,但仅有这些信息不足以使用它们。我们需要将预期消息的架构暴露给客户端。
为了利用 JSON 架构的自动生成,你应该为每个需要架构的数据结构进行注册。
然后,架构可以通过以下命令生成:
这将生成 5 个文件,保存在 ./schema
目录中,分别对应合约接受的 3 种消息类型、查询响应消息和内部状态。这些文件采用标准的 JSON Schema 格式,可以被各种客户端工具使用,既可以自动生成编解码器,也可以根据定义的架构验证传入的 JSON。
现在我们已经将代码上传到 Injective,是时候实例化合约并与之交互了。
输出:
如我们之前所知,我们唯一的 QueryMsg
是 get_count
。
输出:
我们看到 count
是 99,这是在实例化合约时设置的值。.
现在让我们通过增加计数器来与合约进行交互。
如果我们查询合约的 count
,我们会看到:
充值计数器:
现在,如果我们再次查询合约,我们会看到 count
已经重置为提供的值:
除了定义自定义智能合约逻辑外,CosmWasm 还允许合约与底层的 Cosmos SDK 功能进行交互。一个常见的用例是使用 Cosmos SDK 的银行模块从合约向指定地址发送 tokens。
BankMsg::Send
消息允许合约将 tokens 转移到另一个地址。这在各种场景中都很有用,比如分发奖励或将资金返还给用户。
你可以在合约的 execute
函数中构造 BankMsg::Send
消息。此消息需要指定接收地址和要发送的金额。以下是如何构造该消息的示例:
在你的合约中,你可以向 ExecuteMsg
枚举添加一个新变体,以处理银行发送功能。例如:
然后,在 execute
函数中,你可以添加一个 case 来处理这个消息:
恭喜你!你已经创建并与第一个 Injective 智能合约进行了交互,现在知道如何开始在 Injective 上进行 CosmWasm 开发。继续阅读了解如何创建 Web UI 的指南。
交易所
学习如何构建像Helix一样的交易所
Cosmwasm
学习如何使用 CosmWasm 构建任何类型的 dApp
原生模块
了解更多关于Injective原生模块提供的功能
CosmWasm 是专为 Cosmos 生态系统 设计的新型智能合约平台。 你可以在 了解更多信息,或者参考 获取创建 CosmWasm 智能合约 的指南。
此外,请务必查看我们的 。
最低市场订单价格刻度
最低市场订单数量刻度
Injective 使交易者能够创建并交易任意的现货和衍生品市场。此外,Injective 还通过 Injective 链的 实现链上限价订单簿管理、链上交易执行、链上订单匹配、链上交易结算以及链上交易激励分配。
Injective 采用激励机制鼓励交易平台在 Injective 上构建并引导交易活动。向 Injective 交易协议的共享订单簿提交订单的交易平台(了解更多)将获得其引导订单的交易费用的一部分( )作为奖励。交易协议实施了全球最低交易费用标准,其中 Maker 费用为 ,Taker 费用为 。
前往我们的 TypeScript 文档 ,只需几步即可!
此外,请务必查看我们的 。
在 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
来查询/发送交易到测试网的相关信息。 .
在开始之前,请确保已安装 ,以及最新版本的 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 交易模块中的订单,这意味着每个订单可以有一个手续费接收者,该接收者可以获得 40% 的交易手续费。通常,交易所 dApp 会将手续费接收者设置为自己的地址。
该合约应支持哪些代币? 合约中可用的每个代币必须定义一个交易路线。路线是指代币 A 要通过哪些市场才能换取代币 B。例如,如果你想支持 ATOM 和 INJ 之间的交换,则需要通过提供 ATOM/USDT 和 INJ/USDT 的市场 ID 来设置路线,这样它就知道 ATOM 和 INJ 之间的交换路线是 ATOM ⇔ USDT ⇔ INJ。 目前,合约只能支持以 USDT 报价的市场。
应该为该合约提供多少缓冲资金? 作为合约拥有者,你还需要为合约提供资金,当发生交换时,这些资金将被使用。缓冲资金由合约在下单时使用。如果用户想要交换大额资产或在流动性不足的市场中进行交换,则需要更多的缓冲资金。当合约的缓冲资金无法满足用户输入金额时,会发生错误。
目前,缓冲资金应仅为 USDT。
初始化合约状态,包含合约版本和配置详情。配置包括管理员地址和手续费接收者地址。
处理不同类型的交易和管理员功能:
SwapMinOutput: 以最小输出数量进行交换。
SwapExactOutput: 以精确输出数量进行交换。
SetRoute: 设置交换路线。
DeleteRoute: 删除交换路线。
UpdateConfig: 更新合约配置。
WithdrawSupportFunds: 从合约中提取支持资金。
处理来自其他合约或交易的回复。
处理对合约的各种查询:
GetRoute: 获取特定的交换路径。
GetOutputQuantity: 获取给定输入数量的输出数量。
GetInputQuantity: 获取给定输出数量的输入数量。
GetAllRoutes: 获取所有可用的交换路径。
CW-20 是 CosmWasm 中的一种可替代代币规范, loosely 基于 ERC-20 规范。它允许在 CosmWasm 中创建和处理任意的可替代代币,指定用于创建、铸造、销毁和在账户之间转移这些代币的方法。适配器合约将确保只有授权的源 CW-20 合约可以铸造代币(以避免创建“伪造”代币)。
虽然 CW-20 标准相对成熟且完整,但这些代币仅存在于 CosmWasm 上下文中,并完全由发行合约管理(包括跟踪账户余额)。这意味着它们无法直接与 Injective 的原生模块交互(例如,无法通过 Injective 交易模块进行交易,或者在不涉及发行合约的情况下进行转移)。
考虑到上述情况,需要提供一个解决方案,作为 CW-20 和 Injective 银行模块之间的桥梁。
合约的工作流程如下:
注册一个新的 CW-20 代币。
将 X 数量的 CW-20 代币兑换为 Y 数量的 TokenFactory 代币(原始 CW-20 代币将由合约持有)。
将 Y 数量的 TF 代币兑换回 X 数量的 CW-20 代币(CW-20 代币被释放,TokenFactory 代币被销毁)。
RegisterCw20Contract { addr: Addr }
注册一个新的 CW-20 合约(地址),该合约将由适配器处理,并创建一个新的 TokenFactory 代币,格式为 factory/{adapter_contract}/{cw20_contract}
。
Receive { sender: String, amount: Uint128, msg: Binary }
CW-20 接收者接口的实现。
必须仅由 CW-20 合约调用。
RedeemAndTransfer { recipient: Option<String> }
赎回附加的 TokenFactory 代币并将 CW-20 代币转移给接收者。如果未提供接收者,则代币将发送给消息发送者。
RedeemAndSend { recipient: String, submessage: Binary }
赎回附加的 TokenFactory 代币并将 CW-20 代币发送到接收者合约。调用者可以提供可选的子消息。
UpdateMetadata { addr : Addr}
将查询 CW-20 地址(如果已注册)的元数据,并使用 TokenFactory 访问方法调用银行模块中的 setMetadata
。
Params 是一个模块级的配置结构,用于存储系统参数,并定义 拍卖模块 的整体运行方式。
Params: Paramsspace("auction") -> legacy_amino(params)
用于跟踪当前最高出价。
LastBid: 0x01 -> ProtocolBuffer(Bid)
当前拍卖轮次。
AuctionRound: 0x03 -> BigEndian(AuctionRound)
该值会与当前区块时间进行比较,以决定拍卖轮次的结算时间。当导出的链再次导入时,EndingTimeStamp 将更新为未来的下一个时间点。
EndingTimeStamp
: 0x04 -> BigEndian(EndingTimestamp)
用于跟踪最近一次拍卖的结果。
LastAuctionResult: 0x05 -> ProtocolBuffer(LastAuctionResult)
允许在两个不同的代币之间进行即时交换。在后台,它使用原子订单在一个或多个现货市场中下达市场订单。
任何人都可以实例化交换合约的实例。此合约的一个版本已经上传到 Injective 主网,并可以在找到。 在实例化合约之前,作为合约拥有者,你需要回答以下三个问题:
swap contract 的完整 GitHub 仓库可以在找到。
在本文档中,我们将解释 CW20 适配器合约,该合约允许将 CW-20 代币交换为 Injective 发行的原生代币(使用 TokenFactory 模块),反之亦然。关于 CW-20 适配器的 GitHub 仓库,请见。
Injective
Core
拍卖模块包含以下参数:
AuctionPeriod
int64
604800
MinNextBidIncrementRate
math.LegacyDec
"0.0025"
在现货市场中,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
来平掉任何用户的头寸。
初始保证金要求
这是新建头寸时保证金与订单名义价值和标记价格的比例要求。额外的标记价格要求旨在减少交易价格和标记价格在短时间内过度偏离时的强平风险。根据初始保证金比例,订单必须满足以下两个要求:
保证金要求: 保证金必须满足:
Margin ≥ InitialMarginRatio * Price * Quantity
例如,在最大杠杆为 20 倍的市场中,初始保证金比例为 0.05。任何新建头寸的保证金必须至少为其名义价值的 0.05。
标记价格要求: 保证金必须满足标记价格要求:
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(基础货币)减少现有的空头暴露。平仓时的支付将扣除相应的费用。
本文档描述了以下状态转换操作:
向交易模块账户存款
从交易模块账户取款
即时现货市场创建
即时永续合约市场创建
即时交割合约市场创建
现货限价订单创建
批量创建现货限价订单
现货市价订单创建
取消现货订单
批量取消现货订单
衍生品限价订单创建
批量创建衍生品限价订单
衍生品市价订单创建
取消衍生品订单
批量取消衍生品订单
子账户之间转账
向外部账户转账
清算持仓
增加持仓保证金
现货市场参数更新提案
交易模块启用提案
现货市场创建提案
永续合约市场创建提案
交割合约市场创建提案
衍生品市场参数更新提案
交易奖励启动提案
交易奖励更新提案
Begin-blocker 处理
End-blocker 处理
存款操作由 MsgDeposit
执行,该消息包含 Sender
、SubaccountId
和 Amount
字段。
注意:SubaccountId
为可选字段,如果未提供,则会根据 Sender
地址动态计算。
检查 msg.Amount
中指定的 denom
是否为银行供应中存在的有效 denom
。
将代币从个人账户转入 Exchange 模块账户,若失败则回滚操作。
从 msg.SubaccountId
获取 subaccountID
的哈希类型,如果为零子账户,则使用 SdkAddressToSubaccountID
根据 msg.Sender
动态计算。
将 msg.Amount
递增至 subaccountID
的存款余额。
触发 EventSubaccountDeposit
事件,包含 msg.Sender
、subaccountID
和 msg.Amount
。
提现操作由 MsgWithdraw
执行,该消息包含 Sender
、SubaccountId
和 Amount
字段。
注意:msg.ValidateBasic
函数会验证 msg.Sender
对 msg.SubaccountId
的所有权。
从 msg.SubaccountId
获取 subaccountID
的哈希类型。
检查 msg.Amount
中指定的 denom
是否为银行供应中存在的有效 denom
。
从 subaccountID
中减少 msg.Amount
,若失败则回滚操作。
将代币从 Exchange 模块账户发送至 msg.Sender
。
触发 EventSubaccountWithdraw
事件,包含 subaccountID
、msg.Sender
和 msg.Amount
。
即时现货市场启动操作由 MsgInstantSpotMarketLaunch
执行,该消息包含 Sender
、Ticker
、BaseDenom
、QuoteDenom
、MinPriceTickSize
和 MinQuantityTickSize
字段。
根据 msg.BaseDenom
和 msg.QuoteDenom
计算 marketID
。
检查是否已存在相同的市场启动提案,如果已存在,则回滚操作。
使用 msg.Ticker
、msg.BaseDenom
、msg.QuoteDenom
、msg.MinPriceTickSize
、msg.MinQuantityTickSize
启动现货市场,若失败则回滚操作。
从 msg.Sender
向 Exchange 模块账户发送即时上市费用 (params.SpotMarketInstantListingFee
)。
最后将即时上市费用发送至社区支出池。
即时永续市场启动操作由 MsgInstantPerpetualMarketLaunch
执行,该消息包含 Sender
、Ticker
、QuoteDenom
、OracleBase
、OracleQuote
、OracleScaleFactor
、OracleType
、MakerFeeRate
、TakerFeeRate
、InitialMarginRatio
、MaintenanceMarginRatio
、MinPriceTickSize
和 MinQuantityTickSize
字段。
根据 msg.Ticker
、msg.QuoteDenom
、msg.OracleBase
、msg.OracleQuote
和 msg.OracleType
计算 marketID
。
检查是否已存在相同的市场启动提案,如果已存在,则回滚操作。
从 msg.Sender
向 Exchange 模块账户发送即时上市费用 (params.DerivativeMarketInstantListingFee
)。
使用 msg
对象中的必需参数启动永续市场,若失败则回滚操作。
最后将即时上市费用发送至社区支出池。
即时到期期货市场启动操作由 MsgInstantExpiryFuturesMarketLaunch
执行,该消息包含 Sender
、Ticker
、QuoteDenom
、OracleBase
、OracleQuote
、OracleScaleFactor
、OracleType
、Expiry
、MakerFeeRate
、TakerFeeRate
、InitialMarginRatio
、MaintenanceMarginRatio
、MinPriceTickSize
和 MinQuantityTickSize
字段。
根据 msg.Ticker
、msg.QuoteDenom
、msg.OracleBase
、msg.OracleQuote
、msg.OracleType
和 msg.Expiry
计算 marketID
。
检查是否已存在相同的市场启动提案,如果已存在,则回滚操作。
从 msg.Sender
向 Exchange 模块账户发送即时上市费用 (params.DerivativeMarketInstantListingFee
)。
使用 msg
对象中的必需参数启动到期期货市场,若失败则回滚操作。
触发 EventExpiryFuturesMarketUpdate
事件,包含市场信息。
最后将即时上市费用发送至社区支出池。
现货限价订单创建操作由 MsgCreateSpotLimitOrder
执行,该消息包含 Sender
和 Order
字段。
检查现货交易是否已启用,若未启用则回滚操作。
检查订单的价格和数量是否符合市场的最小数量和价格刻度要求。
递增子账户的 TradeNonce
。
如果现货市场 ID 没有引用有效的现货市场,则拒绝该订单。
使用 TradeNonce
计算唯一的订单哈希。
如果子账户的可用存款不足以支付该订单所需的资金,则拒绝订单。
从可用余额中扣除订单所需的资金。
将订单存储在临时限价订单存储和临时市场指示器存储中。
注意:临时存储中的订单会在结束区块处理时执行,若未执行则存入长期存储。
批量创建现货限价订单操作由 MsgBatchCreateSpotLimitOrders
执行,该消息包含 Sender
和 Orders
字段。
遍历 msg.Orders
,并按照 MsgCreateSpotLimitOrder
中的步骤创建每个现货限价订单。
现货市场订单创建操作由 MsgCreateSpotMarketOrder
执行,该消息包含 Sender
和 Order
字段。
检查现货交易是否已启用,若未启用则回滚操作。
检查订单的价格和数量是否符合市场的最小数量和价格刻度要求。
递增子账户的 TradeNonce
。
如果现货市场 ID 没有引用有效的现货市场,则拒绝该订单。
使用 TradeNonce
计算唯一的订单哈希。
检查可用余额是否足以支持市场订单。
计算市场订单的最差可接受价格。
从存款的可用余额中扣除冻结的资金。
将订单存储在临时现货市场订单存储和临时市场指示器存储中。
现货订单取消操作由 MsgCancelSpotOrder
执行,该消息包含 Sender
、MarketId
、SubaccountId
和 OrderHash
字段。
检查现货交易是否已启用,若未启用则回滚操作。
如果现货市场 ID 没有引用有效、挂起或已拆除的现货市场,则拒绝该操作。
检查通过 marketID
、subaccountID
和 orderHash
是否存在现货限价订单。
将保证金冻结金额返还至可用余额。
递增可用余额的保证金冻结金额。
从 ordersStore
和 ordersIndexStore
中删除订单状态。
触发 EventCancelSpotOrder
事件,包含 marketID
和订单信息。
批量取消现货订单操作由 MsgBatchCancelSpotOrders
执行,该消息包含 Sender
和 Data
字段。
遍历 msg.Data
,并按照 MsgCancelSpotOrder
中的步骤取消每个现货订单。
衍生品限价订单创建操作由 MsgCreateDerivativeLimitOrder
执行,该消息包含 Sender
和 Order
字段。
检查衍生品交易是否已启用,若未启用则回滚操作。
如果子账户已在该市场下已下单(注意:限价订单和市场订单不能同时存在),则拒绝该订单。
获取衍生品市场和通过 marketID
获取标记价格。
获取指定 marketID
和 subaccountID
的订单簿元数据(SubaccountOrderbookMetadata
)。
确保限价订单有效:
市场配置(市场 ID 和刻度大小)。
子账户交易 nonce
。
子账户最大订单数量。
如果是减少仓位订单:
确保存在有效的仓位,并且方向相反。
如果订单会导致其他减少仓位订单失效,则拒绝该订单。
如果是限价订单:
确保子账户的存款足以支持保证金冻结。
如果订单方向与现有仓位相反且会导致其他减少仓位订单失效,则取消失效的减少仓位订单。
将订单存储在临时限价订单存储和临时市场指示器存储中。
批量创建衍生品限价订单操作由 MsgBatchCreateDerivativeLimitOrders
执行,该消息包含 Sender
和 Orders
字段。
遍历 msg.Orders
,并按照 MsgCreateDerivativeLimitOrder
中的步骤创建每个衍生品限价订单。
衍生品市场订单创建操作由 MsgCreateDerivativeMarketOrder
执行,该消息包含 Sender
和 Order
字段。
检查衍生品交易是否已启用,若未启用则回滚操作。
检查即将下单的 SubaccountID
是否已有限价衍生品订单或市场订单,若有则拒绝该订单。(注意:永续市场不能同时下两个市场订单或同时存在限价/市场订单)
检查订单的价格和数量是否符合市场的最小数量和价格刻度要求。
递增子账户的 TradeNonce
。
如果衍生品市场 ID 没有引用有效的衍生品市场,则拒绝该订单。
使用 TradeNonce
计算唯一的订单哈希。
检查市场订单的最差价格是否达到最优相反方向订单簿的价格。
检查订单/仓位的保证金金额。
如果是减少仓位订单
A. 检查该子账户在该市场的仓位是否为非空。
B. 检查该订单是否能够平仓。
C. 如果 position.quantity - AggregateReduceOnlyQuantity - order.quantity < 0
,则拒绝该订单。
D. 如果是卖出仓位,将保证金冻结金额设置为零。
如果不是减少仓位订单
A. 检查可用余额是否足以支持市场订单。
B. 如果子账户的可用存款不足以支付该订单所需的资金,则拒绝订单。
C. 从存款的可用余额中扣除冻结的资金。
对于相反方向的仓位,如果 AggregateVanillaQuantity > position.quantity - AggregateReduceOnlyQuantity - order.FillableQuantity
,则可能会使某些现有的减少仓位订单失效,或使新订单本身无效,需要进行相应操作。
将订单存储在临时衍生品市场订单存储和临时市场指示器存储中。
衍生品订单取消操作由 MsgCancelDerivativeOrder
执行,该消息包含 Sender
、MarketId
、SubaccountId
和 OrderHash
字段。
检查衍生品交易是否已启用,若未启用则回滚操作。
如果衍生品市场 ID 没有引用有效的衍生品市场,则拒绝该操作。
检查通过 marketID
、subaccountID
和 orderHash
是否存在有效的衍生品限价订单。
将保证金冻结金额返还至可用余额。
如果订单类型不允许取消,则跳过取消该限价订单。
从 ordersStore
、ordersIndexStore
和 subaccountOrderStore
中删除订单状态。
更新子账户的订单簿元数据。
触发 EventCancelDerivativeOrder
事件,包含 marketID
和订单信息。
批量取消衍生品订单操作由 MsgBatchCancelDerivativeOrders
执行,该消息包含 Sender
和 Data
字段。
遍历 msg.Data
,并按照 MsgCancelDerivativeOrder
中的步骤取消每个衍生品订单。
批量更新订单操作由 MsgBatchUpdateOrders
执行,该消息包含 Sender
和 Orders
字段。
根据指定的 subaccountID
,取消所有在 SpotMarketIdsToCancelAll
和 DerivativeMarketIdsToCancelAll
中列出的市场 ID 中的订单。
遍历 msg.SpotOrdersToCancel
,并按照 MsgCancelSpotOrder
中的步骤取消现货限价订单。如果取消失败,继续下一个订单。取消成功的结果在 MsgBatchUpdateOrdersResponse
中反映为 SpotCancelSuccess
。
遍历 msg.DerivativeOrdersToCancel
,并按照 MsgCancelDerivativeOrder
中的步骤取消衍生品限价订单。如果取消失败,继续下一个订单。取消成功的结果在 MsgBatchUpdateOrdersResponse
中反映为 DerivativeCancelSuccess
。
遍历 msg.SpotOrdersToCreate
,并按照 MsgCreateSpotOrder
中的步骤创建现货限价订单。如果创建失败,继续下一个订单。创建成功的结果在 MsgBatchUpdateOrdersResponse
中反映为 SpotOrderHashes
。
遍历 msg.DerivativeOrdersToCreate
,并按照 MsgCreateDerivativeOrder
中的步骤创建衍生品限价订单。如果创建失败,继续下一个订单。创建成功的结果在 MsgBatchUpdateOrdersResponse
中反映为 DerivativeOrderHashes
。
从子账户之间的转账操作由 MsgSubaccountTransfer
执行,该消息包含 Sender
、SourceSubaccountId
、DestinationSubaccountId
和 Amount
字段。
从 msg.SourceSubaccountId
提取 msg.Amount
,如果失败则回滚交易。
增加 msg.DestinationSubaccountId
的存款余额,增加的金额为 msg.Amount
。
触发 EventSubaccountBalanceTransfer
事件,包含 SrcSubaccountId
、DstSubaccountId
和 msg.Amount
。
备注:对于子账户之间的转账,不需要从银行模块实际转移代币,只需要更改相关记录即可。
外部账户转账由 MsgExternalTransfer
执行,该消息包含 Sender
、SourceSubaccountId
、DestinationSubaccountId
和 Amount
字段。
从 msg.SourceSubaccountId
提取 msg.Amount
,如果失败则回滚交易。
增加 msg.DestinationSubaccountId
的存款余额,增加的金额为 msg.Amount
。
触发 EventSubaccountBalanceTransfer
事件,包含 SrcSubaccountId
、DstSubaccountId
和 msg.Amount
。
备注:对于子账户转账,不需要从银行模块实际转移代币,只需要更改相关记录即可。
事件应区分子账户转账和外部转账。
子账户转账和外部转账没有区别,仍然需要保持不同的消息吗?
清算仓位由 MsgLiquidatePosition
执行,该消息包含 Sender
、SubaccountId
、MarketId
和 Order
字段。
检查衍生品交易是否已启用以清算衍生品市场中的仓位,若未启用则回滚交易。
如果衍生品市场 ID 没有引用有效的衍生品市场,则拒绝该操作。
根据 marketID
获取衍生品市场和标记价格(markPrice)。
获取指定 marketID
和 subaccountID
的仓位信息。
根据仓位信息计算清算价格(liquidationPrice)和破产价格(bankruptcyPrice)。
确定是进行清算还是销毁仓位,如果无法全部清算,则回滚。
取消仓位持有者在给定市场中创建的所有减仓限价单。
应用资金并更新仓位信息。
取消仓位持有者在给定市场中创建的所有市场订单。
检查并增加子账户的交易序列号(nonce),计算订单哈希。
计算清算订单的哈希值。
将清算订单存储到存储中。
通过匹配仓位和清算订单执行清算。
根据清算的支付结果处理:
正向支付:
将一半的支付金额发送给清算人(激励清算机器人)。
将另一半的支付金额发送到保险基金(激励参与保险基金的用户)。
负向支付 - 四个级别的资金回收步骤:
从交易者的可用余额中回收。
通过取消交易者的限价单,从交易者的锁仓余额中回收。
从保险基金中回收。
如果资金不足,暂停市场并将市场添加到存储中,以便在下一个区块中结算,见 BeginBlocker
规范。
如果市场是永续市场,则根据清算价格和数量更新 VWAP 数据。
如果清算订单中仍有剩余,取消订单并返回剩余部分。
增加仓位保证金由 MsgIncreasePositionMargin
执行,该消息包含 Sender
、SourceSubaccountId
、DestinationSubaccountId
、MarketId
和 Amount
字段。
检查衍生品交易是否已启用以增加衍生品市场的仓位保证金,若未启用则回滚交易。
如果衍生品市场 ID 没有引用有效的衍生品市场,则拒绝该操作。
获取 sourceSubaccountID
的存款信息。
如果 deposit.AvailableBalance
小于 msg.Amount
,则回滚交易。
根据 marketID
和 destinationSubaccountID
获取仓位信息,如果仓位不存在,则回滚交易。
从 sourceSubaccountID
减少 msg.Amount
的存款金额。
增加仓位保证金 msg.Amount
并更新存储中的仓位信息。
市场类型的启用通过 ExchangeEnableProposal
执行,该提案包含 Title
、Description
和 ExchangeType
字段。
对提案进行 ValidateBasic
验证。
如果 p.ExchangeType
是现货市场(spot market),则启用现货交易。
如果 p.ExchangeType
是衍生品市场(derivative market),则启用衍生品交易。
现货市场启动由 SpotMarketLaunchProposal
执行,该提案包含 Title
、Description
、Ticker
、BaseDenom
、QuoteDenom
、MinPriceTickSize
和 MinQuantityTickSize
字段。
对提案进行 ValidateBasic
验证。
验证 BaseDenom
和 QuoteDenom
是否有效。
验证基于 msg.BaseDenom
和 msg.QuoteDenom
是否已有相同市场存在。
根据交易模块参数计算 RelayerFeeShareRate
。注意:对于 INJ 货币,清算者分成比例设置为 100%。
保存现货市场,包含计算的 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
字段。
对提案进行 ValidateBasic
验证。
验证 quoteDenom
是否有效。
根据 ticker
、quoteDenom
、oracleBase
、oracleQuote
和 oracleType
计算 marketID
。
验证指定的 marketID
是否已存在有效或无效的永续市场。
尝试通过 oracleBase
、oracleQuote
、oracleScaleFactor
和 oracleType
获取衍生品市场价格,以检查价格预言机。
验证 marketID
是否存在保险基金。
根据交易模块参数计算 defaultFundingInterval
、nextFundingTimestamp
和 relayerFeeShareRate
。
执行 SetDerivativeMarketWithInfo
将市场信息、市场信息和资金对象存储到存储中。
到期期货市场启动由 ExpiryFuturesMarketLaunchProposal
执行,该提案包含 Title
、Description
、Ticker
、QuoteDenom
、OracleBase
、OracleQuote
、OracleScaleFactor
、OracleType
、Expiry
、MakerFeeRate
、TakerFeeRate
、InitialMarginRatio
、MaintenanceMarginRatio
、MinPriceTickSize
和 MinQuantityTickSize
字段。
对提案进行 ValidateBasic
验证。
验证 quoteDenom
是否有效。
根据 p.Ticker
、p.QuoteDenom
、p.OracleBase
、p.OracleQuote
、p.OracleType
和 p.Expiry
计算 marketID
。
验证指定的 marketID
是否已存在有效或无效的到期期货市场。
如果到期时间已经过去(即 ctx.BlockTime()
已经超过),则回滚。
尝试通过 oracleBase
、oracleQuote
、oracleScaleFactor
和 oracleType
获取衍生品市场价格,以检查价格预言机。
验证 marketID
是否存在保险基金。
根据交易模块参数计算 RelayerFeeShareRate
。注意:对于 INJ 货币,清算者分成比例设置为 100%。
执行 SetDerivativeMarketWithInfo
将市场信息和市场信息对象存储到存储中。注意:TwapStartTimestamp
设置为到期时间减去 30 分钟的秒数。
现货市场参数更新由 SpotMarketParamUpdateProposal
执行,该提案包含 Title
、Description
、MarketId
、MakerFeeRate
、TakerFeeRate
、RelayerFeeShareRate
、MinPriceTickSize
、MinQuantityTickSize
和 Status
字段。
对提案进行 ValidateBasic
验证。
根据 p.MarketId
获取现货市场,如果不存在,则回滚。
如果参数不为空,则重置 MakerFeeRate
、TakerFeeRate
、RelayerFeeShareRate
、MinPriceTickSize
、MinQuantityTickSize
和 Status
,如果为空,则保持不变。
验证 MakerFeeRate
是否大于 TakerFeeRate
。
衍生品市场参数更新由 DerivativeMarketParamUpdateProposal
处理,包含标题、描述、市场ID、初始保证金比例、维持保证金比例、做市商费用率、交易者费用率、分销商费用分成比例、最小价格刻度、最小数量刻度和状态。
步骤:
对提案进行基本验证。
通过 p.MarketId
验证衍生品市场是否存在,如果不存在,回滚交易。
如果不为空,则重置初始保证金比例、维持保证金比例、做市商费用率、交易者费用率、分销商费用分成比例、最小价格刻度、最小数量刻度和状态的参数;如果为空,保持原值。
验证做市商费用率大于交易者费用率。
验证初始保证金比例大于维持保证金比例。
调度衍生品市场参数更新,并在 Endblocker
上进行最终更新 - 注意:这是由于衍生品市场参数更新的订单更新,需确保此过程中不会发生异常。
步骤
对提案进行基本验证(ValidateBasic)。
不允许存在已启动的活动。
活动开始时间戳必须是未来时间。
活动的报价币种必须存在。
所有开始时间戳必须匹配持续时间。
设置活动数据(奖励池、信息、市场资格和市场积分乘数)。
触发 EventTradingRewardCampaignUpdate 事件。
步骤
对提案进行基本验证(ValidateBasic)。
所有在CampaignRewardPoolsUpdates中的StartTimestamp必须匹配现有活动。
CampaignDurationSeconds不能修改,但必须与当前活动匹配。
CampaignRewardPoolsUpdates不能修改当前活动,可以包含nil值来删除奖励池。
来自CampaignRewardPoolsAdditions的活动开始时间戳必须在未来。
所有活动的quote denoms必须存在。
删除当前活动的数据(信息、市场资格和市场点数乘数)。
设置活动数据(信息、市场资格和市场点数乘数)。
设置奖励池更新。
设置奖励池添加。
触发EventTradingRewardCampaignUpdate事件。
步骤
验证提案的基本信息
如果当前的费率折扣计划存在,则删除它以及市场资格
定义的报价币种必须存在
如果需要重启费率周期(例如桶数、桶持续时间或报价币种发生变化),则删除所有账户费率桶并重启周期
将第一个已付费桶的时间戳设置为当前区块时间
设置新的费率折扣计划,并删除它以及市场资格
设置新的市场资格
给定拍卖轮次的结算发生在 blockTime ≥ EndingTimeStamp 时。如果在此期间有非零的 INJ 出价(即存在 LastBid),则会执行以下流程:
获胜的 INJ 出价金额将被销毁。
拍卖模块持有的代币篮子将转移给获胜竞标者。
LastAuctionResult 会写入状态,并触发 EventAuctionResult 事件。
LastBid 被清除。
AuctionRound 递增 1,EndingTimestamp 递增 AuctionPeriod。
累积的交易手续费将从交易模块转移到拍卖模块,为即将到来的新一轮拍卖做准备。
如果该轮拍卖未有任何有效出价,现有的代币篮子将被转入下一轮拍卖,并与新的累积手续费篮子合并。
MsgDeposit 定义了一个 SDK 消息,用于将代币从发送者的银行余额转入子账户的交易所存款。
字段描述
Sender 字段描述了进行存款的地址。
SubaccountId 描述了接收存款的子账户 ID。
Amount 指定了存款的金额。
MsgWithdraw
定义了一个 SDK 消息,用于从子账户的存款中提取币到用户的银行余额。
字段描述:
Sender:描述发起提取操作的地址。
SubaccountId:描述接收提取的子账户ID。
Amount:指定提取的金额。
MsgInstantSpotMarketLaunch
定义了一个 SDK 消息,用于通过支付上市费用在没有治理的情况下创建一个新的现货市场。费用将发送到社区支出池。
字段描述
Sender 字段描述了此消息的创建者。
Ticker 描述了现货市场的交易代码。
BaseDenom 指定了用作基础货币的币种类型。
QuoteDenom 指定了用作报价货币的币种类型。
MinPriceTickSize 定义了订单价格的最小价格跳动。
MinQuantityTickSize 定义了订单数量的最小数量跳动。
MsgInstantPerpetualMarketLaunch
定义了一个 SDK 消息,用于通过支付上市费用来创建一个新的永久期货市场,无需治理。费用将发送到社区支出池。
字段描述
Sender 字段描述了此消息的创建者。
Ticker 字段描述了衍生品市场的交易代码。
QuoteDenom 字段描述了用于基础货币的币种类型。
OracleBase 字段描述了预言机的基础货币。
OracleQuote 字段描述了预言机的报价货币。
OracleScaleFactor 字段描述了预言机价格的缩放因子。
OracleType 字段描述了预言机的类型。
MakerFeeRate 字段描述了衍生品市场上做市商的交易费率。
TakerFeeRate 字段描述了衍生品市场上吃单者的交易费率。
InitialMarginRatio 字段描述了衍生品市场的初始保证金比例。
MaintenanceMarginRatio 字段描述了衍生品市场的维持保证金比例。
MinPriceTickSize 字段描述了订单价格和保证金的最小刻度。
MinQuantityTickSize 字段描述了订单数量的最小刻度。
MsgInstantExpiryFuturesMarketLaunch
定义了一个 SDK 消息,用于通过支付上市费用创建一个新的到期期货市场,无需治理。费用将发送到社区支出池。
字段描述
Sender:描述此消息的创建者。
Ticker:描述衍生品市场的标记符。
QuoteDenom:描述用作报价货币的币种类型。
OracleBase:描述预言机基础货币。
OracleQuote:描述预言机报价货币。
OracleScaleFactor:描述预言机价格的缩放因子。
OracleType:描述预言机类型。
Expiry:描述市场的到期时间。
MakerFeeRate:描述衍生品市场上做市商的交易费用率。
TakerFeeRate:描述衍生品市场上交易者的交易费用率。
InitialMarginRatio:描述衍生品市场的初始保证金比率。
MaintenanceMarginRatio:描述衍生品市场的维持保证金比率。
MinPriceTickSize:描述订单价格和保证金的最小刻度大小。
MsgCreateSpotLimitOrder
定义了一个 SDK 消息,用于创建一个新的现货限价订单。
字段描述
Sender 字段描述了此消息的创建者。
Order 字段描述了订单信息。
MsgBatchCreateSpotLimitOrders 定义了一个 SDK 消息,用于创建一批新的现货限价订单。
字段描述
Sender 字段描述了此消息的创建者。
Order 字段描述了订单信息。
MsgCreateSpotMarketOrder
定义了一个 SDK 消息,用于创建一个新的现货市场订单。
字段描述
Sender 字段描述了此消息的创建者。
Order 字段描述了订单信息。
MsgCancelSpotOrder
定义了一个消息,用于取消一个现货订单。
字段描述
Sender 字段描述了此消息的创建者。
MarketId 字段描述了订单所在市场的 ID。
SubaccountId 字段描述了下单的子账户 ID。
OrderHash 字段描述了订单的哈希值。
MsgBatchCancelSpotOrders
定义了批量取消现货订单的消息。
字段描述
Sender 字段描述了该消息的创建者。
Data 字段描述了要取消的订单。
MsgCreateDerivativeLimitOrder
定义了创建衍生品限价订单的消息。
字段描述
Sender 字段描述了消息的创建者。
Order 字段描述了订单的详细信息。
MsgBatchCreateDerivativeLimitOrders
描述了批量创建衍生品限价订单的消息。
字段描述
Sender 字段描述了消息的创建者。
Order 字段描述了订单的详细信息。
MsgCreateDerivativeMarketOrder
是用于创建衍生品市场订单的消息。
字段描述
Sender 字段描述了消息的创建者。
Order 字段描述了订单的详细信息。
MsgCancelDerivativeOrder
是用于取消衍生品订单的消息。
字段描述
Sender 字段描述此消息的创建者。
MarketId 字段描述订单所在市场的 ID。
SubaccountId 字段描述下单的子账户 ID。
OrderHash 字段描述订单的哈希值。
MsgBatchCancelDerivativeOrders
是一个批量取消衍生品订单的消息。
字段描述
Sender 字段描述此消息的创建者。
Data 字段描述要取消的订单。
MsgSubaccountTransfer
是一条用于在子账户之间转移余额的消息。
字段描述
Sender 字段描述消息的创建者。
SourceSubaccountId 字段描述发送币的源子账户。
DestinationSubaccountId 字段描述接收币的目标子账户。
Amount 字段描述要发送的币的数量。
MsgExternalTransfer
是一条将余额从源账户转移到外部子账户的消息。
字段描述
Sender 字段描述消息的创建者。
SourceSubaccountId 字段描述发送币的源子账户。
DestinationSubaccountId 字段描述接收币的目标子账户。
Amount 字段描述要发送的币的数量。
MsgLiquidatePosition
描述了一条清算账户持仓的消息。
字段描述
Sender 字段描述消息的创建者。
SubaccountId 字段描述接收清算金额的子账户。
MarketId 字段描述持仓所在的市场。
Order 字段描述订单信息。
MsgIncreasePositionMargin
描述了一条增加账户保证金的消息。
字段描述
Sender 字段描述消息的创建者。
SourceSubaccountId 字段描述发送余额的源子账户。
DestinationSubaccountId 字段描述接收余额的目标子账户。
MarketId 字段描述持仓所在的市场。
Amount 字段描述增加的金额。
MsgBatchUpdateOrders
允许原子级别地取消和创建现货及衍生品限价订单,并引入了一种新的订单取消模式。执行时,首先进行订单取消(如果有),然后进行订单创建(如果有)。
字段描述
Sender 字段描述消息的创建者。
SubaccountId 字段描述发送者的子账户 ID。
SpotMarketIdsToCancelAll 字段描述发送者希望取消所有未结订单的现货市场 ID 列表。
DerivativeMarketIdsToCancelAll 字段描述发送者希望取消所有未结订单的衍生品市场 ID 列表。
SpotOrdersToCancel 字段描述发送者希望取消的具体现货订单。
DerivativeOrdersToCancel 字段描述发送者希望取消的具体衍生品订单。
SpotOrdersToCreate 字段描述发送者希望创建的现货订单。
DerivativeOrdersToCreate 字段描述发送者希望创建的衍生品订单。
SpotMarketParamUpdateProposal
定义了一条提议更新现货市场参数的 SDK 消息。
字段描述
Title 字段描述提案的标题。
Description 字段描述提案的描述。
MarketId 字段描述要更改参数的市场 ID。
MakerFeeRate 字段描述做市商的目标费用率。
TakerFeeRate 字段描述吃单者的目标费用率。
RelayerFeeShareRate 字段描述中继商费用分成率。
MinPriceTickSize 字段定义订单价格的最小刻度。
MinQuantityTickSize 字段定义订单数量的最小刻度。
Status 字段描述市场的目标状态。
ExchangeEnableProposal
定义了一条提议启用特定交易类型的消息。
字段描述
Title 字段描述提案的标题。
Description 字段描述提案的描述。
ExchangeType 字段描述交易类型,现货或衍生品。
BatchExchangeModificationProposal
定义了一条在交易模块中批量处理多个提案的消息。
字段描述
Title 字段描述提案的标题。
Description 字段描述提案的描述。
SpotMarketParamUpdateProposal 字段描述 SpotMarketParamUpdateProposal。
DerivativeMarketParamUpdateProposal 字段描述 DerivativeMarketParamUpdateProposal。
SpotMarketLaunchProposal 字段描述 SpotMarketLaunchProposal。
PerpetualMarketLaunchProposal 字段描述 PerpetualMarketLaunchProposal。
ExpiryFuturesMarketLaunchProposal 字段描述 ExpiryFuturesMarketLaunchProposal。
TradingRewardCampaignUpdateProposal 字段描述 TradingRewardCampaignUpdateProposal。
SpotMarketLaunchProposal
定义了一条通过治理提议新现货市场的 SDK 消息。
字段描述
Title 字段描述提案的标题。
Description 字段描述提案的描述。
Ticker 字段描述现货市场的交易对符号。
BaseDenom 字段指定用作基础货币的币种类型。
QuoteDenom 字段指定用作报价货币的币种类型。
MinPriceTickSize 字段定义订单价格的最小刻度。
MinQuantityTickSize 字段定义订单数量的最小刻度。
MakerFeeRate 字段描述衍生品市场上做市商的交易费用率。
TakerFeeRate 字段描述衍生品市场上吃单者的交易费用率。
PerpetualMarketLaunchProposal
定义了一条通过治理提议新永久期货市场的 SDK 消息。
字段描述
Title 字段描述提案的标题。
Description 字段描述提案的描述。
Ticker 字段描述衍生品市场的交易对符号。
QuoteDenom 字段描述用作基础货币的币种类型。
OracleBase 字段描述预言机的基础货币。
OracleQuote 字段描述预言机的报价货币。
OracleScaleFactor 字段描述预言机价格的缩放因子。
OracleType 字段描述预言机的类型。
MakerFeeRate 字段描述衍生品市场上做市商的交易费用率。
TakerFeeRate 字段描述衍生品市场上吃单者的交易费用率。
InitialMarginRatio 字段描述衍生品市场的初始保证金比例。
MaintenanceMarginRatio 字段描述衍生品市场的维持保证金比例。
MinPriceTickSize 字段描述订单价格和保证金的最小刻度。
MinQuantityTickSize 字段描述订单数量的最小刻度。
字段描述
Title 字段描述提案的标题。
Description 字段描述提案的描述。
Ticker 字段描述衍生品市场的交易对符号。
QuoteDenom 字段描述用作报价货币的币种类型。
OracleBase 字段描述预言机的基础货币。
OracleQuote 字段描述预言机的报价货币。
OracleScaleFactor 字段描述预言机价格的缩放因子。
OracleType 字段描述预言机的类型。
Expiry 字段描述市场的到期时间。
MakerFeeRate 字段描述衍生品市场上做市商的交易费用率。
TakerFeeRate 字段描述衍生品市场上吃单者的交易费用率。
InitialMarginRatio 字段描述衍生品市场的初始保证金比例。
MaintenanceMarginRatio 字段描述衍生品市场的维持保证金比例。
MinPriceTickSize 字段描述订单价格和保证金的最小刻度。
MinQuantityTickSize 字段描述订单数量的最小刻度。
字段描述
Title 字段描述提案的标题。
Description 字段描述提案的描述。
MarketId 字段描述要更改参数的市场 ID。
InitialMarginRatio 字段描述目标初始保证金比例。
MaintenanceMarginRatio 字段描述目标维持保证金比例。
MakerFeeRate 字段描述目标做市商费用率。
TakerFeeRate 字段描述目标吃单者费用率。
RelayerFeeShareRate 字段描述中继商费用分成率。
MinPriceTickSize 字段定义订单价格的最小刻度。
MinQuantityTickSize 字段定义订单数量的最小刻度。
Status 字段描述市场的目标状态。
OracleParams 字段描述新的预言机参数。
TradingRewardCampaignLaunchProposal
定义了一条提议启动新交易奖励活动的 SDK 消息。
字段描述
Title 字段描述提案的标题。
Description 字段描述提案的描述。
CampaignInfo 字段描述活动信息。
CampaignRewardPools 字段描述奖励池信息。
TradingRewardCampaignUpdateProposal
定义了一条提议更新现有交易奖励活动的 SDK 消息。
字段描述
Title 字段描述提案的标题。
Description 字段描述提案的描述。
CampaignRewardPoolsAdditions 字段描述奖励池新增信息。
CampaignRewardPoolsUpdates 字段描述奖励池更新信息。
FeeDiscountProposal
定义了一条提议启动或更新费用折扣计划的 SDK 消息。
字段描述
Title 字段描述提案的标题。
Description 字段描述提案的描述。
Schedule 字段描述费用折扣计划。
TradingRewardPendingPointsUpdateProposal
定义了一条在归属期间更新特定地址奖励积分的 SDK 消息。
字段描述
Title 字段描述提案的标题。
Description 字段描述提案的描述。
PendingPoolTimestamp 字段描述待处理池的时间戳。
RewardPointUpdates
描述 RewardPointUpdate.
我们通过 Injective CLI 与合约进行了交互,但对于大多数 dApp 用户来说,这并不是理想的方式。一个 Web UI 可以提供更好的体验!我们可以抽象掉复杂性,提供两个按钮—一个用于增加计数,一个用于重置计数,而不是通过 injectived 发送交易消息。
二元期权市场与其他市场不同,它没有基础资产,报价通常使用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 = 仓位已结算/退款(取决于结算情况),市场被拆除
二元期权市场状态转换的性质如下:
结算市场可以通过以下两种方式之一实现:
使用为特定市场注册的提供者oracle。一旦提供者oracle获得了传递价格的权限(上面已解释),具有该权限的地址可以使用MsgRelayProviderPrices消息为特定价格信息源传递价格。
使用MsgAdminUpdateBinaryOptionsMarket
,允许市场的管理员(创建者)直接向市场提交结算价格。
现在我们已经有了 appchain 实例和一些具有初始余额的账户,这些账户可以与 appchain 进行交互。这并不运行 Docker 实例或启动外部进程,它只是将 appchain 的代码作为库加载,以创建一个内存中的实例。
请注意,init_accounts
是一个便捷函数,它创建多个具有相同初始余额的账户。如果你只想创建一个账户,可以改为使用 init_account
。
现在,如果我们想测试一个 CosmWasm 合约,我们需要:
构建 wasm 文件
存储代码
实例化合约
然后,我们就可以开始与合约进行交互了。让我们一步步来操作。
现在,让我们执行合约并验证合约的状态是否正确更新。
在某些情况下,你可能想直接与 appchain 的逻辑进行交互,以设置环境或查询 appchain 的状态。模块包装器提供了方便的函数来与 appchain 的模块进行交互。 让我们尝试与 Exchange 模块进行交互:
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
特定且向后兼容的更改时,将递增修补版本号。
请查阅升级指南以了解如何升级包,以应对破坏性更改。
需要注意的是,我们跟踪包的版本与依赖项的版本是独立的。
在本节中,我们描述了交易所消息的处理过程以及相应的状态更新。每个消息所创建/修改的状态对象都在部分进行了定义。
关于创建用户界面以及引导选项的更全面文档可以在中找到。
例如,请查看。关于使用 Vue 和 开发前端的高级指南可以在该网站的中找到。对于 React 实现,请查看。 现在,与合约的交互变得和点击按钮、使用 MetaMask 签名一样简单(确保账户设置为 Ethereum Goerli Testnet,否则会收到链 ID 不匹配的错误)。
你可能会注意到,在尝试重置计数时,出现了“Unauthorized”(未经授权)错误消息。这是预期的行为!回顾,对于重置操作,只有合约拥有者才被允许重置计数。由于你没有实例化前端交互的确切合约,因此你没有重置计数所需的权限。
结算价格选项在上面的部分中解释。
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
允许输出到标准输出,这样你就可以在运行测试时调试你的智能合约。
更多示例可以在目录中找到。
Active → Expired
到期是市场标准工作流程的一部分。市场的交易立即停止,所有未完成的订单将被取消。此时,市场可以立即由管理员或预言机强制结算,或者在达到 SettlementTimestamp 时,使用最新的预言机价格自然结算。
Expired → Demolished (Settlement)
所有头寸将根据强制结算或自然结算的价格进行结算。市场将无法再进行交易或重新激活。对于自然结算,在 SettlementTimestamp 时间到达时,记录并使用最后的预言机价格进行结算。对于“强制结算”,管理员应发布包含结算价格的 MarketUpdate 消息,该价格应设置在 [0, 1] 的价格区间内。
Active/Expired → Demolished (Refund)
所有头寸将被退款。该市场将无法再进行交易或重新激活。管理员应发布 MarketUpdate 消息,并将结算价格设置为 -1。
MsgPrivilegedExecuteContract
定义了一个方法,用于从交易模块执行具有特权能力的 Cosmwasm 合约。
字段描述
Sender:描述此消息的创建者。
Funds:定义用户用于资助执行的银行币(例如 100inj)。
ContractAddress:定义要执行的合约地址。
Data:定义执行合约时使用的调用数据,详细信息请见下文。
合约接口 如果您希望在合约中启用特权操作,必须实现以下执行方法:
origin 字段是发送特权操作的用户地址。您不需要自己设置此字段,它将由交易模块设置。
name 字段是特权操作的名称。您可以根据需要定义这些名称。
args 字段是特权操作的参数。您可以根据需要定义这些参数。
在 Golang 中,Data 字符串的完整定义是:
用户可以通过发送带有以下数据的 MsgPrivilegedExecuteContract
来调用特权操作:
支持的特权操作
目前支持两种特权操作:
这些特权操作必须设置在 Cosmwasm 响应数据字段内,例如:
PositionTransfer
头寸转移允许合约将衍生头寸从其自身子账户转移到用户的子账户。该头寸不得处于强平状态。仅接收方支付接单商交易手续费,该手续费从其余额中扣除。 目前仅支持从合约的子账户转移到用户的子账户。
SyntheticTrade
合成交易允许合约代表用户在衍生品市场执行合成交易。这不涉及订单簿,仅为纯粹的合成交易。接单商交易手续费仍然适用。子账户 ID 必须设置为合约的子账户 ID 和用户的子账户 ID。
交易模块会触发以下事件:
交易模块包含以下参数:
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
Genesis 状态定义了模块的初始状态,用于设置模块。
Params 是一个模块级配置,用于存储系统参数并定义交易所模块的整体功能。该配置可以通过治理使用由治理模块原生支持的参数更新提案进行修改。 它定义了用于现货市场和衍生品市场的默认费用对象,以及衍生品市场的资金参数和即时上市费用。 用于交易所模块参数存储的 Protobuf 接口。
Balance 用于管理账户余额。该模块将整个余额存储在模块账户中,而每个账户的余额则作为记录进行管理。 余额对象按 subaccount_id 和 denom 存储。
SubaccountNonce 用于表示唯一的订单哈希。
有多个结构用于将订单存储到存储中。
SpotMarket 是用于存储现货市场所需的所有信息和状态的结构。现货市场通过市场的哈希值存储,以便高效查询市场。
SpotOrderBook 是用于存储特定市场的现货限价订单的结构。创建了两个对象,一个用于买单,另一个用于卖单。
DerivativeMarket 是用于存储衍生品市场所需的所有信息和状态的结构。衍生品市场通过市场的哈希值存储,以便高效查询市场。
DerivativeOrderBook 是用于存储特定市场的衍生品限价订单的结构。为买单和卖单分别创建两个对象。
DerivativePosition 是用于存储特定市场上子账户的衍生品头寸的结构。 注意:衍生品订单代表意图,而头寸代表持有。
ExpiryFuturesMarketInfo 是用于存储到期期货市场信息的结构。它通过市场的 ID 存储。
PerpetualMarketInfo
是用于存储永续市场信息的结构。
PerpetualMarketFunding
是用于管理永续市场资金信息的结构。
CampaignRewardPool
是一个结构,用于获取即将到来的交易奖励池。
TradingRewardCampaignInfo
是一个结构,用于获取交易奖励活动的信息。
FeeDiscountProposal
是一个结构,用于提议新的费用折扣计划和持续时间。
DerivativeMarketSettlementInfo
是一个结构,用于管理计划结算的衍生品市场。
交易日志通过事件发出,以跟踪交易历史。
枚举用于描述订单类型、执行类型和市场状态。
保险模块会触发以下事件:
injective.insurance.v1beta1.EventInsuranceFundUpdate
fund
{fundJSON}
injective.insurance.v1beta1.EventInsuranceFundUpdate
fund
{fundJSON}
injective.insurance.v1beta1.EventRequestRedemption
schedule
{scheduleJSON}
injective.insurance.v1beta1.EventInsuranceFundUpdate
fund
{fundJSON}
injective.insurance.v1beta1.EventWithdrawRedemption
schedule
{scheduleJSON}
injective.insurance.v1beta1.EventWithdrawRedemption
redeem_coin
{redeemCoin}
保险模块包含以下参数:
default_redemption_notice_period_duration
time.Duration
time.Hour * 24 * 14
其他模块可以注册操作,以便在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
钩子,用于在传输时存储累计价格。
在每个BeginBlock时,它会检查是否到了支付间隔时间,如果到了,它会为所有的feed处理支付。
步骤
确保是支付间隔的BeginBlock 。
在遍历所有feed配置, 处理奖励支付。
本文件描述了与以下事项相关的状态转换操作:
创建保险基金
承保保险基金
向保险基金请求赎回
自动处理到期的赎回请求
参数描述:Sender
字段描述了保险基金的创建者。Ticker、QuoteDenom、OracleBase、OracleQuote、OracleType、Expiry
字段描述了与保险基金相关的衍生品市场信息。InitialDeposit
字段描述了投入保险基金的初始存款金额。
步骤
获取保险基金的 MarketId——注意,市场可能尚未在交易所上线,但这不是问题。
确保与该 MarketId 关联的保险基金不存在。
确保初始存款金额不为零。
获取唯一的 shareDenom——当请求保险基金创建的 share denom 或当承保一个余额为零且总 share denom 供应量不为零的保险基金时,会递增 share denom。
从创建者账户向保险基金模块账户发送币。
使用 DefaultRedemptionNoticePeriodDuration 和提供的参数创建保险基金对象。
将基金对象的余额设置为初始存款金额。
向创建者账户铸造 InsuranceFundInitialSupply (10^18) 的 shareDenom 代币。
将保险基金对象保存到存储中。
在 BankKeeper 中注册新创建的保险基金 shareDenom 元数据。
参数描述:Sender
字段描述了保险基金的承保人。MarketId
字段描述了与保险基金关联的衍生品市场 ID。Deposit
字段描述了要添加到保险基金的存款金额。
步骤
确保与该 MarketId
关联的保险基金存在。
从发送者账户向模块账户发送承保代币。
根据与 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 与铸造数量。
将保险基金对象保存到存储中
参数描述: Sender
字段描述了保险基金的赎回请求者。MarketId
字段描述了与保险基金关联的衍生品市场 ID。Amount
字段描述了要赎回的份额代币数量。
步骤
确保与 MarketId 关联的保险基金存在。
将 ShareDenom 发送至模块账户。
获取新的赎回计划 ID。
根据保险基金的赎回通知期持续时间和当前区块时间计算 ClaimTime。
计算用于存储待处理赎回(赎回计划)的键。
创建包含详细信息的赎回计划对象。
步骤
交易模块从保险 Keeper 中查找相关的保险基金。
如果 missingFund 为正值,则通过 WithdrawFromInsuranceFund
从保险基金提取相应金额。
如果 missingFund 为负值,则通过 DepositIntoInsuranceFund
向保险基金存入相应金额。
步骤
按 ClaimTime 排序遍历所有已到期的赎回请求,并执行以下操作:
如果 ClaimTime 晚于当前区块时间,则提前中断处理。
确保赎回计划对应的保险基金存在。
根据份额数量计算赎回金额:shareAmt * fund.Balance / fund.TotalShare
。
从模块账户向赎回者账户发送计算出的赎回金额。
燃烧在赎回计划创建时发送至模块账户的份额代币。
删除赎回计划对象。
将保险基金的余额减少赎回金额。
将更新后的保险基金对象存储至存储中。
其他模块可以注册操作,以便在保险基金发生特定事件时执行。这些事件可以注册为在交易事件之前(Before)或之后(After)执行(依据钩子名称)。
可在交易模块中注册以下钩子: 注意:钩子当前不可用,交易模块会直接调用保险 Keeper 的函数。
交易模块从保险 Keeper 中查找相关的保险基金。
如果 missingFund 为正值,则通过 WithdrawFromInsuranceFund
从保险基金提取相应金额。
如果 missingFund 为负值,则通过 DepositIntoInsuranceFund
向保险基金存入相应金额。
阶段 0:确定当前区块中所有在支持费用折扣的市场中下单的账户的费用折扣。
阶段 1:并行处理所有市场订单
现货市场和衍生品市场订单
市场订单将在区块开始时根据挂单簿执行
请注意,市场订单可能会由于后续的预言机更新或限价单取消而在 EndBlocker 中失效。
阶段 2:将市场订单执行结果持久化到存储
现货市场
持久化现货市场订单执行数据
发出相关事件
EventBatchSpotExecution
衍生品市场
持久化衍生品市场订单执行数据
发出相关事件
EventBatchDerivativeExecution
EventCancelDerivativeOrder
阶段 3:并行处理所有限价单
匹配的现货和衍生品限价单
限价单将以频繁批量拍卖模式执行,以确保公平的匹配价格,详细信息请见下文。
请注意,普通限价单可能会由于后续的预言机更新而在 EndBlocker 中失效,而减仓限价单可能会由于后续到来的订单翻转头寸而在 EndBlocker 中失效。
阶段 4:将限价单匹配执行结果和新限价单持久化到存储
现货市场
持久化现货市场匹配执行数据
发出相关事件
EventNewSpotOrders
EventBatchSpotExecution
衍生品市场
持久化衍生品市场匹配执行数据
发出相关事件
EventNewDerivativeOrders
EventBatchDerivativeExecution
EventCancelDerivativeOrder
阶段 5:持久化永续市场资金信息
阶段 6:持久化交易奖励总额和账户积分
阶段 7:持久化新的费用折扣数据
即新的已支付费用和新的账户等级
阶段 8:处理现货市场参数更新(如果有)
阶段 9:处理衍生品市场参数更新(如果有)
阶段 10:发出存款和头寸更新事件
同样,限价单也会以统一的清算价格执行。新的限价单与挂单簿结合,并且只要存在负价差(spread),订单就会被匹配。
清算价格可以是以下几种:
a. 如果最后一个匹配的订单跨越了价格差(spread)方向,则为最佳买/卖订单;b. 如果是衍生品市场,且标记价格位于最后一个匹配订单之间,则为标记价格;c. 如果没有以上条件,则为中间价格(mid price)。
对于符合条件的市场,计算手续费折扣:
手续费折扣作为退款应用,并记录支付的手续费贡献。
中继商费用在应用手续费折扣后计算。
对于符合条件的市场,计算交易奖励积分贡献:
获取做市商和接单商的FeePaidMultiplier。
计算交易奖励积分贡献。
交易奖励积分基于折扣后的交易手续费。
计算手续费退款(或收费)。订单匹配后可能有几种原因导致手续费退款:
这是一个未匹配或部分匹配的限价单,这意味着它将变成一个休息限价单,并且从接单商费用转变为做市商费用。退款为UnmatchedQuantity * (TakerFeeRate - MakerFeeRate)
。请注意,对于负的做市商手续费,我们退还UnmatchedQuantity * TakerFeeRate
。
应用了手续费折扣。我们退还原始手续费与折扣后支付的手续费之间的差额。
订单以更好的价格匹配,从而导致不同的费用。
对于买单,更好的价格意味着更低的价格,因此手续费较低。我们退还手续费价格差额。
对于卖单,更好的价格意味着更高的价格,因此手续费较高。我们收取手续费价格差额。
在所有市场中并发匹配所有可匹配的订单(详细信息见订单匹配)。
中间结果是清算价格和已匹配订单的列表,包括它们的成交数量。
最终结果是所有新事件的临时缓存,以及所有位置、订单、子账户存款、交易奖励积分和已支付费用的更改。
等待所有市场的执行并持久化所有数据。
注意:除了执行结算外,设计还必须考虑市场数据传播的需求,以供链下消费。
一个常见的请求是,基于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 小时更新一次。
检查第一个接收资金支付的市场。如果第一个市场尚未到期接收资金(资金时间戳尚未达到),则跳过所有资金支付。
否则逐一检查每个市场:
如果资金时间戳尚未到期,跳过该市场。
计算资金,公式为 twap + hourlyInterestRate,其中 $\mathrm{twap = \frac{cumulativePrice}{timeInterval * 24}}$
,其中 $\mathrm{timeInterval = lastTimestamp - startingTimestamp}$
。cumulativePrice 是通过每笔交易计算的加权时间差:$\mathrm{\frac{VWAP - markPrice}{markPrice} * timeElapsed}$。
如果需要,将资金限制在 HourlyFundingRateCap
定义的最大值。
设置下一个资金时间戳。
发出 EventPerpetualMarketFundingUpdate
事件。
对于待结算的每个市场:
使用零结算费用和当前标记价格结算市场。
运行社会化损失。这将计算所有市场中缺少的资金总额,然后按比例减少每个盈利头寸的支付。例如,一个市场缺少100 USDT的资金,且有10个相同数量的盈利头寸,则每个头寸的支付将减少10 USDT。
所有头寸都将被强制平仓。
从存储中删除。
对于每个到期的期货市场,从第一个到期的市场开始迭代:
如果市场是过早的,停止迭代。
如果市场已禁用,从存储中删除市场并继续下一个市场。
从预言机获取该市场的累积价格。
如果市场开始成熟,存储该市场的 startingCumulativePrice
。
如果市场已成熟,计算结算价格为 $\mathrm{twap = (currentCumulativePrice - startingCumulativePrice) / twapWindow}$
,并将其添加到待结算市场列表中。
使用定义的结算费用和结算价格结算所有成熟的市场。该过程与之前的结算过程相同(见上文)。注意,社会化损失是一个可选步骤。在常规情况下,市场不需要任何社会化损失。
从存储中删除所有已结算的市场。
检查当前交易奖励活动是否已结束。
如果活动已结束,向符合条件的交易员分发奖励代币。
计算每种奖励币种的可用奖励,公式为 min(campaignRewardTokens, communityPoolRewardTokens)
。
根据交易份额计算每个交易员的奖励,公式为 accountPoints * totalReward / totalTradingRewards
。
从社区池中向交易员发送奖励代币。
重置所有账户的交易奖励积分和总积分。
删除当前活动的结束时间戳。
如果启动新活动,设置下一个当前活动结束时间戳为 CurrentCampaignStartTimestamp + CampaignDurationSeconds
。
如果没有进行中的活动,也没有启动新活动,从存储中删除活动信息、市场资格和市场倍数。
如果最旧的桶的结束时间戳早于 block.timestamp - bucketCount * bucketDuration
:
修剪最旧的桶。
遍历所有的 bucketStartTimestamp + account → FeesPaidAmount
:
从每个账户的 totalPastBucketFeesPaidAmount
中减去 FeesPaidAmount。
删除账户的 account → {tier, TTL timestamp}
。注意,技术上这对于正确性并不是必须的,因为我们会在 Endblocker 中检查 TTL 时间戳,但这是一种状态修剪策略。
更新 CurrBucketStartTimestamp ← CurrBucketStartTimestamp + BucketDuration
。
在每个 EndBlock
处,已到期的赎回请求将被自动处理,具体流程如下:赎回对应的保险基金份额代币将被销毁。按比例计算的报价货币金额将从保险模块提取至承保人的账户余额。
MsgCreateInsuranceFund
定义了用于为衍生品市场创建保险基金的消息。
字段描述
Sender 字段描述了保险基金的创建者。
Ticker、QuoteDenom、OracleBase、OracleQuote、OracleType、Expiry 字段描述了与保险基金对应的衍生品市场信息。
InitialDeposit 指定了用于承保保险基金的初始存款金额。
免责声明:在创建保险基金时,基金本身将保留一小部分份额(1%)(协议拥有的流动性)。建议首次认购金额为 1 美元。
此功能的背后动机是避免在承保基金时出现潜在的舍入问题。例如,如果没有协议拥有的流动性,原始基金创建者如果取出大部分份额,仅留下少量份额,份额代币的价值可能会与原始价值大幅偏离。下一个承保者将不得不提供更大金额的存款,尽管他们获得的份额数量相同。
MsgUnderwrite
定义了用于承保保险基金的消息。
字段描述
Sender 字段描述了保险基金的承保人。
MarketId 字段描述了与保险基金关联的衍生品市场 ID。
Deposit 字段描述了要添加到保险基金的存款金额。
MsgRequestRedemption
定义了用于向保险基金请求赎回的消息。
字段描述
Sender 字段描述了保险基金的赎回请求者。
MarketId 字段描述了与保险基金关联的衍生品市场 ID。
Amount 字段描述了要赎回的份额代币数量。
Params 是一个模块级的配置结构,用于存储系统参数并定义保险模块的整体功能。
Params: Paramsspace("insurance") -> legacy_amino(params)
InsuranceFund
定义了按市场划分的所有保险基金信息。
RedemptionSchedule
定义了用户的赎回计划——赎回不会立即执行,而是每个市场都有一个指定的赎回通知期持续时间(redemption_notice_period_duration
)。
此外,我们引入了 next_share_denom_id
和 next_redemption_schedule_id
来管理来自不同用户的保险基金份额代币的 denom 和赎回计划。
待处理赎回对象用于存储有关赎回请求的所有信息,并在期限过后自动进行赎回。
交易所的 在每个区块结束时运行,按照我们定义的顺序,在治理和质押模块之后,佩吉、拍卖和保险模块之前。特别需要注意的是,治理模块的 EndBlocker 必须在交易所模块的 EndBlocker 之前运行。
FBA(频繁批量拍卖)的目标是防止任何前置交易()。这一目标通过在给定区块内计算一个单一的清算价格来实现,所有匹配的订单都会按照该价格进行清算。
市场订单首先根据区块开始时的挂单簿进行填充。当挂单按其各自的订单价格被执行时,市场订单将以统一的清算价格执行,采用与限价单相同的机制。举个例子,关于市场订单在 FBA 模式下的匹配,详情见 。
关于限价单在 FBA 模式下的匹配示例,请查看 。
您可以在找到相关代码示例。请查看主分支以获取最新的链上代码。
我们应用范式来利用并发进行高效的数据处理。
交易所的 在每个区块的开始运行,作为我们定义顺序中的最后一个模块。
更多详细信息可参考部分的待处理赎回的自动提现。
本节描述了交易消息的处理流程及其对状态的相应更新。每条消息创建或修改的状态对象均在“”部分定义。
Genesis 状态定义了模块的初始状态,用于设置模块。
Params 是一个模块级别的配置,用于存储系统参数并定义 OCR 模块的整体功能。该模块可以通过治理使用参数更新提案进行修改,治理模块原生支持此功能。
OCR 模块参数存储的结构体。
FeedConfig 用于管理 Feed 的配置,每个 Feed 存在一个对应的 FeedConfig。
FeedConfigInfo 用于存储每次传输事件中需要更频繁更新的信息。
Transmission 是用于在存储中保存传输信息的单元。
Report 是用于在存储中保存报告信息的单元。
ReportToSign 保存需要由观察者签名的信息。
OnchainConfig 保存需要在链上管理的 Feed 配置的相关配置。
ContractConfig 保存与合同相关的配置,用于存储 OCR。
FeedProperties 是用于通过 ID 存储 Feed 属性的单元。
PendingPayeeship 是一个记录,当某人将支付权委托给另一个地址时存储此记录。当提议的支付方接受后,该记录将被移除。
ocr模块包含以下参数:
LinkDenom
string
link
PayoutBlockInterval
uint64
100
ModuleAdmin
string
{address}
ocr模块会发出以下事件:
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}
在本节中,我们描述了 OCR 消息的处理过程及其对状态的相应更新。
MsgCreateFeed 是用于创建 Feed 配置的消息,它是一个受限制的消息,仅能由模块管理员执行。
步骤:
确保发送者是模块管理员。
确保 msg.Config.OnchainConfig.LinkDenom 与模块参数中的 LinkDenom 匹配。
从 ctx.ChainID 设置 OnchainConfig.ChainId。
确保相同 FeedId 的 FeedConfig 不存在。
将最新的 EpochAndRound 设置为 (0, 0)。
设置给定 FeedId 的 Feed 配置。
将 Feed 传输计数和观测计数设置为 1。
MsgCreateFeed 是用于更新 Feed 配置的消息,它是一个受限制的消息,仅能由 Feed 管理员或 Feed 计费管理员执行。
步骤:
根据 FeedId 获取之前的 Feed 配置,并确保其存在。
确保发送者是 Feed 管理员或 Feed 计费管理员。
确保计费管理员未更改签名者、传输者和 Feed 管理员。
处理之前的 Feed 配置的奖励支付。
删除之前的 Feed 传输和观测计数。
将最新的 EpochAndRound 设置为 (0, 0)。
如果设置了,则更新签名者、传输者、LinkPerObservation、LinkPerTransmission、LinkDenom、FeedAdmin 和 BillingAdmin。
MsgTransmit 是用于传输特定 Feed 的报告的消息。在广播该消息时,必须有足够的观察者签名才能被接受。
步骤:
获取 feedId 的 epoch 和 round。
通过检查 msg.Epoch 和 msg.Round 确保报告不是过期的。
从 feedId 获取 Feed 配置和配置信息。
检查 msg.ConfigDigest 是否等于 Feed 配置信息的最新配置摘要。
检查传输者是否是 FeedConfig 中配置的有效传输者。
保存传输者的报告。
触发传输事件。
验证签名及签名数量。
增加 Feed 的观测和传输计数。
MsgFundFeedRewardPool 是一条消息,用于向 Feed 奖励池中添加资金,这些资金将分配给传输者和观察者。
步骤:
获取 feedId 的之前奖励池金额。
如果之前的金额为空,则将池金额初始化为零。
确保之前的金额 denom 与存款 denom 相同(如果存在)。
从账户向模块账户(OCR 模块)发送币。
使用金额字段增加更新奖励池金额。
如果设置了钩子,则调用 AfterFundFeedRewardPool 钩子。
MsgFundFeedRewardPool 是一条消息,用于从 Feed 奖励池中提取资金,仅限 Feed 管理员或计费管理员执行。
步骤:
获取 feedId 的 Feed 配置。
确保 msg.Sender 是 Feed 管理员或计费管理员。
为 Feed 处理奖励。
从模块账户中提取指定金额 msg.Amount。
MsgSetPayees 是一条消息,用于为传输者设置支付方,仅限 Feed 管理员执行。一旦设置,只有支付方才能更改。
步骤:
获取 feedId 的 Feed 配置,并确保 Feed 配置存在。
确保 msg.Sender 是 Feed 管理员。
遍历 msg.Transmitters,
确保传输者已经设置了支付方。
为传输者设置支付方。
MsgTransferPayeeship 是一条消息,用于转移特定传输者的 Feed 支付权。在执行后,将创建一个待处理的支付权对象。
步骤:
获取 feedId 的 Feed 配置,并确保 Feed 配置存在。
确保 msg.Sender 是当前的支付方。
检查之前的待处理支付权转移记录,确保之前的支付权转移不会冲突。
设置支付权转移记录。
MsgTransferPayeeship 是一条消息,用于接受特定传输者的 Feed 支付权。
步骤:
获取 feedId 的 Feed 配置,并确保 Feed 配置存在。
获取 msg.Transmitter 和 feedId 的待处理支付权转移记录。
重置 feedId 和传输者的支付方。
OCR 模块用于由验证成员将 Chainlink 的 OCR(Off-Chain Report)信息存储到链上。
链下报告由 N 个节点(Oracles)组成,这些节点从外部来源收集数据。报告在 Oracles 之间以 P2P 方式交换,以获取签名批准。OCR 模块在链上识别一部分节点(Transmitters),这些节点必须将报告提交至模块,第一个成功提交至链上的 Transmitter 可额外获得奖励以覆盖 Gas 费用,其他 Transmitters 不会获得额外奖励。所有参与该轮的 Oracles 都会获得报酬。OCR 模块存储报告中的中位值。
协议定期将 Oracle 报告发送到 OCR 模块。报告协议由三个组件组成:Pacemaker、报告生成和传输。
Pacemaker Pacemaker 驱动报告生成过程,该过程以纪元(Epoch)为单位进行。每个纪元都有一个指定的领导者,Pacemaker 会将启动报告生成协议的任务交给该领导者。如果领导者未能及时生成有效报告,Pacemaker 会中止当前的报告生成并启动一个新的纪元。
报告生成 在给定的纪元中,报告生成协议进入多个回合,在这些回合中收集观测数据,并在满足条件(如心跳和偏差)时生成已签名的 Oracle 报告。回合由领导节点控制,领导节点控制回合的频率,收集观测数据并生成报告。
传输 传输协议随后将生成的报告传输到 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 价格。
授权的Band中继者可以通过MsgRelayBandRates
消息中继多个符号的价格源数据。注册的处理程序遍历MsgRelayBandRates
中存在的所有符号,并为每个符号创建/更新BandPriceState
。
如果中继者不是授权的Band中继者,此消息预期会失败。
Coinbase提供者的中继者可以使用MsgRelayCoinbaseMessages
消息发送价格数据。
每个Coinbase消息通过Coinbase oracle地址0xfCEAdAFab14d46e20144F48824d0C09B1a03F2BC
提供的签名进行身份验证,从而允许任何人提交MsgRelayCoinbaseMessages
。
如果签名验证失败,或者提交的时间戳比最后一次提交的Coinbase价格更旧,则此消息预期会失败。
价格源提供者的中继者可以使用MsgRelayPriceFeedPrice
消息发送价格源数据。
如果中继者(发送者)不是给定基础报价对的授权价格源中继者,或者价格大于10000000,则此消息预期会失败。
任何人都可以广播此消息,无需特定授权。处理程序会检查BandIbcEnabled
标志是否为true
,如果是,则继续发送请求。
MsgRelayPythPrices
是一条消息,用于将Pyth合约的价格中继到oracle模块。
如果中继者(发送者)与oracle模块参数中定义的Pyth合约地址不匹配,则此消息预期会失败。
MsgRelayStorkPrices
是一条消息,用于将Stork合约的价格中继到oracle模块。
如果发生以下情况,则此消息预期会失败:
中继者(发送者)不是授权的oracle发布者,或者assetId
在提供的资产对中不是唯一的
对SignedPriceOfAssetPair
的ECDSA签名验证失败
时间戳之间的差值超过MaxStorkTimestampIntervalNano
(500毫秒)
特定提供者的中继者可以使用MsgRelayProviderPrices
消息发送价格源数据。
如果中继者(发送者)不是给定基础报价对的授权价格源中继者,或者价格大于10000000,则此消息预期会失败。
Chainlink 有多个,包括:
oracle模块当前提供了三个不同的导出keeper接口,这些接口可以传递给需要读取价格源的其他模块。模块应使用最小权限的接口,以提供其所需的功能。
oracle模块的ViewKeeper提供了获取任何支持的oracle类型和oracle对的价格数据以及累计价格数据的功能。
注意,GetPrice
对于Coinbase oracle返回的是5分钟的时间加权平均价格(TWAP)。
BandKeeper 提供了创建/修改/读取/删除BandPricefeed和BandRelayer的功能。
BandIBCKeeper 提供了创建/修改/读取/删除BandIBCOracleRequest、BandIBCPriceState、BandIBCLatestClientID和BandIBCCallDataRecord的功能。
CoinbaseKeeper
提供了创建、修改和读取CoinbasePricefeed数据的功能。
GetCoinbasePrice
返回基于Coinbase提供的CoinbasePriceState.Timestamp
值的5分钟时间加权平均价格(TWAP)。
PriceFeederKeeper 提供了创建/修改/读取/删除PriceFeedPrice和PriceFeedRelayer的功能。
StorkKeeper 提供了创建/修改/读取StorkPricefeed和StorkPublishers数据的功能。
GetStorkPrice
返回StorkPriceState
的价格(value
)。
扩展对其他oracle提供者的支持,包括Chainlink、Razor、DIA、API3、UMA、Pyth以及通过IBC获取的Band oracle数据。 在上述提供者中,正在开发通过IBC集成Band和Pyth oracle数据,以及通过OCR(链下报告)机制集成Chainlink oracle数据。
本规范指定了oracle模块,该模块主要由交易模块使用,以获取外部价格数据。
新的价格源提供者必须首先通过治理提案获得oracle权限,该提案授予一组中继者权限。唯一的例外是Coinbase价格oracle,因为任何人都可以发送Coinbase价格更新,因为这些更新已经由Coinbase oracle私钥独占签名。 示例授权提案:GrantBandOraclePrivilegeProposal、GrantPriceFeederPrivilegeProposal
一旦治理提案获得批准,指定的中继者可以通过发送特定于其oracle类型的中继消息来发布oracle数据。
示例中继消息:MsgRelayBandRates、MsgRelayPriceFeedPrice、MsgRelayCoinbaseMessages
等
收到中继消息后,oracle模块会检查中继者账户是否具有授权权限,并将最新的价格数据持久化到状态中。
其他Cosmos-SDK模块可以通过查询oracle模块来获取特定提供者的最新价格数据。
注意:如果出现任何不一致,价格源权限可以通过治理撤销。
示例撤销提案:RevokeBandOraclePrivilegeProposal、RevokePriceFeederPrivilegeProposal
等
Cosmos SDK区块链可以通过IBC进行相互交互,而Injective支持通过IBC从BandChain获取价格源数据。
为了通过IBC与BandChain的oracle通信,Injective Chain必须首先使用中继者初始化与BandChain上oracle模块的通信通道。
一旦建立连接,将生成一对通道标识符——一个用于Injective Chain,一个用于Band。通道标识符用于Injective Chain将外发的oracle请求数据包路由到Band。同样,Band的oracle模块在发送oracle响应时也使用该通道标识符。
在设置好通信通道后,为了启用Band IBC集成,应该通过治理提案EnableBandIBCProposal。
然后,应该通过AuthorizeBandOracleRequestProposal和UpdateBandOracleRequestProposal确定要通过IBC获取的价格列表。
一旦启用BandIBC,链会定期发送价格请求IBC数据包(OracleRequestPacketData)到bandchain,而bandchain会通过IBC数据包(OracleResponsePacketData)返回价格。 Band链会在足够数量的数据提供者确认后提供价格,并且在发送请求后获取价格需要一定时间。为了在配置的间隔之前请求价格,任何用户都可以广播MsgRequestBandIBCRates消息,这会立即执行。
可以通过GrantBandOraclePrivilegeProposal
将oracle提供者权限授予您的账户。在治理提案通过后,您将能够使用您的提供者中继价格源数据。
您可以根据以下示例提交您的提案:
为了成功通过测试网的提案,YOUR_DEPOSIT
应略低于 min_deposit
值(例如,40000000000000000000inj)。之后,您应该联系Injective开发团队。开发团队将补充您的存款至 min_deposit
并为您的提案投票。
可以通过RevokeProviderPrivilegeProposal
撤销您账户的oracle提供者权限。
可以通过GrantBandOraclePrivilegeProposal
将Band Oracle权限授予Band提供者的中继者账户。
可以通过RevokeBandOraclePrivilegeProposal
从Band提供者的中继者账户撤销Band Oracle权限。
可以通过GrantPriceFeederPrivilegeProposal
将给定基础报价对的价格源权限授予中继者。
可以通过RevokePriceFeederPrivilegeProposal
从中继者账户撤销价格源权限。
该提案用于将一个Band oracle请求添加到列表中。当提案被接受时,Injective链将从bandchain获取更多的价格信息。
该提案用于删除或更新请求。当DeleteRequestId
不为零时,它将删除具有该ID的请求并完成其执行。当DeleteRequestId
为零时,它将使用UpdateOracleRequest.RequestId
更新该ID的请求为UpdateOracleRequest
。
该提案用于启用Band链和Injective链之间的IBC连接。当提案被批准时,它将更新BandIBCParams
为提案中配置的新值。
可以通过GrantStorkPublisherPrivilegeProposal
从发布者授予Stork发布者权限。
可以通过RevokeStorkPublisherPrivilegeProposal
从发布者撤销Stork发布者权限。
oracle模块参数:
PriceState是一个通用类型,用于管理所有oracle类型的累计价格、最新价格及其时间戳。
其中:
Price 表示标准化的小数价格
CumulativePrice 表示自oracle价格源创建以来的累计价格
Timestamp 表示价格状态被中继的区块时间
公式:
$\mathrm{TWAP = \frac{CumulativePrice_2 - CumulativePrice_1}{Timestamp_2 - Timestamp_1}}$
给定符号的Band价格数据表示和存储如下:
BandPriceState
: 0x01 | []byte(symbol) -> ProtocolBuffer(BandPriceState)
请注意,Rate 是从Band链获取的符号的原始USD汇率,经过1e9的缩放(例如,1.42的价格为1420000000),而PriceState 存储的是标准化的小数价格(例如,1.42)。
Band中继者通过其地址存储如下:
BandRelayer
: 0x02 | RelayerAddr -> []byte{}
本节描述了通过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)
当发送价格请求数据包到bandchain时,BandIBCCallDataRecord 存储如下
CalldataRecord
: 0x33 | []byte(ClientId) -> ProtocolBuffer(CalldataRecord)
当治理配置oracle请求发送时,BandIBCOracleRequest 存储如下:
BandOracleRequest
: 0x34 | []byte(RequestId) -> ProtocolBuffer(BandOracleRequest)
BandIBCParams 存储如下,并由治理配置:
BandIBCParams: 0x35 -> ProtocolBuffer(BandIBCParams)
BandIBCParams 包含与band链的IBC连接信息。
注意:
BandIbcEnabled
描述了band IBC连接的状态
IbcSourceChannel
、
IbcVersion
、
IbcPortId
是IBC连接所需的常见参数
IbcRequestInterval
描述了在Injective链的BeginBlock时自动触发的价格获取请求间隔
给定符号("key")的Coinbase价格数据表示和存储如下:
CoinbasePriceState
: 0x21 | []byte(key) -> CoinbasePriceState
请注意,Value 是从Coinbase获取的原始USD价格数据,经过1e6的缩放(例如,1.42的价格为1420000),而PriceState 存储的是标准化的小数价格(例如,1.42)。
给定基础报价对的价格源数据表示和存储如下:
PriceFeedInfo
: 0x11 + Keccak256Hash(base + quote) -> PriceFeedInfo
PriceFeedPriceState: 0x12 + Keccak256Hash(base + quote) -> PriceFeedPriceState
PriceFeedRelayer: 0x13 + Keccak256Hash(base + quote) + relayerAddr -> relayerAddr
提供者价格源数据表示和存储如下:
ProviderInfo: 0x61 + provider + @@@ -> ProviderInfo
ProviderIndex: 0x62 + relayerAddress -> provider
ProviderPrices: 0x63 + provider + @@@ + symbol -> ProviderPriceState
Pyth价格数据表示和存储如下:
PythPriceState: 0x71 + priceID -> PythPriceState
Stork价格数据表示和存储如下:
StorkPriceState: 0x81 + symbol -> PythPriceState
Stork发布者数据表示和存储如下:
Publisher: 0x82 + stork_publisher -> publisher
BandIBCParams
的详细信息可以在中查看。
请注意,CumulativePrice 值遵循设定的约定,并用于允许模块计算两个任意区块时间间隔(t1,t2)之间的时间加权平均价格(TWAP)。
关于Coinbase价格oracle的更多详细信息可以在以及这篇解释性中找到。
本文档旨在从技术角度提供关于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。提交的金额会立即从用户的余额中扣除。该提取请求作为types.OutgoingTransferTx
被添加到外发交易池中,直到它被包含在一个批次中。
此消息允许用户取消尚未批量处理的特定提取请求。用户余额将被退还(金额 + 桥接费用)。
此调用允许任何人提交证据,证明某个验证者签署了一个从未存在的验证者集合或批次。主体包含该批次或验证者集合。
这些消息由Peggy的批次创建子过程发送。
每当某个批次创建者发现汇集的提取请求,且在批量处理后满足其最低批次费用(PEGGO_MIN_BATCH_FEE_USD
)时,会发送此消息。接收到此消息后,Peggy模块会收集所有请求的代币标识符的提取请求,创建一个唯一的代币批次(types.OutgoingTxBatch
),并将其放入外发批次池中。已批量处理的提取请求无法通过MsgCancelSendToEth
取消。
这些消息由Peggy的Oracle子过程发送。
当Peggy合约触发SendToInjectiveEvent
事件时,发送到Injective。每当用户从以太坊向Injective进行单独存款时,就会发生这种情况。
当Peggy合约触发TransactionBatchExecutedEvent
事件时,发送到Injective。这发生在转发者成功调用合约上的submitBatch
函数以完成一批提取请求时。
当Peggy合约触发ValsetUpdatedEvent
事件时,发送到Injective。这发生在转发者成功调用合约上的updateValset
函数以更新以太坊上的验证者集合时。
当Peggy合约触发ERC20DeployedEvent
事件时,发送到Injective。这发生在合约上调用deployERC20
方法以发行一个新的符合桥接条件的代币资产时。
这些消息由Peggy的签名者子过程发送。
当签名者发现一个批次尚未由协调器(验证者)签署时,它会使用其委托的以太坊密钥构造签名,并将确认信息发送到Injective。验证者最终必须为已创建的批次提供确认,否则他们将被处罚。
当签名者发现一个验证者集合更新尚未由协调器(验证者)签署时,它会使用其委托的以太坊密钥构造签名,并将确认信息发送到Injective。验证者最终必须为已创建的验证者集合更新提供确认,否则他们将被处罚。
转发者不会向Injective发送任何消息,而是构造包含Injective数据的以太坊交易,通过submitBatch
和updateValset
方法更新Peggy合约。
这些是直接使用验证者的消息密钥发送的消息。
由管理验证者节点的操作员发送到Injective。在能够启动他们的协调器(peggo)进程之前,他们必须注册一个选定的以太坊地址,以代表其验证者在以太坊上的身份。可选地,可以提供一个额外的Injective地址(Orchestrator字段)以代表该验证者在桥接过程中的身份(peggo)。如果省略,则默认为验证者的自身地址。
此消息设置协调器的委托密钥。
该文档列出了Peggy模块读取/写入其状态的所有数据,以键值对(KV对)的形式。
存储由验证者账户地址索引的委托以太坊地址。
存储由委托以太坊地址索引的验证者账户地址。
当验证者希望将其投票权委托给另一个密钥时,值使用协调器地址作为键进行存储。
这是桥接的验证者集合。由Peggy模块在EndBlocker期间自动创建。 以两种方式存储,第一种是带高度,第二种是没有高度(不安全)。没有高度的方式用于测试以及状态的导出和导入。
最新的验证者集合处罚计数器。用于跟踪需要被处罚的验证者集合以及已经被处罚的验证者集合。
最新验证者集合的计数器。每次新的验证者集合更新时,都会更新该计数器。
用户的提取请求被汇集到Peggy交易池中,准备由批次创建者稍后批量处理。 每个提取请求都通过一个唯一的计数器进行索引,该计数器由Peggy模块在接收到提取请求时设置。
每个由Injective接收到的提取请求的单调递增值。
每个由批次创建者在Injective上创建的批次的单调递增值。
表示最新的处罚区块高度。始终只存储一个值。
表示验证者开始从验证者集合中解除绑定的最新区块高度。用于确定处罚条件。
一个来自计数链的代币标识符将来自一个合约。代币合约和代币标识符以两种方式存储。 首先,代币标识符作为键,值为代币合约。 其次,合约作为键,值为代币合约所代表的代币标识符。
此条目表示最后一次成功中继到以太坊的验证者集合(Valset)。在ValsetUpdatedEvent
的确认在Injective上处理后进行更新。
这是在以太坊上最后一次观察到的事件的计数器。当调用TryAttestation()
时设置此值。此存储中始终只持有一个值。
这是在以太坊上最后一次观察到的事件的区块高度。此存储中始终只会存储一个值。
这是来自特定验证者的最后一个观察到的以太坊事件。每次关联的协调器发送事件声明时都会更新。
确认是多个声明的汇总,随着更多的投票(声明)到来,最终被所有协调器观察到。一旦被观察到,该声明的特定逻辑将被执行。 每个确认都绑定到一个唯一的事件计数器(由Peggy合约生成),并且必须按顺序处理。这是一个正确性问题,如果事务顺序被打乱,则可能会发生交易重放攻击。
一个计算出的哈希值,表明某个验证者集合/代币批次在Injective上确实存在。此检查点也存在于Peggy合约中。在每次新的验证者集合更新和代币批次创建时都会更新。
已知的恶意以太坊地址列表,这些地址被禁止使用桥接。
回顾一下,每个Operator
负责维护两个安全进程:
一个完全同步的Injective链验证节点(injectived
进程)
与两个网络交互的协调器服务(peggo orchestrator
进程)。隐式地,还需要一个完全同步的以太坊节点的RPC端点(请参阅peggo .env
示例)
这两个实体共同完成三件事:
将代币资产从以太坊转移到Injective
将代币资产从Injective转移到以太坊
保持Peggy.sol
合约与Injective上的活动验证者集同步
即使不成为验证者,也可以运行peggo
。当配置为使用与验证者地址无关的地址时,peggo
会自动以“中继者模式”运行。在这种模式下,只有两件事可以发生:
可以在Injective上创建新的代币批次
已确认的验证者集/批次可以被转发到以太坊
所有Operators
都运行他们的peggo
进程,这些进程提交MsgDepositClaim
消息,描述它们观察到的存款。一旦超过66%的所有投票权提交了该特定存款的声明,代表代币就会被铸造并发行到发送者请求的Injective链地址。
这些代表代币的面额前缀为peggy
,后接ERC-20代币的十六进制地址,例如peggy0xdac17f958d2ee523a2206206994597c13d831ec7
。
此端点没有权限限制,因此由验证者和Peggy桥的用户来声明任何给定的ERC-20代币作为某个资产的表示。
peggo
协调者会观察到这个事件,并决定Cosmos资产是否已被准确表示(正确的小数位,正确的名称,没有预先存在的表示)。如果是这样,ERC-20合约地址将被采用并存储为该Cosmos资产在以太坊上的最终表示。
peggo
协调器进程由四个子进程组成,这些子进程在精确的时间间隔内并发运行(循环)。它们分别是:
Relayer:将已确认的验证者集更新和代币批次提交到以太坊上的Peggy
合约。
Batch Creator:观察Injective上的(新)提款,并根据其类型和配置的PEGGO_MIN_BATCH_FEE_USD
值决定哪些提款需要打包。
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 中。
TransactionBatchExecutedEvent
- 表示一个代币批次(提取)已成功转发到以太坊的事件
ValsetUpdatedEvent
- 表示验证器集更新已成功转发到以太坊的事件
SendToInjectiveEvent
- 表示已启动新的 Injective 存款的事件
ERC20DeployedEvent
- 表示新 Cosmos 代币已在以太坊上注册的事件
Injective 的 Oracle 实现忽略以太坊上的最后 12 个区块,以确保区块最终性。实际上,这意味着最新的事件会在发生后的 2-3 分钟后被观察到。
Relayer
将验证器集更新(或代币批次)及其确认打包成以太坊交易,并发送到 Peggy 合约。
请注意,这些消息的费用会根据以太坊 gas 价格的剧烈波动而变化,因此一个单独的批次可能需要消耗超过一百万的 gas。我们为 relayer 奖励做出的一个重要设计决策是始终在以太坊链上发放奖励。这有其缺点,尤其是在验证器集更新奖励的情况下可能会出现一些奇怪的行为。
但其优点是显而易见的,因为以太坊上的消息会支付给 msg.sender
,任何现有的机器人都会在以太坊生态系统中接收并尝试提交这些消息。这使得转发市场更加竞争激烈,减少了类似秘密团体的行为。
本文档描述了 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 上被确认。
验证器集 是一系列以太坊地址,附带规范化的权重,用于表示 Peggy 合约中以太坊上的 Injective 验证器集(Valset)。通过以下机制,Peggy 合约与 Injective 链的验证器集保持同步:
在 Injective 上创建新的验证器集:当以下任意情况发生时,Injective 链上会自动创建新的验证器集:
当前验证器集的累计权重与上一个记录的验证器集相比,差值超过 5%
某个验证器开始从验证器集解绑
在 Injective 上确认验证器集:每个 Operator 负责确认在 Injective 上创建的验证器集更新。Signer 进程通过 MsgConfirmValset
发送这些确认,方法是让验证器的委托以太坊密钥对验证器集数据的压缩表示签名。Peggy 模块验证签名的有效性,并将其持久化到状态中。
更新 Peggy 合约中的验证器集:在 2/3+1 多数验证器提交其确认后,Relayer 通过调用 updateValset
向 Peggy 合约提交新的验证器集数据。Peggy 合约随后验证数据、更新验证器集检查点、将验证器集奖励转给发送者,并触发 ValsetUpdatedEvent
。
在 Injective 上确认 ValsetUpdatedEvent
:Oracle 见证以太坊上的 ValsetUpdatedEvent
,并发送 MsgValsetUpdatedClaim
,通知 Peggy 模块验证器集已在以太坊上更新。
在 Injective 上修剪验证器集:一旦 2/3 多数的验证器对某个 ValsetUpdateEvent
提交了声明,所有先前的验证器集将从 Peggy 模块的状态中修剪掉。
ERC-20代币通过以下机制从以太坊转移到Injective:
将ERC-20代币存入Peggy合约:用户通过调用Peggy合约中的sendToInjective
函数发起将ERC-20代币从以太坊转移到Injective的操作,向Peggy合约存入代币并触发SendToInjectiveEvent
事件。存入的代币将保持锁定状态,直到在未来某个不确定的时刻被提取。该事件包含代币的数量和类型,以及接收资金的Injective链上的目标地址。
确认存款:每个Oracle见证SendToInjectiveEvent
并发送MsgDepositClaim
,该消息包含存款信息,发送到Peggy模块。
在Injective上铸造代币:一旦大多数验证者确认存款声明,存款将被处理。
如果资产来源于以太坊,代币将在Injective链上被铸造,并转移到目标接收者的地址。
如果资产来源于Cosmos-SDK,代币将被解锁并转移到目标接收者的地址。
请求从Injective提取:用户可以通过向Peggy模块发送MsgSendToEth
交易来发起从Injective链到以太坊的资产转移。
如果资产是以太坊原生资产,表示的代币将被销毁。
如果资产是Cosmos SDK原生资产,代币将在模块中被锁定。随后,提取请求将被加入到Outgoing Tx Pool
中。
批次创建:批次创建者观察待处理的提取池。批次创建者(或任何外部第三方)通过向Injective链发送MsgRequestBatch
请求为指定的代币创建批次。Peggy模块将匹配的代币类型的提取请求收集到一个批次中,并将其放入Outgoing Batch Pool
。
批次确认:当检测到存在Outgoing Batch
时,签名者使用其以太坊密钥对该批次进行签名,并向Peggy模块提交MsgConfirmBatch
交易。
将批次提交到Peggy合约:一旦大多数验证者确认了该批次,转发者将调用Peggy合约上的submitBatch
函数,提交该批次及其确认信息。Peggy合约验证签名,更新批次检查点,处理批次中的ERC-20提取请求,将批次费用转移到交易发送者,并触发TransactionBatchExecutedEvent
事件。
发送提取声明到Injective:Oracles见证TransactionBatchExecutedEvent
事件,并向Peggy模块发送包含提取信息的MsgWithdrawClaim
。
修剪批次:一旦大多数验证者提交了他们的MsgWithdrawClaim
,该批次将被删除,所有之前的批次也将被取消。取消的批次中的提取请求将被移回Outgoing Tx Pool
。
注意:虽然批次处理显著降低了单次提取的费用,但这也带来了延迟和实现复杂度。如果用户希望快速提取,他们将需要支付更高的费用。然而,这个费用将大致与非批次系统中每次提取所需的费用相同。
这是Peggy消息类型的参考文档。有关代码参考和精确参数,请参阅。
这些是最终用户通过Injective链Peggy模块发送的消息。有关整个存款和提取过程的更详细概述,请参阅。
Params
是一个模块范围的配置结构,存储参数并定义Peggy模块的整体功能。每个参数的详细规范可以在找到。
特定验证者集合的签名者确认。请参阅。
特定代币批次的签名者确认。请参阅。
OutgoingTxBatch
表示同一代币类型的提取请求集合。在每次成功的MsgRequestBatch
后创建。
以两种方式存储,第一种是带高度,第二种是没有高度(不安全)。没有高度的方式用于测试以及状态的导出和导入。目前, 被硬编码为只接受单一代币类型的批次,并且仅以该代币类型支付奖励。
任何来自以太坊的资产,只要实现了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
)内未提供确认的验证器将面临惩罚。有关验证器惩罚的更多信息,请参阅 。
批次处罚:验证者负责确认批次,如果未能完成此操作,将受到处罚。有关批次处罚的更多信息,请参阅。
[]byte{0x4}
Module params
types.Params
Protobuf encoded
[]byte{0x1} + []byte(validatorAddr)
Ethereum address
common.Address
Protobuf encoded
[]byte{0xfb} + []byte(ethAddress)
Validator address
sdk.ValAddress
Protobuf encoded
[]byte{0xe8} + []byte(AccAddress)
Orchestrator address assigned by a validator
[]byte
Protobuf encoded
[]byte{0x2} + nonce (big endian encoded)
Validator set
types.Valset
Protobuf encoded
[]byte{0xf5}
Nonce
uint64
encoded via big endian
[]byte{0xf6}
Nonce
uint64
encoded via big endian
[]byte{0x3} + (nonce + []byte(AccAddress)
Validator Confirmation
types.MsgValsetConfirm
Protobuf encoded
[]byte{0xe1} + []byte(tokenContract) + nonce + []byte(AccAddress)
Validator Batch Confirmation
types.MsgConfirmBatch
Protobuf encoded
[]byte{0x7} + []byte("lastTxPoolId")
nonce of outgoing withdrawal
uint64
Big endian encoded
[]byte{0x6} + []byte("lastTxPoolId")
Last used withdrawal ID
uint64
Big endian encoded
[]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
[]byte{0x7} + []byte("lastBatchId")
Last used batch ID
uint64
Big endian encoded
[]byte{0xf7}
Latest height a batch slashing occurred
uint64
Big endian encoded
[]byte{0xf8}
Latest height at which a Validator started unbonding
uint64
Big endian encoded
[]byte{0xf3} + []byte(denom)
Token contract address
[]byte
stored in byte format
[]byte{0xf4} + []byte(tokenContract)
Token denom
[]byte
stored in byte format
[]byte{0xfa}
Last observed Valset on Ethereum
types.Valset
Protobuf encoded
[]byte{0xf2}
Last observed event nonce
uint64
Big endian encoded
[]byte{0xf9}
Last observed Ethereum Height
uint64
Protobuf encoded
[]byte{0xf1} + []byte(validator address)
Last observed event by some Validator
types.LastClaimEvent
Protobuf encoded
[]byte{0x5} + event nonce (big endian encoded) + []byte(claimHash)
Attestation of occurred events/claims
types.Attestation
Protobuf encoded
[]byte{0x1b}
Last created checkpoint hash on Injective
gethcommon.Hash
store in byte format
[]byte{0x1c} + []byte(ethereum address)
Empty []byte slice
gethcommon.Hash
stored in byte format]
验证者集合是实际拥有质押的密钥集合,这些密钥会因双重签名或其他不当行为而被处罚。我们通常将链的安全性视为验证者集合的安全性。每条链的情况不同,但这是我们的黄金标准。即使是IBC,其安全性也仅限于参与的两个验证者集合中的最小值。
Eth桥中继是与main injective daemon 一起运行的二进制文件,由验证者集合管理。它纯粹作为代码组织的一部分,负责签署以太坊交易,并观察以太坊上的事件并将其引入 Injective 链状态。它使用以太坊密钥签署发送到以太坊的交易,并使用 Injective 链账户密钥签署来自以太坊的事件。我们可以对任何由验证者集合运行的Eth签名者签署的错误消息添加处罚条件,并能够提供与验证者集合相同的安全性,只是由不同的模块检测恶意证据并决定处罚的程度。如果我们能证明由验证者集合的任何Eth签名者签署的交易是非法或恶意的,那么我们可以在 Injective 链侧进行处罚,并可能提供验证者集合的100%安全性。请注意,这还可以访问3周的解锁期,即使他们立即解锁,也允许证据进行处罚。
以下是我们在Peggy中使用的各种削减条件。
该惩罚条件旨在阻止验证者对从未存在于 Injective Chain 上的验证者集合(validator set)和随机数(nonce)进行签名。其通过一种证据机制实现,任何人都可以提交一条消息,其中包含验证者对伪造的验证者集合的签名。此机制旨在实现以下效果:如果一个验证者联盟意图提交伪造的验证者集合,其中一名叛变者可能导致整个联盟的验证者受到惩罚(slashed)。
该惩罚条件最复杂的部分在于确定某个验证者集合是否从未存在于 Injective 上。为了节省空间,我们需要清理旧的验证者集合。我们可以在键值存储(KV store)中维护一个验证者集合哈希到布尔值的映射,并利用该映射来检查某个验证者集合是否曾经存在。这种方法比存储整个验证者集合更高效,但其增长仍然是无界的。或许可以使用其他加密方法来减少该映射的大小。此外,可以考虑从该映射中删除非常旧的条目,但任何删除操作都会降低该惩罚条件的威慑力。
当前实现的版本存储了所有过去事件的哈希映射,这比存储完整的批次或验证者集合更小,并且不需要频繁访问。一个可能但尚未实现的效率优化是,在一定时间后从该列表中移除哈希值。但这需要为每个哈希存储更多的元数据。
目前,中继器(relayer)尚未实现自动证据提交功能。当签名在以太坊上可见时,再进行惩罚已经无法防止桥接劫持或资金盗窃。此外,由于执行此操作需要验证者集合中 66% 的多数同意,控制多数的一方可能会直接拒绝提交证据。该惩罚条件最常见的应用场景是破坏试图控制桥接的验证者联盟,通过增加他们之间的信任难度,使其难以协调实施此类盗窃行为。
盗窃行为将涉及交换可惩罚的以太坊签名,这为联盟中的任何叛变者手动提交此类消息提供了可能性。
目前,该功能在状态中实现为一个不断增长的哈希数组。
当验证者在交易批次(transaction batch)由 Peggy 模块创建后的 SignedBatchesWindow
时间窗口内未能对其进行签名时,将触发该惩罚条件。此机制旨在防止以下两种不良情况的发生:
验证者未运行正确的二进制文件 验证者可能未在其系统中运行正确的二进制文件,导致其无法履行签名职责。
验证者联盟拒绝签名 超过 1/3 的验证者可能解除绑定(unbond)并拒绝签署更新,从而导致任何交易批次都无法获得足够的签名以提交至 Peggy 以太坊合约。
当验证者未能对由 Peggy 模块生成的验证者集合更新(validator set update)进行签名时,将触发该惩罚条件。此机制旨在防止以下两种不良情况的发生:
验证者未运行正确的二进制文件 验证者可能未在其系统中运行正确的二进制文件,导致其无法履行签名职责。
验证者联盟拒绝签名 超过 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 的惩罚风险。
以太坊预言机代码(目前主要集中在 attestation.go
文件中)是 Peggy 模块的关键组成部分。它使 Peggy 模块能够了解以太坊上发生的事件,例如存款(deposits)和已执行的交易批次(executed batches)。PEGGYSLASH-03 旨在惩罚那些提交了从未在以太坊上发生的事件的声明的验证者。
我们判断一个事件是否在以太坊上发生的唯一方式是通过以太坊事件预言机本身。因此,为了实现此惩罚条件,我们会惩罚那些在与 >2/3 验证者观察到的事件具有相同随机数(nonce)的情况下提交了不同事件声明的验证者。
尽管初衷良好,但该惩罚条件可能不适用于大多数 Peggy 应用场景。这是因为它将安装 Peggy 的 Injective Chain 的正常运行与以太坊链的正常运行绑定在一起。如果以太坊链发生严重分叉,不同验证者可能会在同一事件随机数下看到不同的事件,并因此受到惩罚,尽管他们并无过错。大规模的不公平惩罚将对 Injective Chain 的社会结构造成严重破坏。
该惩罚条件的实际效用在于,如果 >2/3 的验证者形成一个联盟,在某个随机数下提交虚假事件声明,联盟中的部分成员可能会叛变并在同一随机数下提交真实事件声明。如果有足够多的联盟成员叛变,使得真实事件被观察到,那么剩余的联盟成员将因此惩罚条件受到惩罚。然而,这通常需要超过一半的联盟成员叛变。
如果没有足够多的联盟成员叛变,则两个事件都不会被观察到,以太坊预言机将停止运行。这种情况比 PEGGYSLASH-04 实际被触发的场景更为可能。
此外,在联盟成功的情况下,PEGGYSLASH-04 可能会针对诚实验证者触发。这可能使正在形成的联盟更容易威胁那些不愿加入的验证者。
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 可能因以太坊生成的因素而完全停止的可能性。
peggy模块使Injective链能够支持一个可信的、链上双向ERC-20代币桥接到以太坊。在该系统中,以太坊上的ERC-20代币持有者可以将他们的ERC-20代币转换为Injective链上的Cosmos原生币,反之亦然。 这个去中心化的桥接由Injective链的验证者进行安全保障和操作。
Peggy 模块(Injective Chain)
Oracle
(监测Peggy
合约事件并向Peggy
模块发送声明)
EthSigner
(对Valset
和批量确认进行签名并发送至Peggy
模块)
Batch Requester
(向Peggy
模块发送批量代币提取请求)
Valset Relayer
(向Peggy
合约提交验证人集合更新)
Batch Relayer
(向Peggy
合约提交批量代币提取)
此外,Injective Chain
验证人除了运行injectived
节点以签署区块外,还必须运行peggo orchestrator
,以在Ethereum
上的Peggy
智能合约与Injective Chain
上的Peggy
模块之间中继数据。
在Ethereum
上维护Injective Chain
验证人集合的最新检查点
从Ethereum
向Injective Chain
转移ERC-20
代币
从Injective Chain
向Ethereum
转移锚定代币
peggy
模块会触发以下事件:
智能合约(Ethereum)
(链下中继器,又称orchestrator
)
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}
string
reason
{reason_for_slashing}
int64
power
{validator_power}
string
consensus_address
{consensus_addr}
string
operator_address
{operator_addr}
string
moniker
{validator_moniker}
string
validator_address
{validator_addr}
string
orchestrator_address
{orchestrator_addr}
string
operator_eth_address
{eth_addr}
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}
withdrawal_cancelled
bridge_contract
{bridge_contract}
withdrawal_cancelled
bridge_chain_id
{bridge_chain_id}
string
denom
{token_denom}
string
orchestrator_address
{orch_addr}
uint64
batch_nonce
{batch_nonce}
uint64
batch_timeout
{block_height}
[]uint64
batch_tx_ids
{ids}
string
bridge_contract
{bridge_contract}
uint64
bridge_chain_id
{bridge_chain_id}
uint64
batch_id
{id}
uint64
nonce
{nonce}
uint64
valset_nonce
{nonce}
string
orchestrator_address
{prch_addr}
uint64
batch_nonce
{nonce}
string
orchestrator_address
{orch_addr}
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}
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}
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}
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}
在每个区块结束时,将对模块的状态执行以下操作:
当验证者未能对通过 SignedValsetsWindow
的验证者集合更新(valset update)进行签名时,将受到惩罚。换句话说,如果验证者未能在预配置的时间内为验证者集合更新提供确认,他们将因其抵押的 SlashFractionValset
部分被罚没,并立即被监禁(jailed)。
当验证者未能对通过 SignedBatchesWindow
的交易批次(batch)进行签名时,将受到惩罚。换句话说,如果验证者未能在预配置的时间内为交易批次提供确认,他们将因其抵押的 SlashFractionBatch
部分被罚没,并立即被监禁(jailed)。
任何仍在 Outgoing Batch 池中且其 BatchTimeout
(指定的以太坊区块高度,交易批次应在此高度之前执行)已超时的交易批次将被从池中移除,相关的提款请求将重新插入到 Outgoing Tx 池中。
在以下情况下,将自动创建新的验证者集合更新(Validator Set update):
验证者权力变化超过 5%:当最新验证者集合与当前验证者集合之间的权力差异(power diff)超过 5% 时。
验证者开始解绑:当某个验证者开始解绑(unbonding)时。
新的验证者集合最终将被中继(relayed)至以太坊上的 Peggy 合约。
已通过 SignedValsetsWindow
的先前观察到的验证者集合(valsets)将从状态中移除。
处理当前正在投票的所有证明(attestations)(针对特定事件的声明集合)。每个证明依次处理,以确保每个 Peggy 合约事件都被处理。在处理完每个证明后,模块的 lastObservedEventNonce
和 lastObservedEthereumBlockHeight
将被更新。
根据证明中的声明类型,执行以下操作:
MsgDepositClaim:为接收者地址铸造/解锁存入的代币。
MsgWithdrawClaim:从 Outgoing 池中移除相应的交易批次,并取消任何先前的批次。
MsgValsetUpdatedClaim:更新模块的 LastObservedValset
。
MsgERC20DeployedClaim:验证新的代币元数据并将其注册到模块的状态中(denom <-> token_contract)。
之前处理过的证明(高度早于 lastObservedEthereumBlockHeight
)会从模块状态中移除。