diff --git a/.gitignore b/.gitignore index 75238c5..aad66ca 100644 --- a/.gitignore +++ b/.gitignore @@ -23,4 +23,6 @@ yarn.lock contracts/*/.editorconfig packages/*/.editorconfig -lcov.info \ No newline at end of file +lcov.info + +.DS_Store \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index e424e6b..26f5883 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -37,6 +37,23 @@ dependencies = [ "memchr", ] +[[package]] +name = "airdrop" +version = "0.0.1" +dependencies = [ + "anyhow", + "cosmwasm-schema", + "cosmwasm-std", + "cw-ownable", + "cw-storage-plus", + "cw2", + "nibiru-std", + "schemars", + "semver", + "serde", + "thiserror", +] + [[package]] name = "anstream" version = "0.6.5" @@ -87,9 +104,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.75" +version = "1.0.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" +checksum = "59d2a3357dde987206219e78ecfbbb6e8dad06cbb65292758d3270e6254f7355" dependencies = [ "backtrace", ] @@ -316,7 +333,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.43", ] [[package]] @@ -593,21 +610,20 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.16" +version = "0.9.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d2fe95351b870527a5d09bf563ed3c97c0cffb87cf1c78a591bf48bb218d9aa" +checksum = "0e3681d554572a651dda4186cd47240627c3d0114d45a95f6ad27f2f22e7548d" dependencies = [ "autocfg", "cfg-if", "crossbeam-utils", - "memoffset 0.9.0", ] [[package]] name = "crossbeam-queue" -version = "0.3.9" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9bcf5bdbfdd6030fb4a1c497b5d5fc5921aa2f60d359a17e249c0e6df3de153" +checksum = "adc6598521bb5a83d491e8c1fe51db7296019d2ca3cb93cc6c2a20369a4d78a2" dependencies = [ "cfg-if", "crossbeam-utils", @@ -615,9 +631,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.17" +version = "0.8.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d96137f14f244c37f989d9fff8f95e6c18b918e71f36638f8c49112e4c78f" +checksum = "c3a430a770ebd84726f584a90ee7f020d28db52c6d02138900f22341f866d39c" dependencies = [ "cfg-if", ] @@ -921,7 +937,7 @@ dependencies = [ "ident_case", "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.43", ] [[package]] @@ -932,7 +948,7 @@ checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" dependencies = [ "darling_core", "quote", - "syn 2.0.41", + "syn 2.0.43", ] [[package]] @@ -1150,7 +1166,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.43", ] [[package]] @@ -1235,42 +1251,42 @@ checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" [[package]] name = "futures-channel" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" dependencies = [ "futures-core", ] [[package]] name = "futures-core" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" [[package]] name = "futures-io" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bf34a163b5c4c52d0478a4d757da8fb65cabef42ba90515efee0f6f9fa45aaa" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" [[package]] name = "futures-sink" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" [[package]] name = "futures-task" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" [[package]] name = "futures-util" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" dependencies = [ "futures-core", "futures-io", @@ -1685,15 +1701,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "memoffset" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" -dependencies = [ - "autocfg", -] - [[package]] name = "mime" version = "0.3.17" @@ -1800,7 +1807,7 @@ dependencies = [ "cosmwasm-schema", "cosmwasm-std", "quote", - "syn 2.0.41", + "syn 2.0.43", ] [[package]] @@ -1846,9 +1853,9 @@ dependencies = [ [[package]] name = "object" -version = "0.32.1" +version = "0.32.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" dependencies = [ "memchr", ] @@ -1867,9 +1874,9 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "openssl" -version = "0.10.61" +version = "0.10.62" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b8419dc8cc6d866deb801274bba2e6f8f6108c1bb7fcc10ee5ab864931dbb45" +checksum = "8cde4d2d9200ad5909f8dac647e29482e07c3a35de8a13fce7c9c7747ad9f671" dependencies = [ "bitflags 2.4.1", "cfg-if", @@ -1888,7 +1895,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.43", ] [[package]] @@ -1899,9 +1906,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.97" +version = "0.9.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3eaad34cdd97d81de97964fc7f29e2d104f483840d906ef56daa1912338460b" +checksum = "c1665caf8ab2dc9aef43d1c0023bd904633a6a05cb30b0ad59bec2ae986e57a7" dependencies = [ "cc", "libc", @@ -1952,9 +1959,9 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.27" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" +checksum = "69d3587f8a9e599cc7ec2c00e331f71c4e69a5f9a4b8a6efd5b07466b9736f9a" [[package]] name = "predicates" @@ -2025,9 +2032,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.70" +version = "1.0.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b" +checksum = "75cb1540fadbd5b8fbccc4dddad2734eba435053f725621c070711a14bb5f4b8" dependencies = [ "unicode-ident", ] @@ -2052,7 +2059,7 @@ dependencies = [ "itertools 0.11.0", "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.43", ] [[package]] @@ -2415,9 +2422,9 @@ dependencies = [ [[package]] name = "self_cell" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e388332cd64eb80cd595a00941baf513caffae8dce9cfd0467fc9c66397dade6" +checksum = "58bf37232d3bb9a2c4e641ca2a11d83b5062066f88df7fed36c28772046d65ba" [[package]] name = "semver" @@ -2471,7 +2478,7 @@ checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.43", ] [[package]] @@ -2661,9 +2668,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.41" +version = "2.0.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c8b28c477cc3bf0e7966561e3460130e1255f7a1cf71931075f1c5e7a7e269" +checksum = "ee659fb5f3d355364e1f3e5bc10fb82068efbf824a1e9d1c9504244a6469ad53" dependencies = [ "proc-macro2", "quote", @@ -2739,7 +2746,7 @@ checksum = "01742297787513b79cf8e29d1056ede1313e2420b7b3b15d0a768b4921f549df" dependencies = [ "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.43", ] [[package]] @@ -2872,7 +2879,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.43", ] [[package]] @@ -3007,7 +3014,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.43", "wasm-bindgen-shared", ] @@ -3064,7 +3071,7 @@ checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" dependencies = [ "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.43", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -3227,7 +3234,7 @@ dependencies = [ "lazy_static", "libc", "mach", - "memoffset 0.8.0", + "memoffset", "more-asserts", "region", "scopeguard", diff --git a/Cargo.toml b/Cargo.toml index 8796233..8490d08 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,7 +21,7 @@ nibiru-macro = { path = "packages/nibiru-macro" } bash-rs = { path = "packages/bash-rs" } # deps: CosmWasm -cosmwasm-std = { version = "1.5.0", features = ["stargate"] } +cosmwasm-std = { version = "1.5.0", features = ["stargate", "staking"] } cosmwasm-schema = "1.5.0" cw-storage-plus = { version = "1.2.0" } cw-multi-test = { version = "0.20.0" } @@ -52,4 +52,15 @@ lazy_static = "1.4.0" clap = { version = "4.4.7", features = ["derive", "cargo", "env", "string"] } schemars = "0.8.15" home = "0.5" -toml = "0.8" \ No newline at end of file +toml = "0.8" + +[profile.release] +opt-level = 3 +debug = false +rpath = false +lto = true +debug-assertions = false +codegen-units = 1 +panic = 'abort' +incremental = false +overflow-checks = true \ No newline at end of file diff --git a/Cookbook.md b/Cookbook.md new file mode 100644 index 0000000..d1a5d7f --- /dev/null +++ b/Cookbook.md @@ -0,0 +1,581 @@ +# Contracts Cookbook + +This file describes the different messages that can be sent as queries or transactions +to the contracts of this repository with a description of the expected behavior. + +## 1. Core cw3 flex multisig + +This contract is a multisig contract that is backed by a cw4 (group) contract, which independently maintains the voter set. + +### 1.1 Instantiate + +```javascript +{ + "group_addr": "cosmos1...", // this is the group contract that contains the member list + "threshold": { + "absolute_count": {"weight": 2}, + "absolute_percentage": {"percentage": 0.5}, + "threshold_quorum": { "threshold": 0.1, "quorum": 0.2 } + }, + "max_voting_period": "3600s", + // who is able to execute passed proposals + // None means that anyone can execute + "executor": {}, + /// The cost of creating a proposal (if any). + "proposal_deposit": { + "denom": "uusd", + "amount": "1000000" + }, +} +``` + +### 1.2 Execute + +- **Propose** creates a message to be executed by the multisig. It can be executed by anyone. + +```javascript +{ + "propose": { + "title": "My proposal", + "description": "This is a proposal", + "msgs": [ + { + "bank": { + "send": { + "from_address": "cosmos1...", + "to_address": "cosmos1...", + "amount": [{ "denom": "uusd", "amount": "1000000" }] + } + } + } + ], + "latest": { + "at_height": 123456 + } + } +} +``` + +- **Vote** adds a vote to an existing proposal. It can be executed by anyone. + +```javascript +{ + "vote": { + "proposal_id": 1, + "vote": "yes" + } +} +``` + +- **Execute** executes a passed proposal. It can be executed by anyone. + +```javascript +{ + "execute": { + "proposal_id": 1 + } +} +``` + +- **Close** closes an expired proposal. It can be executed by anyone. + +```javascript +{ + "close": { + "proposal_id": 1 + } +} +``` + +### 1.3 Query + +- **Threshold** returns the current threshold necessary for a proposal to be executed. + +```javascript +{ + "threshold": {} +} +``` + +- **Proposal** fetches the details of a specific proposal given its ID. + +```javascript +{ + "proposal": { + "proposal_id": 1 + } +} +``` + +- **ListProposals** lists proposals with optional pagination. `start_after` specifies the ID after which to start listing, and `limit` sets the maximum number of proposals to return. + +```javascript +{ + "list_proposals": { + "start_after": 1, + "limit": 10 + } +} +``` + +- **ReverseProposals** lists proposals in reverse order with optional pagination. `start_before` specifies the ID before which to start listing in reverse, and `limit` sets the maximum number of proposals to return. + +```javascript +{ + "reverse_proposals": { + "start_before": 10, + "limit": 10 + } +} +``` + +- **Vote** retrieves the vote details for a given proposal ID and voter address. + +```javascript +{ + "vote": { + "proposal_id": 1, + "voter": "cosmos1..." + } +} +``` + +- **ListVotes** lists votes for a given proposal, with optional pagination. `start_after` specifies the address after which to start listing votes, and `limit` sets the maximum number of votes to return. + +```javascript +{ + "list_votes": { + "proposal_id": 1, + "start_after": "cosmos1...", + "limit": 10 + } +} +``` + +- **Voter** fetches details about a specific voter by their address. + +```javascript +{ + "voter": { + "address": "cosmos1..." + } +} +``` + +- **ListVoters** lists voters with optional pagination. `start_after` specifies the address after which to start listing voters, and `limit` sets the maximum number of voters to return. + +```javascript +{ + "list_voters": { + "start_after": "cosmos1...", + "limit": 10 + } +} +``` + +- **Config** retrieves the current configuration of the system. + +```javascript +{ + "config": {} +} +``` + +## 2. Core shifter + +Shifter is a simple contract that can execute peg and depth shift to any markets in the x/perp module of Nibiru. +The contract holds a whitelist of addressses that are allowed to execute the shift. + +### 2.1 Instantiate + +The instantiation defines just the onwer of the contract, who wil be able to add and remove addresses from the whitelist, and execute the shifts. + +```javascript +{"owner": "cosmos1..."} +``` + +### 2.2 Execute + +- **ShiftSwapInvariant** executes a depth shift in a market. + +```javascript +{ + "shift_swap_invariant": { + "pair": "uusd:usdr", + "new_swap_invariant": "1000000" + } +} +``` + +- **ShiftPegMultiplier** executes a depth shift on a market. It can be executed by anyone. + +```javascript +{ + "shift_peg_multiplier": { + "pair": "ubtc:unusd", + "new_peg_mult": "20420.69" + } +} +``` + +- **EditOpers** adds or removes addresses from the whitelist. It can be executed by the owner. + +```javascript +{ + "edit_opers": { + "add_oper": {"addr": "cosmos1..."}, + "remove_oper": {"addr": "cosmos1..."}, + } +} +``` + +### 2.3 Query + +The queries have to do with checking permissions of addresses. + +- **HasPerms** checks if an address has permissions to execute shifts. + +```javascript +{ + "has_perms": { + "address": "cosmos1..." + } +} +``` + +- **Perms** query the contract owner and set of operators. + +```javascript +{ + "perms": {}, +} +``` + +## 3. Core token vesting + +This contract implements vesting accounts for the CW20 and native tokens. + +### 3.1 Instantiate + +There's no instantiation message. + +```javascript +{} +``` + +### 3.2 Execute + +- **Receive** + +```javascript +{ + "receive": { + "sender": "cosmos1...", + "amount": "1000000", + "msg": "eyJ2ZXN0X2lkIjoxLCJ2ZXN0X3R5cGUiOiJ2ZXN0In0=", + } +} +``` + +- **RegisterVestingAccount** registers a vesting account + +```javascript +{ + "register_vesting_account": { + "address": "cosmos1...", + "master_address": "cosmos1...", + "vesting_schedule": { + "linear_vesting": { + "start_time": "1703772805", + "end_time": "1703872805", + "vesting_amount": "1000000" + } + } + } +} +``` + +- **DeregisterVestingAccount** deregisters a vesting account + +```javascript +{ + "deregister_vesting_account": { + "address": "cosmos1...", + "denom": "uusd", + "vested_token_recipient": "cosmos1...", // address that will receive the vested tokens after deregistration. If None, tokens are received by the owner address. + "left_vested_token_recipient": "cosmos1...", // address that will receive the left vesting tokens after deregistration. + } +} +``` + +- **Claim** allows to claim vested tokens + +```javascript +{ + "claim": { + "denom": "uusd", + "recipient": "cosmos1...", + } +} +``` + +### 3.3 Query + +- **VestingAccount** returns the vesting account details for a given address. + +```javascript +{ + "vesting_account": { + "address": "cosmos1...", + } +} +``` + +## 4. Nibi Stargate + +This smart contract showcases usage examples for certain Nibiru-specific and Cosmos-SDK-specific. + +### 4.1 Instantiate + +There's no instantiation message. + +```javascript +{} +``` + +### 4.2 Execute + +- **CreateDenom** creates a new denom + +```javascript +{ + "create_denom": { "subdenom": "zzz" } +} +``` + +- **Mint** mints tokens + +```javascript +{ + "mint": { + "coin": { "amount": "[amount]", "denom": "tf/[contract-addr]/[subdenom]" }, + "mint_to": "[mint-to-addr]" + } +} +``` + +- **Burn** burns tokens + +```javascript +{ + "burn": { + "coin": { "amount": "[amount]", "denom": "tf/[contract-addr]/[subdenom]" }, + "burn_from": "[burn-from-addr]" + } +} +``` + +- **ChangeAdmin** changes the admin of a denom + +```javascript +{ + "change_admin": { + "denom": "tf/[contract-addr]/[subdenom]", + "new_admin": "[ADDR]" + } +} +``` + +## 5. Nibi Stargate Perp + +This smart contract showcases usage examples for certain Nibiru-specific for the perp market. + +### 5.1 Instantiate + +The instantiation defines the owner of the contract, who will be able to add and remove addresses from the whitelist, and execute the shifts. + +```javascript +{ + "admin": "cosmos1...", +} +``` +### 5.2 Execute + +- **MarketOrder** places a market order for a specified trading pair. `pair` indicates the trading pair, `is_long` determines if it's a long or short order, `quote_amount` is the amount in the quote currency, `leverage` specifies the leverage to apply, and `base_amount_limit` sets a limit for the amount in the base currency. + +```javascript +{ + "market_order": { + "pair": "BTC/USDT", + "is_long": true, + "quote_amount": "1000000", + "leverage": "2.0", + "base_amount_limit": "5000000" + } +} +``` + +- **ClosePosition** closes an open position for a specified trading pair. + +```javascript +{ + "close_position": { + "pair": "BTC/USDT" + } +} +``` + +- **AddMargin** adds margin to an existing position for a specified trading pair. `margin` is the amount of additional margin to add. + +```javascript +{ + "add_margin": { + "pair": "BTC/USDT", + "margin": {"denom": "usdt", "amount": "100000"} + } +} +``` + +- **RemoveMargin** removes margin from an existing position for a specified trading pair. `margin` is the amount of margin to remove. + +```javascript +{ + "remove_margin": { + "pair": "BTC/USDT", + "margin": {"denom": "usdt", "amount": "50000"} + } +} +``` + +- **MultiLiquidate** triggers multiple liquidations based on the provided arguments. `liquidations` is a list of liquidation arguments specifying the details for each liquidation. + +```javascript +{ + "multi_liquidate": { + "liquidations": [ + { + "pair": "BTC/USDT", + "trader": "cosmos1...", + }, + { + "pair": "BTC/USDT", + "trader": "cosmos1...", + } + ] + } +} +``` + +- **DonateToInsuranceFund** allows donation to the insurance fund. `donation` is the coin and amount to donate. + +```javascript +{ + "donate_to_insurance_fund": { + "donation": {"denom": "usdt", "amount": "100000"} + } +} +``` + +- **Claim** facilitates the claiming of funds. `funds` is an optional field specifying a particular coin and amount to claim, `claim_all` is an optional flag to claim all funds, and `to` is the address to which the funds will be sent. + +```javascript +{ + "claim": { + "funds": {"denom": "usdt", "amount": "100000"}, + "claim_all": true, + "to": "cosmos1..." + } +} +``` + +This format aligns with the style of your previous documentation, ensuring consistency and clarity in the explanation of each function and its parameters. + +## 6. Nusd Valuator + +This smart contract is a simple valuator for the nusd token, which takes one collateral. + +### 6.1 Instantiate + +The owner is the only one who can execute messages in the contract + +```javascript +{ + "owner": "cosmos1...", + "accepted_denoms": "uusdc", +} +``` + +### 6.2 Execute + +- **ChangeDenom** updates the accepted denoms + +```javascript +{ + "change_denom": { + "from: "uusdc", + "to": "uusd", + } +} +``` + +- **AddDenom** adds a new accepted denom + +```javascript +{ + "add_denom": { + "denom": "uusd", + } +} +``` + +- **RemoveDenom** removes an accepted denom + +```javascript +{ + "remove_denom": { + "denom": "uusd", + } +} +``` + +### 6.3 Query + + +- **Mintable** queries the amount of μNUSD that can be minted in exchange for the specified set of `from_coins`. + +```javascript +{ + "mintable": { + "from_coins": ["BTC", "ETH"] + } +} +``` + +- **Redeemable** calculates the amount of a specified `to_denom` currency that is redeemable for a given `redeem_amount` of μNUSD. + +```javascript +{ + "redeemable": { + "redeem_amount": "1000000", + "to_denom": "usdt" + } +} +``` + +- **AcceptedDenoms** retrieves the set of token denominations that are accepted as collateral. + +```javascript +{ + "accepted_denoms": {} +} +``` + +- **RedeemableChoices** provides a set of possible redeemable coin options that could be received when redeeming a specified `redeem_amount` of μNUSD. + +```javascript +{ + "redeemable_choices": { + "redeem_amount": "1000000" + } +} +``` diff --git a/artifacts/airdrop.wasm b/artifacts/airdrop.wasm new file mode 100644 index 0000000..916883e Binary files /dev/null and b/artifacts/airdrop.wasm differ diff --git a/artifacts/checksums.txt b/artifacts/checksums.txt index 480c7d4..ef18516 100644 --- a/artifacts/checksums.txt +++ b/artifacts/checksums.txt @@ -1,10 +1,11 @@ +6e43956a6ae41e032b611529bbbd0112699f96a8126fc32b879cfe14521ad85f airdrop.wasm 382c05baf544f2886de849933ecf59e8bc3bcdcdd552d5a63537bd6d63f2ecf1 controller.wasm -83d726be3842b4df7179b9cfd84c3c9f8856d4252738cf4a6d27fc699f5b43bf cw3_flex_multisig.wasm -f44fd4d9a266f9281bdbedec277d82484c88688ac436d908d41ded1643b0b3f3 incentives.wasm -4c3932c7bcf99112053da52ab15d7718e827d9e71a64d57181c3816dff141e71 lockup.wasm -e6e0180d424b4f546b8d29c8f49d7ced09011a33eccfe124af7daccbac005a37 nibi_stargate.wasm -e2741d21b4bc8a8628e340d1c119a6f5411b4a7d2b69b9e3c7f344bdcb15ec57 nibi_stargate_perp.wasm -ec015f78a854ae7450915b973193e65039050b156af21791899a72d532d2c623 nusd_valuator.wasm -d4b6a34c5c51c1e080b953fcc43b011f6616e7a49b50fb18bf93ec7dddbe9a95 pricefeed.wasm -ce1ff9cd1e5127ae94797f5d013074ef2d9fb4d6c41f7488ead3361626687d5d shifter.wasm -dd8fb20aefaf69951c09f42bfe5d9678678d51b9f3e2bba098f3175fb86785a9 token_vesting.wasm +0ee6293c7ab257139d6b10abb31cafe7a6c00f3fbf2c8be126363f3c1e4e6d80 cw3_flex_multisig.wasm +515a13e891e6bf6a95ab985f653a45668c24991931fc664b64d5a0e803e4ab33 incentives.wasm +159602bf4ca5b4b2430662f0f4cf198fc764608dc2ceb25e35970896e2e4563c lockup.wasm +ced175298f8e6b4af8f414609f3c05c0ec533f1312bac3e650a75974a0e1312f nibi_stargate.wasm +540c83759a1ec8b4a78565345275c6da6c7fdfb420748c2e60d392148eb21bda nibi_stargate_perp.wasm +96c155ab1314f818baf9c0c91373446c822a0f49dccfdbacd3e18cb3579028f7 nusd_valuator.wasm +fb19953e1fdba3ac783dcd3fce80fd28e93f09ac383e0b98326a04a9e6075c08 pricefeed.wasm +decbc751198e3a4fe0ee134f85b1983c0e97d6cfa4c2bad5c084ebb5080566ec shifter.wasm +d9b341cd6b3f5879159f60cd1f4a1eedf82f9358e0989068a2a6317cf3055beb token_vesting.wasm diff --git a/artifacts/cw3_flex_multisig.wasm b/artifacts/cw3_flex_multisig.wasm index 85062c1..8ddab8b 100644 Binary files a/artifacts/cw3_flex_multisig.wasm and b/artifacts/cw3_flex_multisig.wasm differ diff --git a/artifacts/incentives.wasm b/artifacts/incentives.wasm index 08b11b5..cb6ac80 100644 Binary files a/artifacts/incentives.wasm and b/artifacts/incentives.wasm differ diff --git a/artifacts/lockup.wasm b/artifacts/lockup.wasm index b404cce..848e866 100644 Binary files a/artifacts/lockup.wasm and b/artifacts/lockup.wasm differ diff --git a/artifacts/nibi_stargate.wasm b/artifacts/nibi_stargate.wasm index bbe327c..8502ed0 100644 Binary files a/artifacts/nibi_stargate.wasm and b/artifacts/nibi_stargate.wasm differ diff --git a/artifacts/nibi_stargate_perp.wasm b/artifacts/nibi_stargate_perp.wasm index a7f4105..474af57 100644 Binary files a/artifacts/nibi_stargate_perp.wasm and b/artifacts/nibi_stargate_perp.wasm differ diff --git a/artifacts/nusd_valuator.wasm b/artifacts/nusd_valuator.wasm index fbf8447..3525864 100644 Binary files a/artifacts/nusd_valuator.wasm and b/artifacts/nusd_valuator.wasm differ diff --git a/artifacts/pricefeed.wasm b/artifacts/pricefeed.wasm index 88d6dd1..304a4fd 100644 Binary files a/artifacts/pricefeed.wasm and b/artifacts/pricefeed.wasm differ diff --git a/artifacts/shifter.wasm b/artifacts/shifter.wasm index 21e16f0..6c83cae 100644 Binary files a/artifacts/shifter.wasm and b/artifacts/shifter.wasm differ diff --git a/artifacts/token_vesting.wasm b/artifacts/token_vesting.wasm index 1d42183..1d5c793 100644 Binary files a/artifacts/token_vesting.wasm and b/artifacts/token_vesting.wasm differ diff --git a/contracts/airdrop/.cargo/config b/contracts/airdrop/.cargo/config new file mode 100644 index 0000000..b613a59 --- /dev/null +++ b/contracts/airdrop/.cargo/config @@ -0,0 +1,4 @@ +[alias] +wasm = "build --release --target wasm32-unknown-unknown" +wasm-debug = "build --target wasm32-unknown-unknown" +schema = "run --example schema" diff --git a/contracts/airdrop/Cargo.toml b/contracts/airdrop/Cargo.toml new file mode 100644 index 0000000..39ac9e9 --- /dev/null +++ b/contracts/airdrop/Cargo.toml @@ -0,0 +1,36 @@ +[package] +name = "airdrop" +version = "0.0.1" +edition = "2021" +homepage = "https://nibiru.fi" +repository = "https://github.com/NibiruChain/cw-nibiru" +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 = [] + +[dependencies] +cosmwasm-std = { workspace = true } +cosmwasm-schema = { workspace = true } +cw-storage-plus = { workspace = true } +nibiru-std = { workspace = true } +thiserror = { workspace = true } +schemars = "0.8.15" +serde = { version = "1.0.188", default-features = false, features = ["derive"] } +cw-ownable = { workspace = true } +cw2 = { workspace = true } +semver = "1" + +[dev-dependencies] +anyhow = { workspace = true } \ No newline at end of file diff --git a/contracts/airdrop/README.md b/contracts/airdrop/README.md new file mode 100644 index 0000000..fae09cc --- /dev/null +++ b/contracts/airdrop/README.md @@ -0,0 +1,13 @@ +# Airdrop Contract + +## Overview + +The airdrop contract is used to distribute tokens to a list of addresses. An instance of a contract represents a campaign. The contract is initialized with a campaign id, a campaign name, a campaign description, an owner (the deployer of the contract), a list of managers, and funds which become the unallocated amount. + +The token allocation amount starts unallocated and eventually gets allocated to users by the owner and managers. + +The contract owner and managers can allocate/reward users with tokens by calling the `reward_users` function. The `reward_users` function takes a list of addresses and amounts. The total reward amount must be less than the unallocated token amount of the contract. + +## Withdraw + +Only the contract owner can withdraw from the contract (not the managers). The `withdraw` exists to withdraw any leftover tokens after the campaign has ended. There is no check for if the total outstanding reward amount is greater than the amount of funds left in the contract. `withdraw` should only be called after the campaign ends because it could leave the contract in a state where it cannot fulfill a user's outstanding reward amount. Additional funds can be sent to the contract to reverse the withdrawal. diff --git a/contracts/airdrop/src/contract.rs b/contracts/airdrop/src/contract.rs new file mode 100644 index 0000000..09fafab --- /dev/null +++ b/contracts/airdrop/src/contract.rs @@ -0,0 +1,256 @@ +use crate::{ + msg::{ + ExecuteMsg, InstantiateMsg, QueryMsg, RewardUserRequest, + RewardUserResponse, + }, + state::{Campaign, CAMPAIGN, USER_REWARDS}, +}; +#[cfg(not(feature = "library"))] +use cosmwasm_std::entry_point; +use cosmwasm_std::{ + to_json_binary, Addr, BankMsg, Binary, Coin, CosmosMsg, Deps, DepsMut, + Empty, Env, MessageInfo, Response, StdError, StdResult, Uint128, +}; +use cw2::{get_contract_version, set_contract_version}; +use semver::Version; + +pub const CONTRACT_NAME: &str = env!("CARGO_PKG_NAME"); +pub const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn instantiate( + deps: DepsMut, + _env: Env, + info: MessageInfo, + msg: InstantiateMsg, +) -> Result { + set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; + + if info.funds.len() != 1 { + return Err(StdError::generic_err("Only one coin is allowed")); + } + + let bond_denom = deps.querier.query_bonded_denom()?; + let coin = info.funds.get(0).unwrap(); + if coin.denom != bond_denom { + return Err(StdError::generic_err("Only native tokens are allowed")); + } + + let campaign = Campaign { + campaign_id: msg.campaign_id, + campaign_name: msg.campaign_name, + campaign_description: msg.campaign_description, + owner: info.sender.clone(), + managers: msg.managers, + unallocated_amount: coin.amount, + }; + CAMPAIGN.save(deps.storage, &campaign)?; + + Ok(Response::new() + .add_attribute("method", "instantiate") + .add_attribute("owner", info.sender)) +} + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn migrate( + deps: DepsMut, + _env: Env, + _msg: Empty, +) -> Result { + let new_version: Version = CONTRACT_VERSION + .parse() + .map_err(|_| StdError::generic_err("Invalid contract version format"))?; + let current_version = get_contract_version(deps.storage)?; + + if current_version.contract != CONTRACT_NAME { + return Err(StdError::generic_err( + "Can only upgrade from same contract type", + )); + } + + if current_version.version.parse::().unwrap() >= new_version { + return Err(StdError::generic_err( + "Cannot upgrade from a newer contract version", + )); + } + + set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; + + Ok(Response::new().add_attribute("method", "migrate")) +} + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn execute( + deps: DepsMut, + env: Env, + info: MessageInfo, + msg: ExecuteMsg, +) -> Result { + match msg { + ExecuteMsg::RewardUsers { requests } => { + reward_users(deps, env, info, requests) + } + ExecuteMsg::Claim {} => claim(deps, env, info), + ExecuteMsg::Withdraw { amount } => withdraw(deps, env, info, amount), + } +} + +/// Reward a set of users with native tokens from the campaign pool +/// +/// - Requires sender to be the contract owner or a campaign manager. +/// - Ensures there are enough unallocated funds in the campaign +/// - Saves/updates user reward pool balances. +/// - Reduces the available campaign balance +pub fn reward_users( + deps: DepsMut, + _env: Env, + info: MessageInfo, + requests: Vec, +) -> Result { + let mut res = vec![]; + + let mut campaign = CAMPAIGN + .load(deps.storage) + .map_err(|_| StdError::generic_err("Failed to load campaign data"))?; + + if campaign.owner != info.sender && !campaign.managers.contains(&info.sender) + { + return Err(StdError::generic_err("Unauthorized")); + } + + for req in requests { + if campaign.unallocated_amount < req.amount { + return Err(StdError::generic_err( + "Not enough funds in the campaign", + )); + } + + match USER_REWARDS.may_load(deps.storage, req.user_address.clone())? { + Some(mut user_reward) => { + user_reward += req.amount; + USER_REWARDS.save( + deps.storage, + req.user_address.clone(), + &user_reward, + )?; + } + None => { + USER_REWARDS.save( + deps.storage, + req.user_address.clone(), + &req.amount, + )?; + } + }; + campaign.unallocated_amount -= req.amount; + CAMPAIGN.save(deps.storage, &campaign)?; + + res.push(RewardUserResponse { + user_address: req.user_address.clone(), + success: true, + error_msg: "".to_string(), + }); + } + + Ok(Response::new() + .add_attribute("method", "reward_users") + .set_data(to_json_binary(&res).unwrap())) +} + +/// Allow a user to claim any rewards allocated to them +/// +/// Transfers the user's full reward balance to their account. Resets their +/// reward balance to 0. +pub fn claim( + deps: DepsMut, + _env: Env, + info: MessageInfo, +) -> Result { + let bond_denom = deps.querier.query_bonded_denom()?; + + match USER_REWARDS.may_load(deps.storage, info.sender.clone())? { + Some(user_reward) => { + USER_REWARDS.remove(deps.storage, info.sender.clone()); + + Ok(Response::new() + .add_attribute("method", "claim") + .add_message(CosmosMsg::Bank(BankMsg::Send { + to_address: info.sender.to_string(), + amount: vec![Coin { + denom: bond_denom.clone(), + amount: user_reward, + }], + }))) + } + None => Err(StdError::generic_err("User pool does not exist")), + } +} + +/// Allow the contract owner to withdraw native tokens +/// +/// Ensures the requested amount is available in the contract balance. Transfers +/// tokens to the contract owner's account. +pub fn withdraw( + deps: DepsMut, + env: Env, + info: MessageInfo, + amount: Uint128, +) -> Result { + let campaign = CAMPAIGN.load(deps.storage)?; + + if info.sender != campaign.owner { + return Err(StdError::generic_err("Only contract owner can withdraw")); + } + + let bond_denom = deps.querier.query_bonded_denom()?; + + let own_balance: Uint128 = deps + .querier + .query_balance(env.contract.address, bond_denom.clone()) + .map_err(|_| StdError::generic_err("Failed to query contract balance"))? + .amount; + + if amount > own_balance { + return Err(StdError::generic_err("Not enough funds in the contract")); + } + + let res = Response::new() + .add_attribute("method", "withdraw") + .add_message(CosmosMsg::Bank(BankMsg::Send { + to_address: info.sender.to_string(), + amount: vec![Coin { + denom: bond_denom.clone(), + amount, + }], + })); + + Ok(res) +} + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult { + match msg { + QueryMsg::Campaign {} => query_campaign(deps, env), + QueryMsg::GetUserReward { user_address } => { + query_user_reward(deps, env, user_address) + } + } +} + +pub fn query_campaign(deps: Deps, _env: Env) -> StdResult { + match CAMPAIGN.load(deps.storage) { + Ok(campaign) => to_json_binary(&campaign), + Err(_) => Err(StdError::generic_err("Failed to load campaign data")), + } +} + +pub fn query_user_reward( + deps: Deps, + _env: Env, + user_address: Addr, +) -> StdResult { + match USER_REWARDS.load(deps.storage, user_address) { + Ok(user_reward) => to_json_binary(&user_reward), + Err(_) => Err(StdError::generic_err("User reward does not exist")), + } +} diff --git a/contracts/airdrop/src/lib.rs b/contracts/airdrop/src/lib.rs new file mode 100644 index 0000000..b88f588 --- /dev/null +++ b/contracts/airdrop/src/lib.rs @@ -0,0 +1,6 @@ +pub mod contract; +pub mod msg; +pub mod state; + +#[cfg(test)] +mod tests; diff --git a/contracts/airdrop/src/msg.rs b/contracts/airdrop/src/msg.rs new file mode 100644 index 0000000..6ab5696 --- /dev/null +++ b/contracts/airdrop/src/msg.rs @@ -0,0 +1,38 @@ +use cosmwasm_std::{Addr, Uint128}; +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] +pub struct InstantiateMsg { + pub campaign_id: String, + pub campaign_name: String, + pub campaign_description: String, + pub managers: Vec, +} + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] +pub struct RewardUserRequest { + pub user_address: Addr, + pub amount: Uint128, +} + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] +pub struct RewardUserResponse { + pub user_address: Addr, + pub success: bool, + pub error_msg: String, +} + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] +#[serde(rename_all = "snake_case")] +pub enum ExecuteMsg { + RewardUsers { requests: Vec }, + Claim {}, + Withdraw { amount: Uint128 }, +} + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] +#[serde(rename_all = "snake_case")] +pub enum QueryMsg { + Campaign {}, + GetUserReward { user_address: Addr }, +} diff --git a/contracts/airdrop/src/state.rs b/contracts/airdrop/src/state.rs new file mode 100644 index 0000000..b486153 --- /dev/null +++ b/contracts/airdrop/src/state.rs @@ -0,0 +1,18 @@ +use cosmwasm_std::{Addr, Uint128}; +use cw_storage_plus::{Item, Map}; +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] +pub struct Campaign { + pub campaign_id: String, + pub campaign_name: String, + pub campaign_description: String, + + pub unallocated_amount: Uint128, + pub owner: Addr, + pub managers: Vec, +} + +pub const CAMPAIGN: Item = Item::new("campaign"); +pub const USER_REWARDS: Map = Map::new("user_rewards"); diff --git a/contracts/airdrop/src/tests/execute/claim.rs b/contracts/airdrop/src/tests/execute/claim.rs new file mode 100644 index 0000000..22db64c --- /dev/null +++ b/contracts/airdrop/src/tests/execute/claim.rs @@ -0,0 +1,76 @@ +use crate::contract::{claim, instantiate, reward_users}; +use crate::msg::{InstantiateMsg, RewardUserRequest}; +use crate::state::USER_REWARDS; +use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info}; +use cosmwasm_std::{coins, Addr, BankMsg, CosmosMsg, StdError, SubMsg, Uint128}; +use std::vec; + +#[test] +fn test_claim() { + let mut deps = mock_dependencies(); + let env = mock_env(); + + instantiate( + deps.as_mut(), + env.clone(), + mock_info("owner", &coins(1000, "")), + InstantiateMsg { + campaign_id: "campaign_id".to_string(), + campaign_name: "campaign_name".to_string(), + campaign_description: "campaign_description".to_string(), + managers: vec![ + Addr::unchecked("manager1"), + Addr::unchecked("manager2"), + ], + }, + ) + .unwrap(); + + reward_users( + deps.as_mut(), + env.clone(), + mock_info("owner", &[]), + vec![ + RewardUserRequest { + user_address: Addr::unchecked("user1"), + amount: Uint128::new(750), + }, + RewardUserRequest { + user_address: Addr::unchecked("user2"), + amount: Uint128::new(250), + }, + ], + ) + .unwrap(); + + // try to claim from user1 + let resp = + claim(deps.as_mut(), env.clone(), mock_info("user1", &[])).unwrap(); + + assert_eq!( + resp.messages, + vec![SubMsg::new(CosmosMsg::Bank(BankMsg::Send { + to_address: "user1".to_string(), + amount: coins(750, ""), + }))] + ); + assert!(!USER_REWARDS.has(deps.as_ref().storage, Addr::unchecked("user1"))); + + // try to claim from user2 + let resp = + claim(deps.as_mut(), env.clone(), mock_info("user2", &[])).unwrap(); + + assert_eq!( + resp.messages, + vec![SubMsg::new(CosmosMsg::Bank(BankMsg::Send { + to_address: "user2".to_string(), + amount: coins(250, ""), + }))] + ); + assert!(!USER_REWARDS.has(deps.as_ref().storage, Addr::unchecked("user2"))); + + // try to claim from user3 who doesn't exist + let resp = claim(deps.as_mut(), env.clone(), mock_info("user3", &[])); + + assert_eq!(resp, Err(StdError::generic_err("User pool does not exist"))); +} diff --git a/contracts/airdrop/src/tests/execute/mod.rs b/contracts/airdrop/src/tests/execute/mod.rs new file mode 100644 index 0000000..62de483 --- /dev/null +++ b/contracts/airdrop/src/tests/execute/mod.rs @@ -0,0 +1,3 @@ +mod claim; +mod reward_users; +mod withdraw; diff --git a/contracts/airdrop/src/tests/execute/reward_users.rs b/contracts/airdrop/src/tests/execute/reward_users.rs new file mode 100644 index 0000000..2ab8ee6 --- /dev/null +++ b/contracts/airdrop/src/tests/execute/reward_users.rs @@ -0,0 +1,231 @@ +use crate::contract::{instantiate, reward_users}; +use crate::msg::{InstantiateMsg, RewardUserRequest, RewardUserResponse}; +use crate::state::{Campaign, CAMPAIGN, USER_REWARDS}; +use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info}; +use cosmwasm_std::{coins, from_json, Addr, StdError, Uint128}; +use std::vec; + +#[test] +fn test_reward_users_fully_allocated() { + let mut deps = mock_dependencies(); + let env = mock_env(); + + instantiate( + deps.as_mut(), + env.clone(), + mock_info("owner", &coins(1000, "")), + InstantiateMsg { + campaign_id: "campaign_id".to_string(), + campaign_name: "campaign_name".to_string(), + campaign_description: "campaign_description".to_string(), + managers: vec![ + Addr::unchecked("manager1"), + Addr::unchecked("manager2"), + ], + }, + ) + .unwrap(); + + let resp = reward_users( + deps.as_mut(), + env.clone(), + mock_info("owner", &[]), + vec![ + RewardUserRequest { + user_address: Addr::unchecked("user1"), + amount: Uint128::new(750), + }, + RewardUserRequest { + user_address: Addr::unchecked("user2"), + amount: Uint128::new(250), + }, + ], + ) + .unwrap(); + + // assert response + let user_responses: Vec = + from_json(resp.data.unwrap()).unwrap(); + assert_eq!( + user_responses, + vec![ + RewardUserResponse { + user_address: Addr::unchecked("user1"), + success: true, + error_msg: "".to_string(), + }, + RewardUserResponse { + user_address: Addr::unchecked("user2"), + success: true, + error_msg: "".to_string(), + }, + ] + ); + + // assert inner state of the contract + let campaign = CAMPAIGN.load(deps.as_ref().storage).unwrap(); + assert_eq!( + campaign, + Campaign { + owner: Addr::unchecked("owner"), + unallocated_amount: Uint128::zero(), + campaign_id: "campaign_id".to_string(), + campaign_name: "campaign_name".to_string(), + campaign_description: "campaign_description".to_string(), + managers: vec![ + Addr::unchecked("manager1"), + Addr::unchecked("manager2") + ], + } + ); + + assert_eq!( + USER_REWARDS + .load(deps.as_ref().storage, Addr::unchecked("user1")) + .unwrap(), + Uint128::new(750) + ); + + assert_eq!( + USER_REWARDS + .load(deps.as_ref().storage, Addr::unchecked("user2")) + .unwrap(), + Uint128::new(250) + ); +} + +#[test] +fn test_reward_users_as_manager() { + let mut deps = mock_dependencies(); + let env = mock_env(); + + instantiate( + deps.as_mut(), + env.clone(), + mock_info("owner", &coins(1000, "")), + InstantiateMsg { + campaign_id: "campaign_id".to_string(), + campaign_name: "campaign_name".to_string(), + campaign_description: "campaign_description".to_string(), + managers: vec![ + Addr::unchecked("manager1"), + Addr::unchecked("manager2"), + ], + }, + ) + .unwrap(); + + let resp = reward_users( + deps.as_mut(), + env.clone(), + mock_info("manager1", &[]), + vec![ + RewardUserRequest { + user_address: Addr::unchecked("user1"), + amount: Uint128::new(750), + }, + RewardUserRequest { + user_address: Addr::unchecked("user2"), + amount: Uint128::new(250), + }, + ], + ) + .unwrap(); + + // assert response + let user_responses: Vec = + from_json(resp.data.unwrap()).unwrap(); + assert_eq!( + user_responses, + vec![ + RewardUserResponse { + user_address: Addr::unchecked("user1"), + success: true, + error_msg: "".to_string(), + }, + RewardUserResponse { + user_address: Addr::unchecked("user2"), + success: true, + error_msg: "".to_string(), + }, + ] + ); + + // assert inner state of the contract + let campaign = CAMPAIGN.load(deps.as_ref().storage).unwrap(); + assert_eq!( + campaign, + Campaign { + owner: Addr::unchecked("owner"), + unallocated_amount: Uint128::zero(), + campaign_id: "campaign_id".to_string(), + campaign_name: "campaign_name".to_string(), + campaign_description: "campaign_description".to_string(), + managers: vec![ + Addr::unchecked("manager1"), + Addr::unchecked("manager2") + ], + } + ); + + assert_eq!( + USER_REWARDS + .load(deps.as_ref().storage, Addr::unchecked("user1")) + .unwrap(), + Uint128::new(750) + ); + + assert_eq!( + USER_REWARDS + .load(deps.as_ref().storage, Addr::unchecked("user2")) + .unwrap(), + Uint128::new(250) + ); +} + +#[test] +fn test_fails_when_we_try_to_allocate_more_than_available() { + let mut deps = mock_dependencies(); + let env = mock_env(); + + instantiate( + deps.as_mut(), + env.clone(), + mock_info("owner", &coins(1000, "")), + InstantiateMsg { + campaign_id: "campaign_id".to_string(), + campaign_name: "campaign_name".to_string(), + campaign_description: "campaign_description".to_string(), + managers: vec![ + Addr::unchecked("manager1"), + Addr::unchecked("manager2"), + ], + }, + ) + .unwrap(); + + let resp = reward_users( + deps.as_mut(), + env.clone(), + mock_info("manager1", &[]), + vec![ + RewardUserRequest { + user_address: Addr::unchecked("user1"), + amount: Uint128::new(750), + }, + RewardUserRequest { + user_address: Addr::unchecked("user2"), + amount: Uint128::new(250), + }, + RewardUserRequest { + user_address: Addr::unchecked("user3"), + amount: Uint128::new(251), + }, + ], + ); + + assert_eq!( + resp, + Err(StdError::generic_err("Not enough funds in the campaign",)) + ); +} diff --git a/contracts/airdrop/src/tests/execute/withdraw.rs b/contracts/airdrop/src/tests/execute/withdraw.rs new file mode 100644 index 0000000..f64d519 --- /dev/null +++ b/contracts/airdrop/src/tests/execute/withdraw.rs @@ -0,0 +1,115 @@ +use crate::contract::{instantiate, withdraw}; +use crate::msg::InstantiateMsg; +use cosmwasm_std::testing::{ + mock_dependencies, mock_dependencies_with_balance, mock_env, mock_info, +}; +use cosmwasm_std::{coins, Addr, BankMsg, CosmosMsg, StdError, SubMsg, Uint128}; +use std::vec; + +#[test] +fn test_withdraw_ok() { + let mut deps = mock_dependencies_with_balance(&coins(1000, "")); + let env = mock_env(); + + instantiate( + deps.as_mut(), + env.clone(), + mock_info("owner", &coins(1000, "")), + InstantiateMsg { + campaign_id: "campaign_id".to_string(), + campaign_name: "campaign_name".to_string(), + campaign_description: "campaign_description".to_string(), + managers: vec![ + Addr::unchecked("manager1"), + Addr::unchecked("manager2"), + ], + }, + ) + .unwrap(); + + // try to withdraw + let resp = withdraw( + deps.as_mut(), + env.clone(), + mock_info("owner", &[]), + Uint128::new(1000), + ) + .unwrap(); + + assert_eq!( + resp.messages, + vec![SubMsg::new(CosmosMsg::Bank(BankMsg::Send { + to_address: "owner".to_string(), + amount: coins(1000, ""), + }))] + ); +} + +#[test] +fn test_withdraw_too_much() { + let mut deps = mock_dependencies_with_balance(&coins(1000, "")); + let env = mock_env(); + + instantiate( + deps.as_mut(), + env.clone(), + mock_info("owner", &coins(1000, "")), + InstantiateMsg { + campaign_id: "campaign_id".to_string(), + campaign_name: "campaign_name".to_string(), + campaign_description: "campaign_description".to_string(), + managers: vec![ + Addr::unchecked("manager1"), + Addr::unchecked("manager2"), + ], + }, + ) + .unwrap(); + + // try to withdraw + let resp = withdraw( + deps.as_mut(), + env.clone(), + mock_info("owner", &[]), + Uint128::new(1001), + ); + + assert_eq!( + resp, + Err(StdError::generic_err("Not enough funds in the contract")) + ); +} + +#[test] +fn test_withdraw_unauthorized() { + let mut deps = mock_dependencies(); + let env = mock_env(); + + instantiate( + deps.as_mut(), + env.clone(), + mock_info("owner", &coins(1000, "")), + InstantiateMsg { + campaign_id: "campaign_id".to_string(), + campaign_name: "campaign_name".to_string(), + campaign_description: "campaign_description".to_string(), + managers: vec![ + Addr::unchecked("manager1"), + Addr::unchecked("manager2"), + ], + }, + ) + .unwrap(); + + // try to withdraw + let res = withdraw( + deps.as_mut(), + env.clone(), + mock_info("not_owner", &[]), + Uint128::new(1000), + ); + assert_eq!( + res, + Err(StdError::generic_err("Only contract owner can withdraw")) + ); +} diff --git a/contracts/airdrop/src/tests/instantiate.rs b/contracts/airdrop/src/tests/instantiate.rs new file mode 100644 index 0000000..cf0348d --- /dev/null +++ b/contracts/airdrop/src/tests/instantiate.rs @@ -0,0 +1,74 @@ +use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info}; +use cosmwasm_std::{coins, Addr, StdError, Uint128}; + +use crate::contract::instantiate; +use crate::msg::InstantiateMsg; +use crate::state::{Campaign, CAMPAIGN}; + +#[test] +fn test_instantiate() { + let mut deps = mock_dependencies(); + let info = mock_info("sender", &coins(1000, "")); + let env = mock_env(); + let msg = InstantiateMsg { + campaign_id: "campaign_id".to_string(), + campaign_name: "campaign_name".to_string(), + campaign_description: "campaign_description".to_string(), + managers: vec![Addr::unchecked("manager1"), Addr::unchecked("manager2")], + }; + + instantiate(deps.as_mut(), env.clone(), info.clone(), msg.clone()).unwrap(); + + let campaign = CAMPAIGN.load(deps.as_ref().storage).unwrap(); + assert_eq!( + campaign, + Campaign { + owner: Addr::unchecked("sender"), + managers: vec![ + Addr::unchecked("manager1"), + Addr::unchecked("manager2") + ], + unallocated_amount: Uint128::new(1000), + campaign_id: "campaign_id".to_string(), + campaign_name: "campaign_name".to_string(), + campaign_description: "campaign_description".to_string(), + } + ); +} + +#[test] +fn test_instantiate_with_no_funds() { + let mut deps = mock_dependencies(); + let info = mock_info("sender", &[]); + let env = mock_env(); + let msg = InstantiateMsg { + campaign_id: "campaign_id".to_string(), + campaign_name: "campaign_name".to_string(), + campaign_description: "campaign_description".to_string(), + managers: vec![Addr::unchecked("manager1"), Addr::unchecked("manager2")], + }; + + let resp = + instantiate(deps.as_mut(), env.clone(), info.clone(), msg.clone()); + assert_eq!(resp, Err(StdError::generic_err("Only one coin is allowed"))); +} + +#[test] +fn test_instantiate_with_invalid_denom() { + let mut deps = mock_dependencies(); + let info = mock_info("sender", &coins(1000, "foo")); + let env = mock_env(); + let msg = InstantiateMsg { + campaign_id: "campaign_id".to_string(), + campaign_name: "campaign_name".to_string(), + campaign_description: "campaign_description".to_string(), + managers: vec![Addr::unchecked("manager1"), Addr::unchecked("manager2")], + }; + + let resp = + instantiate(deps.as_mut(), env.clone(), info.clone(), msg.clone()); + assert_eq!( + resp, + Err(StdError::generic_err("Only native tokens are allowed")) + ); +} diff --git a/contracts/airdrop/src/tests/mod.rs b/contracts/airdrop/src/tests/mod.rs new file mode 100644 index 0000000..577ea74 --- /dev/null +++ b/contracts/airdrop/src/tests/mod.rs @@ -0,0 +1,3 @@ +mod execute; +mod instantiate; +mod query; diff --git a/contracts/airdrop/src/tests/query/campaign.rs b/contracts/airdrop/src/tests/query/campaign.rs new file mode 100644 index 0000000..36b80bc --- /dev/null +++ b/contracts/airdrop/src/tests/query/campaign.rs @@ -0,0 +1,45 @@ +use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info}; +use cosmwasm_std::{coins, from_json, Addr, Uint128}; + +use crate::contract::{instantiate, query_campaign}; +use crate::msg::InstantiateMsg; +use crate::state::Campaign; + +#[test] +fn test_query_campaign() { + let mut deps = mock_dependencies(); + let env = mock_env(); + + instantiate( + deps.as_mut(), + env.clone(), + mock_info("owner", &coins(1000, "")), + InstantiateMsg { + campaign_id: "campaign_id".to_string(), + campaign_name: "campaign_name".to_string(), + campaign_description: "campaign_description".to_string(), + managers: vec![ + Addr::unchecked("manager1"), + Addr::unchecked("manager2"), + ], + }, + ) + .unwrap(); + + let res = query_campaign(deps.as_ref(), env.clone()).unwrap(); + let campaign: Campaign = from_json(res).unwrap(); + assert_eq!( + campaign, + Campaign { + campaign_id: "campaign_id".to_string(), + campaign_name: "campaign_name".to_string(), + campaign_description: "campaign_description".to_string(), + owner: Addr::unchecked("owner"), + managers: vec![ + Addr::unchecked("manager1"), + Addr::unchecked("manager2") + ], + unallocated_amount: Uint128::new(1000), + } + ); +} diff --git a/contracts/airdrop/src/tests/query/mod.rs b/contracts/airdrop/src/tests/query/mod.rs new file mode 100644 index 0000000..5abd2f9 --- /dev/null +++ b/contracts/airdrop/src/tests/query/mod.rs @@ -0,0 +1,2 @@ +mod campaign; +mod user_pool; diff --git a/contracts/airdrop/src/tests/query/user_pool.rs b/contracts/airdrop/src/tests/query/user_pool.rs new file mode 100644 index 0000000..55a54c1 --- /dev/null +++ b/contracts/airdrop/src/tests/query/user_pool.rs @@ -0,0 +1,73 @@ +use crate::contract::{instantiate, query_user_reward, reward_users}; +use crate::msg::{InstantiateMsg, RewardUserRequest}; +use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info}; +use cosmwasm_std::{coins, from_json, Addr, StdError, Uint128}; +use std::vec; + +#[test] +fn test_query_user_pool() { + let mut deps = mock_dependencies(); + let env = mock_env(); + + instantiate( + deps.as_mut(), + env.clone(), + mock_info("owner", &coins(1000, "")), + InstantiateMsg { + campaign_id: "campaign_id".to_string(), + campaign_name: "campaign_name".to_string(), + campaign_description: "campaign_description".to_string(), + managers: vec![ + Addr::unchecked("manager1"), + Addr::unchecked("manager2"), + ], + }, + ) + .unwrap(); + + reward_users( + deps.as_mut(), + env.clone(), + mock_info("owner", &[]), + vec![RewardUserRequest { + user_address: Addr::unchecked("user1"), + amount: Uint128::new(999), + }], + ) + .unwrap(); + + let res = + query_user_reward(deps.as_ref(), env.clone(), Addr::unchecked("user1")) + .unwrap(); + let user_pool: Uint128 = from_json(res).unwrap(); + assert_eq!(user_pool, Uint128::new(999)); +} + +#[test] +fn test_query_user_pool_empty() { + let mut deps = mock_dependencies(); + let env = mock_env(); + + instantiate( + deps.as_mut(), + env.clone(), + mock_info("owner", &coins(1000, "")), + InstantiateMsg { + campaign_id: "campaign_id".to_string(), + campaign_name: "campaign_name".to_string(), + campaign_description: "campaign_description".to_string(), + managers: vec![ + Addr::unchecked("manager1"), + Addr::unchecked("manager2"), + ], + }, + ) + .unwrap(); + + let res = + query_user_reward(deps.as_ref(), env.clone(), Addr::unchecked("user1")); + assert_eq!( + res, + Err(StdError::generic_err("User reward does not exist")) + ); +} diff --git a/nibiru-std/src/proto/buf/nibiru.inflation.v1.rs b/nibiru-std/src/proto/buf/nibiru.inflation.v1.rs index 9235495..545c195 100644 --- a/nibiru-std/src/proto/buf/nibiru.inflation.v1.rs +++ b/nibiru-std/src/proto/buf/nibiru.inflation.v1.rs @@ -1,4 +1,16 @@ // @generated +/// EventInflationDistribution: Emitted when NIBI tokens are minted on the +/// network based on Nibiru's inflation schedule. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct EventInflationDistribution { + #[prost(message, optional, tag="1")] + pub staking_rewards: ::core::option::Option, + #[prost(message, optional, tag="2")] + pub strategic_reserve: ::core::option::Option, + #[prost(message, optional, tag="3")] + pub community_pool: ::core::option::Option, +} /// InflationDistribution defines the distribution in which inflation is /// allocated through minting on each epoch (staking, community, strategic). It /// excludes the team vesting distribution. diff --git a/nibiru-std/src/proto/buf/nibiru.oracle.v1.rs b/nibiru-std/src/proto/buf/nibiru.oracle.v1.rs index a2c7ad2..4d6815e 100644 --- a/nibiru-std/src/proto/buf/nibiru.oracle.v1.rs +++ b/nibiru-std/src/proto/buf/nibiru.oracle.v1.rs @@ -4,50 +4,51 @@ #[derive(Clone, PartialEq, ::prost::Message)] pub struct Params { /// VotePeriod defines the number of blocks during which voting takes place. - #[prost(uint64, tag = "1")] + #[prost(uint64, tag="1")] pub vote_period: u64, /// VoteThreshold specifies the minimum proportion of votes that must be /// received for a ballot to pass. - #[prost(string, tag = "2")] + #[prost(string, tag="2")] pub vote_threshold: ::prost::alloc::string::String, /// RewardBand defines a maxium divergence that a price vote can have from the /// weighted median in the ballot. If a vote lies within the valid range /// defined by: - /// μ := weightedMedian, - /// validRange := μ ± (μ * rewardBand / 2), + /// μ := weightedMedian, + /// validRange := μ ± (μ * rewardBand / 2), /// then rewards are added to the validator performance. /// Note that if the reward band is smaller than 1 standard /// deviation, the band is taken to be 1 standard deviation.a price - #[prost(string, tag = "3")] + #[prost(string, tag="3")] pub reward_band: ::prost::alloc::string::String, /// The set of whitelisted markets, or asset pairs, for the module. /// Ex. '\["unibi:uusd","ubtc:uusd"\]' - #[prost(string, repeated, tag = "4")] + #[prost(string, repeated, tag="4")] pub whitelist: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, /// SlashFraction returns the proportion of an oracle's stake that gets /// slashed in the event of slashing. `SlashFraction` specifies the exact /// penalty for failing a voting period. - #[prost(string, tag = "5")] + #[prost(string, tag="5")] pub slash_fraction: ::prost::alloc::string::String, /// SlashWindow returns the number of voting periods that specify a /// "slash window". After each slash window, all oracles that have missed more /// than the penalty threshold are slashed. Missing the penalty threshold is /// synonymous with submitting fewer valid votes than `MinValidPerWindow`. - #[prost(uint64, tag = "6")] + #[prost(uint64, tag="6")] pub slash_window: u64, - #[prost(string, tag = "7")] + #[prost(string, tag="7")] pub min_valid_per_window: ::prost::alloc::string::String, - /// Amount of time to look back for TWAP calculations - #[prost(message, optional, tag = "8")] + /// Amount of time to look back for TWAP calculations. + /// Ex: "900.000000069s" corresponds to 900 seconds and 69 nanoseconds in JSON. + #[prost(message, optional, tag="8")] pub twap_lookback_window: ::core::option::Option<::prost_types::Duration>, /// The minimum number of voters (i.e. oracle validators) per pair for it to be /// considered a passing ballot. Recommended at least 4. - #[prost(uint64, tag = "9")] + #[prost(uint64, tag="9")] pub min_voters: u64, /// The validator fee ratio that is given to validators every epoch. - #[prost(string, tag = "10")] + #[prost(string, tag="10")] pub validator_fee_ratio: ::prost::alloc::string::String, - #[prost(uint64, tag = "11")] + #[prost(uint64, tag="11")] pub expiration_blocks: u64, } /// Struct for aggregate prevoting on the ExchangeRateVote. @@ -57,11 +58,11 @@ pub struct Params { #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct AggregateExchangeRatePrevote { - #[prost(string, tag = "1")] + #[prost(string, tag="1")] pub hash: ::prost::alloc::string::String, - #[prost(string, tag = "2")] + #[prost(string, tag="2")] pub voter: ::prost::alloc::string::String, - #[prost(uint64, tag = "3")] + #[prost(uint64, tag="3")] pub submit_block: u64, } /// MsgAggregateExchangeRateVote - struct for voting on @@ -69,26 +70,26 @@ pub struct AggregateExchangeRatePrevote { #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct AggregateExchangeRateVote { - #[prost(message, repeated, tag = "1")] + #[prost(message, repeated, tag="1")] pub exchange_rate_tuples: ::prost::alloc::vec::Vec, - #[prost(string, tag = "2")] + #[prost(string, tag="2")] pub voter: ::prost::alloc::string::String, } /// ExchangeRateTuple - struct to store interpreted exchange rates data to store #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct ExchangeRateTuple { - #[prost(string, tag = "1")] + #[prost(string, tag="1")] pub pair: ::prost::alloc::string::String, - #[prost(string, tag = "2")] + #[prost(string, tag="2")] pub exchange_rate: ::prost::alloc::string::String, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct DatedPrice { - #[prost(string, tag = "1")] + #[prost(string, tag="1")] pub exchange_rate: ::prost::alloc::string::String, - #[prost(uint64, tag = "2")] + #[prost(uint64, tag="2")] pub created_block: u64, } /// Rewards defines a credit object towards validators @@ -97,26 +98,25 @@ pub struct DatedPrice { #[derive(Clone, PartialEq, ::prost::Message)] pub struct Rewards { /// id uniquely identifies the rewards instance of the pair - #[prost(uint64, tag = "1")] + #[prost(uint64, tag="1")] pub id: u64, /// vote_periods defines the vote periods left in which rewards will be /// distributed. - #[prost(uint64, tag = "2")] + #[prost(uint64, tag="2")] pub vote_periods: u64, /// Coins defines the amount of coins to distribute in a single vote period. - #[prost(message, repeated, tag = "3")] - pub coins: - ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag="3")] + pub coins: ::prost::alloc::vec::Vec, } /// Emitted when a price is posted #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct EventPriceUpdate { - #[prost(string, tag = "1")] + #[prost(string, tag="1")] pub pair: ::prost::alloc::string::String, - #[prost(string, tag = "2")] + #[prost(string, tag="2")] pub price: ::prost::alloc::string::String, - #[prost(int64, tag = "3")] + #[prost(int64, tag="3")] pub timestamp_ms: i64, } /// Emitted when a valoper delegates oracle voting rights to a feeder address. @@ -124,11 +124,11 @@ pub struct EventPriceUpdate { #[derive(Clone, PartialEq, ::prost::Message)] pub struct EventDelegateFeederConsent { /// Validator is the Bech32 address that is delegating voting rights. - #[prost(string, tag = "1")] + #[prost(string, tag="1")] pub validator: ::prost::alloc::string::String, /// Feeder is the delegate or representative that will be able to send /// vote and prevote transaction messages. - #[prost(string, tag = "2")] + #[prost(string, tag="2")] pub feeder: ::prost::alloc::string::String, } /// Emitted by MsgAggregateExchangeVote when an aggregate vote is added to state @@ -136,13 +136,13 @@ pub struct EventDelegateFeederConsent { #[derive(Clone, PartialEq, ::prost::Message)] pub struct EventAggregateVote { /// Validator is the Bech32 address to which the vote will be credited. - #[prost(string, tag = "1")] + #[prost(string, tag="1")] pub validator: ::prost::alloc::string::String, /// Feeder is the delegate or representative that will send vote and prevote /// transaction messages on behalf of the voting validator. - #[prost(string, tag = "2")] + #[prost(string, tag="2")] pub feeder: ::prost::alloc::string::String, - #[prost(message, repeated, tag = "3")] + #[prost(message, repeated, tag="3")] pub prices: ::prost::alloc::vec::Vec, } /// Emitted by MsgAggregateExchangePrevote when an aggregate prevote is added @@ -151,34 +151,55 @@ pub struct EventAggregateVote { #[derive(Clone, PartialEq, ::prost::Message)] pub struct EventAggregatePrevote { /// Validator is the Bech32 address to which the vote will be credited. - #[prost(string, tag = "1")] + #[prost(string, tag="1")] pub validator: ::prost::alloc::string::String, /// Feeder is the delegate or representative that will send vote and prevote /// transaction messages on behalf of the voting validator. - #[prost(string, tag = "2")] + #[prost(string, tag="2")] pub feeder: ::prost::alloc::string::String, } +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct EventValidatorPerformance { + /// Validator is the Bech32 address to which the vote will be credited. + #[prost(string, tag="1")] + pub validator: ::prost::alloc::string::String, + /// Tendermint consensus voting power + #[prost(int64, tag="2")] + pub voting_power: i64, + /// RewardWeight: Weight of rewards the validator should receive in units of + /// consensus power. + #[prost(int64, tag="3")] + pub reward_weight: i64, + /// Number of valid votes for which the validator will be rewarded + #[prost(int64, tag="4")] + pub win_count: i64, + /// Number of abstained votes for which there will be no reward or punishment + #[prost(int64, tag="5")] + pub abstain_count: i64, + /// Number of invalid/punishable votes + #[prost(int64, tag="6")] + pub miss_count: i64, +} /// GenesisState defines the oracle module's genesis state. #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct GenesisState { - #[prost(message, optional, tag = "1")] + #[prost(message, optional, tag="1")] pub params: ::core::option::Option, - #[prost(message, repeated, tag = "2")] + #[prost(message, repeated, tag="2")] pub feeder_delegations: ::prost::alloc::vec::Vec, - #[prost(message, repeated, tag = "3")] + #[prost(message, repeated, tag="3")] pub exchange_rates: ::prost::alloc::vec::Vec, - #[prost(message, repeated, tag = "4")] + #[prost(message, repeated, tag="4")] pub miss_counters: ::prost::alloc::vec::Vec, - #[prost(message, repeated, tag = "5")] - pub aggregate_exchange_rate_prevotes: - ::prost::alloc::vec::Vec, - #[prost(message, repeated, tag = "6")] - pub aggregate_exchange_rate_votes: - ::prost::alloc::vec::Vec, - #[prost(string, repeated, tag = "7")] + #[prost(message, repeated, tag="5")] + pub aggregate_exchange_rate_prevotes: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag="6")] + pub aggregate_exchange_rate_votes: ::prost::alloc::vec::Vec, + #[prost(string, repeated, tag="7")] pub pairs: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, - #[prost(message, repeated, tag = "8")] + #[prost(message, repeated, tag="8")] pub rewards: ::prost::alloc::vec::Vec, } /// FeederDelegation is the address for where oracle feeder authority are @@ -187,9 +208,9 @@ pub struct GenesisState { #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct FeederDelegation { - #[prost(string, tag = "1")] + #[prost(string, tag="1")] pub feeder_address: ::prost::alloc::string::String, - #[prost(string, tag = "2")] + #[prost(string, tag="2")] pub validator_address: ::prost::alloc::string::String, } /// MissCounter defines an miss counter and validator address pair used in @@ -197,9 +218,9 @@ pub struct FeederDelegation { #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct MissCounter { - #[prost(string, tag = "1")] + #[prost(string, tag="1")] pub validator_address: ::prost::alloc::string::String, - #[prost(uint64, tag = "2")] + #[prost(uint64, tag="2")] pub miss_counter: u64, } /// QueryExchangeRateRequest is the request type for the Query/ExchangeRate RPC @@ -208,7 +229,7 @@ pub struct MissCounter { #[derive(Clone, PartialEq, ::prost::Message)] pub struct QueryExchangeRateRequest { /// pair defines the pair to query for. - #[prost(string, tag = "1")] + #[prost(string, tag="1")] pub pair: ::prost::alloc::string::String, } /// QueryExchangeRateResponse is response type for the @@ -217,14 +238,15 @@ pub struct QueryExchangeRateRequest { #[derive(Clone, PartialEq, ::prost::Message)] pub struct QueryExchangeRateResponse { /// exchange_rate defines the exchange rate of assets voted by validators - #[prost(string, tag = "1")] + #[prost(string, tag="1")] pub exchange_rate: ::prost::alloc::string::String, } /// QueryExchangeRatesRequest is the request type for the Query/ExchangeRates RPC /// method. #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] -pub struct QueryExchangeRatesRequest {} +pub struct QueryExchangeRatesRequest { +} /// QueryExchangeRatesResponse is response type for the /// Query/ExchangeRates RPC method. #[allow(clippy::derive_partial_eq_without_eq)] @@ -232,27 +254,29 @@ pub struct QueryExchangeRatesRequest {} pub struct QueryExchangeRatesResponse { /// exchange_rates defines a list of the exchange rate for all whitelisted /// pairs. - #[prost(message, repeated, tag = "1")] + #[prost(message, repeated, tag="1")] pub exchange_rates: ::prost::alloc::vec::Vec, } /// QueryActivesRequest is the request type for the Query/Actives RPC method. #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] -pub struct QueryActivesRequest {} +pub struct QueryActivesRequest { +} /// QueryActivesResponse is response type for the /// Query/Actives RPC method. #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct QueryActivesResponse { /// actives defines a list of the pair which oracle prices agreed upon. - #[prost(string, repeated, tag = "1")] + #[prost(string, repeated, tag="1")] pub actives: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, } /// QueryVoteTargetsRequest is the request type for the Query/VoteTargets RPC /// method. #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] -pub struct QueryVoteTargetsRequest {} +pub struct QueryVoteTargetsRequest { +} /// QueryVoteTargetsResponse is response type for the /// Query/VoteTargets RPC method. #[allow(clippy::derive_partial_eq_without_eq)] @@ -260,7 +284,7 @@ pub struct QueryVoteTargetsRequest {} pub struct QueryVoteTargetsResponse { /// vote_targets defines a list of the pairs in which everyone /// should vote in the current vote period. - #[prost(string, repeated, tag = "1")] + #[prost(string, repeated, tag="1")] pub vote_targets: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, } /// QueryFeederDelegationRequest is the request type for the @@ -269,7 +293,7 @@ pub struct QueryVoteTargetsResponse { #[derive(Clone, PartialEq, ::prost::Message)] pub struct QueryFeederDelegationRequest { /// validator defines the validator address to query for. - #[prost(string, tag = "1")] + #[prost(string, tag="1")] pub validator_addr: ::prost::alloc::string::String, } /// QueryFeederDelegationResponse is response type for the @@ -278,7 +302,7 @@ pub struct QueryFeederDelegationRequest { #[derive(Clone, PartialEq, ::prost::Message)] pub struct QueryFeederDelegationResponse { /// feeder_addr defines the feeder delegation of a validator - #[prost(string, tag = "1")] + #[prost(string, tag="1")] pub feeder_addr: ::prost::alloc::string::String, } /// QueryMissCounterRequest is the request type for the Query/MissCounter RPC @@ -287,7 +311,7 @@ pub struct QueryFeederDelegationResponse { #[derive(Clone, PartialEq, ::prost::Message)] pub struct QueryMissCounterRequest { /// validator defines the validator address to query for. - #[prost(string, tag = "1")] + #[prost(string, tag="1")] pub validator_addr: ::prost::alloc::string::String, } /// QueryMissCounterResponse is response type for the @@ -296,7 +320,7 @@ pub struct QueryMissCounterRequest { #[derive(Clone, PartialEq, ::prost::Message)] pub struct QueryMissCounterResponse { /// miss_counter defines the oracle miss counter of a validator - #[prost(uint64, tag = "1")] + #[prost(uint64, tag="1")] pub miss_counter: u64, } /// QueryAggregatePrevoteRequest is the request type for the @@ -305,7 +329,7 @@ pub struct QueryMissCounterResponse { #[derive(Clone, PartialEq, ::prost::Message)] pub struct QueryAggregatePrevoteRequest { /// validator defines the validator address to query for. - #[prost(string, tag = "1")] + #[prost(string, tag="1")] pub validator_addr: ::prost::alloc::string::String, } /// QueryAggregatePrevoteResponse is response type for the @@ -315,14 +339,15 @@ pub struct QueryAggregatePrevoteRequest { pub struct QueryAggregatePrevoteResponse { /// aggregate_prevote defines oracle aggregate prevote submitted by a validator /// in the current vote period - #[prost(message, optional, tag = "1")] + #[prost(message, optional, tag="1")] pub aggregate_prevote: ::core::option::Option, } /// QueryAggregatePrevotesRequest is the request type for the /// Query/AggregatePrevotes RPC method. #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] -pub struct QueryAggregatePrevotesRequest {} +pub struct QueryAggregatePrevotesRequest { +} /// QueryAggregatePrevotesResponse is response type for the /// Query/AggregatePrevotes RPC method. #[allow(clippy::derive_partial_eq_without_eq)] @@ -330,9 +355,8 @@ pub struct QueryAggregatePrevotesRequest {} pub struct QueryAggregatePrevotesResponse { /// aggregate_prevotes defines all oracle aggregate prevotes submitted in the /// current vote period - #[prost(message, repeated, tag = "1")] - pub aggregate_prevotes: - ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag="1")] + pub aggregate_prevotes: ::prost::alloc::vec::Vec, } /// QueryAggregateVoteRequest is the request type for the Query/AggregateVote RPC /// method. @@ -340,7 +364,7 @@ pub struct QueryAggregatePrevotesResponse { #[derive(Clone, PartialEq, ::prost::Message)] pub struct QueryAggregateVoteRequest { /// validator defines the validator address to query for. - #[prost(string, tag = "1")] + #[prost(string, tag="1")] pub validator_addr: ::prost::alloc::string::String, } /// QueryAggregateVoteResponse is response type for the @@ -350,14 +374,15 @@ pub struct QueryAggregateVoteRequest { pub struct QueryAggregateVoteResponse { /// aggregate_vote defines oracle aggregate vote submitted by a validator in /// the current vote period - #[prost(message, optional, tag = "1")] + #[prost(message, optional, tag="1")] pub aggregate_vote: ::core::option::Option, } /// QueryAggregateVotesRequest is the request type for the Query/AggregateVotes /// RPC method. #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] -pub struct QueryAggregateVotesRequest {} +pub struct QueryAggregateVotesRequest { +} /// QueryAggregateVotesResponse is response type for the /// Query/AggregateVotes RPC method. #[allow(clippy::derive_partial_eq_without_eq)] @@ -365,31 +390,32 @@ pub struct QueryAggregateVotesRequest {} pub struct QueryAggregateVotesResponse { /// aggregate_votes defines all oracle aggregate votes submitted in the current /// vote period - #[prost(message, repeated, tag = "1")] + #[prost(message, repeated, tag="1")] pub aggregate_votes: ::prost::alloc::vec::Vec, } /// QueryParamsRequest is the request type for the Query/Params RPC method. #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] -pub struct QueryParamsRequest {} +pub struct QueryParamsRequest { +} /// QueryParamsResponse is the response type for the Query/Params RPC method. #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct QueryParamsResponse { /// params defines the parameters of the module. - #[prost(message, optional, tag = "1")] + #[prost(message, optional, tag="1")] pub params: ::core::option::Option, } /// a snapshot of the prices at a given point in time #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct PriceSnapshot { - #[prost(string, tag = "1")] + #[prost(string, tag="1")] pub pair: ::prost::alloc::string::String, - #[prost(string, tag = "2")] + #[prost(string, tag="2")] pub price: ::prost::alloc::string::String, /// milliseconds since unix epoch - #[prost(int64, tag = "3")] + #[prost(int64, tag="3")] pub timestamp_ms: i64, } /// MsgAggregateExchangeRatePrevote represents a message to submit @@ -397,58 +423,102 @@ pub struct PriceSnapshot { #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct MsgAggregateExchangeRatePrevote { - #[prost(string, tag = "1")] + #[prost(string, tag="1")] pub hash: ::prost::alloc::string::String, /// Feeder is the Bech32 address of the price feeder. A validator may /// specify multiple price feeders by delegating them consent. The validator /// address is also a valid feeder by default. - #[prost(string, tag = "2")] + #[prost(string, tag="2")] pub feeder: ::prost::alloc::string::String, /// Validator is the Bech32 address to which the prevote will be credited. - #[prost(string, tag = "3")] + #[prost(string, tag="3")] pub validator: ::prost::alloc::string::String, } /// MsgAggregateExchangeRatePrevoteResponse defines the /// Msg/AggregateExchangeRatePrevote response type. #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] -pub struct MsgAggregateExchangeRatePrevoteResponse {} +pub struct MsgAggregateExchangeRatePrevoteResponse { +} /// MsgAggregateExchangeRateVote represents a message to submit /// aggregate exchange rate vote. #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct MsgAggregateExchangeRateVote { - #[prost(string, tag = "1")] + #[prost(string, tag="1")] pub salt: ::prost::alloc::string::String, - #[prost(string, tag = "2")] + #[prost(string, tag="2")] pub exchange_rates: ::prost::alloc::string::String, /// Feeder is the Bech32 address of the price feeder. A validator may /// specify multiple price feeders by delegating them consent. The validator /// address is also a valid feeder by default. - #[prost(string, tag = "3")] + #[prost(string, tag="3")] pub feeder: ::prost::alloc::string::String, /// Validator is the Bech32 address to which the vote will be credited. - #[prost(string, tag = "4")] + #[prost(string, tag="4")] pub validator: ::prost::alloc::string::String, } /// MsgAggregateExchangeRateVoteResponse defines the /// Msg/AggregateExchangeRateVote response type. #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] -pub struct MsgAggregateExchangeRateVoteResponse {} +pub struct MsgAggregateExchangeRateVoteResponse { +} /// MsgDelegateFeedConsent represents a message to delegate oracle voting rights /// to another address. #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct MsgDelegateFeedConsent { - #[prost(string, tag = "1")] + #[prost(string, tag="1")] pub operator: ::prost::alloc::string::String, - #[prost(string, tag = "2")] + #[prost(string, tag="2")] pub delegate: ::prost::alloc::string::String, } /// MsgDelegateFeedConsentResponse defines the Msg/DelegateFeedConsent response /// type. #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] -pub struct MsgDelegateFeedConsentResponse {} -// @@protoc_insertion_point(module) \ No newline at end of file +pub struct MsgDelegateFeedConsentResponse { +} +/// MsgEditOracleParams: gRPC tx message for updating the x/oracle module params +/// \[SUDO\] Only callable by sudoers. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct MsgEditOracleParams { + #[prost(string, tag="1")] + pub sender: ::prost::alloc::string::String, + #[prost(string, tag="2")] + pub vote_period: ::prost::alloc::string::String, + /// vote_threshold: \[cosmossdk.io/math.LegacyDec\] TODO: + #[prost(string, tag="3")] + pub vote_threshold: ::prost::alloc::string::String, + /// reward_band: \[cosmossdk.io/math.LegacyDec\] TODO: + #[prost(string, tag="4")] + pub reward_band: ::prost::alloc::string::String, + #[prost(string, repeated, tag="5")] + pub whitelist: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, + /// slash_fraction: \[cosmossdk.io/math.LegacyDec\] TODO: + #[prost(string, tag="6")] + pub slash_fraction: ::prost::alloc::string::String, + #[prost(string, tag="7")] + pub slash_window: ::prost::alloc::string::String, + /// min_valid_per_window: \[cosmossdk.io/math.LegacyDec\] TODO: + #[prost(string, tag="8")] + pub min_valid_per_window: ::prost::alloc::string::String, + #[prost(string, tag="9")] + pub twap_lookback_window: ::prost::alloc::string::String, + #[prost(string, tag="10")] + pub min_voters: ::prost::alloc::string::String, + /// VoteThreshold: \[cosmossdk.io/math.LegacyDec\] TODO: + #[prost(string, tag="11")] + pub validator_fee_ratio: ::prost::alloc::string::String, +} +/// MsgEditOracleParamsResponse defines the Msg/EditOracleParams response +/// type. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct MsgEditOracleParamsResponse { + #[prost(message, optional, tag="1")] + pub new_params: ::core::option::Option, +} +// @@protoc_insertion_point(module) diff --git a/nibiru-std/src/proto/buf/nibiru.perp.v2.rs b/nibiru-std/src/proto/buf/nibiru.perp.v2.rs index 67a15ee..c5d8d31 100644 --- a/nibiru-std/src/proto/buf/nibiru.perp.v2.rs +++ b/nibiru-std/src/proto/buf/nibiru.perp.v2.rs @@ -55,6 +55,10 @@ pub struct Market { /// position size #[prost(string, tag="13")] pub max_funding_rate: ::prost::alloc::string::String, + /// the pair of the oracle that is used to determine the index price + /// for the market + #[prost(string, tag="15")] + pub oracle_pair: ::prost::alloc::string::String, } /// MarketLastVersion is used to store the last version of the market #[allow(clippy::derive_partial_eq_without_eq)] @@ -899,7 +903,7 @@ pub struct MsgDonateToEcosystemFundResponse { // ----------------------- MsgChangeCollateralDenom ----------------------- /// MsgChangeCollateralDenom: Changes the collateral denom for the module. -/// \[Admin\] Only callable by sudoers. +/// \[SUDO\] Only callable by sudoers. #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct MsgChangeCollateralDenom { @@ -945,8 +949,8 @@ pub struct MsgWithdrawEpochRebatesResponse { } // -------------------------- ShiftPegMultiplier -------------------------- -/// ShiftPegMultiplier: gRPC tx msg for changing the peg multiplier. -/// Admin-only. +/// MsgShiftPegMultiplier: gRPC tx msg for changing the peg multiplier. +/// \[SUDO\] Only callable sudoers. #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct MsgShiftPegMultiplier { @@ -963,8 +967,8 @@ pub struct MsgShiftPegMultiplierResponse { } // -------------------------- ShiftSwapInvariant -------------------------- -/// ShiftSwapInvariant: gRPC tx msg for changing the swap invariant. -/// Admin-only. +/// MsgShiftSwapInvariant: gRPC tx msg for changing the swap invariant. +/// \[SUDO\] Only callable sudoers. #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct MsgShiftSwapInvariant { @@ -979,4 +983,41 @@ pub struct MsgShiftSwapInvariant { #[derive(Clone, PartialEq, ::prost::Message)] pub struct MsgShiftSwapInvariantResponse { } +// -------------------------- WithdrawFromPerpFund -------------------------- + +/// MsgWithdrawFromPerpFund: gRPC tx msg for changing the swap invariant. +/// \[SUDO\] Only callable sudoers. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct MsgWithdrawFromPerpFund { + #[prost(string, tag="1")] + pub sender: ::prost::alloc::string::String, + #[prost(string, tag="2")] + pub amount: ::prost::alloc::string::String, + /// Optional denom in case withdrawing assets aside from NUSD. + #[prost(string, tag="3")] + pub denom: ::prost::alloc::string::String, + #[prost(string, tag="4")] + pub to_addr: ::prost::alloc::string::String, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct MsgWithdrawFromPerpFundResponse { +} +// -------------------------- CloseMarket -------------------------- + +/// CloseMarket: gRPC tx msg for closing a market. +/// Admin-only. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct MsgCloseMarket { + #[prost(string, tag="1")] + pub sender: ::prost::alloc::string::String, + #[prost(string, tag="2")] + pub pair: ::prost::alloc::string::String, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct MsgCloseMarketResponse { +} // @@protoc_insertion_point(module) diff --git a/nibiru-std/src/proto/buf/nibiru.sudo.v1.rs b/nibiru-std/src/proto/buf/nibiru.sudo.v1.rs index 7b698dd..87f9f0e 100644 --- a/nibiru-std/src/proto/buf/nibiru.sudo.v1.rs +++ b/nibiru-std/src/proto/buf/nibiru.sudo.v1.rs @@ -9,13 +9,14 @@ pub struct Sudoers { #[prost(string, repeated, tag="2")] pub contracts: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, } -/// GenesisState defines the module's genesis state. +/// GenesisState: State for migrations and genesis for the x/sudo module. #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct GenesisState { #[prost(message, optional, tag="1")] pub sudoers: ::core::option::Option, } +/// EventUpdateSudoers: ABCI event emitted upon execution of "MsgEditSudoers". #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct EventUpdateSudoers { diff --git a/nibiru-std/src/proto/mod.rs b/nibiru-std/src/proto/mod.rs index 4311de8..cfdf394 100644 --- a/nibiru-std/src/proto/mod.rs +++ b/nibiru-std/src/proto/mod.rs @@ -5,8 +5,6 @@ mod type_url_cosmos; mod type_url_nibiru; pub use traits::*; -pub use type_url_cosmos::*; -pub use type_url_nibiru::*; pub mod cosmos { /// Authentication of accounts and transactions. diff --git a/nibiru-std/src/proto/type_url_nibiru.rs b/nibiru-std/src/proto/type_url_nibiru.rs index 9250811..4880116 100644 --- a/nibiru-std/src/proto/type_url_nibiru.rs +++ b/nibiru-std/src/proto/type_url_nibiru.rs @@ -68,6 +68,12 @@ impl Name for nibiru::epochs::QueryCurrentEpochRequest { } // ORACLE tx msg + +impl Name for nibiru::oracle::MsgEditOracleParams { + const NAME: &'static str = "MsgEditOracleParams"; + const PACKAGE: &'static str = PACKAGE_ORACLE; +} + // ORACLE query impl Name for nibiru::oracle::QueryExchangeRateRequest { @@ -260,6 +266,14 @@ impl Name for nibiru::perp::MsgShiftSwapInvariant { const NAME: &'static str = "MsgShiftSwapInvariant"; const PACKAGE: &'static str = PACKAGE_PERP; } +impl Name for nibiru::perp::MsgWithdrawFromPerpFund { + const NAME: &'static str = "MsgWithdrawFromPerpFund"; + const PACKAGE: &'static str = PACKAGE_PERP; +} +impl Name for nibiru::perp::MsgCloseMarket { + const NAME: &'static str = "MsgCloseMarket"; + const PACKAGE: &'static str = PACKAGE_PERP; +} // PERP query diff --git a/packages/core-controller/src/contract.rs b/packages/core-controller/src/contract.rs index 8507841..03eb61a 100644 --- a/packages/core-controller/src/contract.rs +++ b/packages/core-controller/src/contract.rs @@ -57,10 +57,10 @@ pub fn execute( match msg { #[allow(unused_variables, deprecated, unreachable_code)] - ExecuteMsg::InsuranceFundWithdraw { amount, to } => { + ExecuteMsg::WithdrawPerpFund { amount, to } => { check_member(check)?; todo!(); - let _cw_msg = ExecuteMsg::InsuranceFundWithdraw { amount, to }; + let _cw_msg = ExecuteMsg::WithdrawPerpFund { amount, to }; // let res = Response::new() // .add_message(cw_msg) // .add_attributes(vec![attr("action", "insurance_fund_withdraw")]); diff --git a/packages/core-controller/src/msgs.rs b/packages/core-controller/src/msgs.rs index 5dcb304..0b44c73 100644 --- a/packages/core-controller/src/msgs.rs +++ b/packages/core-controller/src/msgs.rs @@ -19,7 +19,7 @@ pub enum ExecuteMsg { enabled: bool, }, #[deprecated(note = "Needs MsgServer impl added to NibiruChain/nibiru")] - InsuranceFundWithdraw { + WithdrawPerpFund { amount: Uint256, to: String, }, diff --git a/scripts/wasm_out.rs b/scripts/wasm_out.rs index f0b9f22..fd50553 100644 --- a/scripts/wasm_out.rs +++ b/scripts/wasm_out.rs @@ -94,7 +94,7 @@ impl WasmCompilationScheme { ); // current_dir, current_dir_name, image, IMAGE_VERSION - run_bash_and_print(cmd).map_err(|bash_err| anyhow::anyhow!(bash_err)) + run_bash_and_print(&cmd).map_err(|bash_err| anyhow::anyhow!(bash_err)) } fn print_run_preamble(&self) {