From 02590249c348c556a7b5d797ce6f642a362ab114 Mon Sep 17 00:00:00 2001 From: Ali Behjati Date: Fri, 29 Apr 2022 14:58:19 +0000 Subject: [PATCH 1/8] Add initial files It's a copy of simple example that uses an oracle interface which allows mocking --- Cargo.toml | 1 + examples/terra-contract-advanced/Cargo.toml | 40 + .../terra-contract-advanced/Developing.md | 94 + examples/terra-contract-advanced/README.md | 116 + .../examples/schema.rs | 29 + .../schema/execute_msg.json | 6 + .../schema/fetch_price_response.json | 43 + .../schema/instantiate_msg.json | 22 + .../schema/query_msg.json | 18 + .../terra-contract-advanced/schema/state.json | 26 + .../terra-contract-advanced/src/contract.rs | 142 + examples/terra-contract-advanced/src/lib.rs | 3 + examples/terra-contract-advanced/src/msg.rs | 39 + examples/terra-contract-advanced/src/state.rs | 43 + .../terra-contract-advanced/tools/.gitignore | 1 + .../terra-contract-advanced/tools/deploy.js | 237 ++ .../tools/package-lock.json | 2573 +++++++++++++++++ .../tools/package.json | 20 + .../terra-contract-advanced/tools/query.js | 37 + 19 files changed, 3490 insertions(+) create mode 100644 examples/terra-contract-advanced/Cargo.toml create mode 100644 examples/terra-contract-advanced/Developing.md create mode 100644 examples/terra-contract-advanced/README.md create mode 100644 examples/terra-contract-advanced/examples/schema.rs create mode 100644 examples/terra-contract-advanced/schema/execute_msg.json create mode 100644 examples/terra-contract-advanced/schema/fetch_price_response.json create mode 100644 examples/terra-contract-advanced/schema/instantiate_msg.json create mode 100644 examples/terra-contract-advanced/schema/query_msg.json create mode 100644 examples/terra-contract-advanced/schema/state.json create mode 100644 examples/terra-contract-advanced/src/contract.rs create mode 100644 examples/terra-contract-advanced/src/lib.rs create mode 100644 examples/terra-contract-advanced/src/msg.rs create mode 100644 examples/terra-contract-advanced/src/state.rs create mode 100644 examples/terra-contract-advanced/tools/.gitignore create mode 100644 examples/terra-contract-advanced/tools/deploy.js create mode 100644 examples/terra-contract-advanced/tools/package-lock.json create mode 100644 examples/terra-contract-advanced/tools/package.json create mode 100644 examples/terra-contract-advanced/tools/query.js diff --git a/Cargo.toml b/Cargo.toml index 0859f58..a6bb36a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,4 +6,5 @@ members = [ "pyth-sdk-solana/test-contract", "pyth-sdk-terra", "examples/terra-contract", + "examples/terra-contract-advanced", ] diff --git a/examples/terra-contract-advanced/Cargo.toml b/examples/terra-contract-advanced/Cargo.toml new file mode 100644 index 0000000..ce14774 --- /dev/null +++ b/examples/terra-contract-advanced/Cargo.toml @@ -0,0 +1,40 @@ +[package] +name = "example-terra-contract-advanced" +version = "0.1.0" +authors = ["Pyth Data Association"] +edition = "2018" + +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] +# for more explicit tests, cargo test --features=backtraces +backtraces = ["cosmwasm-std/backtraces"] +# use library feature to disable all instantiate/execute/query exports +library = [] + +[package.metadata.scripts] +optimize = """docker run --rm -v "$(pwd)":/code \ + --mount type=volume,source="$(basename "$(pwd)")_cache",target=/code/target \ + --mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \ + cosmwasm/rust-optimizer:0.12.5 +""" + +[dependencies] +cosmwasm-std = { version = "0.16.2" } +cosmwasm-storage = { version = "0.16.0" } +cw-storage-plus = "0.8.0" +schemars = "0.8" +serde = { version = "1.0", default-features = false, features = ["derive"] } +pyth-sdk-terra = { version = "0.3.0", path = "../../pyth-sdk-terra" } # Remove path and use version only when you use this example on your own. + +[dev-dependencies] +cosmwasm-schema = { version = "0.16.0" } diff --git a/examples/terra-contract-advanced/Developing.md b/examples/terra-contract-advanced/Developing.md new file mode 100644 index 0000000..ecf7f00 --- /dev/null +++ b/examples/terra-contract-advanced/Developing.md @@ -0,0 +1,94 @@ +# Developing + +This document contains guidance regarding building, testing and preparing your contracts for production. + +## Prerequisites + +Before starting, make sure you have [rustup](https://rustup.rs/) along with a +recent `rustc` and `cargo` version installed. Rust version 1.58.1 or above is required. + +And you need to have the `wasm32-unknown-unknown` target installed as well. + +You can check that via: + +```sh +rustc --version +cargo --version +rustup target list --installed +# if wasm32 is not listed above, run this +rustup target add wasm32-unknown-unknown +``` + +This example uses relative paths in `Cargo.toml`; you must remove any `path` components within `Cargo.toml` dependencies if you intend to compile this code outside of the `pyth-sdk-terra` repository, otherwise this will fail to compile. For example: + +```diff +- pyth-sdk-terra = { version = "0.3.0", path = "../../pyth-sdk-terra" } ++ pyth-sdk-terra = { version = "0.3.0" } +``` + +## Compiling + +After changing the contract, make sure you can compile and run it before +making any changes. Go into the repository and do: + +```sh +# this will produce a wasm build in ./target/wasm32-unknown-unknown/release/exxamle_terra_contract.wasm +cargo build --release --target wasm32-unknown-unknown +``` +## Generating JSON Schema + +While the Wasm calls (`instantiate`, `execute`, `query`) accept JSON, this is not enough +information to use it. You need to expose the schema for the expected messages to the +clients. You can generate this schema by calling `cargo run --example schema`, which will output +4 files in `./schema`, corresponding to the 3 message types the contract accepts, +as well as the internal `State`. + +These files are in standard json-schema format, which should be usable by various +client side tools, either to auto-generate codecs, or just to validate incoming +json wrt. the defined schema. + +## Preparing the Wasm bytecode for production + +Before you upload it to a chain, you need to ensure the smallest output size possible, +as this will be included in the body of a transaction. You also want to have a +reproducible build process, so third parties can verify that the uploaded Wasm +code did indeed come from the claimed rust code. + +To solve both these issues, CosmWasm have produced `rust-optimizer`, a docker image to +produce an extremely small build output in a consistent manner. The suggested way +to run it is this: + +```sh +cd path/to/cargo/root +docker run --rm -v "$(pwd)":/code \ + --mount type=volume,source="$(basename "$(pwd)")_cache",target=/code/target \ + --mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \ + cosmwasm/rust-optimizer:0.12.6 +``` + +Or, If you're on an arm64 machine, you should use a docker image built with arm64. +```sh +cd path/to/cargo/root +docker run --rm -v "$(pwd)":/code \ + --mount type=volume,source="$(basename "$(pwd)")_cache",target=/code/target \ + --mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \ + cosmwasm/rust-optimizer-arm64:0.12.6 +``` + +You must mount the contract code to `/code`. You can use a absolute path instead +of `$(pwd)` if you don't want to `cd` to the directory first. The other two +volumes are nice for speedup. Mounting `/code/target` in particular is useful +to avoid docker overwriting your local dev files with root permissions. +Note the `/code/target` cache is unique for each contract being compiled to limit +interference, while the registry cache is global. + +This is rather slow compared to local compilations, especially the first compile +of a given contract. The use of the two volume caches is very useful to speed up +following compiles of the same contract. + +This produces an `artifacts` directory with a `PROJECT_NAME.wasm`, as well as +`checksums.txt`, containing the Sha256 hash of the wasm file. +The wasm file is compiled deterministically (anyone else running the same +docker on the same git commit should get the identical file with the same Sha256 hash). +It is also stripped and minimized for upload to a blockchain (it is also compressed using +gzip in the uploading process to make it even smaller). diff --git a/examples/terra-contract-advanced/README.md b/examples/terra-contract-advanced/README.md new file mode 100644 index 0000000..92f3dd1 --- /dev/null +++ b/examples/terra-contract-advanced/README.md @@ -0,0 +1,116 @@ +# Pyth SDK Example Contract for Terra + +This repository contains an example contract that demonstrates how to read the Pyth price from the Pyth on-chain contract. +The example [contract](src/contract.rs) has two functions: + +* `instantiate` sets the Pyth contract address and price feed id that the contract uses. + This function is intended to be called once when the contract is deployed. + See the [Terra SDK README](../../pyth-sdk-terra/README.md) for the list of possible price feed ids. +* `query` queries the Pyth contract to get the current price for the configured price feed id. + +## Testnet Demo + +This example contract is running on Terra testnet at `terra16h868tx50d3w37ry7c5lzzg648f7yetu39p5pd`. +This contract has been instantiated to return the price of `Crypto.LUNA/USD`. +You can query the contract from this repo by running: + +```sh +cd tools/ +# Install dependencies (if you haven't done so already) +npm install +# Query the contract +npm run query -- --network testnet --contract terra16h868tx50d3w37ry7c5lzzg648f7yetu39p5pd +``` + +Or by going to the contract address in [Terra Finder](https://finder.terra.money/) you can query make a query like below: +``` +{ + "fetch_price": {} +} +``` + +If the query is successful, the output should look like: +``` +{ + current_price: { price: "8704350000", conf: "3150000", expo: -8 }, + ema_price: { price: "8665158600", conf: "2965370", expo: -8 } +} +``` + +If the price feed is currently not available you will see: +``` +rpc error: code = Unknown desc = Generic error: Current price is not available: contract query failed +``` + +## Developing + +If you would like to deploy a changed version of this contract, the process consists of two steps: + +1. Build the WASM for the contract. +2. Upload the code and instantiate a new contract. + +### Build WASM + +See the [Developing instructions](Developing.md) for how to build the WASM for the contract. +The instructions in that document will build a file called `example_terra_contract.wasm` under the `artifacts/` directory. + +### Upload and Instantiate Contract + +The tools directory contains a deployment script that will upload a WASM file and instantiate a new contract with it. +You can run that script on the built WASM file as follows: + +``` sh +cd tools/ +npm install +npm run deploy -- --network testnet --artifact ../artifacts/example_terra_contract.wasm --mnemonic "..." --instantiate +``` + +This command will deploy the contract to `testnet` and sets its owner to the wallet with the provided `mnemonic`. +Note that you have to populate the `--mnemonic` flag with the seedphrase for a valid Terra wallet with some LUNA for the specified network. + +If successful, the output should look like: +``` +Storing WASM: ../artifacts/example_terra_contract.wasm (183749 bytes) +Deploy fee: 44682uluna +Code ID: 53199 +Instantiating a contract +Sleeping for 10 seconds for store transaction to finalize. +Instantiated Pyth Example at terra123456789yelw23uh22nadqlyjvtl7s5527er97 (0x0000000000000000000000001234567896267ee5479752a7d683e49317ff4294) +Deployed pyth example contract at terra123456789yelw23uh22nadqlyjvtl7s5527er97 +``` + +By default, the deployment script sets the price feed to `Crypto.LUNA/USD` but you can change it in [deploy.js](tools/deploy.js). + +### Querying the Contract + +Once the contract is instantiated, you can query it by running: + +```sh +npm run query -- --network testnet --contract +``` + +### Migrating the Contract +You can also migrate an existing contract by passing the `--migrate --contract terra123456xyzqwe..` arguments to the deploy command: + +``` sh +npm run deploy -- --network testnet --artifact ../artifacts/example_terra_contract.wasm --mnemonic "..." --migrate --contract "terra123..." +``` + +This command will replace the code for the given contract with the specified WASM artifact. +If successful, the output should look like: + +``` +Storing WASM: ../artifacts/example_terra_contract.wasm (183749 bytes) +Deploy fee: 44682uluna +Code ID: 53227 +Sleeping for 10 seconds for store transaction to finalize. +Migrating contract terra123456789yelw23uh22nadqlyjvtl7s5527er97 to 53227 +Contract terra123456789yelw23uh22nadqlyjvtl7s5527er97 code_id successfully updated to 53227 +``` + +### Troubleshooting + +When deploying the contract, you may encounter gateway timeout or account sequence mismatch errors. +If this happens, check terra finder to determine if your transaction succeeded -- sometimes transactions succeed despite timing out. +Note that the deployment script submits multiple transactions. +If any of them fails, simply rerun the entire script; there is no problem re-running the successful transactions. diff --git a/examples/terra-contract-advanced/examples/schema.rs b/examples/terra-contract-advanced/examples/schema.rs new file mode 100644 index 0000000..3e1f046 --- /dev/null +++ b/examples/terra-contract-advanced/examples/schema.rs @@ -0,0 +1,29 @@ +use std::env::current_dir; +use std::fs::create_dir_all; + +use cosmwasm_schema::{ + export_schema, + remove_schemas, + schema_for, +}; + +use example_terra_contract_advanced::msg::{ + ExecuteMsg, + FetchPriceResponse, + InstantiateMsg, + QueryMsg, +}; +use example_terra_contract_advanced::state::State; + +fn main() { + let mut out_dir = current_dir().unwrap(); + out_dir.push("schema"); + create_dir_all(&out_dir).unwrap(); + remove_schemas(&out_dir).unwrap(); + + export_schema(&schema_for!(InstantiateMsg), &out_dir); + export_schema(&schema_for!(ExecuteMsg), &out_dir); + export_schema(&schema_for!(QueryMsg), &out_dir); + export_schema(&schema_for!(State), &out_dir); + export_schema(&schema_for!(FetchPriceResponse), &out_dir); +} diff --git a/examples/terra-contract-advanced/schema/execute_msg.json b/examples/terra-contract-advanced/schema/execute_msg.json new file mode 100644 index 0000000..b3d18b4 --- /dev/null +++ b/examples/terra-contract-advanced/schema/execute_msg.json @@ -0,0 +1,6 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ExecuteMsg", + "type": "string", + "enum": [] +} diff --git a/examples/terra-contract-advanced/schema/fetch_price_response.json b/examples/terra-contract-advanced/schema/fetch_price_response.json new file mode 100644 index 0000000..311fc6f --- /dev/null +++ b/examples/terra-contract-advanced/schema/fetch_price_response.json @@ -0,0 +1,43 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "FetchPriceResponse", + "type": "object", + "required": [ + "current_price", + "ema_price" + ], + "properties": { + "current_price": { + "$ref": "#/definitions/Price" + }, + "ema_price": { + "$ref": "#/definitions/Price" + } + }, + "definitions": { + "Price": { + "description": "A price with a degree of uncertainty, represented as a price +- a confidence interval.\n\nThe confidence interval roughly corresponds to the standard error of a normal distribution. Both the price and confidence are stored in a fixed-point numeric representation, `x * 10^expo`, where `expo` is the exponent. For example:\n\n``` use pyth_sdk::Price; Price { price: 12345, conf: 267, expo: -2 }; // represents 123.45 +- 2.67 Price { price: 123, conf: 1, expo: 2 }; // represents 12300 +- 100 ```\n\n`Price` supports a limited set of mathematical operations. All of these operations will propagate any uncertainty in the arguments into the result. However, the uncertainty in the result may overestimate the true uncertainty (by at most a factor of `sqrt(2)`) due to computational limitations. Furthermore, all of these operations may return `None` if their result cannot be represented within the numeric representation (e.g., the exponent is so small that the price does not fit into an i64). Users of these methods should (1) select their exponents to avoid this problem, and (2) handle the `None` case gracefully.", + "type": "object", + "required": [ + "conf", + "expo", + "price" + ], + "properties": { + "conf": { + "description": "Confidence Interval.", + "type": "string" + }, + "expo": { + "description": "Exponent.", + "type": "integer", + "format": "int32" + }, + "price": { + "description": "Price.", + "type": "string" + } + } + } + } +} diff --git a/examples/terra-contract-advanced/schema/instantiate_msg.json b/examples/terra-contract-advanced/schema/instantiate_msg.json new file mode 100644 index 0000000..f5db739 --- /dev/null +++ b/examples/terra-contract-advanced/schema/instantiate_msg.json @@ -0,0 +1,22 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "InstantiateMsg", + "type": "object", + "required": [ + "price_feed_id", + "pyth_contract_addr" + ], + "properties": { + "price_feed_id": { + "$ref": "#/definitions/Identifier" + }, + "pyth_contract_addr": { + "type": "string" + } + }, + "definitions": { + "Identifier": { + "type": "string" + } + } +} diff --git a/examples/terra-contract-advanced/schema/query_msg.json b/examples/terra-contract-advanced/schema/query_msg.json new file mode 100644 index 0000000..c24903e --- /dev/null +++ b/examples/terra-contract-advanced/schema/query_msg.json @@ -0,0 +1,18 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "QueryMsg", + "oneOf": [ + { + "type": "object", + "required": [ + "fetch_price" + ], + "properties": { + "fetch_price": { + "type": "object" + } + }, + "additionalProperties": false + } + ] +} diff --git a/examples/terra-contract-advanced/schema/state.json b/examples/terra-contract-advanced/schema/state.json new file mode 100644 index 0000000..b75abc8 --- /dev/null +++ b/examples/terra-contract-advanced/schema/state.json @@ -0,0 +1,26 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "State", + "type": "object", + "required": [ + "price_feed_id", + "pyth_contract_addr" + ], + "properties": { + "price_feed_id": { + "$ref": "#/definitions/Identifier" + }, + "pyth_contract_addr": { + "$ref": "#/definitions/Addr" + } + }, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "Identifier": { + "type": "string" + } + } +} diff --git a/examples/terra-contract-advanced/src/contract.rs b/examples/terra-contract-advanced/src/contract.rs new file mode 100644 index 0000000..97caffb --- /dev/null +++ b/examples/terra-contract-advanced/src/contract.rs @@ -0,0 +1,142 @@ +#[cfg(not(feature = "library"))] +use cosmwasm_std::entry_point; +use cosmwasm_std::{ + to_binary, + Binary, + Deps, + DepsMut, + Env, + MessageInfo, + Response, + StdError, + StdResult, +}; + +use crate::msg::{ + ExecuteMsg, + FetchPriceResponse, + InstantiateMsg, + MigrateMsg, + QueryMsg, +}; +use crate::state::{ + Oracle, + State, + STATE, +}; + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn migrate(_deps: DepsMut, _env: Env, _msg: MigrateMsg) -> StdResult { + Ok(Response::new().add_attribute("method", "migrate")) +} + +/// The instantiate function is invoked when the contract is first deployed. +/// This function sets configuration values that are used by the query function. +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn instantiate( + deps: DepsMut, + _env: Env, + _info: MessageInfo, + msg: InstantiateMsg, +) -> StdResult { + let state = match msg { + InstantiateMsg::StubOracle { maybe_price } => State { + oracle: Oracle::Stub(maybe_price), + }, + InstantiateMsg::PythOracle { + ref contract_addr, + price_id, + } => State { + oracle: Oracle::Pyth(deps.api.addr_validate(contract_addr.as_ref())?, price_id), + }, + }; + + STATE.save(deps.storage, &state)?; + + Ok(Response::new() + .add_attribute("method", "instantiate") + .add_attribute("price_id", format!("{:?}", msg))) +} + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn execute( + _deps: DepsMut, + _env: Env, + _info: MessageInfo, + _msg: ExecuteMsg, +) -> StdResult { + Ok(Response::new().add_attribute("method", "execute")) +} + +/// Query the Pyth contract the current price of the configured price feed. +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { + match msg { + QueryMsg::FetchPrice {} => to_binary(&query_fetch_price(deps)?), + } +} + +fn query_fetch_price(deps: Deps) -> StdResult { + let state = STATE.load(deps.storage)?; + + let price = state + .oracle + .get_price(&deps.querier) + .ok_or_else(|| StdError::not_found("Current price is not available"))?; + + Ok(FetchPriceResponse { price }) +} + +#[cfg(test)] +mod tests { + use cosmwasm_std::testing::mock_dependencies; + use cosmwasm_std::DepsMut; + use pyth_sdk_terra::Price; + + use crate::msg::FetchPriceResponse; + use crate::state::{ + Oracle, + State, + STATE, + }; + + use super::query_fetch_price; + + pub fn set_price(deps: DepsMut, maybe_price: Option) { + STATE + .save( + deps.storage, + &State { + oracle: Oracle::Stub(maybe_price), + }, + ) + .unwrap(); + } + + #[test] + pub fn test_query_fetch_price_ok() { + let mut deps = mock_dependencies(&[]); + + let price = Price { + price: 1000, + conf: 5, + expo: 0, + }; + + set_price(deps.as_mut(), Some(price)); + + assert_eq!( + query_fetch_price(deps.as_ref()), + Ok(FetchPriceResponse { price }) + ); + } + + #[test] + pub fn test_query_fetch_price_unavailable() { + let mut deps = mock_dependencies(&[]); + + set_price(deps.as_mut(), None); + + assert!(query_fetch_price(deps.as_ref()).is_err()); + } +} diff --git a/examples/terra-contract-advanced/src/lib.rs b/examples/terra-contract-advanced/src/lib.rs new file mode 100644 index 0000000..4934c19 --- /dev/null +++ b/examples/terra-contract-advanced/src/lib.rs @@ -0,0 +1,3 @@ +pub mod contract; +pub mod msg; +pub mod state; diff --git a/examples/terra-contract-advanced/src/msg.rs b/examples/terra-contract-advanced/src/msg.rs new file mode 100644 index 0000000..7213942 --- /dev/null +++ b/examples/terra-contract-advanced/src/msg.rs @@ -0,0 +1,39 @@ +use pyth_sdk_terra::{ + Price, + PriceIdentifier, +}; +use schemars::JsonSchema; +use serde::{ + Deserialize, + Serialize, +}; + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] +#[serde(rename_all = "snake_case")] +pub struct MigrateMsg {} + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] +pub enum InstantiateMsg { + StubOracle { + maybe_price: Option, + }, + PythOracle { + contract_addr: String, + price_id: PriceIdentifier, + }, +} + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] +#[serde(rename_all = "snake_case")] +pub enum ExecuteMsg {} + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] +#[serde(rename_all = "snake_case")] +pub enum QueryMsg { + FetchPrice {}, +} + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] +pub struct FetchPriceResponse { + pub price: Price, +} diff --git a/examples/terra-contract-advanced/src/state.rs b/examples/terra-contract-advanced/src/state.rs new file mode 100644 index 0000000..87b7901 --- /dev/null +++ b/examples/terra-contract-advanced/src/state.rs @@ -0,0 +1,43 @@ +use cosmwasm_std::{ + Addr, + QuerierWrapper, +}; +use schemars::JsonSchema; +use serde::{ + Deserialize, + Serialize, +}; + +use cw_storage_plus::Item; +use pyth_sdk_terra::{ + query_price_feed, + Price, + PriceIdentifier, +}; + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] +pub struct State { + pub oracle: Oracle, +} + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] +pub enum Oracle { + Stub(Option), + Pyth(Addr, PriceIdentifier), +} + +impl Oracle { + pub fn get_price(&self, querier: &QuerierWrapper) -> Option { + match self { + Self::Stub(maybe_price) => *maybe_price, + Self::Pyth(contract_addr, price_id) => { + let price_feed = query_price_feed(querier, contract_addr.to_string(), *price_id) + .ok()? + .price_feed; + price_feed.get_ema_price() + } + } + } +} + +pub const STATE: Item = Item::new("state"); diff --git a/examples/terra-contract-advanced/tools/.gitignore b/examples/terra-contract-advanced/tools/.gitignore new file mode 100644 index 0000000..c2658d7 --- /dev/null +++ b/examples/terra-contract-advanced/tools/.gitignore @@ -0,0 +1 @@ +node_modules/ diff --git a/examples/terra-contract-advanced/tools/deploy.js b/examples/terra-contract-advanced/tools/deploy.js new file mode 100644 index 0000000..2382544 --- /dev/null +++ b/examples/terra-contract-advanced/tools/deploy.js @@ -0,0 +1,237 @@ +import { LCDClient, MnemonicKey } from "@terra-money/terra.js"; +import { + MsgInstantiateContract, + MsgMigrateContract, + MsgStoreCode, +} from "@terra-money/terra.js"; +import { readFileSync } from "fs"; +import { Bech32, toHex } from "@cosmjs/encoding"; +import { zeroPad } from "ethers/lib/utils.js"; +import axios from "axios"; +import yargs from "yargs"; +import {hideBin} from "yargs/helpers"; +import assert from "assert"; + +export const TERRA_GAS_PRICES_URL = "https://fcd.terra.dev/v1/txs/gas_prices"; + +const argv = yargs(hideBin(process.argv)) + .option('network', { + description: 'Which network to deploy to', + choices: ['testnet'], + required: true + }) + .option('artifact', { + description: 'Path to Pyth Example artifact', + type: 'string', + required: false + }) + .option('mnemonic', { + description: 'Mnemonic (private key)', + type: 'string', + required: true + }) + .option('instantiate', { + description: 'Instantiate contract if set (default: disabled)', + type: 'boolean', + default: false, + required: false + }) + .option('migrate', { + description: 'Migrate an existing contract if set (default: disabled)', + type: 'boolean', + default: false, + required: false + }) + .option('contract', { + description: 'Contract address, used only for migration', + type: 'string', + required: false, + default: '' + }) + .option('code-id', { + description: 'Code Id, if provided this will be used for migrate/instantiate and no code will be uploaded', + type: 'number', + requred: false + }) + .help() + .alias('help', 'h').argv; + +const artifact = argv.artifact; + +/* Set up terra client & wallet. It won't fail because inputs are validated with yargs */ + +const CONFIG = { + testnet: { + terraHost: { + URL: "https://bombay-lcd.terra.dev", + chainID: "bombay-12", + name: "testnet", + }, + pythContractAddress: "terra1wzs3rgzgjdde3kg7k3aaz6qx7sc5dcwxqe9fuc", + // Change this field to change which price feed is read by the deployed contract. + // The current value is the LUNA/USD price feed. + pythPriceFeedId: "6de025a4cf28124f8ea6cb8085f860096dbc36d9c40002e221fc449337e065b2" + } +} + +const terraHost = CONFIG[argv.network].terraHost; +const pythContractAddress = CONFIG[argv.network].pythContractAddress; +const pythPriceFeedId = CONFIG[argv.network].pythPriceFeedId; + + +const lcd = new LCDClient(terraHost); + +const feeDenoms = ["uluna"]; + +const gasPrices = await axios + .get(TERRA_GAS_PRICES_URL) + .then((result) => result.data); + +const wallet = lcd.wallet( + new MnemonicKey({ + mnemonic: argv.mnemonic + }) +); + +/* Deploy artifacts */ + +var codeId; + +if (argv.codeId !== undefined) { + codeId = argv.codeId; +} else { + if (argv.artifact === undefined) { + console.error("Artifact is not provided. Please at least provide artifact or code id"); + process.exit(1); + } + + const contract_bytes = readFileSync(artifact); + console.log(`Storing WASM: ${artifact} (${contract_bytes.length} bytes)`); + + const store_code = new MsgStoreCode( + wallet.key.accAddress, + contract_bytes.toString("base64") + ); + + const feeEstimate = await lcd.tx.estimateFee( + wallet.key.accAddress, + [store_code], + { + feeDenoms, + gasPrices, + } + ); + + console.log("Deploy fee: ", feeEstimate.amount.toString()); + + const tx = await wallet.createAndSignTx({ + msgs: [store_code], + feeDenoms, + gasPrices, + fee: feeEstimate, + }); + + const rs = await lcd.tx.broadcast(tx); + + try { + const ci = /"code_id","value":"([^"]+)/gm.exec(rs.raw_log)[1]; + codeId = parseInt(ci); + } catch(e) { + console.error("Encountered an error in parsing deploy code result. Printing raw log") + console.error(rs.raw_log); + throw(e); + } + + console.log("Code ID: ", codeId); + + if (argv.instantiate || argv.migrate) { + console.log("Sleeping for 10 seconds for store transaction to finalize."); + await sleep(10000); + } +} + +if (argv.instantiate) { + console.log("Instantiating a contract"); + + async function instantiate(codeId, inst_msg) { + var address; + await wallet + .createAndSignTx({ + msgs: [ + new MsgInstantiateContract( + wallet.key.accAddress, + wallet.key.accAddress, + codeId, + inst_msg + ), + ], + }) + .then((tx) => lcd.tx.broadcast(tx)) + .then((rs) => { + try { + address = /"contract_address","value":"([^"]+)/gm.exec(rs.raw_log)[1]; + } catch (e) { + console.error("Encountered an error in parsing instantiation result. Printing raw log") + console.error(rs.raw_log); + throw(e); + } + }); + console.log(`Instantiated Pyth Example at ${address} (${convert_terra_address_to_hex(address)})`); + return address; + } + + const contractAddress = await instantiate(codeId, { + price_feed_id: pythPriceFeedId, + pyth_contract_addr: pythContractAddress + }); + + console.log(`Deployed pyth example contract at ${contractAddress}`); +} + +if (argv.migrate) { + if (argv.contract === '') { + console.error("Contract address is not provided. Provide it using --contract"); + process.exit(1); + } + + console.log(`Migrating contract ${argv.contract} to ${codeId}`); + + const tx = await wallet.createAndSignTx({ + msgs: [ + new MsgMigrateContract( + wallet.key.accAddress, + argv.contract, + codeId, + { + "action": "" + }, + { uluna: 1000 } + ), + ], + feeDenoms, + gasPrices, + }); + + const rs = await lcd.tx.broadcast(tx); + var resultCodeId; + try { + resultCodeId = /"code_id","value":"([^"]+)/gm.exec(rs.raw_log)[1]; + assert.equal(codeId, resultCodeId) + } catch (e) { + console.error("Encountered an error in parsing migration result. Printing raw log") + console.error(rs.raw_log); + throw(e); + } + + console.log(`Contract ${argv.contract} code_id successfully updated to ${resultCodeId}`); +} + +// Terra addresses are "human-readable", but for cross-chain registrations, we +// want the "canonical" version +function convert_terra_address_to_hex(human_addr) { + return "0x" + toHex(zeroPad(Bech32.decode(human_addr).data, 32)); +} + +function sleep(ms) { + return new Promise(resolve => setTimeout(resolve, ms)); +} diff --git a/examples/terra-contract-advanced/tools/package-lock.json b/examples/terra-contract-advanced/tools/package-lock.json new file mode 100644 index 0000000..dc3cac8 --- /dev/null +++ b/examples/terra-contract-advanced/tools/package-lock.json @@ -0,0 +1,2573 @@ +{ + "name": "tools", + "version": "1.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "tools", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "@cosmjs/encoding": "^0.26.2", + "@terra-money/terra.js": "^2.0.11", + "ethers": "^5.4.4", + "yargs": "^17.0.1" + } + }, + "node_modules/@cosmjs/encoding": { + "version": "0.26.6", + "resolved": "https://registry.npmjs.org/@cosmjs/encoding/-/encoding-0.26.6.tgz", + "integrity": "sha512-dU0P2Um9ZB5yHpQYq+a6XnPKV4LD1kHd3nggbD0smn7wTwWW1XJKlms40SBZHtbm4dW9wPaPGf4yOkwwBdJO+w==", + "dependencies": { + "base64-js": "^1.3.0", + "bech32": "^1.1.4", + "readonly-date": "^1.0.0" + } + }, + "node_modules/@cosmjs/encoding/node_modules/bech32": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz", + "integrity": "sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ==" + }, + "node_modules/@ethersproject/abi": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.6.0.tgz", + "integrity": "sha512-AhVByTwdXCc2YQ20v300w6KVHle9g2OFc28ZAFCPnJyEpkv1xKXjZcSTgWOlv1i+0dqlgF8RCF2Rn2KC1t+1Vg==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/address": "^5.6.0", + "@ethersproject/bignumber": "^5.6.0", + "@ethersproject/bytes": "^5.6.0", + "@ethersproject/constants": "^5.6.0", + "@ethersproject/hash": "^5.6.0", + "@ethersproject/keccak256": "^5.6.0", + "@ethersproject/logger": "^5.6.0", + "@ethersproject/properties": "^5.6.0", + "@ethersproject/strings": "^5.6.0" + } + }, + "node_modules/@ethersproject/abstract-provider": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@ethersproject/abstract-provider/-/abstract-provider-5.6.0.tgz", + "integrity": "sha512-oPMFlKLN+g+y7a79cLK3WiLcjWFnZQtXWgnLAbHZcN3s7L4v90UHpTOrLk+m3yr0gt+/h9STTM6zrr7PM8uoRw==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bignumber": "^5.6.0", + "@ethersproject/bytes": "^5.6.0", + "@ethersproject/logger": "^5.6.0", + "@ethersproject/networks": "^5.6.0", + "@ethersproject/properties": "^5.6.0", + "@ethersproject/transactions": "^5.6.0", + "@ethersproject/web": "^5.6.0" + } + }, + "node_modules/@ethersproject/abstract-signer": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@ethersproject/abstract-signer/-/abstract-signer-5.6.0.tgz", + "integrity": "sha512-WOqnG0NJKtI8n0wWZPReHtaLkDByPL67tn4nBaDAhmVq8sjHTPbCdz4DRhVu/cfTOvfy9w3iq5QZ7BX7zw56BQ==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/abstract-provider": "^5.6.0", + "@ethersproject/bignumber": "^5.6.0", + "@ethersproject/bytes": "^5.6.0", + "@ethersproject/logger": "^5.6.0", + "@ethersproject/properties": "^5.6.0" + } + }, + "node_modules/@ethersproject/address": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@ethersproject/address/-/address-5.6.0.tgz", + "integrity": "sha512-6nvhYXjbXsHPS+30sHZ+U4VMagFC/9zAk6Gd/h3S21YW4+yfb0WfRtaAIZ4kfM4rrVwqiy284LP0GtL5HXGLxQ==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bignumber": "^5.6.0", + "@ethersproject/bytes": "^5.6.0", + "@ethersproject/keccak256": "^5.6.0", + "@ethersproject/logger": "^5.6.0", + "@ethersproject/rlp": "^5.6.0" + } + }, + "node_modules/@ethersproject/base64": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@ethersproject/base64/-/base64-5.6.0.tgz", + "integrity": "sha512-2Neq8wxJ9xHxCF9TUgmKeSh9BXJ6OAxWfeGWvbauPh8FuHEjamgHilllx8KkSd5ErxyHIX7Xv3Fkcud2kY9ezw==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.6.0" + } + }, + "node_modules/@ethersproject/basex": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@ethersproject/basex/-/basex-5.6.0.tgz", + "integrity": "sha512-qN4T+hQd/Md32MoJpc69rOwLYRUXwjTlhHDIeUkUmiN/JyWkkLLMoG0TqvSQKNqZOMgN5stbUYN6ILC+eD7MEQ==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.6.0", + "@ethersproject/properties": "^5.6.0" + } + }, + "node_modules/@ethersproject/bignumber": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@ethersproject/bignumber/-/bignumber-5.6.0.tgz", + "integrity": "sha512-VziMaXIUHQlHJmkv1dlcd6GY2PmT0khtAqaMctCIDogxkrarMzA9L94KN1NeXqqOfFD6r0sJT3vCTOFSmZ07DA==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.6.0", + "@ethersproject/logger": "^5.6.0", + "bn.js": "^4.11.9" + } + }, + "node_modules/@ethersproject/bytes": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@ethersproject/bytes/-/bytes-5.6.0.tgz", + "integrity": "sha512-3hJPlYemb9V4VLfJF5BfN0+55vltPZSHU3QKUyP9M3Y2TcajbiRrz65UG+xVHOzBereB1b9mn7r12o177xgN7w==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/logger": "^5.6.0" + } + }, + "node_modules/@ethersproject/constants": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@ethersproject/constants/-/constants-5.6.0.tgz", + "integrity": "sha512-SrdaJx2bK0WQl23nSpV/b1aq293Lh0sUaZT/yYKPDKn4tlAbkH96SPJwIhwSwTsoQQZxuh1jnqsKwyymoiBdWA==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bignumber": "^5.6.0" + } + }, + "node_modules/@ethersproject/contracts": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@ethersproject/contracts/-/contracts-5.6.0.tgz", + "integrity": "sha512-74Ge7iqTDom0NX+mux8KbRUeJgu1eHZ3iv6utv++sLJG80FVuU9HnHeKVPfjd9s3woFhaFoQGf3B3iH/FrQmgw==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/abi": "^5.6.0", + "@ethersproject/abstract-provider": "^5.6.0", + "@ethersproject/abstract-signer": "^5.6.0", + "@ethersproject/address": "^5.6.0", + "@ethersproject/bignumber": "^5.6.0", + "@ethersproject/bytes": "^5.6.0", + "@ethersproject/constants": "^5.6.0", + "@ethersproject/logger": "^5.6.0", + "@ethersproject/properties": "^5.6.0", + "@ethersproject/transactions": "^5.6.0" + } + }, + "node_modules/@ethersproject/hash": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@ethersproject/hash/-/hash-5.6.0.tgz", + "integrity": "sha512-fFd+k9gtczqlr0/BruWLAu7UAOas1uRRJvOR84uDf4lNZ+bTkGl366qvniUZHKtlqxBRU65MkOobkmvmpHU+jA==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/abstract-signer": "^5.6.0", + "@ethersproject/address": "^5.6.0", + "@ethersproject/bignumber": "^5.6.0", + "@ethersproject/bytes": "^5.6.0", + "@ethersproject/keccak256": "^5.6.0", + "@ethersproject/logger": "^5.6.0", + "@ethersproject/properties": "^5.6.0", + "@ethersproject/strings": "^5.6.0" + } + }, + "node_modules/@ethersproject/hdnode": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@ethersproject/hdnode/-/hdnode-5.6.0.tgz", + "integrity": "sha512-61g3Jp3nwDqJcL/p4nugSyLrpl/+ChXIOtCEM8UDmWeB3JCAt5FoLdOMXQc3WWkc0oM2C0aAn6GFqqMcS/mHTw==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/abstract-signer": "^5.6.0", + "@ethersproject/basex": "^5.6.0", + "@ethersproject/bignumber": "^5.6.0", + "@ethersproject/bytes": "^5.6.0", + "@ethersproject/logger": "^5.6.0", + "@ethersproject/pbkdf2": "^5.6.0", + "@ethersproject/properties": "^5.6.0", + "@ethersproject/sha2": "^5.6.0", + "@ethersproject/signing-key": "^5.6.0", + "@ethersproject/strings": "^5.6.0", + "@ethersproject/transactions": "^5.6.0", + "@ethersproject/wordlists": "^5.6.0" + } + }, + "node_modules/@ethersproject/json-wallets": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@ethersproject/json-wallets/-/json-wallets-5.6.0.tgz", + "integrity": "sha512-fmh86jViB9r0ibWXTQipxpAGMiuxoqUf78oqJDlCAJXgnJF024hOOX7qVgqsjtbeoxmcLwpPsXNU0WEe/16qPQ==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/abstract-signer": "^5.6.0", + "@ethersproject/address": "^5.6.0", + "@ethersproject/bytes": "^5.6.0", + "@ethersproject/hdnode": "^5.6.0", + "@ethersproject/keccak256": "^5.6.0", + "@ethersproject/logger": "^5.6.0", + "@ethersproject/pbkdf2": "^5.6.0", + "@ethersproject/properties": "^5.6.0", + "@ethersproject/random": "^5.6.0", + "@ethersproject/strings": "^5.6.0", + "@ethersproject/transactions": "^5.6.0", + "aes-js": "3.0.0", + "scrypt-js": "3.0.1" + } + }, + "node_modules/@ethersproject/keccak256": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@ethersproject/keccak256/-/keccak256-5.6.0.tgz", + "integrity": "sha512-tk56BJ96mdj/ksi7HWZVWGjCq0WVl/QvfhFQNeL8fxhBlGoP+L80uDCiQcpJPd+2XxkivS3lwRm3E0CXTfol0w==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.6.0", + "js-sha3": "0.8.0" + } + }, + "node_modules/@ethersproject/logger": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@ethersproject/logger/-/logger-5.6.0.tgz", + "integrity": "sha512-BiBWllUROH9w+P21RzoxJKzqoqpkyM1pRnEKG69bulE9TSQD8SAIvTQqIMZmmCO8pUNkgLP1wndX1gKghSpBmg==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ] + }, + "node_modules/@ethersproject/networks": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@ethersproject/networks/-/networks-5.6.0.tgz", + "integrity": "sha512-DaVzgyThzHgSDLuURhvkp4oviGoGe9iTZW4jMEORHDRCgSZ9K9THGFKqL+qGXqPAYLEgZTf5z2w56mRrPR1MjQ==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/logger": "^5.6.0" + } + }, + "node_modules/@ethersproject/pbkdf2": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@ethersproject/pbkdf2/-/pbkdf2-5.6.0.tgz", + "integrity": "sha512-Wu1AxTgJo3T3H6MIu/eejLFok9TYoSdgwRr5oGY1LTLfmGesDoSx05pemsbrPT2gG4cQME+baTSCp5sEo2erZQ==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.6.0", + "@ethersproject/sha2": "^5.6.0" + } + }, + "node_modules/@ethersproject/properties": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@ethersproject/properties/-/properties-5.6.0.tgz", + "integrity": "sha512-szoOkHskajKePTJSZ46uHUWWkbv7TzP2ypdEK6jGMqJaEt2sb0jCgfBo0gH0m2HBpRixMuJ6TBRaQCF7a9DoCg==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/logger": "^5.6.0" + } + }, + "node_modules/@ethersproject/providers": { + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/@ethersproject/providers/-/providers-5.6.1.tgz", + "integrity": "sha512-w8Wx15nH+aVDvnoKCyI1f3x0B5idmk/bDJXMEUqCfdO8Eadd0QpDx9lDMTMmenhOmf9vufLJXjpSm24D3ZnVpg==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/abstract-provider": "^5.6.0", + "@ethersproject/abstract-signer": "^5.6.0", + "@ethersproject/address": "^5.6.0", + "@ethersproject/basex": "^5.6.0", + "@ethersproject/bignumber": "^5.6.0", + "@ethersproject/bytes": "^5.6.0", + "@ethersproject/constants": "^5.6.0", + "@ethersproject/hash": "^5.6.0", + "@ethersproject/logger": "^5.6.0", + "@ethersproject/networks": "^5.6.0", + "@ethersproject/properties": "^5.6.0", + "@ethersproject/random": "^5.6.0", + "@ethersproject/rlp": "^5.6.0", + "@ethersproject/sha2": "^5.6.0", + "@ethersproject/strings": "^5.6.0", + "@ethersproject/transactions": "^5.6.0", + "@ethersproject/web": "^5.6.0", + "bech32": "1.1.4", + "ws": "7.4.6" + } + }, + "node_modules/@ethersproject/providers/node_modules/bech32": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz", + "integrity": "sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ==" + }, + "node_modules/@ethersproject/providers/node_modules/ws": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", + "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==", + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/@ethersproject/random": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@ethersproject/random/-/random-5.6.0.tgz", + "integrity": "sha512-si0PLcLjq+NG/XHSZz90asNf+YfKEqJGVdxoEkSukzbnBgC8rydbgbUgBbBGLeHN4kAJwUFEKsu3sCXT93YMsw==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.6.0", + "@ethersproject/logger": "^5.6.0" + } + }, + "node_modules/@ethersproject/rlp": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@ethersproject/rlp/-/rlp-5.6.0.tgz", + "integrity": "sha512-dz9WR1xpcTL+9DtOT/aDO+YyxSSdO8YIS0jyZwHHSlAmnxA6cKU3TrTd4Xc/bHayctxTgGLYNuVVoiXE4tTq1g==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.6.0", + "@ethersproject/logger": "^5.6.0" + } + }, + "node_modules/@ethersproject/sha2": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@ethersproject/sha2/-/sha2-5.6.0.tgz", + "integrity": "sha512-1tNWCPFLu1n3JM9t4/kytz35DkuF9MxqkGGEHNauEbaARdm2fafnOyw1s0tIQDPKF/7bkP1u3dbrmjpn5CelyA==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.6.0", + "@ethersproject/logger": "^5.6.0", + "hash.js": "1.1.7" + } + }, + "node_modules/@ethersproject/signing-key": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@ethersproject/signing-key/-/signing-key-5.6.0.tgz", + "integrity": "sha512-S+njkhowmLeUu/r7ir8n78OUKx63kBdMCPssePS89So1TH4hZqnWFsThEd/GiXYp9qMxVrydf7KdM9MTGPFukA==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.6.0", + "@ethersproject/logger": "^5.6.0", + "@ethersproject/properties": "^5.6.0", + "bn.js": "^4.11.9", + "elliptic": "6.5.4", + "hash.js": "1.1.7" + } + }, + "node_modules/@ethersproject/solidity": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@ethersproject/solidity/-/solidity-5.6.0.tgz", + "integrity": "sha512-YwF52vTNd50kjDzqKaoNNbC/r9kMDPq3YzDWmsjFTRBcIF1y4JCQJ8gB30wsTfHbaxgxelI5BfxQSxD/PbJOww==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bignumber": "^5.6.0", + "@ethersproject/bytes": "^5.6.0", + "@ethersproject/keccak256": "^5.6.0", + "@ethersproject/logger": "^5.6.0", + "@ethersproject/sha2": "^5.6.0", + "@ethersproject/strings": "^5.6.0" + } + }, + "node_modules/@ethersproject/strings": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@ethersproject/strings/-/strings-5.6.0.tgz", + "integrity": "sha512-uv10vTtLTZqrJuqBZR862ZQjTIa724wGPWQqZrofaPI/kUsf53TBG0I0D+hQ1qyNtllbNzaW+PDPHHUI6/65Mg==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.6.0", + "@ethersproject/constants": "^5.6.0", + "@ethersproject/logger": "^5.6.0" + } + }, + "node_modules/@ethersproject/transactions": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@ethersproject/transactions/-/transactions-5.6.0.tgz", + "integrity": "sha512-4HX+VOhNjXHZyGzER6E/LVI2i6lf9ejYeWD6l4g50AdmimyuStKc39kvKf1bXWQMg7QNVh+uC7dYwtaZ02IXeg==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/address": "^5.6.0", + "@ethersproject/bignumber": "^5.6.0", + "@ethersproject/bytes": "^5.6.0", + "@ethersproject/constants": "^5.6.0", + "@ethersproject/keccak256": "^5.6.0", + "@ethersproject/logger": "^5.6.0", + "@ethersproject/properties": "^5.6.0", + "@ethersproject/rlp": "^5.6.0", + "@ethersproject/signing-key": "^5.6.0" + } + }, + "node_modules/@ethersproject/units": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@ethersproject/units/-/units-5.6.0.tgz", + "integrity": "sha512-tig9x0Qmh8qbo1w8/6tmtyrm/QQRviBh389EQ+d8fP4wDsBrJBf08oZfoiz1/uenKK9M78yAP4PoR7SsVoTjsw==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bignumber": "^5.6.0", + "@ethersproject/constants": "^5.6.0", + "@ethersproject/logger": "^5.6.0" + } + }, + "node_modules/@ethersproject/wallet": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@ethersproject/wallet/-/wallet-5.6.0.tgz", + "integrity": "sha512-qMlSdOSTyp0MBeE+r7SUhr1jjDlC1zAXB8VD84hCnpijPQiSNbxr6GdiLXxpUs8UKzkDiNYYC5DRI3MZr+n+tg==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/abstract-provider": "^5.6.0", + "@ethersproject/abstract-signer": "^5.6.0", + "@ethersproject/address": "^5.6.0", + "@ethersproject/bignumber": "^5.6.0", + "@ethersproject/bytes": "^5.6.0", + "@ethersproject/hash": "^5.6.0", + "@ethersproject/hdnode": "^5.6.0", + "@ethersproject/json-wallets": "^5.6.0", + "@ethersproject/keccak256": "^5.6.0", + "@ethersproject/logger": "^5.6.0", + "@ethersproject/properties": "^5.6.0", + "@ethersproject/random": "^5.6.0", + "@ethersproject/signing-key": "^5.6.0", + "@ethersproject/transactions": "^5.6.0", + "@ethersproject/wordlists": "^5.6.0" + } + }, + "node_modules/@ethersproject/web": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@ethersproject/web/-/web-5.6.0.tgz", + "integrity": "sha512-G/XHj0hV1FxI2teHRfCGvfBUHFmU+YOSbCxlAMqJklxSa7QMiHFQfAxvwY2PFqgvdkxEKwRNr/eCjfAPEm2Ctg==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/base64": "^5.6.0", + "@ethersproject/bytes": "^5.6.0", + "@ethersproject/logger": "^5.6.0", + "@ethersproject/properties": "^5.6.0", + "@ethersproject/strings": "^5.6.0" + } + }, + "node_modules/@ethersproject/wordlists": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@ethersproject/wordlists/-/wordlists-5.6.0.tgz", + "integrity": "sha512-q0bxNBfIX3fUuAo9OmjlEYxP40IB8ABgb7HjEZCL5IKubzV3j30CWi2rqQbjTS2HfoyQbfINoKcTVWP4ejwR7Q==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.6.0", + "@ethersproject/hash": "^5.6.0", + "@ethersproject/logger": "^5.6.0", + "@ethersproject/properties": "^5.6.0", + "@ethersproject/strings": "^5.6.0" + } + }, + "node_modules/@terra-money/terra.js": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@terra-money/terra.js/-/terra.js-2.0.11.tgz", + "integrity": "sha512-33MrW3SGEmbHzjAA93dw1VA6IKbp6gYJbOTUkQzQbi+msIUf4m9ZewB6v5b8UZJfTfEe/T5RjXCWQHmANsz11w==", + "dependencies": { + "axios": "^0.21.1", + "bech32": "^2.0.0", + "bip32": "^2.0.6", + "bip39": "^3.0.3", + "bufferutil": "^4.0.3", + "decimal.js": "^10.2.1", + "jscrypto": "^1.0.1", + "readable-stream": "^3.6.0", + "secp256k1": "^4.0.2", + "tmp": "^0.2.1", + "utf-8-validate": "^5.0.5", + "ws": "^7.4.2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@types/node": { + "version": "10.12.18", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.18.tgz", + "integrity": "sha512-fh+pAqt4xRzPfqA6eh3Z2y6fyZavRIumvjhaCL753+TVkGKGhpPeyrJG2JftD0T9q4GF00KjefsQ+PQNDdWQaQ==" + }, + "node_modules/aes-js": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-3.0.0.tgz", + "integrity": "sha1-4h3xCtbCBTKVvLuNq0Cwnb6ofk0=" + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/axios": { + "version": "0.21.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", + "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", + "dependencies": { + "follow-redirects": "^1.14.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "node_modules/base-x": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.8.tgz", + "integrity": "sha512-Rl/1AWP4J/zRrk54hhlxH4drNxPJXYUaKffODVI53/dAsV4t9fBxyxYKAVPU1XBHxYwOWP9h9H0hM2MVw4YfJA==", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/bech32": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/bech32/-/bech32-2.0.0.tgz", + "integrity": "sha512-LcknSilhIGatDAsY1ak2I8VtGaHNhgMSYVxFrGLXv+xLHytaKZKcaUJJUE7qmBr7h33o5YQwP55pMI0xmkpJwg==" + }, + "node_modules/bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "dependencies": { + "file-uri-to-path": "1.0.0" + } + }, + "node_modules/bip32": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/bip32/-/bip32-2.0.6.tgz", + "integrity": "sha512-HpV5OMLLGTjSVblmrtYRfFFKuQB+GArM0+XP8HGWfJ5vxYBqo+DesvJwOdC2WJ3bCkZShGf0QIfoIpeomVzVdA==", + "dependencies": { + "@types/node": "10.12.18", + "bs58check": "^2.1.1", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "tiny-secp256k1": "^1.1.3", + "typeforce": "^1.11.5", + "wif": "^2.0.6" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/bip39": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/bip39/-/bip39-3.0.4.tgz", + "integrity": "sha512-YZKQlb752TrUWqHWj7XAwCSjYEgGAk+/Aas3V7NyjQeZYsztO8JnQUaCWhcnL4T+jL8nvB8typ2jRPzTlgugNw==", + "dependencies": { + "@types/node": "11.11.6", + "create-hash": "^1.1.0", + "pbkdf2": "^3.0.9", + "randombytes": "^2.0.1" + } + }, + "node_modules/bip39/node_modules/@types/node": { + "version": "11.11.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-11.11.6.tgz", + "integrity": "sha512-Exw4yUWMBXM3X+8oqzJNRqZSwUAaS4+7NdvHqQuFi/d+synz++xmX3QIf+BFqneW8N31R8Ky+sikfZUXq07ggQ==" + }, + "node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=" + }, + "node_modules/bs58": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz", + "integrity": "sha1-vhYedsNU9veIrkBx9j806MTwpCo=", + "dependencies": { + "base-x": "^3.0.2" + } + }, + "node_modules/bs58check": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/bs58check/-/bs58check-2.1.2.tgz", + "integrity": "sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA==", + "dependencies": { + "bs58": "^4.0.0", + "create-hash": "^1.1.0", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/bufferutil": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.4.tgz", + "integrity": "sha512-VNxjXUCrF3LvbLgwfkTb5LsFvk6pGIn7OBb9x+3o+iJ6mKw0JTUp4chBFc88hi1aspeZGeZG9jAIbpFYPQSLZw==", + "hasInstallScript": true, + "dependencies": { + "node-gyp-build": "^4.2.0" + }, + "engines": { + "node": ">=6.14.2" + } + }, + "node_modules/cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "node_modules/create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "dependencies": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "node_modules/create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "dependencies": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "node_modules/decimal.js": { + "version": "10.3.1", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.3.1.tgz", + "integrity": "sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ==" + }, + "node_modules/elliptic": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", + "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", + "dependencies": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "engines": { + "node": ">=6" + } + }, + "node_modules/ethers": { + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/ethers/-/ethers-5.6.1.tgz", + "integrity": "sha512-qtl/2W+dwmUa5Z3JqwsbV3JEBZZHNARe5K/A2ePcNAuhJYnEKIgGOT/O9ouPwBijSqVoQnmQMzi5D48LFNOY2A==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/abi": "5.6.0", + "@ethersproject/abstract-provider": "5.6.0", + "@ethersproject/abstract-signer": "5.6.0", + "@ethersproject/address": "5.6.0", + "@ethersproject/base64": "5.6.0", + "@ethersproject/basex": "5.6.0", + "@ethersproject/bignumber": "5.6.0", + "@ethersproject/bytes": "5.6.0", + "@ethersproject/constants": "5.6.0", + "@ethersproject/contracts": "5.6.0", + "@ethersproject/hash": "5.6.0", + "@ethersproject/hdnode": "5.6.0", + "@ethersproject/json-wallets": "5.6.0", + "@ethersproject/keccak256": "5.6.0", + "@ethersproject/logger": "5.6.0", + "@ethersproject/networks": "5.6.0", + "@ethersproject/pbkdf2": "5.6.0", + "@ethersproject/properties": "5.6.0", + "@ethersproject/providers": "5.6.1", + "@ethersproject/random": "5.6.0", + "@ethersproject/rlp": "5.6.0", + "@ethersproject/sha2": "5.6.0", + "@ethersproject/signing-key": "5.6.0", + "@ethersproject/solidity": "5.6.0", + "@ethersproject/strings": "5.6.0", + "@ethersproject/transactions": "5.6.0", + "@ethersproject/units": "5.6.0", + "@ethersproject/wallet": "5.6.0", + "@ethersproject/web": "5.6.0", + "@ethersproject/wordlists": "5.6.0" + } + }, + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" + }, + "node_modules/follow-redirects": { + "version": "1.14.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz", + "integrity": "sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/hash-base": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", + "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", + "dependencies": { + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "dependencies": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "node_modules/hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "dependencies": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/js-sha3": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", + "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==" + }, + "node_modules/jscrypto": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/jscrypto/-/jscrypto-1.0.2.tgz", + "integrity": "sha512-r+oNJLGTv1nkNMBBq3c70xYrFDgJOYVgs2OHijz5Ht+0KJ0yObD0oYxC9mN72KLzVfXw+osspg6t27IZvuTUxw==", + "bin": { + "jscrypto": "bin/cli.js" + } + }, + "node_modules/md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" + }, + "node_modules/minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=" + }, + "node_modules/minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/nan": { + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz", + "integrity": "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==" + }, + "node_modules/node-addon-api": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-2.0.2.tgz", + "integrity": "sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA==" + }, + "node_modules/node-gyp-build": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.3.0.tgz", + "integrity": "sha512-iWjXZvmboq0ja1pUGULQBexmxq8CV4xBhX7VDOTbL7ZR4FOowwY/VOtRxBN/yKxmdGoIp4j5ysNT4u3S2pDQ3Q==", + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pbkdf2": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", + "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==", + "dependencies": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readonly-date": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/readonly-date/-/readonly-date-1.0.0.tgz", + "integrity": "sha512-tMKIV7hlk0h4mO3JTmmVuIlJVXjKk3Sep9Bf5OH0O+758ruuVkUy2J9SttDLm91IEX/WHlXPSpxMGjPj4beMIQ==" + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/scrypt-js": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/scrypt-js/-/scrypt-js-3.0.1.tgz", + "integrity": "sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA==" + }, + "node_modules/secp256k1": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-4.0.2.tgz", + "integrity": "sha512-UDar4sKvWAksIlfX3xIaQReADn+WFnHvbVujpcbr+9Sf/69odMwy2MUsz5CKLQgX9nsIyrjuxL2imVyoNHa3fg==", + "hasInstallScript": true, + "dependencies": { + "elliptic": "^6.5.2", + "node-addon-api": "^2.0.0", + "node-gyp-build": "^4.2.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + }, + "bin": { + "sha.js": "bin.js" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tiny-secp256k1": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/tiny-secp256k1/-/tiny-secp256k1-1.1.6.tgz", + "integrity": "sha512-FmqJZGduTyvsr2cF3375fqGHUovSwDi/QytexX1Se4BPuPZpTE5Ftp5fg+EFSuEf3lhZqgCRjEG3ydUQ/aNiwA==", + "hasInstallScript": true, + "dependencies": { + "bindings": "^1.3.0", + "bn.js": "^4.11.8", + "create-hmac": "^1.1.7", + "elliptic": "^6.4.0", + "nan": "^2.13.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/tmp": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", + "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", + "dependencies": { + "rimraf": "^3.0.0" + }, + "engines": { + "node": ">=8.17.0" + } + }, + "node_modules/typeforce": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/typeforce/-/typeforce-1.18.0.tgz", + "integrity": "sha512-7uc1O8h1M1g0rArakJdf0uLRSSgFcYexrVoKo+bzJd32gd4gDy2L/Z+8/FjPnU9ydY3pEnVPtr9FyscYY60K1g==" + }, + "node_modules/utf-8-validate": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.6.tgz", + "integrity": "sha512-hoY0gOf9EkCw+nimK21FVKHUIG1BMqSiRwxB/q3A9yKZOrOI99PP77BxmarDqWz6rG3vVYiBWfhG8z2Tl+7fZA==", + "hasInstallScript": true, + "dependencies": { + "node-gyp-build": "^4.2.0" + }, + "engines": { + "node": ">=6.14.2" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "node_modules/wif": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/wif/-/wif-2.0.6.tgz", + "integrity": "sha1-CNP1IFbGZnkplyb63g1DKudLRwQ=", + "dependencies": { + "bs58check": "<3.0.0" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "node_modules/ws": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.5.tgz", + "integrity": "sha512-BAkMFcAzl8as1G/hArkxOxq3G7pjUqQ3gzYbLL0/5zNkph70e+lCoxBGnm6AW1+/aiNeV4fnKqZ8m4GZewmH2w==", + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs": { + "version": "17.3.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.3.1.tgz", + "integrity": "sha512-WUANQeVgjLbNsEmGk20f+nlHgOqzRFpiGWVaBrYGYIGANIIu3lWjoyi0fNlFmJkvfhCZ6BXINe7/W2O2bV4iaA==", + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.0.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.1.tgz", + "integrity": "sha512-9BK1jFpLzJROCI5TzwZL/TU4gqjK5xiHV/RfWLOahrjAko/e4DJkRDZQXfvqAsiZzzYhgAzbgz6lg48jcm4GLg==", + "engines": { + "node": ">=12" + } + } + }, + "dependencies": { + "@cosmjs/encoding": { + "version": "0.26.6", + "resolved": "https://registry.npmjs.org/@cosmjs/encoding/-/encoding-0.26.6.tgz", + "integrity": "sha512-dU0P2Um9ZB5yHpQYq+a6XnPKV4LD1kHd3nggbD0smn7wTwWW1XJKlms40SBZHtbm4dW9wPaPGf4yOkwwBdJO+w==", + "requires": { + "base64-js": "^1.3.0", + "bech32": "^1.1.4", + "readonly-date": "^1.0.0" + }, + "dependencies": { + "bech32": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz", + "integrity": "sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ==" + } + } + }, + "@ethersproject/abi": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.6.0.tgz", + "integrity": "sha512-AhVByTwdXCc2YQ20v300w6KVHle9g2OFc28ZAFCPnJyEpkv1xKXjZcSTgWOlv1i+0dqlgF8RCF2Rn2KC1t+1Vg==", + "requires": { + "@ethersproject/address": "^5.6.0", + "@ethersproject/bignumber": "^5.6.0", + "@ethersproject/bytes": "^5.6.0", + "@ethersproject/constants": "^5.6.0", + "@ethersproject/hash": "^5.6.0", + "@ethersproject/keccak256": "^5.6.0", + "@ethersproject/logger": "^5.6.0", + "@ethersproject/properties": "^5.6.0", + "@ethersproject/strings": "^5.6.0" + } + }, + "@ethersproject/abstract-provider": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@ethersproject/abstract-provider/-/abstract-provider-5.6.0.tgz", + "integrity": "sha512-oPMFlKLN+g+y7a79cLK3WiLcjWFnZQtXWgnLAbHZcN3s7L4v90UHpTOrLk+m3yr0gt+/h9STTM6zrr7PM8uoRw==", + "requires": { + "@ethersproject/bignumber": "^5.6.0", + "@ethersproject/bytes": "^5.6.0", + "@ethersproject/logger": "^5.6.0", + "@ethersproject/networks": "^5.6.0", + "@ethersproject/properties": "^5.6.0", + "@ethersproject/transactions": "^5.6.0", + "@ethersproject/web": "^5.6.0" + } + }, + "@ethersproject/abstract-signer": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@ethersproject/abstract-signer/-/abstract-signer-5.6.0.tgz", + "integrity": "sha512-WOqnG0NJKtI8n0wWZPReHtaLkDByPL67tn4nBaDAhmVq8sjHTPbCdz4DRhVu/cfTOvfy9w3iq5QZ7BX7zw56BQ==", + "requires": { + "@ethersproject/abstract-provider": "^5.6.0", + "@ethersproject/bignumber": "^5.6.0", + "@ethersproject/bytes": "^5.6.0", + "@ethersproject/logger": "^5.6.0", + "@ethersproject/properties": "^5.6.0" + } + }, + "@ethersproject/address": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@ethersproject/address/-/address-5.6.0.tgz", + "integrity": "sha512-6nvhYXjbXsHPS+30sHZ+U4VMagFC/9zAk6Gd/h3S21YW4+yfb0WfRtaAIZ4kfM4rrVwqiy284LP0GtL5HXGLxQ==", + "requires": { + "@ethersproject/bignumber": "^5.6.0", + "@ethersproject/bytes": "^5.6.0", + "@ethersproject/keccak256": "^5.6.0", + "@ethersproject/logger": "^5.6.0", + "@ethersproject/rlp": "^5.6.0" + } + }, + "@ethersproject/base64": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@ethersproject/base64/-/base64-5.6.0.tgz", + "integrity": "sha512-2Neq8wxJ9xHxCF9TUgmKeSh9BXJ6OAxWfeGWvbauPh8FuHEjamgHilllx8KkSd5ErxyHIX7Xv3Fkcud2kY9ezw==", + "requires": { + "@ethersproject/bytes": "^5.6.0" + } + }, + "@ethersproject/basex": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@ethersproject/basex/-/basex-5.6.0.tgz", + "integrity": "sha512-qN4T+hQd/Md32MoJpc69rOwLYRUXwjTlhHDIeUkUmiN/JyWkkLLMoG0TqvSQKNqZOMgN5stbUYN6ILC+eD7MEQ==", + "requires": { + "@ethersproject/bytes": "^5.6.0", + "@ethersproject/properties": "^5.6.0" + } + }, + "@ethersproject/bignumber": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@ethersproject/bignumber/-/bignumber-5.6.0.tgz", + "integrity": "sha512-VziMaXIUHQlHJmkv1dlcd6GY2PmT0khtAqaMctCIDogxkrarMzA9L94KN1NeXqqOfFD6r0sJT3vCTOFSmZ07DA==", + "requires": { + "@ethersproject/bytes": "^5.6.0", + "@ethersproject/logger": "^5.6.0", + "bn.js": "^4.11.9" + } + }, + "@ethersproject/bytes": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@ethersproject/bytes/-/bytes-5.6.0.tgz", + "integrity": "sha512-3hJPlYemb9V4VLfJF5BfN0+55vltPZSHU3QKUyP9M3Y2TcajbiRrz65UG+xVHOzBereB1b9mn7r12o177xgN7w==", + "requires": { + "@ethersproject/logger": "^5.6.0" + } + }, + "@ethersproject/constants": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@ethersproject/constants/-/constants-5.6.0.tgz", + "integrity": "sha512-SrdaJx2bK0WQl23nSpV/b1aq293Lh0sUaZT/yYKPDKn4tlAbkH96SPJwIhwSwTsoQQZxuh1jnqsKwyymoiBdWA==", + "requires": { + "@ethersproject/bignumber": "^5.6.0" + } + }, + "@ethersproject/contracts": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@ethersproject/contracts/-/contracts-5.6.0.tgz", + "integrity": "sha512-74Ge7iqTDom0NX+mux8KbRUeJgu1eHZ3iv6utv++sLJG80FVuU9HnHeKVPfjd9s3woFhaFoQGf3B3iH/FrQmgw==", + "requires": { + "@ethersproject/abi": "^5.6.0", + "@ethersproject/abstract-provider": "^5.6.0", + "@ethersproject/abstract-signer": "^5.6.0", + "@ethersproject/address": "^5.6.0", + "@ethersproject/bignumber": "^5.6.0", + "@ethersproject/bytes": "^5.6.0", + "@ethersproject/constants": "^5.6.0", + "@ethersproject/logger": "^5.6.0", + "@ethersproject/properties": "^5.6.0", + "@ethersproject/transactions": "^5.6.0" + } + }, + "@ethersproject/hash": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@ethersproject/hash/-/hash-5.6.0.tgz", + "integrity": "sha512-fFd+k9gtczqlr0/BruWLAu7UAOas1uRRJvOR84uDf4lNZ+bTkGl366qvniUZHKtlqxBRU65MkOobkmvmpHU+jA==", + "requires": { + "@ethersproject/abstract-signer": "^5.6.0", + "@ethersproject/address": "^5.6.0", + "@ethersproject/bignumber": "^5.6.0", + "@ethersproject/bytes": "^5.6.0", + "@ethersproject/keccak256": "^5.6.0", + "@ethersproject/logger": "^5.6.0", + "@ethersproject/properties": "^5.6.0", + "@ethersproject/strings": "^5.6.0" + } + }, + "@ethersproject/hdnode": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@ethersproject/hdnode/-/hdnode-5.6.0.tgz", + "integrity": "sha512-61g3Jp3nwDqJcL/p4nugSyLrpl/+ChXIOtCEM8UDmWeB3JCAt5FoLdOMXQc3WWkc0oM2C0aAn6GFqqMcS/mHTw==", + "requires": { + "@ethersproject/abstract-signer": "^5.6.0", + "@ethersproject/basex": "^5.6.0", + "@ethersproject/bignumber": "^5.6.0", + "@ethersproject/bytes": "^5.6.0", + "@ethersproject/logger": "^5.6.0", + "@ethersproject/pbkdf2": "^5.6.0", + "@ethersproject/properties": "^5.6.0", + "@ethersproject/sha2": "^5.6.0", + "@ethersproject/signing-key": "^5.6.0", + "@ethersproject/strings": "^5.6.0", + "@ethersproject/transactions": "^5.6.0", + "@ethersproject/wordlists": "^5.6.0" + } + }, + "@ethersproject/json-wallets": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@ethersproject/json-wallets/-/json-wallets-5.6.0.tgz", + "integrity": "sha512-fmh86jViB9r0ibWXTQipxpAGMiuxoqUf78oqJDlCAJXgnJF024hOOX7qVgqsjtbeoxmcLwpPsXNU0WEe/16qPQ==", + "requires": { + "@ethersproject/abstract-signer": "^5.6.0", + "@ethersproject/address": "^5.6.0", + "@ethersproject/bytes": "^5.6.0", + "@ethersproject/hdnode": "^5.6.0", + "@ethersproject/keccak256": "^5.6.0", + "@ethersproject/logger": "^5.6.0", + "@ethersproject/pbkdf2": "^5.6.0", + "@ethersproject/properties": "^5.6.0", + "@ethersproject/random": "^5.6.0", + "@ethersproject/strings": "^5.6.0", + "@ethersproject/transactions": "^5.6.0", + "aes-js": "3.0.0", + "scrypt-js": "3.0.1" + } + }, + "@ethersproject/keccak256": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@ethersproject/keccak256/-/keccak256-5.6.0.tgz", + "integrity": "sha512-tk56BJ96mdj/ksi7HWZVWGjCq0WVl/QvfhFQNeL8fxhBlGoP+L80uDCiQcpJPd+2XxkivS3lwRm3E0CXTfol0w==", + "requires": { + "@ethersproject/bytes": "^5.6.0", + "js-sha3": "0.8.0" + } + }, + "@ethersproject/logger": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@ethersproject/logger/-/logger-5.6.0.tgz", + "integrity": "sha512-BiBWllUROH9w+P21RzoxJKzqoqpkyM1pRnEKG69bulE9TSQD8SAIvTQqIMZmmCO8pUNkgLP1wndX1gKghSpBmg==" + }, + "@ethersproject/networks": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@ethersproject/networks/-/networks-5.6.0.tgz", + "integrity": "sha512-DaVzgyThzHgSDLuURhvkp4oviGoGe9iTZW4jMEORHDRCgSZ9K9THGFKqL+qGXqPAYLEgZTf5z2w56mRrPR1MjQ==", + "requires": { + "@ethersproject/logger": "^5.6.0" + } + }, + "@ethersproject/pbkdf2": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@ethersproject/pbkdf2/-/pbkdf2-5.6.0.tgz", + "integrity": "sha512-Wu1AxTgJo3T3H6MIu/eejLFok9TYoSdgwRr5oGY1LTLfmGesDoSx05pemsbrPT2gG4cQME+baTSCp5sEo2erZQ==", + "requires": { + "@ethersproject/bytes": "^5.6.0", + "@ethersproject/sha2": "^5.6.0" + } + }, + "@ethersproject/properties": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@ethersproject/properties/-/properties-5.6.0.tgz", + "integrity": "sha512-szoOkHskajKePTJSZ46uHUWWkbv7TzP2ypdEK6jGMqJaEt2sb0jCgfBo0gH0m2HBpRixMuJ6TBRaQCF7a9DoCg==", + "requires": { + "@ethersproject/logger": "^5.6.0" + } + }, + "@ethersproject/providers": { + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/@ethersproject/providers/-/providers-5.6.1.tgz", + "integrity": "sha512-w8Wx15nH+aVDvnoKCyI1f3x0B5idmk/bDJXMEUqCfdO8Eadd0QpDx9lDMTMmenhOmf9vufLJXjpSm24D3ZnVpg==", + "requires": { + "@ethersproject/abstract-provider": "^5.6.0", + "@ethersproject/abstract-signer": "^5.6.0", + "@ethersproject/address": "^5.6.0", + "@ethersproject/basex": "^5.6.0", + "@ethersproject/bignumber": "^5.6.0", + "@ethersproject/bytes": "^5.6.0", + "@ethersproject/constants": "^5.6.0", + "@ethersproject/hash": "^5.6.0", + "@ethersproject/logger": "^5.6.0", + "@ethersproject/networks": "^5.6.0", + "@ethersproject/properties": "^5.6.0", + "@ethersproject/random": "^5.6.0", + "@ethersproject/rlp": "^5.6.0", + "@ethersproject/sha2": "^5.6.0", + "@ethersproject/strings": "^5.6.0", + "@ethersproject/transactions": "^5.6.0", + "@ethersproject/web": "^5.6.0", + "bech32": "1.1.4", + "ws": "7.4.6" + }, + "dependencies": { + "bech32": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz", + "integrity": "sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ==" + }, + "ws": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", + "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==", + "requires": {} + } + } + }, + "@ethersproject/random": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@ethersproject/random/-/random-5.6.0.tgz", + "integrity": "sha512-si0PLcLjq+NG/XHSZz90asNf+YfKEqJGVdxoEkSukzbnBgC8rydbgbUgBbBGLeHN4kAJwUFEKsu3sCXT93YMsw==", + "requires": { + "@ethersproject/bytes": "^5.6.0", + "@ethersproject/logger": "^5.6.0" + } + }, + "@ethersproject/rlp": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@ethersproject/rlp/-/rlp-5.6.0.tgz", + "integrity": "sha512-dz9WR1xpcTL+9DtOT/aDO+YyxSSdO8YIS0jyZwHHSlAmnxA6cKU3TrTd4Xc/bHayctxTgGLYNuVVoiXE4tTq1g==", + "requires": { + "@ethersproject/bytes": "^5.6.0", + "@ethersproject/logger": "^5.6.0" + } + }, + "@ethersproject/sha2": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@ethersproject/sha2/-/sha2-5.6.0.tgz", + "integrity": "sha512-1tNWCPFLu1n3JM9t4/kytz35DkuF9MxqkGGEHNauEbaARdm2fafnOyw1s0tIQDPKF/7bkP1u3dbrmjpn5CelyA==", + "requires": { + "@ethersproject/bytes": "^5.6.0", + "@ethersproject/logger": "^5.6.0", + "hash.js": "1.1.7" + } + }, + "@ethersproject/signing-key": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@ethersproject/signing-key/-/signing-key-5.6.0.tgz", + "integrity": "sha512-S+njkhowmLeUu/r7ir8n78OUKx63kBdMCPssePS89So1TH4hZqnWFsThEd/GiXYp9qMxVrydf7KdM9MTGPFukA==", + "requires": { + "@ethersproject/bytes": "^5.6.0", + "@ethersproject/logger": "^5.6.0", + "@ethersproject/properties": "^5.6.0", + "bn.js": "^4.11.9", + "elliptic": "6.5.4", + "hash.js": "1.1.7" + } + }, + "@ethersproject/solidity": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@ethersproject/solidity/-/solidity-5.6.0.tgz", + "integrity": "sha512-YwF52vTNd50kjDzqKaoNNbC/r9kMDPq3YzDWmsjFTRBcIF1y4JCQJ8gB30wsTfHbaxgxelI5BfxQSxD/PbJOww==", + "requires": { + "@ethersproject/bignumber": "^5.6.0", + "@ethersproject/bytes": "^5.6.0", + "@ethersproject/keccak256": "^5.6.0", + "@ethersproject/logger": "^5.6.0", + "@ethersproject/sha2": "^5.6.0", + "@ethersproject/strings": "^5.6.0" + } + }, + "@ethersproject/strings": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@ethersproject/strings/-/strings-5.6.0.tgz", + "integrity": "sha512-uv10vTtLTZqrJuqBZR862ZQjTIa724wGPWQqZrofaPI/kUsf53TBG0I0D+hQ1qyNtllbNzaW+PDPHHUI6/65Mg==", + "requires": { + "@ethersproject/bytes": "^5.6.0", + "@ethersproject/constants": "^5.6.0", + "@ethersproject/logger": "^5.6.0" + } + }, + "@ethersproject/transactions": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@ethersproject/transactions/-/transactions-5.6.0.tgz", + "integrity": "sha512-4HX+VOhNjXHZyGzER6E/LVI2i6lf9ejYeWD6l4g50AdmimyuStKc39kvKf1bXWQMg7QNVh+uC7dYwtaZ02IXeg==", + "requires": { + "@ethersproject/address": "^5.6.0", + "@ethersproject/bignumber": "^5.6.0", + "@ethersproject/bytes": "^5.6.0", + "@ethersproject/constants": "^5.6.0", + "@ethersproject/keccak256": "^5.6.0", + "@ethersproject/logger": "^5.6.0", + "@ethersproject/properties": "^5.6.0", + "@ethersproject/rlp": "^5.6.0", + "@ethersproject/signing-key": "^5.6.0" + } + }, + "@ethersproject/units": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@ethersproject/units/-/units-5.6.0.tgz", + "integrity": "sha512-tig9x0Qmh8qbo1w8/6tmtyrm/QQRviBh389EQ+d8fP4wDsBrJBf08oZfoiz1/uenKK9M78yAP4PoR7SsVoTjsw==", + "requires": { + "@ethersproject/bignumber": "^5.6.0", + "@ethersproject/constants": "^5.6.0", + "@ethersproject/logger": "^5.6.0" + } + }, + "@ethersproject/wallet": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@ethersproject/wallet/-/wallet-5.6.0.tgz", + "integrity": "sha512-qMlSdOSTyp0MBeE+r7SUhr1jjDlC1zAXB8VD84hCnpijPQiSNbxr6GdiLXxpUs8UKzkDiNYYC5DRI3MZr+n+tg==", + "requires": { + "@ethersproject/abstract-provider": "^5.6.0", + "@ethersproject/abstract-signer": "^5.6.0", + "@ethersproject/address": "^5.6.0", + "@ethersproject/bignumber": "^5.6.0", + "@ethersproject/bytes": "^5.6.0", + "@ethersproject/hash": "^5.6.0", + "@ethersproject/hdnode": "^5.6.0", + "@ethersproject/json-wallets": "^5.6.0", + "@ethersproject/keccak256": "^5.6.0", + "@ethersproject/logger": "^5.6.0", + "@ethersproject/properties": "^5.6.0", + "@ethersproject/random": "^5.6.0", + "@ethersproject/signing-key": "^5.6.0", + "@ethersproject/transactions": "^5.6.0", + "@ethersproject/wordlists": "^5.6.0" + } + }, + "@ethersproject/web": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@ethersproject/web/-/web-5.6.0.tgz", + "integrity": "sha512-G/XHj0hV1FxI2teHRfCGvfBUHFmU+YOSbCxlAMqJklxSa7QMiHFQfAxvwY2PFqgvdkxEKwRNr/eCjfAPEm2Ctg==", + "requires": { + "@ethersproject/base64": "^5.6.0", + "@ethersproject/bytes": "^5.6.0", + "@ethersproject/logger": "^5.6.0", + "@ethersproject/properties": "^5.6.0", + "@ethersproject/strings": "^5.6.0" + } + }, + "@ethersproject/wordlists": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@ethersproject/wordlists/-/wordlists-5.6.0.tgz", + "integrity": "sha512-q0bxNBfIX3fUuAo9OmjlEYxP40IB8ABgb7HjEZCL5IKubzV3j30CWi2rqQbjTS2HfoyQbfINoKcTVWP4ejwR7Q==", + "requires": { + "@ethersproject/bytes": "^5.6.0", + "@ethersproject/hash": "^5.6.0", + "@ethersproject/logger": "^5.6.0", + "@ethersproject/properties": "^5.6.0", + "@ethersproject/strings": "^5.6.0" + } + }, + "@terra-money/terra.js": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@terra-money/terra.js/-/terra.js-2.0.11.tgz", + "integrity": "sha512-33MrW3SGEmbHzjAA93dw1VA6IKbp6gYJbOTUkQzQbi+msIUf4m9ZewB6v5b8UZJfTfEe/T5RjXCWQHmANsz11w==", + "requires": { + "axios": "^0.21.1", + "bech32": "^2.0.0", + "bip32": "^2.0.6", + "bip39": "^3.0.3", + "bufferutil": "^4.0.3", + "decimal.js": "^10.2.1", + "jscrypto": "^1.0.1", + "readable-stream": "^3.6.0", + "secp256k1": "^4.0.2", + "tmp": "^0.2.1", + "utf-8-validate": "^5.0.5", + "ws": "^7.4.2" + } + }, + "@types/node": { + "version": "10.12.18", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.18.tgz", + "integrity": "sha512-fh+pAqt4xRzPfqA6eh3Z2y6fyZavRIumvjhaCL753+TVkGKGhpPeyrJG2JftD0T9q4GF00KjefsQ+PQNDdWQaQ==" + }, + "aes-js": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-3.0.0.tgz", + "integrity": "sha1-4h3xCtbCBTKVvLuNq0Cwnb6ofk0=" + }, + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "axios": { + "version": "0.21.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", + "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", + "requires": { + "follow-redirects": "^1.14.0" + } + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "base-x": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.8.tgz", + "integrity": "sha512-Rl/1AWP4J/zRrk54hhlxH4drNxPJXYUaKffODVI53/dAsV4t9fBxyxYKAVPU1XBHxYwOWP9h9H0hM2MVw4YfJA==", + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" + }, + "bech32": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/bech32/-/bech32-2.0.0.tgz", + "integrity": "sha512-LcknSilhIGatDAsY1ak2I8VtGaHNhgMSYVxFrGLXv+xLHytaKZKcaUJJUE7qmBr7h33o5YQwP55pMI0xmkpJwg==" + }, + "bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "requires": { + "file-uri-to-path": "1.0.0" + } + }, + "bip32": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/bip32/-/bip32-2.0.6.tgz", + "integrity": "sha512-HpV5OMLLGTjSVblmrtYRfFFKuQB+GArM0+XP8HGWfJ5vxYBqo+DesvJwOdC2WJ3bCkZShGf0QIfoIpeomVzVdA==", + "requires": { + "@types/node": "10.12.18", + "bs58check": "^2.1.1", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "tiny-secp256k1": "^1.1.3", + "typeforce": "^1.11.5", + "wif": "^2.0.6" + } + }, + "bip39": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/bip39/-/bip39-3.0.4.tgz", + "integrity": "sha512-YZKQlb752TrUWqHWj7XAwCSjYEgGAk+/Aas3V7NyjQeZYsztO8JnQUaCWhcnL4T+jL8nvB8typ2jRPzTlgugNw==", + "requires": { + "@types/node": "11.11.6", + "create-hash": "^1.1.0", + "pbkdf2": "^3.0.9", + "randombytes": "^2.0.1" + }, + "dependencies": { + "@types/node": { + "version": "11.11.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-11.11.6.tgz", + "integrity": "sha512-Exw4yUWMBXM3X+8oqzJNRqZSwUAaS4+7NdvHqQuFi/d+synz++xmX3QIf+BFqneW8N31R8Ky+sikfZUXq07ggQ==" + } + } + }, + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=" + }, + "bs58": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz", + "integrity": "sha1-vhYedsNU9veIrkBx9j806MTwpCo=", + "requires": { + "base-x": "^3.0.2" + } + }, + "bs58check": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/bs58check/-/bs58check-2.1.2.tgz", + "integrity": "sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA==", + "requires": { + "bs58": "^4.0.0", + "create-hash": "^1.1.0", + "safe-buffer": "^5.1.2" + } + }, + "bufferutil": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.4.tgz", + "integrity": "sha512-VNxjXUCrF3LvbLgwfkTb5LsFvk6pGIn7OBb9x+3o+iJ6mKw0JTUp4chBFc88hi1aspeZGeZG9jAIbpFYPQSLZw==", + "requires": { + "node-gyp-build": "^4.2.0" + } + }, + "cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "requires": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "requires": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "decimal.js": { + "version": "10.3.1", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.3.1.tgz", + "integrity": "sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ==" + }, + "elliptic": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", + "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", + "requires": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" + }, + "ethers": { + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/ethers/-/ethers-5.6.1.tgz", + "integrity": "sha512-qtl/2W+dwmUa5Z3JqwsbV3JEBZZHNARe5K/A2ePcNAuhJYnEKIgGOT/O9ouPwBijSqVoQnmQMzi5D48LFNOY2A==", + "requires": { + "@ethersproject/abi": "5.6.0", + "@ethersproject/abstract-provider": "5.6.0", + "@ethersproject/abstract-signer": "5.6.0", + "@ethersproject/address": "5.6.0", + "@ethersproject/base64": "5.6.0", + "@ethersproject/basex": "5.6.0", + "@ethersproject/bignumber": "5.6.0", + "@ethersproject/bytes": "5.6.0", + "@ethersproject/constants": "5.6.0", + "@ethersproject/contracts": "5.6.0", + "@ethersproject/hash": "5.6.0", + "@ethersproject/hdnode": "5.6.0", + "@ethersproject/json-wallets": "5.6.0", + "@ethersproject/keccak256": "5.6.0", + "@ethersproject/logger": "5.6.0", + "@ethersproject/networks": "5.6.0", + "@ethersproject/pbkdf2": "5.6.0", + "@ethersproject/properties": "5.6.0", + "@ethersproject/providers": "5.6.1", + "@ethersproject/random": "5.6.0", + "@ethersproject/rlp": "5.6.0", + "@ethersproject/sha2": "5.6.0", + "@ethersproject/signing-key": "5.6.0", + "@ethersproject/solidity": "5.6.0", + "@ethersproject/strings": "5.6.0", + "@ethersproject/transactions": "5.6.0", + "@ethersproject/units": "5.6.0", + "@ethersproject/wallet": "5.6.0", + "@ethersproject/web": "5.6.0", + "@ethersproject/wordlists": "5.6.0" + } + }, + "file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" + }, + "follow-redirects": { + "version": "1.14.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz", + "integrity": "sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w==" + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" + }, + "glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "hash-base": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", + "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", + "requires": { + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + } + }, + "hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "requires": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "requires": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" + }, + "js-sha3": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", + "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==" + }, + "jscrypto": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/jscrypto/-/jscrypto-1.0.2.tgz", + "integrity": "sha512-r+oNJLGTv1nkNMBBq3c70xYrFDgJOYVgs2OHijz5Ht+0KJ0yObD0oYxC9mN72KLzVfXw+osspg6t27IZvuTUxw==" + }, + "md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" + }, + "minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=" + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "nan": { + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz", + "integrity": "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==" + }, + "node-addon-api": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-2.0.2.tgz", + "integrity": "sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA==" + }, + "node-gyp-build": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.3.0.tgz", + "integrity": "sha512-iWjXZvmboq0ja1pUGULQBexmxq8CV4xBhX7VDOTbL7ZR4FOowwY/VOtRxBN/yKxmdGoIp4j5ysNT4u3S2pDQ3Q==" + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + }, + "pbkdf2": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", + "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==", + "requires": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "readonly-date": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/readonly-date/-/readonly-date-1.0.0.tgz", + "integrity": "sha512-tMKIV7hlk0h4mO3JTmmVuIlJVXjKk3Sep9Bf5OH0O+758ruuVkUy2J9SttDLm91IEX/WHlXPSpxMGjPj4beMIQ==" + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "requires": { + "glob": "^7.1.3" + } + }, + "ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + }, + "scrypt-js": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/scrypt-js/-/scrypt-js-3.0.1.tgz", + "integrity": "sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA==" + }, + "secp256k1": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-4.0.2.tgz", + "integrity": "sha512-UDar4sKvWAksIlfX3xIaQReADn+WFnHvbVujpcbr+9Sf/69odMwy2MUsz5CKLQgX9nsIyrjuxL2imVyoNHa3fg==", + "requires": { + "elliptic": "^6.5.2", + "node-addon-api": "^2.0.0", + "node-gyp-build": "^4.2.0" + } + }, + "sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "requires": { + "safe-buffer": "~5.2.0" + } + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "tiny-secp256k1": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/tiny-secp256k1/-/tiny-secp256k1-1.1.6.tgz", + "integrity": "sha512-FmqJZGduTyvsr2cF3375fqGHUovSwDi/QytexX1Se4BPuPZpTE5Ftp5fg+EFSuEf3lhZqgCRjEG3ydUQ/aNiwA==", + "requires": { + "bindings": "^1.3.0", + "bn.js": "^4.11.8", + "create-hmac": "^1.1.7", + "elliptic": "^6.4.0", + "nan": "^2.13.2" + } + }, + "tmp": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", + "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", + "requires": { + "rimraf": "^3.0.0" + } + }, + "typeforce": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/typeforce/-/typeforce-1.18.0.tgz", + "integrity": "sha512-7uc1O8h1M1g0rArakJdf0uLRSSgFcYexrVoKo+bzJd32gd4gDy2L/Z+8/FjPnU9ydY3pEnVPtr9FyscYY60K1g==" + }, + "utf-8-validate": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.6.tgz", + "integrity": "sha512-hoY0gOf9EkCw+nimK21FVKHUIG1BMqSiRwxB/q3A9yKZOrOI99PP77BxmarDqWz6rG3vVYiBWfhG8z2Tl+7fZA==", + "requires": { + "node-gyp-build": "^4.2.0" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "wif": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/wif/-/wif-2.0.6.tgz", + "integrity": "sha1-CNP1IFbGZnkplyb63g1DKudLRwQ=", + "requires": { + "bs58check": "<3.0.0" + } + }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "ws": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.5.tgz", + "integrity": "sha512-BAkMFcAzl8as1G/hArkxOxq3G7pjUqQ3gzYbLL0/5zNkph70e+lCoxBGnm6AW1+/aiNeV4fnKqZ8m4GZewmH2w==", + "requires": {} + }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==" + }, + "yargs": { + "version": "17.3.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.3.1.tgz", + "integrity": "sha512-WUANQeVgjLbNsEmGk20f+nlHgOqzRFpiGWVaBrYGYIGANIIu3lWjoyi0fNlFmJkvfhCZ6BXINe7/W2O2bV4iaA==", + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.0.0" + } + }, + "yargs-parser": { + "version": "21.0.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.1.tgz", + "integrity": "sha512-9BK1jFpLzJROCI5TzwZL/TU4gqjK5xiHV/RfWLOahrjAko/e4DJkRDZQXfvqAsiZzzYhgAzbgz6lg48jcm4GLg==" + } + } +} diff --git a/examples/terra-contract-advanced/tools/package.json b/examples/terra-contract-advanced/tools/package.json new file mode 100644 index 0000000..9640c4a --- /dev/null +++ b/examples/terra-contract-advanced/tools/package.json @@ -0,0 +1,20 @@ +{ + "name": "tools", + "version": "1.0.0", + "description": "", + "main": "deploy.js", + "type": "module", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "deploy": "node deploy.js", + "query": "node query.js" + }, + "author": "", + "license": "ISC", + "dependencies": { + "@terra-money/terra.js": "^2.0.11", + "@cosmjs/encoding": "^0.26.2", + "ethers": "^5.4.4", + "yargs": "^17.0.1" + } +} diff --git a/examples/terra-contract-advanced/tools/query.js b/examples/terra-contract-advanced/tools/query.js new file mode 100644 index 0000000..06e60e5 --- /dev/null +++ b/examples/terra-contract-advanced/tools/query.js @@ -0,0 +1,37 @@ +import { LCDClient } from "@terra-money/terra.js"; +import yargs from "yargs"; +import {hideBin} from "yargs/helpers"; + +export const TERRA_GAS_PRICES_URL = "https://fcd.terra.dev/v1/txs/gas_prices"; + +const argv = yargs(hideBin(process.argv)) + .option('network', { + description: 'Which network to deploy to', + choices: ['testnet'], + required: true + }) + .option('contract', { + description: 'Contract address to query', + type: 'string', + required: true, + }) + .help() + .alias('help', 'h').argv; + +/* Set up terra client & wallet. It won't fail because inputs are validated with yargs */ + +const CONFIG = { + testnet: { + terraHost: { + URL: "https://bombay-lcd.terra.dev", + chainID: "bombay-12", + name: "testnet", + }, + } +} + +const lcd = new LCDClient(CONFIG[argv.network].terraHost); + + +let queryResult = await lcd.wasm.contractQuery(argv.contract, { "fetch_price": {} }) +console.log(queryResult) From b88fb2dcadab23814b60bac7b820901d2766ec56 Mon Sep 17 00:00:00 2001 From: Reisen Date: Tue, 3 May 2022 02:00:51 +0000 Subject: [PATCH 2/8] example/advanced: add documentation --- .../terra-contract-advanced/src/contract.rs | 21 ++++++++++++++++--- examples/terra-contract-advanced/src/msg.rs | 6 ++++++ examples/terra-contract-advanced/src/state.rs | 16 +++++++++++++- 3 files changed, 39 insertions(+), 4 deletions(-) diff --git a/examples/terra-contract-advanced/src/contract.rs b/examples/terra-contract-advanced/src/contract.rs index 97caffb..a3519f9 100644 --- a/examples/terra-contract-advanced/src/contract.rs +++ b/examples/terra-contract-advanced/src/contract.rs @@ -30,8 +30,16 @@ pub fn migrate(_deps: DepsMut, _env: Env, _msg: MigrateMsg) -> StdResult StdResult { match msg { @@ -76,6 +83,8 @@ pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { } } +/// Allow the caller to query the current (most recent) price the Oracle has observed, which is +/// stored in the contract state. fn query_fetch_price(deps: Deps) -> StdResult { let state = STATE.load(deps.storage)?; @@ -102,6 +111,9 @@ mod tests { use super::query_fetch_price; + /// set_price provides a helper that mutates the terra state for the example contract. This can + /// be used to make modifications to the state before invoking the contract itself. This is + /// helpful for testing contract behaviour with various states. pub fn set_price(deps: DepsMut, maybe_price: Option) { STATE .save( @@ -113,6 +125,8 @@ mod tests { .unwrap(); } + /// Quick test to confirm that after calling set_price, querying the contract state produces + /// the new price. #[test] pub fn test_query_fetch_price_ok() { let mut deps = mock_dependencies(&[]); @@ -131,6 +145,7 @@ mod tests { ); } + /// Quick test to make sure that when removing any price, the query fails. #[test] pub fn test_query_fetch_price_unavailable() { let mut deps = mock_dependencies(&[]); diff --git a/examples/terra-contract-advanced/src/msg.rs b/examples/terra-contract-advanced/src/msg.rs index 7213942..09fd0e6 100644 --- a/examples/terra-contract-advanced/src/msg.rs +++ b/examples/terra-contract-advanced/src/msg.rs @@ -12,6 +12,12 @@ use serde::{ #[serde(rename_all = "snake_case")] pub struct MigrateMsg {} +/// InstantiateMsg is provided during contract initialization. In this example, we define the +/// message as an choice of Oracle implementation that the deployer of the contract can pick from +/// to instantiate with. +/// +/// 1) PythOracle is simply an address of the Pyth contract to interact with. +/// 2) StubOracle provides a mock oracle showing how to unit test the contract against Pyth. #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] pub enum InstantiateMsg { StubOracle { diff --git a/examples/terra-contract-advanced/src/state.rs b/examples/terra-contract-advanced/src/state.rs index 87b7901..2739219 100644 --- a/examples/terra-contract-advanced/src/state.rs +++ b/examples/terra-contract-advanced/src/state.rs @@ -15,6 +15,13 @@ use pyth_sdk_terra::{ PriceIdentifier, }; +/// When the contract is initialized, the user can choose an Oracle to instantiate the contract +/// with. This choice is stored in the contract state using the `Oracle` enum. When the contract is +/// later interacted with, the selected oracle implementation is read from state in order to allow +/// the contract to dynamically choose the oracle it will use. +/// +/// In this example, we do this explicitly to show how to provide a Stub oracle to test against. +/// See `contract.rs` tests for examples. #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] pub struct State { pub oracle: Oracle, @@ -22,11 +29,18 @@ pub struct State { #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] pub enum Oracle { - Stub(Option), + /// Use Pyth as an Oracle, specifying the Pyth contract address. Pyth(Addr, PriceIdentifier), + + /// A Stub oracle, which returns a constant price stored in contract state. This is useful for + /// testing as it has no cross-contract interactions. + Stub(Option), } impl Oracle { + /// The `get_price` method will attempt to find the price of an asset. This method chooses the + /// oracle it will query based on the contract state. This function is an example of how to + /// mock oracle behaviour: note the `stub` match arm. pub fn get_price(&self, querier: &QuerierWrapper) -> Option { match self { Self::Stub(maybe_price) => *maybe_price, From 6741a230cec2ac1ad9ff79c8b45334fe9f01d81d Mon Sep 17 00:00:00 2001 From: Reisen Date: Tue, 3 May 2022 11:40:40 +0000 Subject: [PATCH 3/8] example/advanced: add price action test --- examples/terra-contract-advanced/Cargo.toml | 3 + .../terra-contract-advanced/src/contract.rs | 107 +++++++++++++++++- examples/terra-contract-advanced/src/msg.rs | 2 +- 3 files changed, 109 insertions(+), 3 deletions(-) diff --git a/examples/terra-contract-advanced/Cargo.toml b/examples/terra-contract-advanced/Cargo.toml index ce14774..c36585b 100644 --- a/examples/terra-contract-advanced/Cargo.toml +++ b/examples/terra-contract-advanced/Cargo.toml @@ -38,3 +38,6 @@ pyth-sdk-terra = { version = "0.3.0", path = "../../pyth-sdk-terra" } # Remove p [dev-dependencies] cosmwasm-schema = { version = "0.16.0" } +stochastic = { version = "0.5.2" } +probability = { version = "0.15.12" } +fraction = { version = "0.10.0" } diff --git a/examples/terra-contract-advanced/src/contract.rs b/examples/terra-contract-advanced/src/contract.rs index a3519f9..13a0d64 100644 --- a/examples/terra-contract-advanced/src/contract.rs +++ b/examples/terra-contract-advanced/src/contract.rs @@ -98,11 +98,18 @@ fn query_fetch_price(deps: Deps) -> StdResult { #[cfg(test)] mod tests { - use cosmwasm_std::testing::mock_dependencies; + use cosmwasm_std::testing::{ + mock_dependencies, + mock_env, + mock_info, + }; use cosmwasm_std::DepsMut; use pyth_sdk_terra::Price; - use crate::msg::FetchPriceResponse; + use crate::msg::{ + ExecuteMsg, + FetchPriceResponse, + }; use crate::state::{ Oracle, State, @@ -154,4 +161,100 @@ mod tests { assert!(query_fetch_price(deps.as_ref()).is_err()); } + + /// This test produces a stream of prices mimicing a real asset using fractional brownian + /// motion, and shows how to use `set_price` to feed this price stream into the contract to + /// test the contract behaviour when dealing with realistic price data. + /// + /// See the README for a visual graph of the price action generated by this test. + #[test] + pub fn test_stochastic_price_action() { + // Libraries used to generate simulated price action. (Consider switching to `noise`) + use probability::source; + use stochastic::gaussian::fractional::Motion; + + // We use tools from `fraction` to go from `f64` generated by stochastic to `Price`. + use fraction::Decimal; + use fraction::ToPrimitive; + + // Mock Terra components. + let mut deps = mock_dependencies(&[]); + let env = mock_env(); + let info = mock_info("test_contract", &[]); + + // Create a random source for our data, note that this source produces a deterministic + // random number stream and so will always produce consistent test data. + let mut source = source::default(); + + // Use a mildly jagged fractional brownian motion source (hurst 0.70) to model natural + // price movement. + let action = Motion::new(0.70); + let prices = action.sample(5000, 0.1, &mut source); + + // Stochastic generates negative and positive values, here we find the smallest negative + // value and shfit all values above this value to get a positive graph. Note that f64 does + // not implement `Ord` so we cannot simply `prices.iter().min()` to get the minimum. + // Instead we rely on `min_by`/`partial_cmp` and assume every `a` and `b` is comparable as + // `a < b` for our test case. + // + // See `PartialOrd` vs `Ord` applied to `f64 to understand why this is important in Rust if + // the reasoning behind this isn't clear. + let minima = -prices + .clone() + .into_iter() + .min_by(|a, b| a.partial_cmp(b).unwrap()) + .unwrap(); + + // This is the core of our test: we iterate through the 5000 generated points, and pass + // them to our contract. At the end, we can evaluate how the contract behaved in response + // to the price action. + for n in 0..5000 { + // Stochastic is generating +/- price action, shift everything above 0.0 to model more + // realistic price data. + let price = prices[n]; + let price = minima + price; + + // Use fraction to convert f64 to a u64 + u8, this matches how `Price` represents + // values in Pyth as an integer + exponent. + let price = Decimal::from(price); + let expo = price.get_precision(); // Number of decimal places. + let price = price * Decimal::from(10usize.pow(expo.into())); // Shift `.` to the right. + let price = price.to_u64().unwrap(); // Get value without `.` + + // Construct our `Price` with our newly calculated `price * 10^expo` values. + let price = Price { + price: price as i64, + conf: 5, + expo: -(expo as i32), + }; + + // Replace the price in the state before executing the contract. + set_price(deps.as_mut(), Some(price)); + + // Finally, invoke the contract itself! + super::execute( + deps.as_mut(), + env, + info, + ExecuteMsg, + ).unwrap(); + + // Test time. + // + // Here, you can insert code to analyze how your contract behaves around the price + // action above. Modifying the stochastic params and number of data points will help + // with testing different behaviours against the Oracle. The test here will depend on + // how you wish to use the Oracle. For example, if your program is sensitive to sudden + // price action, you will want to check here if your program breaks during violent + // rises and drops. + // + // See the README for a visual graph of the price action produced by this example test, + // but be sure to experiment with the input price data yourself to fully test your + // contract behaviour. + // + // REPLACE ME WITH A TEST! + unimplemented!("Replace me with a test against the resulting contract data!"); + } + + } } diff --git a/examples/terra-contract-advanced/src/msg.rs b/examples/terra-contract-advanced/src/msg.rs index 09fd0e6..4ad07f4 100644 --- a/examples/terra-contract-advanced/src/msg.rs +++ b/examples/terra-contract-advanced/src/msg.rs @@ -31,7 +31,7 @@ pub enum InstantiateMsg { #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] #[serde(rename_all = "snake_case")] -pub enum ExecuteMsg {} +pub struct ExecuteMsg; #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] #[serde(rename_all = "snake_case")] From 0c6d8526189375537800463eb01435f12bd5e7eb Mon Sep 17 00:00:00 2001 From: Reisen Date: Tue, 3 May 2022 11:41:04 +0000 Subject: [PATCH 4/8] example/advanced: update README to reflect new example --- examples/terra-contract-advanced/README.md | 60 +++++--------------- examples/terra-contract-advanced/prices.png | Bin 0 -> 66004 bytes 2 files changed, 14 insertions(+), 46 deletions(-) create mode 100644 examples/terra-contract-advanced/prices.png diff --git a/examples/terra-contract-advanced/README.md b/examples/terra-contract-advanced/README.md index 92f3dd1..51a3062 100644 --- a/examples/terra-contract-advanced/README.md +++ b/examples/terra-contract-advanced/README.md @@ -1,50 +1,17 @@ # Pyth SDK Example Contract for Terra -This repository contains an example contract that demonstrates how to read the Pyth price from the Pyth on-chain contract. -The example [contract](src/contract.rs) has two functions: +This repository contains an example contract that demonstrates how to use and +mock the Pyth oracle. This also includes a test showing an example of how to +generate mocked Pyth Price values to test with. -* `instantiate` sets the Pyth contract address and price feed id that the contract uses. - This function is intended to be called once when the contract is deployed. - See the [Terra SDK README](../../pyth-sdk-terra/README.md) for the list of possible price feed ids. -* `query` queries the Pyth contract to get the current price for the configured price feed id. +The test itself can be found in `contract.rs`, which feeds the contract with +the following price action: -## Testnet Demo - -This example contract is running on Terra testnet at `terra16h868tx50d3w37ry7c5lzzg648f7yetu39p5pd`. -This contract has been instantiated to return the price of `Crypto.LUNA/USD`. -You can query the contract from this repo by running: - -```sh -cd tools/ -# Install dependencies (if you haven't done so already) -npm install -# Query the contract -npm run query -- --network testnet --contract terra16h868tx50d3w37ry7c5lzzg648f7yetu39p5pd -``` - -Or by going to the contract address in [Terra Finder](https://finder.terra.money/) you can query make a query like below: -``` -{ - "fetch_price": {} -} -``` - -If the query is successful, the output should look like: -``` -{ - current_price: { price: "8704350000", conf: "3150000", expo: -8 }, - ema_price: { price: "8665158600", conf: "2965370", expo: -8 } -} -``` - -If the price feed is currently not available you will see: -``` -rpc error: code = Unknown desc = Generic error: Current price is not available: contract query failed -``` +![](./prices.png) ## Developing -If you would like to deploy a changed version of this contract, the process consists of two steps: +If you would like to deploy this contract, the process consists of two steps: 1. Build the WASM for the contract. 2. Upload the code and instantiate a new contract. @@ -56,8 +23,9 @@ The instructions in that document will build a file called `example_terra_contra ### Upload and Instantiate Contract -The tools directory contains a deployment script that will upload a WASM file and instantiate a new contract with it. -You can run that script on the built WASM file as follows: +The tools directory contains a deployment script that will upload a WASM file +and instantiate a new contract with it. You can run that script on the built +WASM file as follows: ``` sh cd tools/ @@ -65,8 +33,10 @@ npm install npm run deploy -- --network testnet --artifact ../artifacts/example_terra_contract.wasm --mnemonic "..." --instantiate ``` -This command will deploy the contract to `testnet` and sets its owner to the wallet with the provided `mnemonic`. -Note that you have to populate the `--mnemonic` flag with the seedphrase for a valid Terra wallet with some LUNA for the specified network. +This command will deploy the contract to `testnet` and sets its owner to the +wallet with the provided `mnemonic`. Note that you have to populate the +`--mnemonic` flag with the seedphrase for a valid Terra wallet with some LUNA +for the specified network. If successful, the output should look like: ``` @@ -79,8 +49,6 @@ Instantiated Pyth Example at terra123456789yelw23uh22nadqlyjvtl7s5527er97 (0x000 Deployed pyth example contract at terra123456789yelw23uh22nadqlyjvtl7s5527er97 ``` -By default, the deployment script sets the price feed to `Crypto.LUNA/USD` but you can change it in [deploy.js](tools/deploy.js). - ### Querying the Contract Once the contract is instantiated, you can query it by running: diff --git a/examples/terra-contract-advanced/prices.png b/examples/terra-contract-advanced/prices.png new file mode 100644 index 0000000000000000000000000000000000000000..aa2da002e6a09851e96b6588037f8bfc2b14bf2c GIT binary patch literal 66004 zcmeFZg`kq$__+kQ_&FF~ zI5^nY3v+W@{?7}ztnEy=+Y_a_;35PzGV1my6p0@47b{)-t0@W#g?cD;SIOn$%9yi@ z;s6cahIJgP)?%Uqg~qib^YAGaRbVj7*<6?vfe? zuBhL9zgBG$$2!0Fpw-fWp;vUSl%jg$tAex#E4SNtNRCv_JI z|9(p)M+t){v-RZu!*7W6J zBqIGMoCD!Y~H$F!*8ellYq(o0~6#gU>NBF?AQ2 z$WYwJ}}5kGt+uiMUxmEnSh>{CuoPGbA{&igT3 zT9eDm=h)cT{QUiY7qrkf_!HB7QeqE{uBU9+oNnRuKJg%;7Qolj)3b4M@{fo=9W)1yDzj?qHa<9CXF28D+Y9o8OiwlLs^hXw}nEDHW? zZPn|D6XeejyK?2qO5MvdtWoCXcJ}t_6E&X1HDYukPKE>o1kt(dS5&$#_>l&;wzoI7 zf8fVLSy@?KyMCSbkD9o+xWnqmWtbo7NY*E$moDL?Dg=o*uj#gg(cN-hqkR(`yt&oJ z-O$r>0e+up?`e%Lds*Xo$RsFu8P#2A^eBW{kdT*`H$sL*=IPVs)zQ)dlP>xcBO^mY zWUB`T2cJPnNlEF#biNp?5Y*Jtdi(C(tNneKqd)UWxNRTAy~XCbl1M&frt0>mKcl>5 z!+1wj)OK%8%VIbmYbaN%A<6qBXsRJ#IB%v#$G!~?4i4k( z+f?yF*3B7;Uu~B9uOmCSC7dB3CMHJZHhiJYXXfVoA24`cA`w60k-T>g3k3(^J6t8= z?c0~jLwPAM%v)YZw=}+4WE5$SmRbuP>5rGb=wUkAoA5?`+9N#5RL+vu&@d~`E-@ea z9WH)?LWR(Yki&M=I8;(m(chS?zhys1gsS`Xi)y+h{K4Vw>eT9L=$9`numvu{Jo;*K zwLG`3KKtRZzpA!t)1>|3;i17mrZVo{=Jq!Kfp~w5d$#pN4Qutu)vH%ydGxSh!$~n+ zV0|2`yAvBOc7Sg=Qp{T>Cn4c8ZYlWuHwziTNU^E!_IwwOr`40x)YLNj`SU0V_NdQt zNn#5gi+DHp8X6kDm#{XzQ5QbL)R-u` z*?5r%Wj;|ON+V=N>wLLG?c5JIju`t@(pY3ajX>Qsa!jvp}b}DD7*dG4*^N07fWU{@uo1(wXeUh?H0O|g+=Efg#HTVuK7`Oy!+hgV8?XrR58YSX*u@&`^KZ=<6oce2dk;5 zM0a}r{P}Y@QckQjYAjowUj<^#*MoDdF*;tY=CeOKI%X{)G#H%0J3q7gLC`$*&@a*yh8HK^LDqm3Tamt0j`#?u$nn1cgR#b;mBjwR5dhy#tE2yNAWN)FeJ1Ne@*iG86^nez1rw{l~W zhb&~#@7{ebk$d*+LRNY@kH%?82+`cql9_@tje$TVAqLLN;4Dw`aPqx?vck_PDVVjr zm7*@Y!yL7O@46ongdKnHRy!=d>-1dW$I8gti_H*-Y-R91dbaL``QWo)V1>;T)241~ zThMb1v!Di{T=fJF_p+Lzp8T~-?GqD8kJj=u%Rk$TcLY;HVB`=HseEfL8)(zb=pf1Q zDL%gIormoL9oFESpe;Ap0-+5deG z<@1=e0p{LJYjDE*v}5dM|11RUvvPMfaZrpxLKJXXvmDjWpFfXwdM)`9;-J|ob7bp| z({}GAQc|A>^sauD-hZ}?Qd$;LZrk3~svI?q#vE;TEst^Y^G8Pe+oJX3SKTGjS?62eF<9m)jCM57WEXc2&z$Q1tctC{q zlVN$K#D4$j)3BandpkSGp;yR)x-%5na#m+&XZK-;ch`D*3z+uez=YU1J3r`4d(xBh zT&Vry?Tg+gf7mV&-p^S&gjmIV(?UABXV=Cm(FFNlzp_b( z(W);WT%w?`e;8$4m>N8{kJGUi$5m=IhK&kr(%0teC0*JUHGbo`Jou6q7ljOS^7rrF zPhBPtF`Hg=;$C-rGI6}xqB*7aPG@6u%4Y6*W{)I@ICp=ueEv8=_%hykS_wBdVRm+Q zc}>j_B$>eNt5rB$hj`T+%F4NvCJq64Mi8?zw zVaG{4eoWLH&JYgM6^HS%cnlNjd9e7kJeU%KwLnF!8$*Fw2%P;m_m8~(1qLm4t;R6X zyZ|tc0MP7XhI9(d2G}88pMhMSrz|=kaB8B($rC5+yq3LvOHzh7dg<_fpeQ>8sg8Id z6V&jJx6H1iW+J#Vkg4?`eVQPDAt4*Xy%~zS?M(i(b96L9j+tV$V9PsNaWC}3`-Hx) zazi2#j{6%^XfOBOm8nqTZj6v|J7u+Eo!R;KH7=W<>rbabg~lg$F&NCiNbc?Gx%Jvp zt9*_QtPGUevuAJXk1>ECyvz6?wwep3tEMV!rsW;OYs{1>1UvRR;Y@~#?M;V^cdoAg z{{3Cs>yObl0Oj9I`&f73joY}d!YO&bPnsM36sXcKh^^l46v5TWI`9amvY)KO%KbPz ztd?>ZFHF3Ck3>{yIS8lUsJ}&x`ib)zS}!at488xgBE+_vHs5o;lg_s*W8Av?)7>h| zuX0Iq--zji`zPI*xVUl$F`;kX@VS1BmQ0>Z!*PcEDy)uwW%msksU&re$a)o|MQ3N{ z=#L-UM^#2~W|9NwdtI@oCnxK0&(r?k(kzpocgq^0v$e6A^a?q1=FBhm_3DXg3Rbl(4BndaB!Bz} zE|z(YyDf88V<5jw=d@iR4=&ktJw4tlUo~h68+aZqo7yX!EGRG?BL-n78_Eq$s^-BT z-4C{L%8c5gOW5zN4(2@9R39H7XYKe1X{&YIb)If+YASipumY}NG2fXWE!PaiiJFZK zPrQIB+4FA}REKbUhXFGEKsM>P`B|SQu{?LJZzEw++J3j(9Ah5KwRGosReJqL8T?&F<=nMdA^gsjLlBs3h zI?fl!6&8!VsTjVC!I6=Xg9JtphzGqYomPLJJV32Lrqwtse$n$vRmIToOXrR1uTK+x zC-hs56hAaig1fdIy-G$fT4WMFC`S1DmC*CZgS_J6_oX_J0wQHt(7S*j7CkrxOwK5t zYE`=_ubY4Ef_krPTEub5(B_TWJ%Q8GF0sRG6{GZy5Bvc)mIqXI1pkz+tXO=5_>;32 zG^Z0zFFFj!f;ifKXW^NzqKNk~W(Zx}=5z8Z4T+&Lckf~e1%FWEFwV%xkPa{R_*2-~ z6*dCd#f8(oz3%vgsM$+W`D9wDuC1-1YbtWryti7}sw#+SZ$)@+cL-TP zutL^Lvv7W8rTx{bS7pb!I<;2cv($3ts5v3S@GZNPyn2OgHQ@B%!Gq}NXq7BG%B(o; zTO1rASuq*wA0RH;v29kn?-@f#G5~M~u(Js2(gHd0;AcsPlwkiogRu`1WdQ6WY~3G+(f3yL})ZhnEeKis4UZW5-z07lcz ztJ-=qeq(*f4I=ADkqmoV+n&RZP#nx_8Rh+%Ut8<^W8LgF2nhHC0qf#&AaOpT~Py&Un9eQRl5uAX@pYoOIt5DwMMd)4_nS zeNKc>FBB)`b+}qum7BEGbqI;?*YBoa^8sP?i;ne7UrwH3OUj(7bX+EPj_T~b_1a%8 za07DR@1Gv<7IUpI;~3I==T0(AzX|joVGYu*SiYzi7wo7xjp5cAtek)W{(>W=25ApU z0t2YZvH@0Dbbh?e0VOt+p*FKp_wQ4Abea$4768mIfU)sI)SZLv1FXfcd0ut%@9F6e z3K=uGOOH|&B6HvHH4h(PmdhirnwXe4K{AL8>-!2j1btd;Ir>dF3bWHEXK%FloUyep%ns`Tj+0E>{q|i-X6tN^*qd*v=pFnz&*8u@66KRl?je|pfpn>Q%b z*|TROS`DiT`-4wDC`1FKbFTdKNn+g#s1DwJ;nnf#_{yBy%4uA{bL<1~!B~X{2T!ME zS`OvX(b9!_M?ruj=n_A@Lun8po4UVRI)UKQl$J$%C`v!xvc*PC&d;|zXYAC34;rrV z6hTQaz12mR*-D24$$reNd^Ek3tOP_@a>Uu;s(g;nU%6n5z&-BD9dmU?%q0D%Xn#C)00033_L{onNiW zg_nj(rJ!X=39sYgB-HyKinl7n z;%{0?y1L#*>Hdhgvaz=(vU|57MmLE z+F#J;esM)o(jsdF6Hy* z_RLZZTU%ZLLO3WODx3Mc?kHy2p(HYt^nnXQA3uy_r9yiH@8SZuH(7~&YGEM|;LIlZ zMwTVr62!c*3PdgJ)y$LL(tC4X8skbF1B@xh$bM`D2sxwiv?W*z|!go18PY7H{akc$EoVn;XEO^ct zi`fr7L~0-*KoP_*aMDN#K>=v1@|sD1V^23oBjxiS1x%sD7HHvGnW)vVR);VmSgHeT zgt^u!Z0dv?CO_X_{~Oh#xwN3hciGVOJp`sU{<*m%2RV(_aF-GSxtE~G{3;yfh*?t3 z&91AHh-@i(A054AY{qvp_N3PyFc$mMMYIbHL+ws=pbov86npR6fZ_VKq&M) z+@+#LUyEt`1$0LF7998S40}OUZ zE_-Av(RrDi+dj_I&7p34fV1dV!&*q@n5bU{x~566`& zcjvw0`e;OM-!5oul*;iGJJ~l#;U`BJnNs-)2;Z2s=Sl0lPayxIL$nn#`X}S)1rb7b z1=dZlOn(mwMl~oCRKJRO9>i+aXjqa(1XFNt8CPa4OJU6c!#al>K|;yngIS-L_|i&@ z0AM(01~!)ufu^%r{{YZ;up&&y>rZO^dTn7L-^~}@pESIq@e70maQ#SMmfK8WCwiZ_ zf&idj=Y!=k`4Vq(eVrsj=?xmKsafGb0JYl#Gc$JQb~yAe;Sdfjx+b}=-L{yhp#w<# z9h41(^L?CnhYC2!yQZeskyFC?{F_9o0u~ApEfDU(L><HT#@LNrq=+*SQSLw97+>FaFKN(=ecK1 zivUa}0o*Q%2Pp0R{r%V@x3O}2JbvSLpW0e+WJ(}{GGDufPZop&=i`>gK9ASwu``g* z*y&5((|?IRjp44@$98pf6)v5fpP#PY8=n>>2O^9@+vVk+nw=fr!QOf}EHO#~^2-Gp z8uOiFYI^#xFgg)xbju~s4iMJkJpG+OAQSDv_f|)TCTDN~On--BiP!VMfryd^g-BW7 zzki>cnel6He@rXtc1gr(WfJ#wE$C1sxzn)R8^EVbL2^WxV@S&Z0E(#KSRlFpoD`Qx zzJ8A}f-8b*KqC7o@s^yGDiV2HPzw}CN|s3c2=;e1L|ZlcO{ zJ7n;g{ZtiOqRaP%6aSTwlHeOzK%$hePp|wc(l~dzcwq0~UR+EzQQ(rFtp~f`I7KmSsYMoF| zomc}%DjKsiW>fkhwZfIj;z3eKF{3swJU2JDmF6;tL1I%i=S#l`vwmQ6>pUPgQ3^X; zL!b&AT}GheK79Cajh+1!>`|a;U%`qS06L)*brV3N(Mzl4TAG^qMs4H}@)ADOH*%9QiGN)B) zK0ZEq*vF79ip_`4r$~o>2f{&XT`@QAp8alC#d)#3@Iqmyl|YziJwPW&`mlWo1Tq2&uTP1*nm|0kG*q(l@0|hi9F76G~&hOv92gTwH z=$H#5B{$#>GBlo0+%N<%*9>|34M-9wpRBAK(xKEw8?m?T=X59QUhV^~vz_^IL9^D2 z4vrYG(FoNFMa99+qAvu)3$T$Lb5lx7h0fF7@_~pq2~ndFV$Iux1Y&DzYlwT)h(dux z*T}S4*2eK5%1ZGE*Zz^u{RDX8{A2!6&3j)!_4kkNE?@SpEP{ecwv`@Kv`GR{B0 zCjHk-GH#w;rIT*_{at*713Ag|VMMVyR zf3FyL;o-xF_{78|4uUvAWTZEKudSW`v_!l*HPvⅈ99G07@dEpZ0J4@A6&}dy}01 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%0AQke;v%4OxE=%$!vtFe#k(@Ng*fOa(nm)_w>cu^W zSFY)uh+^`$HGro``~0bANwlAtu}3bKr`e>{a2=&=lmNc$PgbpPI7_+_EJ2vVDBPoD zn2KDqdtbi$Dza)aRqk;Bk4rw`9vL)gTi~!rJtqJl>#hl_pXFRYoAdVrZ~Kx+J@7k_ zJ_4NN$B@f|Oviy@3;XrO-}(`c)G(`wDY3!OFfH#*DafrBz5lUClc$)An{{z^XtEz~ z$F{YZZw7>G5&i=f%^Zjc=|0x`B4Vx?rQFayP^=z;K zF~v}l>OzNjxvmSQaKTV!6sivfJeHa~_VAOke;W_qb=`~13JOZf)XK_r!t3bI&|HS` zOED(>F}Mz*>q`t@`fv|4aOuMq_PaBF+|SjLpsn2=Ki)kgtWIAXDpS~@GXD@Q!|{U9FP|GvKg2R1{OGTF zB2Tp(Eu&-Zsk71#=Zi3{$YfLS-I(cWZ%WD~vA1}x##BtzPgnLc!(oHr&;iO80D?3f8T)w6wCzbpKZdhs-cA_|?@QGwMD;*_EOk(?4`2PA@3r zgd5b|w)o5BSR6-6D2B&z%ugEjo%gjtkX#)0;^@Zqa~ zfc2XUddsJKEu?~J-e$$$1oB$5VKB%=96D%kBO(wYUS63LG}#b_pO=>x ze6QQ@=pj<_pN=g+cKQ8rP++16g_jR7XL(pVP-_5&4dg{}BO}&Z0zm0}0Jb#**15dg z(M^<2dF@*8;Gn9cq~x!TNRHYqT!?J}x(#~cK$-f5jujLTNC8(cL|XkIDFcT(mw9O! z8D_xYKmZ3xmt20cOs@(o*vjha*S!AFTzUTDh3!v3`1S+w8r(MwJUq(g+pl5bF0>*T zV1I`_9|-Nc&#eCo>bLyH76y)Qj6(W`39RaH$;jYPYi$UlfWD460U*gY>CEuIuzp%A z-K7>k$RW^z00yB+Th-21zTa4pk)vM%UUt1i09BG+y&42#Eix~UX`pCTBa!Teq~v)7 zz24EWtoGvMi zTiKha1y-1dgaibI1(1uaa$x@acjjiqX%yU$U{L?s(eYZI^??Mom(&7SR{sazueNikBrKF|`de+z1LzpQuH`iAp1$i|py2m#Ht>G~HK4|CZ|%=ZD#qCP(UW- z{8@K{eXgZVeHT6Pp8$@1!~ZjYGyiSgZZb#<0+Zm&hM;gVe^r(>@Ql;*FGT>`@{n6J zWOass#wCa3i-+!R8~Ci9w!YDw;`jPL)ZLV|KWoe8=6#IuyvRKDqq~OwpD_o^m!_bv z)l@@#)U~l1~zixcXZftaZhE=*7puLl3eH5B{n$)#_ZQ8s?_zEfDd0z@M$O zxz0tSvbY%7%Y8qoH2@0*BevR#GlWo?hm-EVe3nhOyyCZc1f`sJR*YMDud7{_qd?3p zC}FDKq5AqRF%@;Z7+@&iDJk_zivVku(i$r(GqVwLS4h|y76_y$gWA~-2I@75tRca|dS+aQXvJr2ka4Qc z&qeBum1qw6hf7D7NQ9~lD@}IKud$76iI{Z5UFbm1q?^A|MU1WvL|>s)MBvCF^kQ`2 zltwh3O_&&$3~L&=@k5(15D?a%bfsMwkQW7@vIXB?<#^D7QP1Fxj`M@P10sNiUn3Y11hsc|R013u4S`kq55)u@jPU%bAJj5` zh`qxLuLovcpp7SYL)qsKP~#11CN;~`mL%_*l6m7#Fl~xwvQdT(*9{Ya3e_Mpr19qJ zxw**QGq>zcaMhL3?L=;AAE6t99dx_%iL*YPquJ=C1aE}J)Yo510ZE@Q)Mn;kj9KsA z>5JTD8GYn-ffIR9oLKNch+#S*F@R?+V756>0g-eDde}Zk#V{ zdo$pHKc|f5F$ahZ9s$9?Q3f4->canz*v{m#ysV{5&BH3sK2+gh%w7;y5xs`W0imNv zzrbanE7D)I_7O|p6%S_kZbZZMB}HKOrTfdES0d&GM#1f`>qg4#IA4v zrlaF%0_ogWsz=3@uuyzJTR8Cz@bGDi*Xo#L+oY6$Vh_V|z@6O{&rgEbPe)d#;AMnl zJT7#>eIaNJ-{RKXdnJji+$4^QxNjkl-I12|ZxYkxh;@QQ56t5Mi|qv{Jd>4g{90J> z2gv#_n2m*y5V%CkeMr$7q4fJTPl@fZC43Dg>4)xAI9JJSPWGu&<`SC{IicS{L_rT- zXuK9XP;9sQ^6p0cLTRI77_mKj@sS;j!c+OQlCp3R(Wb1K-*|v#+nPE2UTaI#A`Gze zpiepPp_G3wSgd)zKqrej~9wv4&eP*nZ`z;}6n>k|p8A}U`SP9d6_p^E_ zNfm#zYu}g{4;oJr|dQp#I^}X#T^H@ViZCJ4_Rg6F-Gq;j&^5u?a)>fU8S ze=)52(EGsfVd?qnz%qt^?ztZnPIzR1f(9lMas)k(c)Q_Bm+@`iez;ss$nLafI4Z;* zvtRqlRs4GUr9}e@Qsuce>72Z+(fv%yV;nyN202%lh@2t}0Czhs0CN zt?8JQF6CXe$nNMt{VL{rAseF@%|gMFZUMo{mho*OALIJj0_g^Uijau~JodL%^cA>D zH?ZDryU20|MS1JI{P6#mNIXa_S8R4R9F=Sp<#yuNxiJ~Z2FaVci@cBaPSrkz`Jue|(@ zN}%KTbhF?qc1;ygk|36T@`#7;Szj4Yfd{H>no$ix$37`^uFZ|^1!{$o1HV=uHtz3SJ2f_I^@@ilDo(GEU%y5EMjvr zMYl+%Ez(}J%E*+J6M6-lIs6e89g7S<=Z97gR|p;ab71dh;->kmxkJB+QKIfj`opEL zo~j&5O`5lxu~w-!D8vgJ3q@`w1+=^~!T$<6ATM%k1po?cUKi>4;cY}LO`sPHPDls( z9INWi@|<#Jzt}jPs9tso2s5U-AhsAat+b4On>Qp>Gwhv0aJ&A&rDsRDf7=@VS z&Fl?{B;i@+5p7fvy4F@szWlA9H&(`fS%Kxm@1V+b@=2)pt$^R{#d(wTDJMaWyI0(H zBnD6K&dSf-hc^$R?AqlPO%T=Ng(#90ts9)#W?*pq0haAGtR`_9TugKBx{tya8B3o&6!-NvihabReZ7w&f0^ELYs}p1TU$cs zlRUcjR0BJbnR3rw?2g=}m3d;0yWrv4n6htTm2tl1<6-baYGTISi>WLOlGLfGnR}Jv z0_4q9iI~~cyr)I;*N>dL*WEKFgEhDRxZJZ9PdG^oY_6RbJX{I-s76K}cMj{`%l#>L z>>7?*5&O*f#ic=KlZ4&dd1x*XdY@LV&Bn5WBb{I=QmkJcp8caoUCxu{<7)LmEdBR7E=wFYu(oWeL(SW#$a~>MnavA1hb!oHz3UVxf9|E>6$6~nV#atQf2*U` z`QMGs{U4gH0;;OCYhOY_>6UI#q?B%HK|lqh+n~E!X=wo!X(^>q=@Kc6?w0Nn>H7C^ zzJJ!NHEU+P_nvsqd-i_z6A`<7Dl}iD8ii6wh;&{aySc7J*@o@c^|W~56hHJ9Q@P~t zEA26fn%YHC@}OIiF!K?z8j=QX|3*D)5MLROe(&P=wYo;-89fo|a4~aRK7gf#@nL>D zSAX5C`No*4SjA_WUi)QQaob{S`PC(+nS{I26fJyUE=2@R(r)X_n_67`Av%h^u?C|qy>`qzBe;xF zXy^TB=c?GhsT|!ct!0eV(i`RvnqsEJZeBHrNt$~-SLq`XeEo^&yQF@MKkEZ`>pwFQ zqRIqgFT5_&q^adzel4@^&`TWjFxDVxF_OkqwTwTnhvBfLU;voIZFm zUp&%DOQ@1rtN#ogfBy$w${GzRItqti{cU7!{>^$JCyw2)_aPRnd%JPl5$S;ggM%|k zI>yb2NsbHg)ezZd*EI8fUz~4UjF2?8dU12R3L9+FMw@+}@e%E@`{^-8Erz`SY z9Q%>3O7(B4_-3?qF@lS9pKIrh=Ttl!bP{dp=Z#&gSWqn8EZcS3bb`SmI9_!Sc=)iG(xst0M;)DHUodkhBi2| zx7njRz0BqpFo`2tE(*C<i#=Y&!P{Z5SC|8Sk~{`_t}B6Xp8)`{0BhedTAG5vOA& zo)R(nc_%tZEg!j9$1JM1zU5}O7D`W`Cac}-@ths{PDG=(g)_})vi$+SN5Vjn0?XL{=rOcvpl6mZS|nu_4-zf>Huh&F zUDmfYKh7_miXvFQi&-x(i&S39Gm=QZ#z(o{BvFWH7+>XggOHsvv+>af7c^lVt>yX#_Eps6Jr>n>Y z3d0!ivW(C%_PPWtVROL@qS2kLnMd6($DRlzpYfGHbWc$356LU>5*J%VW1_vC%Fw*{ z*rjR0gP=PgBj|0O&}Vn5?eXO60=R!)e%|qx(egBZ5D@9dv&CW@!ffM$h4Lp<)}X+c zC$P~x;mX=yJ3c6~UiWSi^oSv{)ETZ(mNT%Mdq{dE*DS{vw)J^99KbuiK#Eb=w&PF! zY$&ghe3&Rj79jh5Z5>eugO0YPeIvz_(+4v*f0POjz4nz;YOR0&3Tz;>`+X} z9QtWu_123iij%w4RBN-Z#`j;(G>mlIQexMej7^Da+MeS-J-CN^GBK6&WOq+me=Pdm zl^cJq=4-OZW};IH^IO~IYS-HxnQw{iJHelpEpV11^ozH|RR*BKRg4bE?M0inK=Fo3 zb5W@N&o>#Gi^*XbhWO;2ZRt>zwo_H%=5F#E*-A1Z>l10bkC50V@x!>=n7Kc{8 z?-!Vh8Js8#(#p)k1}!=%O;6`8*mct|Sma4FpIceu8EM+hEtaxqvS1#>pLi|XKnV?O z=AMa(C_vi>SEZ!S21F}wUig5cOif^A7Ph)%X1v@`LlgVCr0UmsbvI&Wa%X(y z`qb`noJ{lgea(--KBkbPky3W_fKb^WtoI#dXL?$G?RjxAw>;;;ZqIP|t3YHphS%~N zc|sxe^G4+sbe%{P1$>TOJ@wo8Lru+~ygYVD2=*zcF!Sq;l{* z8K+Q@G5iUu{fIE7kzc>c?^Wr`6SqVI#j4h46*AQd_<l zR}aB)5V3p$wY~*d`cXlmoM5A*tl30P~ z7eo_j^17Te1#631>$vZnq@n6X*C^vfl6Lgl*V@YTr2wb`=suLDtE z8Z3OW-__$Wte2KIeWH`a{q|le{^5Zzewf|zZCq1P{rz2Bs;j0B{(=OQ!{)(BGg^j| zMpo(Hf-EH&dwY8ifs+*pDNwr&(WIs^ALgzgHAKX&yw z+<4JlQAXlsw1-@gI9^tA^in5j?c+bt4!TxXM_-4JkT7d|IEOCK@f1|lF^g0kYr1LD z+--aJ?(qnQ`ArF9k(E9X5kt4lHsg7kFxO6x`<#Kp7dLTM_=g;}-`HbSxKsJPt3Uei zqU5@7G{OEc+2HcVmi!~7futTKV2!~v7*T7$@Hb4qI8?tFArgR%WH4Cl&;I$z{WBC) z8dQ*r{gu0Ff?2i@H(q*|J`O6fiJR|yT);Dhx44A`J33XoP)j-kXGg~k-^$Bo?ONin z!BpFlH=N5!b?7H$tDGcj4QHa?91=zOHIt?lv`Hg6)o3OhKt9#yfi)}qQninQbIx$2vP?O;#T_? z8Y*gP?^;!AxKCv&6G9%WyfL)rK4`A+9EbBP1Ll1P2!b*Ck-ZyRV?Gdi10 z2W!iNSIg^<@J_DO0TvSQnBjCS;OUK+Yo!jii_|*}nhi*o4V|v=@2RPxnd<2!n28M^ z;E*ii@~0VmC#K#PlujbK!miKLYKdQkx2<+D7W7;v-#vTqkGX&Uk6!vX6Ai78u~&Em z^d>bnm$FKUE3kvZOG{#gFyrddTlpqzA3TEBuSzaD2B~Zo?siKtOM;giZnzI@wE{^8 z0;Tul4V5;Bd#8Gv>z5`(>1!FK$?C<(rKJQytGat5*S-t53=!ZG#5p`4FQ{si$#3lZ z6LzzO@BN_1K988@(8Pd?xjEmM@#(!T^|!?z`V!39p@uPYOPT1FGm)ECl;Yv(I!0HM zr}rjlQMO}bf7FgbZC*`yvePmE1@QQ-urWV|Y{~#V@0a4;a{WeQM1uHD&XhGKN*KR} zzU%&LEwr|}a0f;kWce!{$WFXZ+HMx8r7|ozZ=&5%j|!`J(L8a9T5X@wT}{`6edi_v z#VBjsy5|*Xwu_78Y`HS5*0qrixrDF9n;ghCq`c^^U}wkef3!e}CAc# zpbYlZVNgz39B$8;3-_-f;~_~&qxC059DfBu3zwqG>NMmXQ4YQ3sHB%whY#gcv1RQN z->}BOowh6}nOmPtXcdD4uMj$SXkD#ke`+GknQj!DwLdHGX1^7fM-y@*C$FrGNUWmi zH|FW0`%lc^x-U<)6E;L|Rld0x7p}URx!ad==}7x@Mlv!`lgbD2ZXBbqtBvmzF=zMTPM1qA~OLu{eD0a$(e%3V3L z!R^UBvVYzWGqnTqs$3{uGwhbpm6bw#cr7P$5Y-MW8_Y|Wp!0_SKg6FCVH`y%Zt?0U zg^yqzT()572rG!E{m`t|Qddwr!Zm!Sb#THy&?+(2YvGn`bx9|M@yKg&iG;dLZ!U1Z>l}(W+W2)T z%F~Lg@4|rvi)SiWZt2FbnU(E^Oh;Ql_O@y5=zg%i4gTeF;I+$$=LqBJA&Kq?d>5(CQiVQk;%IE8wRNm!6Cu;upf8lYVrkJPq9VgI(giJSX^ zq8hT;fu>w8y4Aw^QWIVj*q4u_6VtBVbbH)nIpyN8iNtDXs^0b9dJYV3bf-gaC(J1gE&C}TUqJ(FWrAmTmaO0nY_>+vg>FBePqkE|Z5kvWJE`b7Xz1KzrL(1RkAHGltC7asXU3Q6m@LQi=ej?P6A#T=?*)M=?~cHSQK zF#E_w3joeTM^A6>M&hUEHyoYxhUll6KJJrT*SfX#=7@C*zd|(b4Xh}Gb1t5vlZU7G zeg^T*C#`0y6}+)*7}!v?l>O9|2w=SQ0_W>#L5RN&ic zi!pcYgsxd;W-P=y>;}u(<^M=Z{kc8%!tAl!aN3dbr-+_w@znWY!p@YUw$^K32*XtG z8}>&D1rd9cI$i8_kP6X?PG`?i*Tn~V{_hweG6O10TbG+&ett`_XhF|1L_6BsW%K)I zPLI;h!q|iVruNVhm9Vr`yg80d^IV?Be*8&YIUzbCLtI1qnc4i#!S7W*s`BN+mUS2X zOKGk;??>afP;Vr!x2>K#Gh{n7Vk!M@%hG;&y^iF_9pggyC-0R~=VO-|7mdHgboE&Y zF(pY%+9!Vs%guLjZ_FD;7VYThy&74d5XE`5LnMu0v^fD6=azPMZJ=kibjv_OQBXbL ziWg}If8pSi)r-8`%wknPm!OlgADs*ed8MstuUkV?le?!IFfRc$)|hW;LHomr-5rMd zknYy9wb?POA)I22-zuBoW(pVq5`t>79t;jOF>kwP!tp9kW1ZF2@T~sl6fqTtw2NOcgCMTt$F!6wZ7x-iO+VdiMsgb zCjCsxm=vSjr`AsU#1Xb47w`8V=(1PM`JE`eZ zG|6zVXSg`|#7JG7rK(P8Uaq9&+8R2KwxgqHSe#4_R-ae+Yzc0DEr>NA$U2<>oqPwn zIHYby@B|l^_>2s~P&4FC0Ne1j$2w3eP$<}d$g)?04+S=fZunHo;Re%kuUtQ?0W9zr z)sX5-lbg}pbm6$fb>x#4>4EarA3ENQ3;`Ch5n1x}xFz?E)Q$U&86!j$zt_A@p}h&)@AludUk-t!7K#uw!$k|!CI23 zQFWtKJ}^jHfVwC#exSfyD6R3J>dkrvBi(v9-@ z^BWr5h#ERp;!k*f%32bzE&f4O`4;c*aS|2mENO?9FV<|i>`WZgh#d8iRX#kg!j3FA zP1_EZ*GQ}v^jP?4vavb0$)vj(&g3!mBkgtmSh1l*&hZDh#lG!#4T{JO$~cA z;V!-#V%`UD1bqE2ILlLJ9^_H6D_F)|Ed`V;wbW*DgMqt@URmSlyYKQ_L0RDsvIG8a zB=`sbKgEn4^*cr{@p(#lk}8Zlsh%WgB2Ku_C4MnX8pg(cId+%r50fHZWWkY1JM`Zp zymm}6C7i~VPjd}5s3*7*{U+uYF|n97$Q_YRrvx1C2`OD}e#;S>AbGQK+! ziMLc_d<~6_(?`@8)nCfuX)+7y`u8(pzE7;@wMz&%_r}6&898G<>!{RxKR8~Jx?nBa zAgcLs){~Te(kOD8j(?sY0(ZCv)79hUSJ7pwMM)GKzPxpW4NesHx{`t{^DAJq*}n%rcpi`8MV{TgnDXwb7@ndfGLax$xcd=T z8T^>RvpEp#?PLoFnORvefvf)&rsI)UD1$o)NMu`yKj6213BI@P_e8=!;7R>&DR_5T zbSa&zHx4f~`IOK!l+@61Z=~(2E9NQu?r|A5?YFSlnJSv8$9>;@CamP{!8CoeKz8yb z)Rb7~mdSaXdCRaZ!PJsq&&rt5KbO6eZ+Sd(z(71HOfpg+PCmET9v#9e|TfZV|6@p zj7oaU_}(dYr(wuSGPdsVZ1wAPa!P4MCo0dZ>N-E+axxKXaU)INz>jx5AiAexv&Ltx z+2u8Wdsmk7Ws<3=??L2rcUFt(qou(2iI-m&*U9GG{%i@{^8;i6sWA8)LIh6tN&^&( zjFyg3r0jRUv=B;1h0%NC%yoE7>2Y&$wE)jiLy{>te16x5XF*b!VRm=_7ZXLtKuXel zgWIE(;^OFb*a79+ z;HJ_{>eo%jSH>erXfvP7a#G1x%CB{cd6w-;F9DYBRoVm=QZbF(Qm_tf9d~GorZn;GwXoY)gdiUT5V{jZ6|+Lq=40=lzunFhQC;N0%H#K#=0x92 zo75pgspCMw&~oYBlVL*jFR@{&Xeh6xwaMN`R~A_c7LDTDedr74c&nedT7;PDw8fcG zN$eUC2rZ^AHSh?0PpFP5HC9Ss5%hGGHG!RiM3x`uWjOeMs+N=JOtXJ0Y9teG&v$t^ zdXP)$oE%o6HxGA)Z8)9TsbCpTdZr%eUwVr3nkACxc-4zj=iOc)(Bp4s_cBVP22job zQz;Z`X(&Ruk%%85TPjEdzz;P8ph?;j;I6{l+_rOd`Mw;YmT=RbOj6}yg6T7Nc}^GP z*-kuNy;)OyQC~i3-kKbjL0cph$KJdAX=JJEr{-vfpnWRsRsq$yV>Ox0LE7Az>02p* zUYno3Dj{TDw`XZ>_-%y^QaIJ~-G9Cn3lbg9Si@>#3EmAdu$L<Y ze-`QpG^lm%_+Enf=*{T%rQInk4ZfyAPOagK)hMd{a`C0S>g%Dh3o&8&)$Z?c%2`8P z;zT<1@lt-V2l6&ql^I9{jP=O>qIw(NyArSKdxn}Kw6ECAeHTRe_jGiW=FSW=%ggVtvlDSd zkhgTO|82|bXg8nq%Pxx9ptBWZ;$ZKy`{FNci00FR?`T7jJ$~aEEP-sSpqz!(o z%kz7V`4T^iJ`V8K{hg8Fs%#ZgVlExAhhu`Hl0Sb?=1|GCq%tM4vIT!%+mZ=Syh}4S z7@0y$`Fvivo0ZXd1C3ap_u93uJIqw@^(Jq%6)^*!;l^cgRWB~e1zR%_I-*5dXPP7NAOn1wjqq?JR`&xDviVHgaKp&^hBcsif z)l7Nzn({(ouWY#F(C~x%e5A3Z~A32FR%le^%uV?ztO%{O}pDEpQ z3A~VZyQfPr0aHHv=v`XwB^)m`XD7Uw^4c0cTaO=@q4Sh9jt|W=Vzz4Y&re-TyLJ6i z08e8(+m@G8PC|3(`r5Ew~!)k49Z7M)b48F{*t@3zaiV7HVsJ_}h5Znt!wM-$fM5| zP&p@5aBw^o(W-qZqOrQfSgv8sAC)kAdW$rmsf2#`qKjd2PV`pyG#`-(-EzUp)h!u~ zk^ysN+k}BCO^6@p>)#lTU@wuwO*_3K8M+vuP`b_79I>`Y+V24!$!KI@KJUp9Ci2TW zyZH5fFADaQUo4+fPOQ98tgQMn-No#uFbK~bPxTH@PNBJ_iVWFh85X|YjOws|cX_}Z z%6C3S<1mXQaUbt-e8sxq>(}1T6ul<9Rwh%N{rQ)MCQ1lw!-tr~qU52Udp{2oM^K9hmYwdBm97mYJW>i|*+K}mV#auXqs1IW?0lTHx_u4hG%f7BlY!9b9d=<=5 zBW|??N_IcEH%3q6e_$QF!g7CQ-Ml!J4~~{QG6vA$ zZ9_p=eW5br_BOo!ju4ut=Qbl^Bn~P?kee|>nl%vBAqE?lA!!Xh=!*Bm8B83O77=3hfv@Lm2ZLJvisZl`oct?9Qje?~h zQ%>D49~s#oTaTXymnrGj>bOh4nrAh97{yhu=p@$sSbMz?VA=5zO~Vk%zwkCCnw6zi zuW_#V8#rLRj)v>vd`Q0I|4vPI8dPhmtE+rgeXLV!00*^zNmTFD6hX9N(71gaesXd$ z&=a75MTAs;y(OP?rAEN0g_Vsh;;-;F0sQZOZ|YV4f#f|Hp%zuvG*$di?WTJTEXp$jxQ0mUE9gnZ6H!(P+{#$=!7Vq)bYxf6g zSH|X4uvl6Mk_Ja>dgloqL@|}Cf2NOvjvj?ljG}7x@dHzIOBg|E&NQzQ#T3`9Z&FIOj*+kd+ zdX2Z3m}HjV*F)Uxa89;(t|ngCvKgi?nnKu}t^fJlz`eF9_%nu)#TPkY{y2qpkw zinu^e16=MhOeo=RATZB5oyX(O$`su&FM*dP?O+|chdG} z$yLIoc%x3MOWn{F(}O3h!MVchwvo%j2{+2VQL2(j4+0H;mta48hc`4%+#fQ6TBWYF z^S$~+p`4s@eY=Llqc{g8-F;D?XpR@*_71-pKovQqu!WogU=~?c=;JZi`|bTw<*C8c zFQ$MzfVJMITjN!P!^S>#u`!ndF|Y|t`*QBsKISQY7#SLhwLVsHrOtihD+oD5fE|eT z?Ag}^#atv;4>-*fbyr()AH0L)tj|zjv!-%fTZ7m+sVkuVZ43& zwhUnE!;=#vs1{i{J`+N+^$|Sb7Z!&v{CkBCJaXLYS2p!EI zlXTyjYOu7mm4|=${2c`ef`XfyU?sQ+!4!iItYTfOtIQ!znv;u54vfwHk5@Pho$oBw zUV?NbsG_8hm@U*7YzE&zkYVn>_Y3wUg%$R+q!WdbxY@}2**#Qul;-)~LjBK?gXNW3 z!@RkHbCoGyA|`M%PJ7+QIdik#aQ|FAm}q5=AlKG5{=5RBcKhhwoe_G$gF_5yJ08=1 zlu*U-oE*b$iiSc|=T7AdnEjBbW;Rvt1)8ciLU#JGQsc?#2^!L8WGFr$q=M{D&o-=5rpN&%^zgty-GWBkEgW zcP85|Yc3V+*%t;6YI1L!1_Z&d)6gpabp#Q!-|h43`%j+J+WT*Znf98$ZxsS!CxB?C zkPOF19gVo}AwugS;|A)8hzQS}HcG@+^`ITu5@sH!$#s+TJ85_p!4zu@JtMa==7;9(=XaBW%9l>2g>VGsayKM6Cw|S z@PF&nEpOH$RuU__Hyp7t0D<7+Fe7+5AwrbTpMURmOE1y2@FBSlWmf&{!NEACZ`OH$ zHncuhbYsm18-!(MU{@*PzW#>etwOlY3XRQKeN2-G``Bk8C7UqfPPMm_nn`Y_V{pA5Iv`8X{Ed0N6MlN+Xyk+!78_ESRBS%<{FXBLF_Z!st)eEl-elIOy zLt!RBZ6@IUq%Tjq=W6I}=#Y)UodxQ$cYt!!JJ?uRU4zLsfA6Y)o*fLOENxvi25EKf z7y3L@2v-iPYKt4kFgNb}u0LOJ-(yN4E#WL5X|sL}7l5q9)}fq^fU z0tmtbcNmHF1@}-EYjGNC>X8oW==N3r2|Ijz{2uEHd%M@Spj=~}lrql{Zg)IuvDj{# zl0Ah1`{|yE&&aSEc|~=ulSZO2Bh#;)kb}Q=F{NnCITe$8xZ$^;d3DvsrzI-=RLrDQP{P4W^9XxXqdKxC`Jk5*wJC6n?uxOyU)!e2tE!@s*^f);!Fg@v^mbffZW#*-3ZMDn_a!BvirMA5@lq@-NB}3ofC8kVq#{Nln@9C3a(c|+6hqd zu;EYPfHSebztomre}6xcevz0+h8PQMY#3?Z1~+BE)?t7J8rXY!df)AL;T;DAM9?D= z)6m$IA3nCWz7Ay%#Ce5*d{jW#gIkD_f+7@NA)i0H@}Rpxt3OpfIy#D+sJS^as3JYb zK^oZ8*Y_Nn!&K>jbYTW%03oHM=qoBJ?iCJlUZ1@P@A~yi9*hX!p?3Vs8IWY7nM+uq zAN}?%^;9`v9iS^!fP@yfLO}n4L{1oDAQ^5@1Hjk!^!5(YfOj~4g+6|WELwwcY{B)e zG`D~NcyVRIWS^g(U&QZN0M53r?;enuAO;5D60G9l;`K^6YKRznS6qyb6@&)PnCn9D zf+#Aw0R{w!#uP@gOAQVXO@^g}UPlqF08h5Fi};n>0s^iTC$0)C1%-v9yIn{u6<{8U z4<3-fOl5SK@UmMUAS42Sgb)rVWf*5SU>YtNaGBs2pl=v$P;`Xx z7a_Ph^VBHP@@^grpRXUt{-VoU8xll!yx79&7o4><8}>5RGY(x{y%%fD^5Vq{3HRvV z{`qm~_xWOY#5Whf?E&a{;Q+tU1DBO}VhO1I-@Yvy-m`;_y?P}t1@oY8f6S&FykZgc z#K@1UE(l# zJL~xRLPM#0u9=qr9hIK`^4UqrXX&b;Qg|^c#YL~C) zC|;^7&}+Tj=t#Bi&zv-!)CvU8O zBZq}9920aovFFCSLLb*zG(TtoKY4q1*A`nHJe}fGC-fda0cV`&CvbtypfE($KJ=+# z=i?(1ON%+^=!dHzd7j)_;maR-ex1!*$>Xf2WJ@vnRKH91d7g9QzI;mOiQ}GMN_y+P z_xOpv;VqQiw;fmpsf^!JG)u({*7%<%7t%`Tw6HS-ipZ7aP|GpjeT9otB~Y5*nJ!6# z+Fp2VqVEc^V_>QA(_MSY|73&S-sk`pWCVg9m_##G%_dmoIxA@jxZ*cEyAp)A+cv~4 z?xz=ju~*YD*PXs`_A#~bs}B4q^B@(L%a50;UfgkYL#NF?^sKTKJRjQQa1s`1<3wE+ zMb7A9ui>Mr^_PFsZuvIWM!RsZ-isC-)4l@6djH`=BqL}R=0|PYGcCPCL!ki35g|+f zlqr-;V4j)Lzu{U1ml8n>cL%ACU4BTU7u(8aSa$s2rN6dG{d1CQV&_i-=}n6=!BQ7P zH_gIk=4(!h8s;ODdm7;sx_&fc{vd{_ob@NB zye%}WO7`|V^Z}>f(KAlDv9WF{%C0Zx7=;CK}g@?b@$MFV4nSgh`x_1`~DPD%QzyxRt5S+et%s zEOf5M=uz}qzCQ9sm}$-PPsz>@C;6~Q_Zd0Y!|J@-!EyGu_dVQpOe~!xh%GmkPv5O? zvfi=FTv+&Xah?+7;XpG~eBdetC;H&J;%tYGZ&A?0y1~-x0hb%JJDZaotg6J<2KnDo6%jQfa1VgmhXeh z#-RCW&S{tJGddsB)O!=#;d{LWidJ1`*0aAfye?v9puT3V4E^?ul_XBmXa6@;uwOwd zz+DMBzGMi8wWMxSY{Gl37(pk!Y@QM=!IjiO0GA_cJbXoe^7;@HI``%3A$Lvmz6NpE z2~m!cMO!=$f~XLcJ0^$bJA*DgDaxXuc}%(}zNKux!|CrTG(KR>Z5~n*RZaAW?M1&F z%K{aN>N|Eu5OyiLy9-q$8|FR5asrhViFLW z4YgTZTS0Qp(`v3R60bjlRF{jrnz_qu@WmnG^N30J%z`eX)jkqL3>vj}X^T;c6D|K+ zH_l-x=xKMt6J4pIY`dMyzPR;i=Ft9A!a-wk3B3Qm0;=t~=Kaq|)5v2o1e%{&Sk%v( zO=aL!##6U`6X2NVFjxnF8UXJoAJY*zB)ojdjF7)!C7+qgm2Fe|e^?W)O8w}QYK0%W zNznX<^iA1R+MbfFlR)TGE}BawOFTc6CA$B2NK-du$6B~jL`ZuCnY+&7obmoi%7E&_ z1nNq7e2TYuez!{;2M6a1JoSN~*_F$EFax6`1*k=!(~E{C8fi@s=^hmMJMe^s z5BiqIzkbc}@5n?$31gQ$YF-Ztj!!fdw}iinlq*wzb>-qsmEv-!lHDhpb_;Rp7MrOF zvz;;S*Q_Zq?`!Sg8pz+QAwR` z5SOQXnN^pebisKwVZOWuWFsgp*7m2}$L;Ee9U70G?C$`djn{J>5Uwkz=Lti&OpT9a z>SO#;zkA&X_F>>x&#$b_h?9C4DmqwO%@Y7W=ynoWwx=taetL|95Ri0kjK`2Z#s{ln zsay08owV0Q1fqy$Cx$~@N(_Ogch-7^=tn6;RWEL!*`0RvD_P-{M+`ax;u&yWo(1*I?dfS#)mCNw8FvgjhT)yexJNZ5P*0hj z_Vzo?ld*y8(RYR??kvl$v3b21uuwjPWyMo7PXcQfVmI_`Bcr1gw6qcjEvf)-Ls3cj zR*#P@asBnSx@*oNiTrxWl35{kPV@RtvAg}dn5_EaZuy<$l8kv&={Fn#8#~X9I&ti} zeH_Bqj*dl_@pI_qDw-}I#B4-+I54y~4guE$rtUEEY}qFze^(JLj%&mM*ZM;TsMJ7f z0FF(X`RXCfJ!d?vusxumN@`cHQ&#;r(9+(kzi}-d%OsY`gD1F{p2(%^Zy@F+#{~Kg z>3(WWJn6Bdt&yevQ^GqtdoDdR-`GMY=&}3^u+JL~Xw%R4IXCrp!xVT+^S6I&zYxJx z<>6;dlRwFDZX?(E{er7xdEW`@d)~%FW)$+u5t+g;?@GoD7GEzE&#?Mgd!}&sCzd&E zKNmBYXu8$N>U47Bspok51rwzUkH(&3BeXX98nK-O8BWOy7hksz?=-EZf%mc!ToGVp$rEeq|og)ZXfzBKpdV8vf+pi0-T$>M33 z-Xs>v>V7jKc&ZlQMh7XO6`}s!iHQIGqV>4+cH8o^kwk-bDT+^crH1CpViEh&3c0IO zcgzd6eo3ll-OJ3DqjoD>xX#Y5h-?t|@s3PtRRhEb0-H$_ZuUs?V4}ABLM_(!v)SC# z1B^5ENIw0|m#A-=yP>6WNxexTq`{c(TFGOM7nN6|5N`1Et>GC5A7$-_?+5^nn{%y; z@aYld3~-Ks+w+C;!1%?u&KCKE{p!kIVkpIVy*R&*%sh!)-<{9&M4@Jef5JqRESZ#< zdCBDbS2~Kl7DnFcJiFtt@r90Iv&|#yMft9lj#s;T(C#q)F7Eo)mdKUNPn7do3M?nb zGw)!OTgq|0xY1;D)I24lW5bE5Pi`sm^!v5Y?EdWS)|~U_<8Q|eHOX4;kJ}jzOQ@;y zvoh;6Kj0*z60{~Osad2>If0Ja48T69i-3e)9;UB^6CcUQ)j|NoyID2{l%th?u&R`c z>=V4oMLnMLHahf&TK(%iK1My`jW$<_?_MD!k*$R+)Tn`eNk+G0p?#-Rbvp|US!5G2 z_F5oEvQeQIBLq+>2#P~+a(Li44dS31Mzv(9ZHU6~`IsC`balCFRwp3i)xyi}fe#^E zB?H191b5yKR?P%OO0w*0!E}lyjP>`k!sCD9xM7%3iS|B_4X=v8BOvo_Xlub$CVh&2 zd`t6|w=NWSwg+}oOF028NHn0w;_m;gSK7JufygU=PwCp{@P?#>$L9C=?dxy+i* za&xkwm!auBdLI;b|5ho*Yd&z;sXfxCH;krBdp#Q`BkfMIv=%@YY(ilip_5ma*AZGx z_}|-~V3Rx)G_k{tlZrAh%+e|i6Bsb#L;{Lm8df-n5DD2_t)#o%05S!Zttkjf!rtdIfH9$G2|~} z@=^CWvqb$q%5qfm;m9Lx1LVN##>4~~ztH%puG;>%)&hXuh~vBj zicuz4-a2Q)d3KY1nCkfYcUDGrz|a+-0YYd8LD+<|eX6`|ni`U9?w{(1pu+xdFDu(s z9f`MM%;QznDHFn-OmAsBbyHS%`LVy4Rz+K@kQ0mistaC_$m3?o`IFxsjwb}_2` z_KaN(4FfAoQirSsWi{em@ciQENr*CUMH)5vOG=&n)m(NJt3R0XF&F-BQ&CwtHXWf% z-Bw0=pn3>Wcpkl-2@0J~j-#JGO*Qj=;hxAv745i}tHt=Gbk}<+f=L&qNUgIKQ}^|& z0!k#EW3qT(K0-IY31&(23kzoZ>!a0wesDR#%PD-4RW@8cSJBPwfE-%QBl8{}9K63=T*n$hPgK(Z;+{res_9r-z6bk( zocnlnt*ZkR-#auD#BJuMMOd42uN)^I1iC)$`p(<&bF|}r>ZP`0`AF%Qx2_DxvvNQc zY9;1SGEn8p*i`VnID_r;=1Ve?e_wnLeRghaEI8~r(NQMV*!~x7KgaDFo9!WPIm6?| zX0+?UHnrYb6ehwwGZv+*lo%*J>Z=3G<7lYSh`$ThbX*C3WK3A-PET}6HO{p(*q;?) zJ@c}0600zf?k9GR1;v*1W43oUsaliE6b-}7EluPd0|A)71ALcpY$c0>mFssqW0cG) z7SMx+1U|c->%OD*}|-D1?AR5XO1XqjJDz| z4euCaLHpo^l-%05{E#nS9$|Lbh;@-Ls_kL5|;GWD2n`Iqqd1;{_60)cn2v3YVbdgZ;f?Z(hk zfePov;yamtd0q>*_Pp*9PLJifaf1xe)sV?~)%J|EtIC`vx{AKy62o5Trm?V`XJuV{ z67ZSwmh>Ur>-x`08@-P5EcuwFNBYX~`##G3W?= z{u5?;)VqyV^9~-2dj8Oq*d&YZgf&!WtGtIBuH+}RpbbA*xw5{Suz*j{v;?y7{ql$A zxZ%R>Jt-gw14|fb9;EH%4E@twoS0-X*t&*>o5`g6Q2|N^0kipK!4hE90Y^sw& zlV!>{7ma>-QzrG1wp#r#&83GzRg1rCsP}gSG2Wl@tcY6eWf04TQ?8NH{gSe`T#?~= zx-N*{%p5E-x7URC^|5M|-lWvR1|3{jIejg@Ew`+sPP9z*=@=Ha3Ml7DfExuMg>GGG z_#{0k38yD5Q%t6TJuDk0pmBS41NY_;1NL#R%5rdAdgK-Dxr6);gGuAT5xu zxAKa$vF4n-6Hfidl_hVF#RQA5-}tvjRJGoldxx2GJ5%S;V9clbC1BRpp6PAQbMyZt zPlRU}biV&>l!>ol4_%)F&w~B(7;l+Jk;p23MYaFvcE6ZkN*j2m45ERY0d^((mr=_2 zVeB&c!#Do_5RwuT|9hWj)$7s-(*ZU4-;F+U#tZS5`LSl;*&QI^oGy$#Uyge=F;%kp zXMySmF+2k$A3l6X2&%A5NNgX6%X>!+wQw_)$in%ZWt3mleZGyEc z8(IC=LNg{^uGv$i1`dAWy=@ylmSus+dz#XVJ~z!NFp(Hyw5FIZUqS9D3~LZtOa$DD z+vl$&85ySdTX*r9c#uNaOB3%CiQG=CL74g1KwZwH%dDZ7L|rG0Y{bANby21?;^?j7 zgWqcWe=i~T<@nh9zrFRx6_4g-r?c$%roZ}+6z*)>c9?zPG zFT}o$ve$$Ncl6NoR;9=89^^ZvIxF!fQi9AEWH~`}`PZGmyZ`{B7g@jVpU!uuSd6sl zZ4aa$FA}&Df0dDD`znaWIhQ!^2i6l4zIR^!9ACaXMJwkZFUcqsv-;#GL)$(7k8t?E zwO7Ika4}{06UAR-oO-$7rRa0(2r|5)4IKvBd_r%ZnaoPtr3Z2Y{ykMYu^Xh^lg=~$ z`yL3Y#9=aiA)aaNtB40geACrT&%c+4e}8v~+1@v5*uAj|%qY+sytke&4WAMh8=356 zlX~tAw-7#AKkzXH3u8I;%AeWKHzqtDGr%WND0=GGUE>~uUB3HHR&)V^uBc^7n%VYO zBqp*DNC2M-fBhCz7a7wo*Zh4wqZAa1I!af5YLVWYhb%Z_AXph#9$ z78(q5ZUYJm`W=|n@te2eASEjhjy_#rk?@&*LH%!SNWaLc|L+>YjFKeDm z$yt#XS*Q2$7NTc40vc)MDv~fC-7Dqm7HYi%|0W9C2Da4sp#`F8Mr{M0Y5Fx77li_+ z69fC2OF&VRLYV?p0qk2+EH_-`?d?ncE2w|KTAAYX|FiUyAMbM6U73>37}6}ZuAToK z<@R@u3=|5m;DyY?eS2iegBr6GFwhhJ5-}Wt6G^U z*~lq`m`WvV+7cBdQ`F;p+SZ~IwJM}?OlOCR97YbAlt_}$gx2(sHI1DoOLBU@7oL4~ z_j%v<>5tOKJ@;JqbzS%MJAHrOZ?ye1rEEEcG_9%x@&N$X02!eo5O&-gPAWjLD6P2o zwT8C_*@Qhaar<3r;fmYLLtu&99JXz%G1GY9J5g>#0w%=(MSK|KFdo*KW;qRJE%KF^KZD zl#7XL+@JrS*PzyRIqt(8t<8291y5OLHEhj_7i(%phTdxKy)CM4dt_|2A9eJ}h{1jg z8^TH)Z+d)JLRp7rXxPoOSic{YPIEeASzj++Oa_yj?{f_0Vf3JPkBaXBKo}Rk$VcZ7 zE6sRFZAI$M;Mq8(9px+d%1`p$B5)nsUQq%a5uhlJcH*vg?)_zkB|1x%%uqXp?j_RQh>alB2az+N-{~l>LosUtO%}QZ6eX}b$Tn=Y<&GVUgD$9-17v{iTB3QCk(YO-Evsq2BBUQ) zzut!2GhyqEt@|Ap(6Ux{J&2_xsQvkBs4;Tge>i&d^o0w@L3K(>_^GK8{s#a{-vme@ z@C=Bv(lRoBXq){9TtIzE@;kJ=pFBw;<$b_WQM2hP1=+V6fH2Ujj{u@9H9!Ac+iV6S z7B$Fw`T3acR4(^o8{+ZP=g$3DS!s@p_}fon0e5iyh7D0Wa;$+oC0X2;FRM6>Jx5}W z9a|F`YCJMJs>6|`(U}xPdu?oO?>MI}0h|CiNB{%3Zr#e&T?UjWq?sKY9np}5i9GFp zpe$_E9tBb|_^z%yTgB9T3~_9140U7+cXy3t+Y8G%^0KmKO4PugFtiq_L9^ zeqbY3+vMW%l#}6Q`K4%OJ{)*W-Io4QffJT6d zdGjhP8>sMwt5@?iQq~fWdFo}ynKx#n`VeLt%Nf)^c;Pg7TaQ;2fmc-6x7tz{Phf-{0`7*MlRn!@jd%8I=r4`Fcj1il9QKC#V=Fl z7so8|u;H7n%4pF3KYlkgTBAoBh0>>&W%aBAXp1q-Zk!>qeDH5ej$75ov-Cn|uFnt| z*qUi(!u6n_6RGKIH~FwS0J+J2@x|XtRPR`B&DE^()FYatm_lSmIpbVxGT^Y&u3d|{ zvFfqPpXMykFbW49?k6WFXTM~hzQsoSSuv5o9`^sz^i+^?v$5xW`qINH7G#Uf;cX zn+DeCNE9U>rPZ4fTtCR70YF*+0|HMe(InK6khes~CX^7}KB(sroWC@UKJlhVOQ$>K z>;Mv0jnrIA`qr>ino*}G7V&M=9F7^#rVBMRe#ZL%921c~0)y*vm@@ANP)Q8h5&GNK zfuI}q+6P2-V&!I#v(=tBmv4&9Wd3J{iiK{qLds^K!ZNe6mQ!@5v_ zcewY6q1APVJN13Vy~`eX>TNR)1I-?{9s;0yRA#KbKQ`01dUROQ6|@%05NHVY1~8Vr zWlQ{9v5JMbF2%HCVHEZnw;5o%L=Yjb|25!vT0bCm7dHV8L*Hg0R_*}$1cTjteK%T= zZjZI*xkr-^_gTxu%<6gn-W5x#O0h#ijP8O5Bx)D^kt>r)x@3o?j3Iw>#%0M)@DqE$ zXeWsU)hh^a`>x-IZHqwblP2#7DxH#pnADG$D-gXf;mg!Qwm}t1TxsefSW#pd;RC{N z2crK8ta%cio?L{zR15i>R{3tPoYF-BBVet11n*p$TH*a#FAJ~AE_&4U8#mm*Uu81z zX?&#H)Oe)%j0{zaLoF$M>|+YL@I6|Z0T)aP78OO0`eO&2=jEwb?E3`f>cl-y&$EID zgu7sBTbcn!O$4Mo@fb+EC?;Y)Hiq2ij7b8_Y;XVj1{$>&3aI+Z3dd9Q5|!{~pXI3A zvHHKj;c>d@rqkW8VxM0s3m+fu5Dq~7<@j^+q2BlJa|3j=w0LRL-=RdXhhhSEP>bZf zPC*%xtu)H&%I3T;9kM|n&Y}|GrnJW`ix2$RmFIsnGN=f!jfWJO^dxZOocI6`g_ArH)Z>SY@19XJ_Y5F~MRmaHyA?kRfLSKn8#1*EqkvZ`hxY zrxthexZY@7VFS-D84-vBe|lVfeZ5Cnq3P%M8tnyeQF8evL+_L;1kiYM@>35seNoy9 z@NY|aG*;~V{Znq*vjE-~mOTU-riPEy$n3^8A>OO3^z=#)zlh_dJ(LuVryWdP_uj-j zr`a7s8&m|yLS;o6Xkr3)H&qS+3FBA#(}>+EId z|E8ndzSQxS*^wjn>OvQX!QA!F!@|aJxkmI;R+O2e2X`HJ3M~;Siz`0VdQgfj!=Le@c|ak{`EE&7s3{Y zwt~*s)gp?@yn0oD3p@tX=(bM@#STc<*@;XEEJs0!DQ{yCm_u11pAG@%2ReEZt+5~m(UO}ARyWaEq zmhSiRB9c}VAt!Ap3lI03!?VADBLht<_!}YHuW%|dva+P%4?P0)h60`bSY*dK8w(_+ zk+@1(cE*fK)L#0Yvs6rb8hU?Qg>x5FFF>EeQ;&ngF%7}v!#4kpQ?ao#+s9NaI@TaU zLesXZI^S?{(Zmpa^-Z#>3u6Dl`6ll6-Rj|K0M)M+6-sv36LU1bJ+OOTyWjCNJ-De0 z5FJ!hR0zGuONYlJj7 z;YcESpy+%&24v8QHlu|E=})LWn3MgeX2P!1jKozw3yER@;{UwTlL%(;dK!d{4SBqQ zTn=TJa|N>CN_VeYYGOahHbyQ^y=(VUO{6bkRh3>m71xt@+r5Zi=iVz6w{xHg;9TUZ zMZHB_UWG~Y&V%#x85=71(9h;XTL7)GR7$1Wu0fPVWV3N1l13?DlxC{iiQ{Du z%Y&9y^1bleF-Xd_dQU9^G+tcQ6V9G>Zi_296-@KV5k0cL+PLOs)3&)gw`_6$Sh>%z z-KZd|yTFA1O583)KwmZIAXkENE;yh{zNTUneg(_-oej*kGzHk9dT!1Z8gKCBmW`xu zk7$;no{zbEJlqIxU*t8W#@lG}qOlPOaw&><;KPQz=l-aWXe?i`BF(`sD%nM=^5Q<1 z?c3)<8OZ2`er3+U&o&{j(5}A)xNJ3GS0_8ku;%Rq8oZ{)w!$4RFUdd$aPJ3!6F>Jr z>vF0by0fv!v3%Q>05>jcT_(b(y*p96&;`hg`V~aA3p6#WI^(X(PNxR)ze)*FqImUl z>?d}CiFrnYE6#N)0?k}WaOzqeqV( zVC+Pu2Zut1AP2?mfxLVHwqMK$7A?me``jCV@f{FtM5eCsJ13C>(sLxW zB!D%#cH_o03YfS&OkHS9%hCl(_20s~k&M`+tx}8Z3ny>XbbM%5NpUwAP}h2<`THKx z>i0+qJuiV43bjS1rX6m>u!WB(SAaeacUH-r_yD~qH;M_52L|glZ$O3-5Ft?>85zlJ zYHFIzub!|XkVdOT=!nG3@;x(3?s!{UN7t2|c%!qu@U-!lP}&py>H{y~??;X>;c9f5 zz>k4XA%(;KpHIFD7)fxgFybZWYGLRD`HLsb%8T~)XOJRV zK_#L^NB;?vPOF7%BK^{aYRVM_(}liv|F~UTeBt`7Tf4{M>B8bcLh$Hd5PG=!3K3qz zQHYf&M}Qj9S{81KQ9;24nK{SWIN}j zyHszc@e+_~fIvy&5cqR>B#a$&hxYX0TI5Mu!v#pGcY$YyD_D%l(3MF0bPQW8MU~|i z$usa|(h>U&Fo}sCpBLF_Ht7{4LXu|D%9ZDgzek&NhU3i%|L<>H4YRo(zld`OJpi7c zT`lS;OF4G(}Kf$@1LQdd)}*2k$!q{w#pgnN5f~Ng$iKtx9 zAYN>-P-{L7eqZvC0UgIYE@44Ji`z^b%&gMV?|wDbj%tsC!{D%UXFYT;6Q9)7xMA*- z&>p!L)Q7boR!(~L$}unbtKesv^D)K1-`5J_OrHVLngF+w4H+!-Mbh`eQ9W7Vnmas1 zUeB&)m!1;G7&#x}@*b{R*~|n1ZN9|^32lCX|IpP z2=TD-J90dTiRmifbKG&%lmlqJ0!Jrtym5Rb0*{{Dn(vj+28$f@4M<#RQ(I zCnHo6=uH0fm$+&wWa?yryKgeYHH!Lg!R{ZUr$66xd-ng6y;d#>#J5>yy3(T*OBC@V z*BWff1iFS?7e!&mfldqFh6IJ6gGVemVtKcva^QGD^#;#Wh!rWvEXAQ?22K0!aGAhq zb96^eHSE_hEb&~!BeLMTDQQ`+6{)aLX(B!^tcCl2628GS>cLRg9O9`^pdj*js&oG> z@&z)~0f4~y1>VZH0m?M5vY2?d1b{R)QWUY_0Ly$`P=C=t8PZ-<1iBAjW{?PVA)IIh z^X%_)O(;rYiR@2PdcCOm7Sz^M{GqU$*JbxnL{}afI>DvWDl+%S~Q+>NySyI0A+R zg4l2Vnv*EBgzVg~b566%q~XLOb(l>)*k4TQAASQjG;(w6Q2tNx@$O_)aPASz2K;GM z$ci$9`aP*~5bjPN+yz~T@sSmjEBa0uUUP7-o>Y{x&10UzY{*c#p~HT{wvuMIVbYol n_i!48gw}@!k|{qEH7>KS!KdqVz?~Bm{;XTO!7|(2_2~Zqkk9Jn literal 0 HcmV?d00001 From 84063a1661309e1efc5c498ecbaf1b70b5764f64 Mon Sep 17 00:00:00 2001 From: Reisen Date: Tue, 3 May 2022 13:43:18 +0000 Subject: [PATCH 5/8] example/advanced: update comments with clearer language --- .../terra-contract-advanced/src/contract.rs | 48 ++++++++----------- 1 file changed, 21 insertions(+), 27 deletions(-) diff --git a/examples/terra-contract-advanced/src/contract.rs b/examples/terra-contract-advanced/src/contract.rs index 13a0d64..c3b3dc4 100644 --- a/examples/terra-contract-advanced/src/contract.rs +++ b/examples/terra-contract-advanced/src/contract.rs @@ -30,15 +30,15 @@ pub fn migrate(_deps: DepsMut, _env: Env, _msg: MigrateMsg) -> StdResult StdResult { } } -/// Allow the caller to query the current (most recent) price the Oracle has observed, which is -/// stored in the contract state. +/// Allow the caller to query the current (most recent) price, the behaviour of this function +/// depends on which Oracle the contract has been configured with. fn query_fetch_price(deps: Deps) -> StdResult { let state = STATE.load(deps.storage)?; @@ -118,9 +118,8 @@ mod tests { use super::query_fetch_price; - /// set_price provides a helper that mutates the terra state for the example contract. This can - /// be used to make modifications to the state before invoking the contract itself. This is - /// helpful for testing contract behaviour with various states. + /// set_price provides a helper that mutates our contract state. We can use this to modify the + /// mock Oracle price in our tests. pub fn set_price(deps: DepsMut, maybe_price: Option) { STATE .save( @@ -152,7 +151,8 @@ mod tests { ); } - /// Quick test to make sure that when removing any price, the query fails. + /// Quick test to make sure that when setting the price to nothing, the query no longer returns + /// a price. #[test] pub fn test_query_fetch_price_unavailable() { let mut deps = mock_dependencies(&[]); @@ -163,10 +163,10 @@ mod tests { } /// This test produces a stream of prices mimicing a real asset using fractional brownian - /// motion, and shows how to use `set_price` to feed this price stream into the contract to - /// test the contract behaviour when dealing with realistic price data. + /// motion. It uses `set_price` to feed this price stream into the contract. This can be used + /// to test how the contract behaves in various scenarios such as price crashes. /// - /// See the README for a visual graph of the price action generated by this test. + /// See the README for a visual graph of the price data used in this test. #[test] pub fn test_stochastic_price_action() { // Libraries used to generate simulated price action. (Consider switching to `noise`) @@ -183,7 +183,7 @@ mod tests { let info = mock_info("test_contract", &[]); // Create a random source for our data, note that this source produces a deterministic - // random number stream and so will always produce consistent test data. + // random number stream and so will always produce the same test data. let mut source = source::default(); // Use a mildly jagged fractional brownian motion source (hurst 0.70) to model natural @@ -205,9 +205,9 @@ mod tests { .min_by(|a, b| a.partial_cmp(b).unwrap()) .unwrap(); - // This is the core of our test: we iterate through the 5000 generated points, and pass - // them to our contract. At the end, we can evaluate how the contract behaved in response - // to the price action. + // This is the core of our test: we iterate through the 5000 generated prices, and pass + // them to our contract. In each iteration, we can evaluate how the contract behaves in + // response to the price change. for n in 0..5000 { // Stochastic is generating +/- price action, shift everything above 0.0 to model more // realistic price data. @@ -245,14 +245,8 @@ mod tests { // action above. Modifying the stochastic params and number of data points will help // with testing different behaviours against the Oracle. The test here will depend on // how you wish to use the Oracle. For example, if your program is sensitive to sudden - // price action, you will want to check here if your program breaks during violent - // rises and drops. - // - // See the README for a visual graph of the price action produced by this example test, - // but be sure to experiment with the input price data yourself to fully test your - // contract behaviour. - // - // REPLACE ME WITH A TEST! + // price changes, then you might want to check here for example if your program state + // is as expected. unimplemented!("Replace me with a test against the resulting contract data!"); } From b8280ba900cde2d66d3e9240ef3488fbc15c969a Mon Sep 17 00:00:00 2001 From: Reisen Date: Tue, 3 May 2022 13:47:28 +0000 Subject: [PATCH 6/8] example/advanced: simplify README.md --- examples/terra-contract-advanced/README.md | 79 ++-------------------- 1 file changed, 4 insertions(+), 75 deletions(-) diff --git a/examples/terra-contract-advanced/README.md b/examples/terra-contract-advanced/README.md index 51a3062..3918526 100644 --- a/examples/terra-contract-advanced/README.md +++ b/examples/terra-contract-advanced/README.md @@ -1,84 +1,13 @@ -# Pyth SDK Example Contract for Terra +# Pyth SDK Example Contract with Example Mocking This repository contains an example contract that demonstrates how to use and mock the Pyth oracle. This also includes a test showing an example of how to -generate mocked Pyth Price values to test with. +generate mocked Pyth Price data to test against. The test itself can be found in `contract.rs`, which feeds the contract with the following price action: ![](./prices.png) -## Developing - -If you would like to deploy this contract, the process consists of two steps: - -1. Build the WASM for the contract. -2. Upload the code and instantiate a new contract. - -### Build WASM - -See the [Developing instructions](Developing.md) for how to build the WASM for the contract. -The instructions in that document will build a file called `example_terra_contract.wasm` under the `artifacts/` directory. - -### Upload and Instantiate Contract - -The tools directory contains a deployment script that will upload a WASM file -and instantiate a new contract with it. You can run that script on the built -WASM file as follows: - -``` sh -cd tools/ -npm install -npm run deploy -- --network testnet --artifact ../artifacts/example_terra_contract.wasm --mnemonic "..." --instantiate -``` - -This command will deploy the contract to `testnet` and sets its owner to the -wallet with the provided `mnemonic`. Note that you have to populate the -`--mnemonic` flag with the seedphrase for a valid Terra wallet with some LUNA -for the specified network. - -If successful, the output should look like: -``` -Storing WASM: ../artifacts/example_terra_contract.wasm (183749 bytes) -Deploy fee: 44682uluna -Code ID: 53199 -Instantiating a contract -Sleeping for 10 seconds for store transaction to finalize. -Instantiated Pyth Example at terra123456789yelw23uh22nadqlyjvtl7s5527er97 (0x0000000000000000000000001234567896267ee5479752a7d683e49317ff4294) -Deployed pyth example contract at terra123456789yelw23uh22nadqlyjvtl7s5527er97 -``` - -### Querying the Contract - -Once the contract is instantiated, you can query it by running: - -```sh -npm run query -- --network testnet --contract -``` - -### Migrating the Contract -You can also migrate an existing contract by passing the `--migrate --contract terra123456xyzqwe..` arguments to the deploy command: - -``` sh -npm run deploy -- --network testnet --artifact ../artifacts/example_terra_contract.wasm --mnemonic "..." --migrate --contract "terra123..." -``` - -This command will replace the code for the given contract with the specified WASM artifact. -If successful, the output should look like: - -``` -Storing WASM: ../artifacts/example_terra_contract.wasm (183749 bytes) -Deploy fee: 44682uluna -Code ID: 53227 -Sleeping for 10 seconds for store transaction to finalize. -Migrating contract terra123456789yelw23uh22nadqlyjvtl7s5527er97 to 53227 -Contract terra123456789yelw23uh22nadqlyjvtl7s5527er97 code_id successfully updated to 53227 -``` - -### Troubleshooting - -When deploying the contract, you may encounter gateway timeout or account sequence mismatch errors. -If this happens, check terra finder to determine if your transaction succeeded -- sometimes transactions succeed despite timing out. -Note that the deployment script submits multiple transactions. -If any of them fails, simply rerun the entire script; there is no problem re-running the successful transactions. +For information on how to build and deploy this contract, refer to the README +of the simple example contract [here](../terra-contract/README.md). From 7e423e2c8306cf7d32b013e0e18cf3f4d56c10da Mon Sep 17 00:00:00 2001 From: Reisen Date: Tue, 3 May 2022 13:55:36 +0000 Subject: [PATCH 7/8] example/advanced: fix lint and format checks --- .../terra-contract-advanced/src/contract.rs | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/examples/terra-contract-advanced/src/contract.rs b/examples/terra-contract-advanced/src/contract.rs index c3b3dc4..ee37da6 100644 --- a/examples/terra-contract-advanced/src/contract.rs +++ b/examples/terra-contract-advanced/src/contract.rs @@ -174,8 +174,10 @@ mod tests { use stochastic::gaussian::fractional::Motion; // We use tools from `fraction` to go from `f64` generated by stochastic to `Price`. - use fraction::Decimal; - use fraction::ToPrimitive; + use fraction::{ + Decimal, + ToPrimitive, + }; // Mock Terra components. let mut deps = mock_dependencies(&[]); @@ -217,7 +219,7 @@ mod tests { // Use fraction to convert f64 to a u64 + u8, this matches how `Price` represents // values in Pyth as an integer + exponent. let price = Decimal::from(price); - let expo = price.get_precision(); // Number of decimal places. + let expo = price.get_precision(); // Number of decimal places. let price = price * Decimal::from(10usize.pow(expo.into())); // Shift `.` to the right. let price = price.to_u64().unwrap(); // Get value without `.` @@ -232,12 +234,7 @@ mod tests { set_price(deps.as_mut(), Some(price)); // Finally, invoke the contract itself! - super::execute( - deps.as_mut(), - env, - info, - ExecuteMsg, - ).unwrap(); + super::execute(deps.as_mut(), env, info, ExecuteMsg).unwrap(); // Test time. // @@ -247,8 +244,7 @@ mod tests { // how you wish to use the Oracle. For example, if your program is sensitive to sudden // price changes, then you might want to check here for example if your program state // is as expected. - unimplemented!("Replace me with a test against the resulting contract data!"); + println!("Replace me with a test against the resulting contract data!"); } - } } From 3343f7d4e6ae72678958e719b8f1a26765c8b0f6 Mon Sep 17 00:00:00 2001 From: Reisen Date: Tue, 3 May 2022 14:00:36 +0000 Subject: [PATCH 8/8] example/advanced: use released pyth-sdk-terra --- examples/terra-contract-advanced/Cargo.toml | 2 +- examples/terra-contract/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/terra-contract-advanced/Cargo.toml b/examples/terra-contract-advanced/Cargo.toml index c36585b..3e8d734 100644 --- a/examples/terra-contract-advanced/Cargo.toml +++ b/examples/terra-contract-advanced/Cargo.toml @@ -34,7 +34,7 @@ cosmwasm-storage = { version = "0.16.0" } cw-storage-plus = "0.8.0" schemars = "0.8" serde = { version = "1.0", default-features = false, features = ["derive"] } -pyth-sdk-terra = { version = "0.3.0", path = "../../pyth-sdk-terra" } # Remove path and use version only when you use this example on your own. +pyth-sdk-terra = { version = "0.4.0", path = "../../pyth-sdk-terra" } # Remove path and use version only when you use this example on your own. [dev-dependencies] cosmwasm-schema = { version = "0.16.0" } diff --git a/examples/terra-contract/Cargo.toml b/examples/terra-contract/Cargo.toml index c12e528..79a6d6d 100644 --- a/examples/terra-contract/Cargo.toml +++ b/examples/terra-contract/Cargo.toml @@ -34,7 +34,7 @@ cosmwasm-storage = { version = "0.16.0" } cw-storage-plus = "0.8.0" schemars = "0.8" serde = { version = "1.0", default-features = false, features = ["derive"] } -pyth-sdk-terra = { version = "0.3.0", path = "../../pyth-sdk-terra" } # Remove path and use version only when you use this example on your own. +pyth-sdk-terra = { version = "0.4.0", path = "../../pyth-sdk-terra" } # Remove path and use version only when you use this example on your own. [dev-dependencies] cosmwasm-schema = { version = "0.16.0" }