From cc29b8dd70db1bbc969c6f48350fdbaa4fb1bbaf Mon Sep 17 00:00:00 2001 From: sotnikov-s Date: Sun, 27 Oct 2024 17:12:34 +0400 Subject: [PATCH] add how to register ICQ contract --- Cargo.lock | 18 + Cargo.toml | 22 +- .../howto/register_kv_icq/.cargo/config | 6 + .../howto/register_kv_icq/Cargo.toml | 34 ++ .../howto/register_kv_icq/Makefile | 11 + .../howto/register_kv_icq/README.md | 3 + .../neutron_interchain_queries-schema.rs | 30 + .../register_kv_icq/schema/execute_msg.json | 562 ++++++++++++++++++ .../schema/instantiate_msg.json | 6 + .../register_kv_icq/schema/query_msg.json | 293 +++++++++ .../howto/register_kv_icq/src/contract.rs | 153 +++++ .../howto/register_kv_icq/src/lib.rs | 19 + .../howto/register_kv_icq/src/msg.rs | 25 + .../howto/register_kv_icq/src/state.rs | 7 + 14 files changed, 1188 insertions(+), 1 deletion(-) create mode 100644 contracts/docs/interchainqueries/howto/register_kv_icq/.cargo/config create mode 100644 contracts/docs/interchainqueries/howto/register_kv_icq/Cargo.toml create mode 100644 contracts/docs/interchainqueries/howto/register_kv_icq/Makefile create mode 100644 contracts/docs/interchainqueries/howto/register_kv_icq/README.md create mode 100644 contracts/docs/interchainqueries/howto/register_kv_icq/examples/neutron_interchain_queries-schema.rs create mode 100644 contracts/docs/interchainqueries/howto/register_kv_icq/schema/execute_msg.json create mode 100644 contracts/docs/interchainqueries/howto/register_kv_icq/schema/instantiate_msg.json create mode 100644 contracts/docs/interchainqueries/howto/register_kv_icq/schema/query_msg.json create mode 100644 contracts/docs/interchainqueries/howto/register_kv_icq/src/contract.rs create mode 100644 contracts/docs/interchainqueries/howto/register_kv_icq/src/lib.rs create mode 100644 contracts/docs/interchainqueries/howto/register_kv_icq/src/msg.rs create mode 100644 contracts/docs/interchainqueries/howto/register_kv_icq/src/state.rs diff --git a/Cargo.lock b/Cargo.lock index 416ce3c..5bf2ef3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -726,6 +726,24 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "howto_register_kv_icq" +version = "0.1.0" +dependencies = [ + "base64 0.21.7", + "cosmos-sdk-proto 0.20.0", + "cosmwasm-schema 2.1.4", + "cosmwasm-std 2.1.4", + "cw-storage-plus 2.0.0", + "cw2 2.0.0", + "neutron-sdk", + "neutron-std", + "prost 0.12.6", + "schemars", + "serde", + "serde-json-wasm 1.0.1", +] + [[package]] name = "ibc_transfer" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index b9be99c..d107e67 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,24 @@ [workspace] -members = ["contracts/*"] +members = [ + "contracts/balance-tracker", + "contracts/before-send-hook", + "contracts/client_updater", + "contracts/dex", + "contracts/dex_stargate", + "contracts/echo", + "contracts/ibc_transfer", + "contracts/marketmap", + "contracts/msg_receiver", + "contracts/neutron_interchain_queries", + "contracts/neutron_interchain_txs", + "contracts/neutron_validator_test", + "contracts/oracle", + "contracts/price-feed", + "contracts/reflect", + "contracts/stargate_querier", + "contracts/tokenfactory", + "contracts/docs/interchainqueries/howto/*", +] [profile.release] opt-level = 3 @@ -14,6 +33,7 @@ overflow-checks = true [workspace.dependencies] neutron-sdk = { package = "neutron-sdk", git = "https://github.com/neutron-org/neutron-sdk", branch = "main" } +neutron-std = { git = "https://github.com/neutron-org/neutron-std", branch = "feat/clean-bindings" } prost = "0.12.4" prost-types = "0.12.4" cosmos-sdk-proto = { version = "0.20.0", default-features = false } diff --git a/contracts/docs/interchainqueries/howto/register_kv_icq/.cargo/config b/contracts/docs/interchainqueries/howto/register_kv_icq/.cargo/config new file mode 100644 index 0000000..257ac0c --- /dev/null +++ b/contracts/docs/interchainqueries/howto/register_kv_icq/.cargo/config @@ -0,0 +1,6 @@ +[alias] +wasm = "build --release --target wasm32-unknown-unknown" +wasm-debug = "build --target wasm32-unknown-unknown" +unit-test = "test --lib" +integration-test = "test --test integration" +schema = "run --example contracts/docs/interchainqueries/howto/register_kv_icq-schema" diff --git a/contracts/docs/interchainqueries/howto/register_kv_icq/Cargo.toml b/contracts/docs/interchainqueries/howto/register_kv_icq/Cargo.toml new file mode 100644 index 0000000..f639ebd --- /dev/null +++ b/contracts/docs/interchainqueries/howto/register_kv_icq/Cargo.toml @@ -0,0 +1,34 @@ +[package] +name = "howto_register_kv_icq" +version = "0.1.0" +edition = "2021" + +exclude = [ + # Those files are rust-optimizer artifacts. You might want to commit them for convenience but they should not be part of the source code publication. + "contract.wasm", + "hash.txt", +] + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[lib] +crate-type = ["cdylib", "rlib"] + +[features] +library = [] + +[dependencies] +cosmwasm-std = { workspace = true, features = ["stargate", "staking"] } +cw2 = { workspace = true } +schemars = { workspace = true } +serde = { workspace = true } +neutron-sdk = { workspace = true } +neutron-std = { workspace = true } +cosmos-sdk-proto = { workspace = true } +cw-storage-plus = { workspace = true } +prost = { workspace = true } +serde-json-wasm = { workspace = true } + +[dev-dependencies] +base64 = { workspace = true } +cosmwasm-schema = { workspace = true } diff --git a/contracts/docs/interchainqueries/howto/register_kv_icq/Makefile b/contracts/docs/interchainqueries/howto/register_kv_icq/Makefile new file mode 100644 index 0000000..0ddb709 --- /dev/null +++ b/contracts/docs/interchainqueries/howto/register_kv_icq/Makefile @@ -0,0 +1,11 @@ +# Validators Registry + +CURRENT_DIR = $(shell pwd) +CURRENT_DIR_RELATIVE = $(notdir $(shell pwd)) + +clippy: + rustup component add clippy || true + cargo clippy --all-targets --all-features --workspace -- -D warnings + +test: clippy + cargo unit-test diff --git a/contracts/docs/interchainqueries/howto/register_kv_icq/README.md b/contracts/docs/interchainqueries/howto/register_kv_icq/README.md new file mode 100644 index 0000000..1024743 --- /dev/null +++ b/contracts/docs/interchainqueries/howto/register_kv_icq/README.md @@ -0,0 +1,3 @@ +# HowTo register a KV Interchain Query contract + +A simple contract that implements the flow described in the [How to register an Interchain Query using neutron-sdk](https://docs.neutron.org/neutron/modules/interchain-queries/how-to#how-to-register-an-interchain-query-using-neutron-sdk) documentation section. diff --git a/contracts/docs/interchainqueries/howto/register_kv_icq/examples/neutron_interchain_queries-schema.rs b/contracts/docs/interchainqueries/howto/register_kv_icq/examples/neutron_interchain_queries-schema.rs new file mode 100644 index 0000000..7225f36 --- /dev/null +++ b/contracts/docs/interchainqueries/howto/register_kv_icq/examples/neutron_interchain_queries-schema.rs @@ -0,0 +1,30 @@ +// Copyright 2022 Neutron +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::env::current_dir; +use std::fs::create_dir_all; + +use cosmwasm_schema::{export_schema, remove_schemas, schema_for}; +use neutron_interchain_queries::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; + +fn main() { + let mut out_dir = current_dir().unwrap(); + out_dir.push("schema"); + create_dir_all(&out_dir).unwrap(); + remove_schemas(&out_dir).unwrap(); + + export_schema(&schema_for!(InstantiateMsg), &out_dir); + export_schema(&schema_for!(ExecuteMsg), &out_dir); + export_schema(&schema_for!(QueryMsg), &out_dir); +} diff --git a/contracts/docs/interchainqueries/howto/register_kv_icq/schema/execute_msg.json b/contracts/docs/interchainqueries/howto/register_kv_icq/schema/execute_msg.json new file mode 100644 index 0000000..0faca5d --- /dev/null +++ b/contracts/docs/interchainqueries/howto/register_kv_icq/schema/execute_msg.json @@ -0,0 +1,562 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ExecuteMsg", + "oneOf": [ + { + "type": "object", + "required": [ + "register_balances_query" + ], + "properties": { + "register_balances_query": { + "type": "object", + "required": [ + "addr", + "connection_id", + "denoms", + "update_period" + ], + "properties": { + "addr": { + "type": "string" + }, + "connection_id": { + "type": "string" + }, + "denoms": { + "type": "array", + "items": { + "type": "string" + } + }, + "update_period": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "register_bank_total_supply_query" + ], + "properties": { + "register_bank_total_supply_query": { + "type": "object", + "required": [ + "connection_id", + "denoms", + "update_period" + ], + "properties": { + "connection_id": { + "type": "string" + }, + "denoms": { + "type": "array", + "items": { + "type": "string" + } + }, + "update_period": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "register_distribution_fee_pool_query" + ], + "properties": { + "register_distribution_fee_pool_query": { + "type": "object", + "required": [ + "connection_id", + "update_period" + ], + "properties": { + "connection_id": { + "type": "string" + }, + "update_period": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "register_staking_validators_query" + ], + "properties": { + "register_staking_validators_query": { + "type": "object", + "required": [ + "connection_id", + "update_period", + "validators" + ], + "properties": { + "connection_id": { + "type": "string" + }, + "update_period": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "validators": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "register_government_proposals_query" + ], + "properties": { + "register_government_proposals_query": { + "type": "object", + "required": [ + "connection_id", + "proposals_ids", + "update_period" + ], + "properties": { + "connection_id": { + "type": "string" + }, + "proposals_ids": { + "type": "array", + "items": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "update_period": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "register_government_proposal_votes_query" + ], + "properties": { + "register_government_proposal_votes_query": { + "type": "object", + "required": [ + "connection_id", + "proposals_ids", + "update_period", + "voters" + ], + "properties": { + "connection_id": { + "type": "string" + }, + "proposals_ids": { + "type": "array", + "items": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "update_period": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "voters": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "register_transfers_query" + ], + "properties": { + "register_transfers_query": { + "type": "object", + "required": [ + "connection_id", + "recipients", + "update_period" + ], + "properties": { + "connection_id": { + "type": "string" + }, + "min_height": { + "type": [ + "integer", + "null" + ], + "format": "uint64", + "minimum": 0.0 + }, + "recipients": { + "type": "array", + "items": { + "type": "string" + } + }, + "update_period": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "register_delegator_delegations_query" + ], + "properties": { + "register_delegator_delegations_query": { + "type": "object", + "required": [ + "connection_id", + "delegator", + "update_period", + "validators" + ], + "properties": { + "connection_id": { + "type": "string" + }, + "delegator": { + "type": "string" + }, + "update_period": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "validators": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "register_delegator_unbonding_delegations_query" + ], + "properties": { + "register_delegator_unbonding_delegations_query": { + "type": "object", + "required": [ + "connection_id", + "delegator", + "update_period", + "validators" + ], + "properties": { + "connection_id": { + "type": "string" + }, + "delegator": { + "type": "string" + }, + "update_period": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "validators": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "register_validators_signing_info_query" + ], + "properties": { + "register_validators_signing_info_query": { + "type": "object", + "required": [ + "connection_id", + "update_period", + "validators" + ], + "properties": { + "connection_id": { + "type": "string" + }, + "update_period": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "validators": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "update_interchain_query" + ], + "properties": { + "update_interchain_query": { + "type": "object", + "required": [ + "query_id" + ], + "properties": { + "new_keys": { + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/definitions/KVKey" + } + }, + "new_recipient": { + "type": [ + "string", + "null" + ] + }, + "new_update_period": { + "type": [ + "integer", + "null" + ], + "format": "uint64", + "minimum": 0.0 + }, + "query_id": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "remove_interchain_query" + ], + "properties": { + "remove_interchain_query": { + "type": "object", + "required": [ + "query_id" + ], + "properties": { + "query_id": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Used only in integration tests framework to simulate failures. It tries to register query where keys is an empty array.", + "type": "object", + "required": [ + "integration_tests_register_query_empty_keys" + ], + "properties": { + "integration_tests_register_query_empty_keys": { + "type": "object", + "required": [ + "connection_id" + ], + "properties": { + "connection_id": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Used only in integration tests framework to simulate failures. It tries to register query where in keys array there is a key with empty path.", + "type": "object", + "required": [ + "integration_tests_register_query_empty_path" + ], + "properties": { + "integration_tests_register_query_empty_path": { + "type": "object", + "required": [ + "connection_id" + ], + "properties": { + "connection_id": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Used only in integration tests framework to simulate failures. It tries to register query where in keys array there is a key with empty id.", + "type": "object", + "required": [ + "integration_tests_register_query_empty_id" + ], + "properties": { + "integration_tests_register_query_empty_id": { + "type": "object", + "required": [ + "connection_id" + ], + "properties": { + "connection_id": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Used only in integration tests framework to simulate failures. After executing this message, contract will attempt to alter state, zero out kv query statistics and then fail, all of this happening in sudo kv callback handler.", + "type": "object", + "required": [ + "integration_tests_set_query_mock" + ], + "properties": { + "integration_tests_set_query_mock": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Used only in integration tests framework to simulate failures. After executing this message, contract will revert back to normal behaviour.", + "type": "object", + "required": [ + "integration_tests_unset_query_mock" + ], + "properties": { + "integration_tests_unset_query_mock": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + } + ], + "definitions": { + "Binary": { + "description": "Binary is a wrapper around Vec to add base64 de/serialization with serde. It also adds some helper methods to help encode inline.\n\nThis is only needed as serde-json-{core,wasm} has a horrible encoding for Vec. See also .", + "type": "string" + }, + "KVKey": { + "description": "Describes a KV key for which you want to get value from the storage on remote chain", + "type": "object", + "required": [ + "key", + "path" + ], + "properties": { + "key": { + "description": "*key** is a key you want to read from the storage", + "allOf": [ + { + "$ref": "#/definitions/Binary" + } + ] + }, + "path": { + "description": "*path** is a path to the storage (storage prefix) where you want to read value by key (usually name of cosmos-packages module: 'staking', 'bank', etc.)", + "type": "string" + } + }, + "additionalProperties": false + } + } +} diff --git a/contracts/docs/interchainqueries/howto/register_kv_icq/schema/instantiate_msg.json b/contracts/docs/interchainqueries/howto/register_kv_icq/schema/instantiate_msg.json new file mode 100644 index 0000000..1352613 --- /dev/null +++ b/contracts/docs/interchainqueries/howto/register_kv_icq/schema/instantiate_msg.json @@ -0,0 +1,6 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "InstantiateMsg", + "type": "object", + "additionalProperties": false +} diff --git a/contracts/docs/interchainqueries/howto/register_kv_icq/schema/query_msg.json b/contracts/docs/interchainqueries/howto/register_kv_icq/schema/query_msg.json new file mode 100644 index 0000000..939d527 --- /dev/null +++ b/contracts/docs/interchainqueries/howto/register_kv_icq/schema/query_msg.json @@ -0,0 +1,293 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "QueryMsg", + "oneOf": [ + { + "type": "object", + "required": [ + "balance" + ], + "properties": { + "balance": { + "type": "object", + "required": [ + "query_id" + ], + "properties": { + "query_id": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "bank_total_supply" + ], + "properties": { + "bank_total_supply": { + "type": "object", + "required": [ + "query_id" + ], + "properties": { + "query_id": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "distribution_fee_pool" + ], + "properties": { + "distribution_fee_pool": { + "type": "object", + "required": [ + "query_id" + ], + "properties": { + "query_id": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "staking_validators" + ], + "properties": { + "staking_validators": { + "type": "object", + "required": [ + "query_id" + ], + "properties": { + "query_id": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "validators_signing_infos" + ], + "properties": { + "validators_signing_infos": { + "type": "object", + "required": [ + "query_id" + ], + "properties": { + "query_id": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "government_proposals" + ], + "properties": { + "government_proposals": { + "type": "object", + "required": [ + "query_id" + ], + "properties": { + "query_id": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "government_proposal_votes" + ], + "properties": { + "government_proposal_votes": { + "type": "object", + "required": [ + "query_id" + ], + "properties": { + "query_id": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "get_delegations" + ], + "properties": { + "get_delegations": { + "type": "object", + "required": [ + "query_id" + ], + "properties": { + "query_id": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "get_unbonding_delegations" + ], + "properties": { + "get_unbonding_delegations": { + "type": "object", + "required": [ + "query_id" + ], + "properties": { + "query_id": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "get_registered_query" + ], + "properties": { + "get_registered_query": { + "type": "object", + "required": [ + "query_id" + ], + "properties": { + "query_id": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "get_recipient_txs" + ], + "properties": { + "get_recipient_txs": { + "type": "object", + "required": [ + "recipient" + ], + "properties": { + "recipient": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "kv_callback_stats" + ], + "properties": { + "kv_callback_stats": { + "type": "object", + "required": [ + "query_id" + ], + "properties": { + "query_id": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "get_transfers_number" + ], + "properties": { + "get_transfers_number": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] +} diff --git a/contracts/docs/interchainqueries/howto/register_kv_icq/src/contract.rs b/contracts/docs/interchainqueries/howto/register_kv_icq/src/contract.rs new file mode 100644 index 0000000..7a79011 --- /dev/null +++ b/contracts/docs/interchainqueries/howto/register_kv_icq/src/contract.rs @@ -0,0 +1,153 @@ +// Copyright 2022 Neutron +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::msg::{ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg}; +use crate::state::{ICQ_ID_TO_WATCHED_ADDR, REMOTE_BALANCES}; +use cosmwasm_std::{ + entry_point, from_json, to_json_binary, Binary, CosmosMsg, Deps, DepsMut, Env, MessageInfo, + Reply, ReplyOn, Response, StdError, StdResult, SubMsg, +}; +use cw2::set_contract_version; +use neutron_sdk::interchain_queries::v047::{ + queries::query_balance, register_queries::new_register_balances_query_msg, +}; +use neutron_sdk::sudo::msg::SudoMsg; +use neutron_sdk::{NeutronError, NeutronResult}; +use neutron_std::types::neutron::interchainqueries::MsgRegisterInterchainQueryResponse; +use prost::Message; + +const CONTRACT_NAME: &str = concat!("crates.io:neutron-contracts__", env!("CARGO_PKG_NAME")); +const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); + +/// Reply ID used to tell this kind of reply call apart. +const REGISTER_BALANCES_ICQ_REPLY_ID: u64 = 1; + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn instantiate( + deps: DepsMut, + _env: Env, + _info: MessageInfo, + _msg: InstantiateMsg, +) -> NeutronResult { + set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; + Ok(Response::default()) +} + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn execute( + _deps: DepsMut, + env: Env, + _info: MessageInfo, + msg: ExecuteMsg, +) -> NeutronResult> { + match msg { + ExecuteMsg::RegisterBalancesQuery { + connection_id, + addr, + denoms, + update_period, + } => register_balances_query(env, connection_id, addr, denoms, update_period), + } +} + +pub fn register_balances_query( + env: Env, + connection_id: String, + addr: String, + denoms: Vec, + update_period: u64, +) -> NeutronResult> { + let msg = new_register_balances_query_msg( + env.contract.address, + connection_id, + addr.clone(), + denoms, + update_period, + )?; + + // Send the ICQ registration message as a submessage to receive a reply callback + Ok(Response::new().add_submessage(SubMsg { + id: REGISTER_BALANCES_ICQ_REPLY_ID, + payload: to_json_binary(&addr)?, + msg: CosmosMsg::Custom(msg), + gas_limit: None, + reply_on: ReplyOn::Success, + })) +} + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> NeutronResult { + match msg { + QueryMsg::Balances { address } => query_balances(deps, address), + } +} + +pub fn query_balances(deps: Deps, addr: String) -> NeutronResult { + Ok(to_json_binary(&REMOTE_BALANCES.load(deps.storage, addr)?)?) +} + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn migrate(_deps: DepsMut, _env: Env, _msg: MigrateMsg) -> StdResult { + Ok(Response::default()) +} + +#[entry_point] +pub fn sudo(deps: DepsMut, env: Env, msg: SudoMsg) -> NeutronResult { + match msg { + SudoMsg::KVQueryResult { query_id } => sudo_kv_query_result(deps, env, query_id), + _ => Ok(Response::default()), + } +} + +#[entry_point] +pub fn reply(deps: DepsMut, _env: Env, msg: Reply) -> NeutronResult { + match msg.id { + REGISTER_BALANCES_ICQ_REPLY_ID => { + // decode the reply msg result as MsgRegisterInterchainQueryResponse + let resp = MsgRegisterInterchainQueryResponse::decode( + msg.result + .into_result() + .map_err(StdError::generic_err)? + .msg_responses[0] + .clone() + .value + .to_vec() + .as_slice(), + )?; + + // memorize the address that corresponds to the query id to use it later in the + // SudoMsg::KVQueryResult handler. + let addr: String = from_json(&msg.payload)?; + ICQ_ID_TO_WATCHED_ADDR.save(deps.storage, resp.id, &addr)?; + + Ok(Response::default()) + } + _ => Err(NeutronError::InvalidReplyID(msg.id)), + } +} + +/// The contract's callback for KV query results. Note that only the query id is provided, so you +/// need to read the query result from the state. +pub fn sudo_kv_query_result(deps: DepsMut, env: Env, query_id: u64) -> NeutronResult { + // Get the last submitted ICQ result from the Neutron ICQ module storage + let balance_resp = query_balance(deps.as_ref(), env.clone(), query_id)?; + // Get the address that was registered for the ICQ + let addr = ICQ_ID_TO_WATCHED_ADDR.load(deps.storage, query_id)?; + + // Put your business logic here + // For this example we just preserve the freshly fetched balances in the contract's state + REMOTE_BALANCES.save(deps.storage, addr, &balance_resp.balances)?; + + Ok(Response::default()) +} diff --git a/contracts/docs/interchainqueries/howto/register_kv_icq/src/lib.rs b/contracts/docs/interchainqueries/howto/register_kv_icq/src/lib.rs new file mode 100644 index 0000000..9159fa3 --- /dev/null +++ b/contracts/docs/interchainqueries/howto/register_kv_icq/src/lib.rs @@ -0,0 +1,19 @@ +// Copyright 2022 Neutron +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![warn(clippy::unwrap_used, clippy::expect_used)] + +pub mod contract; +pub mod msg; +pub mod state; diff --git a/contracts/docs/interchainqueries/howto/register_kv_icq/src/msg.rs b/contracts/docs/interchainqueries/howto/register_kv_icq/src/msg.rs new file mode 100644 index 0000000..3fce8ab --- /dev/null +++ b/contracts/docs/interchainqueries/howto/register_kv_icq/src/msg.rs @@ -0,0 +1,25 @@ +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] +pub struct InstantiateMsg {} + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] +#[serde(rename_all = "snake_case")] +pub enum ExecuteMsg { + RegisterBalancesQuery { + connection_id: String, + update_period: u64, + addr: String, + denoms: Vec, + }, +} + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] +#[serde(rename_all = "snake_case")] +pub enum QueryMsg { + Balances { address: String }, +} + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] +pub struct MigrateMsg {} diff --git a/contracts/docs/interchainqueries/howto/register_kv_icq/src/state.rs b/contracts/docs/interchainqueries/howto/register_kv_icq/src/state.rs new file mode 100644 index 0000000..4dcd276 --- /dev/null +++ b/contracts/docs/interchainqueries/howto/register_kv_icq/src/state.rs @@ -0,0 +1,7 @@ +use cw_storage_plus::Map; +use neutron_sdk::interchain_queries::v047::types::Balances; + +/// Contains addresses of all watched addresses mapped to respective Interchain query ID. +pub const ICQ_ID_TO_WATCHED_ADDR: Map = Map::new("icq_id_to_watched_addr"); +/// Contains last submitted balances of remote addresses. +pub const REMOTE_BALANCES: Map = Map::new("remote_balances");