From 4f23a587080a55b09dc979558f96125efbd8f9f3 Mon Sep 17 00:00:00 2001 From: toteki <63419657+toteki@users.noreply.github.com> Date: Sun, 7 Aug 2022 16:08:01 -0700 Subject: [PATCH 01/38] draft --- CHANGELOG.md | 2 + networks/umeemania-1/genesis.json | 1634 ++++++++++---------- proto/umee/leverage/v1/leverage.proto | 8 + proto/umee/leverage/v1/tx.proto | 7 +- swagger/swagger.yaml | 582 ++----- x/leverage/client/tests/tests.go | 2 +- x/leverage/keeper/borrows.go | 14 + x/leverage/keeper/borrows_test.go | 6 +- x/leverage/keeper/collateral.go | 42 +- x/leverage/keeper/collateral_test.go | 6 +- x/leverage/keeper/exchange_rate.go | 6 +- x/leverage/keeper/filter.go | 2 +- x/leverage/keeper/grpc_query.go | 2 +- x/leverage/keeper/grpc_query_test.go | 2 +- x/leverage/keeper/iter.go | 2 +- x/leverage/keeper/keeper.go | 160 +- x/leverage/keeper/keeper_test.go | 12 +- x/leverage/keeper/liquidate.go | 19 +- x/leverage/keeper/reserves.go | 23 +- x/leverage/keeper/store.go | 7 +- x/leverage/keeper/{loaned.go => supply.go} | 15 +- x/leverage/keeper/token.go | 33 - x/leverage/keeper/validate.go | 48 +- x/leverage/simulation/genesis.go | 13 + x/leverage/simulation/operations.go | 21 +- x/leverage/simulation/operations_test.go | 53 +- x/leverage/simulation/params.go | 5 + x/leverage/spec/07_params.md | 7 +- x/leverage/types/errors.go | 1 + x/leverage/types/leverage.pb.go | 161 +- x/leverage/types/params.go | 27 + x/leverage/types/token.go | 25 +- x/leverage/types/token_test.go | 28 +- x/leverage/types/tx.pb.go | 7 +- 34 files changed, 1450 insertions(+), 1532 deletions(-) rename x/leverage/keeper/{loaned.go => supply.go} (80%) diff --git a/CHANGELOG.md b/CHANGELOG.md index f5ce6a3577..3e50e529cd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -61,6 +61,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ - [1140](https://github.com/umee-network/umee/pull/1140) Rename MarketSize query to TotalSuppliedValue, and TokenMarketSize to TotalSupplied. - [1188](https://github.com/umee-network/umee/pull/1188) Remove all individual queries which duplicate market_summary fields. - [1199](https://github.com/umee-network/umee/pull/1199) Move all queries which require address input (e.g. `supplied`, `collateral_value`, `borrow_limit`) into aggregate queries `acccount_summary` or `account_balances`. +- [XXXX](https://github.com/umee-network/umee/pull/XXXX) Add leverage parameter DirectLiquidationFee ### Features @@ -79,6 +80,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ - [1203](https://github.com/umee-network/umee/pull/1203) Add Swagger docs. - [1212](https://github.com/umee-network/umee/pull/1212) Add `util/checkers` utility package providing common check / validation functions. - [1220](https://github.com/umee-network/umee/pull/1220) Submit oracle prevotes / vote txs via the CLI. +- [XXXX](https://github.com/umee-network/umee/pull/XXXX) Liquidation reward_denom can now be either token or uToken ### Improvements diff --git a/networks/umeemania-1/genesis.json b/networks/umeemania-1/genesis.json index c7408ded7b..8d693acdfe 100644 --- a/networks/umeemania-1/genesis.json +++ b/networks/umeemania-1/genesis.json @@ -1,864 +1,878 @@ { - "genesis_time": "2022-04-05T15:39:08.904252Z", - "chain_id": "umeemania-1", - "initial_height": "1", - "consensus_params": { - "block": { - "max_bytes": "22020096", - "max_gas": "-1", - "time_iota_ms": "1000" - }, - "evidence": { - "max_age_num_blocks": "100000", - "max_age_duration": "172800000000000", - "max_bytes": "1048576" - }, - "validator": { - "pub_key_types": ["ed25519"] + "genesis_time": "2022-04-05T15:39:08.904252Z", + "chain_id": "umeemania-1", + "initial_height": "1", + "consensus_params": { + "block": { + "max_bytes": "22020096", + "max_gas": "-1", + "time_iota_ms": "1000" + }, + "evidence": { + "max_age_num_blocks": "100000", + "max_age_duration": "172800000000000", + "max_bytes": "1048576" + }, + "validator": { + "pub_key_types": [ + "ed25519" + ] + }, + "version": {} + }, + "app_hash": "", + "app_state": { + "auth": { + "params": { + "max_memo_characters": "256", + "tx_sig_limit": "7", + "tx_size_cost_per_byte": "10", + "sig_verify_cost_ed25519": "590", + "sig_verify_cost_secp256k1": "1000" }, - "version": {} + "accounts": [ + { + "@type": "/cosmos.auth.v1beta1.BaseAccount", + "address": "umee1xpyya7quj4rx2pm3xclspyvh5gmfxwn2hwlt80", + "pub_key": null, + "account_number": "0", + "sequence": "0" + }, + { + "@type": "/cosmos.auth.v1beta1.BaseAccount", + "address": "umee1kpqyxsafxzw3k07rmhycf3jtwtmntrlgvdnj67", + "pub_key": null, + "account_number": "0", + "sequence": "0" + }, + { + "@type": "/cosmos.auth.v1beta1.BaseAccount", + "address": "umee1zl7pjgzfmcgwa0c7vgxklmkykuvmu77yxj75mp", + "pub_key": null, + "account_number": "0", + "sequence": "0" + }, + { + "@type": "/cosmos.auth.v1beta1.BaseAccount", + "address": "umee1e98evcznumc2njy9z36x5cv2p75lsagzfkwyqv", + "pub_key": null, + "account_number": "0", + "sequence": "0" + }, + { + "@type": "/cosmos.auth.v1beta1.BaseAccount", + "address": "umee1dak0cr5ha793jsrxkn3lghyc9ptkdj322m5xvr", + "pub_key": null, + "account_number": "0", + "sequence": "0" + }, + { + "@type": "/cosmos.auth.v1beta1.BaseAccount", + "address": "umee15a0n83ggtlzlu5jyny7ecdx2a5g4az47um3whs", + "pub_key": null, + "account_number": "0", + "sequence": "0" + }, + { + "@type": "/cosmos.auth.v1beta1.BaseAccount", + "address": "umee1ca6z887q92cmrwx0jxcpm52fh8gf7j0kgjgvhf", + "pub_key": null, + "account_number": "0", + "sequence": "0" + }, + { + "@type": "/cosmos.auth.v1beta1.BaseAccount", + "address": "umee1gczvdlfhljmvt7yzm3l5654fmjp4ucaael9jvr", + "pub_key": null, + "account_number": "0", + "sequence": "0" + }, + { + "@type": "/cosmos.auth.v1beta1.BaseAccount", + "address": "umee1fmca3ew9r5qfy3c66w69swwpm8czhccnulu6fp", + "pub_key": null, + "account_number": "0", + "sequence": "0" + }, + { + "@type": "/cosmos.auth.v1beta1.BaseAccount", + "address": "umee1hwn8tc4z4e43jt02tdjza0jzylqy4t4ry4ung5", + "pub_key": null, + "account_number": "0", + "sequence": "0" + }, + { + "@type": "/cosmos.auth.v1beta1.BaseAccount", + "address": "umee1385dtql6vzs9uqjz5grmf9ls49lsx88lehcgmv", + "pub_key": null, + "account_number": "0", + "sequence": "0" + }, + { + "@type": "/cosmos.auth.v1beta1.BaseAccount", + "address": "umee1a7aydr659p03j4svzqs5kkddmz7w70slu5hgch", + "pub_key": null, + "account_number": "0", + "sequence": "0" + }, + { + "@type": "/cosmos.auth.v1beta1.BaseAccount", + "address": "umee1jp90x75tnv87h8faz98jzjdp4hkgtrzraa3k9z", + "pub_key": null, + "account_number": "0", + "sequence": "0" + }, + { + "@type": "/cosmos.auth.v1beta1.BaseAccount", + "address": "umee1nc26yck588gpskuenf4y693vpgqjk590f9q594", + "pub_key": null, + "account_number": "0", + "sequence": "0" + }, + { + "@type": "/cosmos.auth.v1beta1.BaseAccount", + "address": "umee1l6tmph3s9vhg5m5t5akcc5yap2cmqh6emjv5uz", + "pub_key": null, + "account_number": "0", + "sequence": "0" + } + ] }, - "app_hash": "", - "app_state": { - "auth": { - "params": { - "max_memo_characters": "256", - "tx_sig_limit": "7", - "tx_size_cost_per_byte": "10", - "sig_verify_cost_ed25519": "590", - "sig_verify_cost_secp256k1": "1000" + "authz": { + "authorization": [] + }, + "bank": { + "params": { + "send_enabled": [], + "default_send_enabled": true + }, + "balances": [ + { + "address": "umee1zl7pjgzfmcgwa0c7vgxklmkykuvmu77yxj75mp", + "coins": [ + { + "denom": "uumee", + "amount": "90000000000000000" + } + ] }, - "accounts": [ - { - "@type": "/cosmos.auth.v1beta1.BaseAccount", - "address": "umee1xpyya7quj4rx2pm3xclspyvh5gmfxwn2hwlt80", - "pub_key": null, - "account_number": "0", - "sequence": "0" - }, - { - "@type": "/cosmos.auth.v1beta1.BaseAccount", - "address": "umee1kpqyxsafxzw3k07rmhycf3jtwtmntrlgvdnj67", - "pub_key": null, - "account_number": "0", - "sequence": "0" - }, - { - "@type": "/cosmos.auth.v1beta1.BaseAccount", - "address": "umee1zl7pjgzfmcgwa0c7vgxklmkykuvmu77yxj75mp", - "pub_key": null, - "account_number": "0", - "sequence": "0" - }, - { - "@type": "/cosmos.auth.v1beta1.BaseAccount", - "address": "umee1e98evcznumc2njy9z36x5cv2p75lsagzfkwyqv", - "pub_key": null, - "account_number": "0", - "sequence": "0" - }, - { - "@type": "/cosmos.auth.v1beta1.BaseAccount", - "address": "umee1dak0cr5ha793jsrxkn3lghyc9ptkdj322m5xvr", - "pub_key": null, - "account_number": "0", - "sequence": "0" - }, - { - "@type": "/cosmos.auth.v1beta1.BaseAccount", - "address": "umee15a0n83ggtlzlu5jyny7ecdx2a5g4az47um3whs", - "pub_key": null, - "account_number": "0", - "sequence": "0" - }, - { - "@type": "/cosmos.auth.v1beta1.BaseAccount", - "address": "umee1ca6z887q92cmrwx0jxcpm52fh8gf7j0kgjgvhf", - "pub_key": null, - "account_number": "0", - "sequence": "0" - }, - { - "@type": "/cosmos.auth.v1beta1.BaseAccount", - "address": "umee1gczvdlfhljmvt7yzm3l5654fmjp4ucaael9jvr", - "pub_key": null, - "account_number": "0", - "sequence": "0" - }, - { - "@type": "/cosmos.auth.v1beta1.BaseAccount", - "address": "umee1fmca3ew9r5qfy3c66w69swwpm8czhccnulu6fp", - "pub_key": null, - "account_number": "0", - "sequence": "0" - }, - { - "@type": "/cosmos.auth.v1beta1.BaseAccount", - "address": "umee1hwn8tc4z4e43jt02tdjza0jzylqy4t4ry4ung5", - "pub_key": null, - "account_number": "0", - "sequence": "0" - }, - { - "@type": "/cosmos.auth.v1beta1.BaseAccount", - "address": "umee1385dtql6vzs9uqjz5grmf9ls49lsx88lehcgmv", - "pub_key": null, - "account_number": "0", - "sequence": "0" - }, - { - "@type": "/cosmos.auth.v1beta1.BaseAccount", - "address": "umee1a7aydr659p03j4svzqs5kkddmz7w70slu5hgch", - "pub_key": null, - "account_number": "0", - "sequence": "0" - }, - { - "@type": "/cosmos.auth.v1beta1.BaseAccount", - "address": "umee1jp90x75tnv87h8faz98jzjdp4hkgtrzraa3k9z", - "pub_key": null, - "account_number": "0", - "sequence": "0" - }, - { - "@type": "/cosmos.auth.v1beta1.BaseAccount", - "address": "umee1nc26yck588gpskuenf4y693vpgqjk590f9q594", - "pub_key": null, - "account_number": "0", - "sequence": "0" - }, - { - "@type": "/cosmos.auth.v1beta1.BaseAccount", - "address": "umee1l6tmph3s9vhg5m5t5akcc5yap2cmqh6emjv5uz", - "pub_key": null, - "account_number": "0", - "sequence": "0" - } - ] + { + "address": "umee1xpyya7quj4rx2pm3xclspyvh5gmfxwn2hwlt80", + "coins": [ + { + "denom": "uumee", + "amount": "90000000000000000" + } + ] + }, + { + "address": "umee1gczvdlfhljmvt7yzm3l5654fmjp4ucaael9jvr", + "coins": [ + { + "denom": "uumee", + "amount": "90000000000000000" + } + ] + }, + { + "address": "umee1fmca3ew9r5qfy3c66w69swwpm8czhccnulu6fp", + "coins": [ + { + "denom": "uumee", + "amount": "90000000000000000" + } + ] + }, + { + "address": "umee1dak0cr5ha793jsrxkn3lghyc9ptkdj322m5xvr", + "coins": [ + { + "denom": "uumee", + "amount": "90000000000000000" + } + ] + }, + { + "address": "umee1385dtql6vzs9uqjz5grmf9ls49lsx88lehcgmv", + "coins": [ + { + "denom": "uumee", + "amount": "90000000000000000" + } + ] + }, + { + "address": "umee1jp90x75tnv87h8faz98jzjdp4hkgtrzraa3k9z", + "coins": [ + { + "denom": "uumee", + "amount": "90000000000000000" + } + ] + }, + { + "address": "umee1nc26yck588gpskuenf4y693vpgqjk590f9q594", + "coins": [ + { + "denom": "uumee", + "amount": "90000000000000000" + } + ] + }, + { + "address": "umee15a0n83ggtlzlu5jyny7ecdx2a5g4az47um3whs", + "coins": [ + { + "denom": "uumee", + "amount": "90000000000000000" + } + ] + }, + { + "address": "umee1kpqyxsafxzw3k07rmhycf3jtwtmntrlgvdnj67", + "coins": [ + { + "denom": "uumee", + "amount": "90000000000000000" + } + ] + }, + { + "address": "umee1hwn8tc4z4e43jt02tdjza0jzylqy4t4ry4ung5", + "coins": [ + { + "denom": "uumee", + "amount": "90000000000000000" + } + ] + }, + { + "address": "umee1ca6z887q92cmrwx0jxcpm52fh8gf7j0kgjgvhf", + "coins": [ + { + "denom": "uumee", + "amount": "90000000000000000" + } + ] + }, + { + "address": "umee1e98evcznumc2njy9z36x5cv2p75lsagzfkwyqv", + "coins": [ + { + "denom": "uumee", + "amount": "90000000000000000" + } + ] + }, + { + "address": "umee1a7aydr659p03j4svzqs5kkddmz7w70slu5hgch", + "coins": [ + { + "denom": "uumee", + "amount": "90000000000000000" + } + ] + }, + { + "address": "umee1l6tmph3s9vhg5m5t5akcc5yap2cmqh6emjv5uz", + "coins": [ + { + "denom": "uumee", + "amount": "90000000000000000" + } + ] + } + ], + "supply": [], + "denom_metadata": [ + { + "base": "uumee", + "denom_units": [ + { + "aliases": [ + "microumee" + ], + "denom": "uumee", + "exponent": 0 + }, + { + "aliases": [], + "denom": "UMEE", + "exponent": 6 + } + ], + "description": "The native staking token of the Umee network.", + "display": "UMEE", + "name": "UMEE", + "symbol": "UMEE" + }, + { + "base": "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2", + "denom_units": [ + { + "aliases": [ + "microatom" + ], + "denom": "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2", + "exponent": 0 + }, + { + "aliases": [], + "denom": "ATOM", + "exponent": 6 + } + ], + "description": "The native staking and governance token of Gaia", + "display": "ATOM", + "name": "Cosmos", + "symbol": "ATOM" + }, + { + "base": "ibc/04F5F501207C3626A2C14BFEF654D51C2E0B8F7CA578AB8ED272A66FE4E48097", + "denom_units": [ + { + "aliases": [ + "microjuno" + ], + "denom": "ibc/04F5F501207C3626A2C14BFEF654D51C2E0B8F7CA578AB8ED272A66FE4E48097", + "exponent": 0 + }, + { + "aliases": [], + "denom": "JUNO", + "exponent": 6 + } + ], + "description": "The native staking and governance token of Juno", + "display": "JUNO", + "name": "Juno", + "symbol": "JUNO" + }, + { + "base": "ibc/ED07A3391A112B175915CD8FAF43A2DA8E4790EDE12566649D0C2F97716B8518", + "denom_units": [ + { + "aliases": [ + "microatom" + ], + "denom": "ibc/ED07A3391A112B175915CD8FAF43A2DA8E4790EDE12566649D0C2F97716B8518", + "exponent": 0 + }, + { + "aliases": [], + "denom": "OSMO", + "exponent": 6 + } + ], + "description": "The native staking and governance token of Osmosis", + "display": "OSMO", + "name": "Osmosis", + "symbol": "OSMO" + } + ] + }, + "bech32ibc": { + "nativeHRP": "osmo", + "hrpIBCRecords": [] + }, + "capability": { + "index": "1", + "owners": [] + }, + "crisis": { + "constant_fee": { + "denom": "uumee", + "amount": "1000" + } + }, + "distribution": { + "params": { + "community_tax": "0.020000000000000000", + "base_proposer_reward": "0.010000000000000000", + "bonus_proposer_reward": "0.040000000000000000", + "withdraw_addr_enabled": true }, - "authz": { - "authorization": [] + "fee_pool": { + "community_pool": [] }, - "bank": { - "params": { - "send_enabled": [], - "default_send_enabled": true - }, - "balances": [ - { - "address": "umee1zl7pjgzfmcgwa0c7vgxklmkykuvmu77yxj75mp", - "coins": [ - { - "denom": "uumee", - "amount": "90000000000000000" - } - ] - }, - { - "address": "umee1xpyya7quj4rx2pm3xclspyvh5gmfxwn2hwlt80", - "coins": [ - { - "denom": "uumee", - "amount": "90000000000000000" - } - ] - }, - { - "address": "umee1gczvdlfhljmvt7yzm3l5654fmjp4ucaael9jvr", - "coins": [ - { - "denom": "uumee", - "amount": "90000000000000000" - } - ] - }, - { - "address": "umee1fmca3ew9r5qfy3c66w69swwpm8czhccnulu6fp", - "coins": [ - { - "denom": "uumee", - "amount": "90000000000000000" - } - ] - }, - { - "address": "umee1dak0cr5ha793jsrxkn3lghyc9ptkdj322m5xvr", - "coins": [ - { - "denom": "uumee", - "amount": "90000000000000000" - } - ] - }, - { - "address": "umee1385dtql6vzs9uqjz5grmf9ls49lsx88lehcgmv", - "coins": [ - { - "denom": "uumee", - "amount": "90000000000000000" - } - ] - }, - { - "address": "umee1jp90x75tnv87h8faz98jzjdp4hkgtrzraa3k9z", - "coins": [ - { - "denom": "uumee", - "amount": "90000000000000000" - } - ] - }, - { - "address": "umee1nc26yck588gpskuenf4y693vpgqjk590f9q594", - "coins": [ - { - "denom": "uumee", - "amount": "90000000000000000" - } - ] - }, - { - "address": "umee15a0n83ggtlzlu5jyny7ecdx2a5g4az47um3whs", - "coins": [ - { - "denom": "uumee", - "amount": "90000000000000000" - } - ] - }, - { - "address": "umee1kpqyxsafxzw3k07rmhycf3jtwtmntrlgvdnj67", - "coins": [ - { - "denom": "uumee", - "amount": "90000000000000000" - } - ] - }, - { - "address": "umee1hwn8tc4z4e43jt02tdjza0jzylqy4t4ry4ung5", - "coins": [ - { - "denom": "uumee", - "amount": "90000000000000000" - } - ] - }, - { - "address": "umee1ca6z887q92cmrwx0jxcpm52fh8gf7j0kgjgvhf", - "coins": [ + "delegator_withdraw_infos": [], + "previous_proposer": "", + "outstanding_rewards": [], + "validator_accumulated_commissions": [], + "validator_historical_rewards": [], + "validator_current_rewards": [], + "delegator_starting_infos": [], + "validator_slash_events": [] + }, + "evidence": { + "evidence": [] + }, + "feegrant": { + "allowances": [] + }, + "genutil": { + "gen_txs": [ + { + "body": { + "messages": [ { - "denom": "uumee", - "amount": "90000000000000000" - } - ] - }, - { - "address": "umee1e98evcznumc2njy9z36x5cv2p75lsagzfkwyqv", - "coins": [ + "@type": "/cosmos.staking.v1beta1.MsgCreateValidator", + "description": { + "moniker": "banana", + "identity": "", + "website": "", + "security_contact": "", + "details": "" + }, + "commission": { + "rate": "0.100000000000000000", + "max_rate": "0.200000000000000000", + "max_change_rate": "0.010000000000000000" + }, + "min_self_delegation": "1", + "delegator_address": "umee15a0n83ggtlzlu5jyny7ecdx2a5g4az47um3whs", + "validator_address": "umeevaloper15a0n83ggtlzlu5jyny7ecdx2a5g4az47ulkpx6", + "pubkey": { + "@type": "/cosmos.crypto.ed25519.PubKey", + "key": "Q0c69lMlQRagx+Nk7pylSJyVp4Vfck/5TzeJ2z1G9Vg=" + }, + "value": { + "denom": "uumee", + "amount": "100000000000" + } + }, { - "denom": "uumee", - "amount": "90000000000000000" + "@type": "/gravity.v1.MsgSetOrchestratorAddress", + "validator": "umeevaloper15a0n83ggtlzlu5jyny7ecdx2a5g4az47ulkpx6", + "orchestrator": "umee1gczvdlfhljmvt7yzm3l5654fmjp4ucaael9jvr", + "eth_address": "0xfac87ECc6009Be4a856CB30846F82ea0B94d8C01" } - ] + ], + "memo": "08840edc6775bd7a298269f3343c554e5af0359f@172.17.0.2:26656", + "timeout_height": "0", + "extension_options": [], + "non_critical_extension_options": [] }, - { - "address": "umee1a7aydr659p03j4svzqs5kkddmz7w70slu5hgch", - "coins": [ + "auth_info": { + "signer_infos": [ { - "denom": "uumee", - "amount": "90000000000000000" + "public_key": { + "@type": "/cosmos.crypto.secp256k1.PubKey", + "key": "A5MfFNWQUPRuw0GDlKwSr8p3ptDjYl5UyZkxQabWwaP5" + }, + "mode_info": { + "single": { + "mode": "SIGN_MODE_DIRECT" + } + }, + "sequence": "0" } - ] + ], + "fee": { + "amount": [], + "gas_limit": "200000", + "payer": "", + "granter": "" + } }, - { - "address": "umee1l6tmph3s9vhg5m5t5akcc5yap2cmqh6emjv5uz", - "coins": [ - { - "denom": "uumee", - "amount": "90000000000000000" - } - ] - } - ], - "supply": [], - "denom_metadata": [ - { - "base": "uumee", - "denom_units": [ + "signatures": [ + "KnVWLes4AWR+YN6QChksZLRJ5q7vW5Fq0Gg5WmuvvIdaJ0V8Xz9mvsbfaFoyA8Lmh9N4sbPX/Cq/PzD5QrI/wA==" + ] + }, + { + "body": { + "messages": [ { - "aliases": ["microumee"], - "denom": "uumee", - "exponent": 0 + "@type": "/cosmos.staking.v1beta1.MsgCreateValidator", + "description": { + "moniker": "alley", + "identity": "", + "website": "", + "security_contact": "", + "details": "" + }, + "commission": { + "rate": "0.100000000000000000", + "max_rate": "0.200000000000000000", + "max_change_rate": "0.010000000000000000" + }, + "min_self_delegation": "1", + "delegator_address": "umee1jp90x75tnv87h8faz98jzjdp4hkgtrzraa3k9z", + "validator_address": "umeevaloper1jp90x75tnv87h8faz98jzjdp4hkgtrzraeke5g", + "pubkey": { + "@type": "/cosmos.crypto.ed25519.PubKey", + "key": "yarR/0r9WW9IuFOIxj5oLIMMZKamUid6DnkyMp9J9nU=" + }, + "value": { + "denom": "uumee", + "amount": "100000000000" + } }, { - "aliases": [], - "denom": "UMEE", - "exponent": 6 + "@type": "/gravity.v1.MsgSetOrchestratorAddress", + "validator": "umeevaloper1jp90x75tnv87h8faz98jzjdp4hkgtrzraeke5g", + "orchestrator": "umee1l6tmph3s9vhg5m5t5akcc5yap2cmqh6emjv5uz", + "eth_address": "0xfacEFb63F7CDFf0ca7a7A064639b3956Ad9f3acC" } ], - "description": "The native staking token of the Umee network.", - "display": "UMEE", - "name": "UMEE", - "symbol": "UMEE" + "memo": "08f30f7119ae1317082d50fac6a08e6f2400126f@172.17.0.2:26656", + "timeout_height": "0", + "extension_options": [], + "non_critical_extension_options": [] }, - { - "base": "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2", - "denom_units": [ - { - "aliases": ["microatom"], - "denom": "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2", - "exponent": 0 - }, + "auth_info": { + "signer_infos": [ { - "aliases": [], - "denom": "ATOM", - "exponent": 6 + "public_key": { + "@type": "/cosmos.crypto.secp256k1.PubKey", + "key": "Aoux/zmfmVaIEzmpjgV6k2oXbqVVP7iXKPng0XxiiHB6" + }, + "mode_info": { + "single": { + "mode": "SIGN_MODE_DIRECT" + } + }, + "sequence": "0" } ], - "description": "The native staking and governance token of Gaia", - "display": "ATOM", - "name": "Cosmos", - "symbol": "ATOM" + "fee": { + "amount": [], + "gas_limit": "200000", + "payer": "", + "granter": "" + } }, - { - "base": "ibc/04F5F501207C3626A2C14BFEF654D51C2E0B8F7CA578AB8ED272A66FE4E48097", - "denom_units": [ + "signatures": [ + "4lhhQT1SS+ycJ583K3K5KiTP6moiEhscFrAF5N69NXxj1yiMy7v2N64rH7bYhoDmxs9QXAx8mAtrHQAsGJDPgQ==" + ] + }, + { + "body": { + "messages": [ { - "aliases": ["microjuno"], - "denom": "ibc/04F5F501207C3626A2C14BFEF654D51C2E0B8F7CA578AB8ED272A66FE4E48097", - "exponent": 0 + "@type": "/cosmos.staking.v1beta1.MsgCreateValidator", + "description": { + "moniker": "festival", + "identity": "", + "website": "", + "security_contact": "", + "details": "" + }, + "commission": { + "rate": "0.100000000000000000", + "max_rate": "0.200000000000000000", + "max_change_rate": "0.010000000000000000" + }, + "min_self_delegation": "1", + "delegator_address": "umee1xpyya7quj4rx2pm3xclspyvh5gmfxwn2hwlt80", + "validator_address": "umeevaloper1xpyya7quj4rx2pm3xclspyvh5gmfxwn2h2cyk9", + "pubkey": { + "@type": "/cosmos.crypto.ed25519.PubKey", + "key": "pppHqyu2KFGb08WVT/wE7aW3FxKM36taED+cyLt7dEE=" + }, + "value": { + "denom": "uumee", + "amount": "100000000000" + } }, { - "aliases": [], - "denom": "JUNO", - "exponent": 6 + "@type": "/gravity.v1.MsgSetOrchestratorAddress", + "validator": "umeevaloper1xpyya7quj4rx2pm3xclspyvh5gmfxwn2h2cyk9", + "orchestrator": "umee1zl7pjgzfmcgwa0c7vgxklmkykuvmu77yxj75mp", + "eth_address": "0xfacf66789DD2fA6d80A36353f900922cb6D990F1" } ], - "description": "The native staking and governance token of Juno", - "display": "JUNO", - "name": "Juno", - "symbol": "JUNO" + "memo": "67cd50cac0eff503a7c9efd537d369cc7a3ebe96@172.17.0.2:26656", + "timeout_height": "0", + "extension_options": [], + "non_critical_extension_options": [] }, - { - "base": "ibc/ED07A3391A112B175915CD8FAF43A2DA8E4790EDE12566649D0C2F97716B8518", - "denom_units": [ - { - "aliases": ["microatom"], - "denom": "ibc/ED07A3391A112B175915CD8FAF43A2DA8E4790EDE12566649D0C2F97716B8518", - "exponent": 0 - }, + "auth_info": { + "signer_infos": [ { - "aliases": [], - "denom": "OSMO", - "exponent": 6 + "public_key": { + "@type": "/cosmos.crypto.secp256k1.PubKey", + "key": "A7ikWhnGN/ayRth06lI8+Dr71Irs9EM2W6ZmdHCulIz7" + }, + "mode_info": { + "single": { + "mode": "SIGN_MODE_DIRECT" + } + }, + "sequence": "0" } ], - "description": "The native staking and governance token of Osmosis", - "display": "OSMO", - "name": "Osmosis", - "symbol": "OSMO" + "fee": { + "amount": [], + "gas_limit": "200000", + "payer": "", + "granter": "" + } + }, + "signatures": [ + "Rwm/C5SKEJlyzPJs2h2t+t2GORYQ1OiWqayz26Y0yXRN+p6wZBQHWYnf3BxBNZlhEmE+R3QzI5gI4UuvNFxl6g==" + ] + } + ] + }, + "gov": { + "starting_proposal_id": "1", + "deposits": [], + "votes": [], + "proposals": [], + "deposit_params": { + "min_deposit": [ + { + "denom": "uumee", + "amount": "10000000" } - ] + ], + "max_deposit_period": "172800s" }, - "bech32ibc": { - "nativeHRP": "osmo", - "hrpIBCRecords": [] + "voting_params": { + "voting_period": "600s" }, - "capability": { - "index": "1", - "owners": [] + "tally_params": { + "quorum": "0.334000000000000000", + "threshold": "0.500000000000000000", + "veto_threshold": "0.334000000000000000" + } + }, + "gravity": { + "params": { + "gravity_id": "defaultgravityid", + "contract_source_hash": "", + "bridge_ethereum_address": "0x0000000000000000000000000000000000000000", + "bridge_chain_id": "5", + "signed_valsets_window": "10000", + "signed_batches_window": "10000", + "signed_logic_calls_window": "10000", + "target_batch_timeout": "43200000", + "average_block_time": "5000", + "average_ethereum_block_time": "15000", + "slash_fraction_valset": "0.001000000000000000", + "slash_fraction_batch": "0.001000000000000000", + "slash_fraction_logic_call": "0.001000000000000000", + "unbond_slashing_valsets_window": "10000", + "slash_fraction_bad_eth_signature": "0.001000000000000000", + "valset_reward": { + "denom": "", + "amount": "0" + }, + "bridge_active": true, + "ethereum_blacklist": [] }, - "crisis": { - "constant_fee": { - "denom": "uumee", - "amount": "1000" - } + "gravity_nonces": { + "latest_valset_nonce": "0", + "last_observed_nonce": "0", + "last_slashed_valset_nonce": "0", + "last_slashed_batch_block": "0", + "last_slashed_logic_call_block": "0", + "last_tx_pool_id": "0", + "last_batch_id": "0" }, - "distribution": { + "valsets": [], + "valset_confirms": [], + "batches": [], + "batch_confirms": [], + "logic_calls": [], + "logic_call_confirms": [], + "attestations": [], + "delegate_keys": [], + "erc20_to_denoms": [], + "unbatched_transfers": [] + }, + "ibc": { + "client_genesis": { + "clients": [], + "clients_consensus": [], + "clients_metadata": [], "params": { - "community_tax": "0.020000000000000000", - "base_proposer_reward": "0.010000000000000000", - "bonus_proposer_reward": "0.040000000000000000", - "withdraw_addr_enabled": true - }, - "fee_pool": { - "community_pool": [] - }, - "delegator_withdraw_infos": [], - "previous_proposer": "", - "outstanding_rewards": [], - "validator_accumulated_commissions": [], - "validator_historical_rewards": [], - "validator_current_rewards": [], - "delegator_starting_infos": [], - "validator_slash_events": [] - }, - "evidence": { - "evidence": [] + "allowed_clients": [ + "06-solomachine", + "07-tendermint" + ] + }, + "create_localhost": false, + "next_client_sequence": "0" }, - "feegrant": { - "allowances": [] + "connection_genesis": { + "connections": [], + "client_connection_paths": [], + "next_connection_sequence": "0", + "params": { + "max_expected_time_per_block": "30000000000" + } }, - "genutil": { - "gen_txs": [ - { - "body": { - "messages": [ - { - "@type": "/cosmos.staking.v1beta1.MsgCreateValidator", - "description": { - "moniker": "banana", - "identity": "", - "website": "", - "security_contact": "", - "details": "" - }, - "commission": { - "rate": "0.100000000000000000", - "max_rate": "0.200000000000000000", - "max_change_rate": "0.010000000000000000" - }, - "min_self_delegation": "1", - "delegator_address": "umee15a0n83ggtlzlu5jyny7ecdx2a5g4az47um3whs", - "validator_address": "umeevaloper15a0n83ggtlzlu5jyny7ecdx2a5g4az47ulkpx6", - "pubkey": { - "@type": "/cosmos.crypto.ed25519.PubKey", - "key": "Q0c69lMlQRagx+Nk7pylSJyVp4Vfck/5TzeJ2z1G9Vg=" - }, - "value": { - "denom": "uumee", - "amount": "100000000000" - } - }, - { - "@type": "/gravity.v1.MsgSetOrchestratorAddress", - "validator": "umeevaloper15a0n83ggtlzlu5jyny7ecdx2a5g4az47ulkpx6", - "orchestrator": "umee1gczvdlfhljmvt7yzm3l5654fmjp4ucaael9jvr", - "eth_address": "0xfac87ECc6009Be4a856CB30846F82ea0B94d8C01" - } - ], - "memo": "08840edc6775bd7a298269f3343c554e5af0359f@172.17.0.2:26656", - "timeout_height": "0", - "extension_options": [], - "non_critical_extension_options": [] - }, - "auth_info": { - "signer_infos": [ - { - "public_key": { - "@type": "/cosmos.crypto.secp256k1.PubKey", - "key": "A5MfFNWQUPRuw0GDlKwSr8p3ptDjYl5UyZkxQabWwaP5" - }, - "mode_info": { - "single": { - "mode": "SIGN_MODE_DIRECT" - } - }, - "sequence": "0" - } - ], - "fee": { - "amount": [], - "gas_limit": "200000", - "payer": "", - "granter": "" - } - }, - "signatures": [ - "KnVWLes4AWR+YN6QChksZLRJ5q7vW5Fq0Gg5WmuvvIdaJ0V8Xz9mvsbfaFoyA8Lmh9N4sbPX/Cq/PzD5QrI/wA==" - ] - }, - { - "body": { - "messages": [ - { - "@type": "/cosmos.staking.v1beta1.MsgCreateValidator", - "description": { - "moniker": "alley", - "identity": "", - "website": "", - "security_contact": "", - "details": "" - }, - "commission": { - "rate": "0.100000000000000000", - "max_rate": "0.200000000000000000", - "max_change_rate": "0.010000000000000000" - }, - "min_self_delegation": "1", - "delegator_address": "umee1jp90x75tnv87h8faz98jzjdp4hkgtrzraa3k9z", - "validator_address": "umeevaloper1jp90x75tnv87h8faz98jzjdp4hkgtrzraeke5g", - "pubkey": { - "@type": "/cosmos.crypto.ed25519.PubKey", - "key": "yarR/0r9WW9IuFOIxj5oLIMMZKamUid6DnkyMp9J9nU=" - }, - "value": { - "denom": "uumee", - "amount": "100000000000" - } - }, - { - "@type": "/gravity.v1.MsgSetOrchestratorAddress", - "validator": "umeevaloper1jp90x75tnv87h8faz98jzjdp4hkgtrzraeke5g", - "orchestrator": "umee1l6tmph3s9vhg5m5t5akcc5yap2cmqh6emjv5uz", - "eth_address": "0xfacEFb63F7CDFf0ca7a7A064639b3956Ad9f3acC" - } - ], - "memo": "08f30f7119ae1317082d50fac6a08e6f2400126f@172.17.0.2:26656", - "timeout_height": "0", - "extension_options": [], - "non_critical_extension_options": [] - }, - "auth_info": { - "signer_infos": [ - { - "public_key": { - "@type": "/cosmos.crypto.secp256k1.PubKey", - "key": "Aoux/zmfmVaIEzmpjgV6k2oXbqVVP7iXKPng0XxiiHB6" - }, - "mode_info": { - "single": { - "mode": "SIGN_MODE_DIRECT" - } - }, - "sequence": "0" - } - ], - "fee": { - "amount": [], - "gas_limit": "200000", - "payer": "", - "granter": "" - } - }, - "signatures": [ - "4lhhQT1SS+ycJ583K3K5KiTP6moiEhscFrAF5N69NXxj1yiMy7v2N64rH7bYhoDmxs9QXAx8mAtrHQAsGJDPgQ==" - ] - }, - { - "body": { - "messages": [ - { - "@type": "/cosmos.staking.v1beta1.MsgCreateValidator", - "description": { - "moniker": "festival", - "identity": "", - "website": "", - "security_contact": "", - "details": "" - }, - "commission": { - "rate": "0.100000000000000000", - "max_rate": "0.200000000000000000", - "max_change_rate": "0.010000000000000000" - }, - "min_self_delegation": "1", - "delegator_address": "umee1xpyya7quj4rx2pm3xclspyvh5gmfxwn2hwlt80", - "validator_address": "umeevaloper1xpyya7quj4rx2pm3xclspyvh5gmfxwn2h2cyk9", - "pubkey": { - "@type": "/cosmos.crypto.ed25519.PubKey", - "key": "pppHqyu2KFGb08WVT/wE7aW3FxKM36taED+cyLt7dEE=" - }, - "value": { - "denom": "uumee", - "amount": "100000000000" - } - }, - { - "@type": "/gravity.v1.MsgSetOrchestratorAddress", - "validator": "umeevaloper1xpyya7quj4rx2pm3xclspyvh5gmfxwn2h2cyk9", - "orchestrator": "umee1zl7pjgzfmcgwa0c7vgxklmkykuvmu77yxj75mp", - "eth_address": "0xfacf66789DD2fA6d80A36353f900922cb6D990F1" - } - ], - "memo": "67cd50cac0eff503a7c9efd537d369cc7a3ebe96@172.17.0.2:26656", - "timeout_height": "0", - "extension_options": [], - "non_critical_extension_options": [] - }, - "auth_info": { - "signer_infos": [ - { - "public_key": { - "@type": "/cosmos.crypto.secp256k1.PubKey", - "key": "A7ikWhnGN/ayRth06lI8+Dr71Irs9EM2W6ZmdHCulIz7" - }, - "mode_info": { - "single": { - "mode": "SIGN_MODE_DIRECT" - } - }, - "sequence": "0" - } - ], - "fee": { - "amount": [], - "gas_limit": "200000", - "payer": "", - "granter": "" - } - }, - "signatures": [ - "Rwm/C5SKEJlyzPJs2h2t+t2GORYQ1OiWqayz26Y0yXRN+p6wZBQHWYnf3BxBNZlhEmE+R3QzI5gI4UuvNFxl6g==" - ] - } - ] + "channel_genesis": { + "channels": [], + "acknowledgements": [], + "commitments": [], + "receipts": [], + "send_sequences": [], + "recv_sequences": [], + "ack_sequences": [], + "next_channel_sequence": "0" + } + }, + "leverage": { + "params": { + "complete_liquidation_threshold": "0.100000000000000000", + "minimum_close_factor": "0.010000000000000000", + "oracle_reward_factor": "0.010000000000000000", + "small_liquidation_size": "100.000000000000000000", + "direct_liquidation_fee": "0.100000000000000000" }, - "gov": { - "starting_proposal_id": "1", - "deposits": [], - "votes": [], - "proposals": [], - "deposit_params": { - "min_deposit": [ - { - "denom": "uumee", - "amount": "10000000" - } - ], - "max_deposit_period": "172800s" + "registry": [ + { + "base_borrow_rate": "0.05", + "base_denom": "uumee", + "collateral_weight": "0.10", + "exponent": 6, + "kink_borrow_rate": "0.30", + "kink_utilization_rate": "0.80", + "liquidation_incentive": "0.10", + "liquidation_threshold": "0.15", + "max_borrow_rate": "0.70", + "reserve_factor": "0.10", + "symbol_denom": "UMEE" }, - "voting_params": { - "voting_period": "600s" + { + "base_borrow_rate": "0.02", + "base_denom": "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2", + "collateral_weight": "0.50", + "exponent": 6, + "kink_borrow_rate": "0.20", + "kink_utilization_rate": "0.80", + "liquidation_incentive": "0.10", + "liquidation_threshold": "0.55", + "max_borrow_rate": "0.80", + "reserve_factor": "0.10", + "symbol_denom": "ATOM" }, - "tally_params": { - "quorum": "0.334000000000000000", - "threshold": "0.500000000000000000", - "veto_threshold": "0.334000000000000000" - } - }, - "gravity": { - "params": { - "gravity_id": "defaultgravityid", - "contract_source_hash": "", - "bridge_ethereum_address": "0x0000000000000000000000000000000000000000", - "bridge_chain_id": "5", - "signed_valsets_window": "10000", - "signed_batches_window": "10000", - "signed_logic_calls_window": "10000", - "target_batch_timeout": "43200000", - "average_block_time": "5000", - "average_ethereum_block_time": "15000", - "slash_fraction_valset": "0.001000000000000000", - "slash_fraction_batch": "0.001000000000000000", - "slash_fraction_logic_call": "0.001000000000000000", - "unbond_slashing_valsets_window": "10000", - "slash_fraction_bad_eth_signature": "0.001000000000000000", - "valset_reward": { - "denom": "", - "amount": "0" - }, - "bridge_active": true, - "ethereum_blacklist": [] - }, - "gravity_nonces": { - "latest_valset_nonce": "0", - "last_observed_nonce": "0", - "last_slashed_valset_nonce": "0", - "last_slashed_batch_block": "0", - "last_slashed_logic_call_block": "0", - "last_tx_pool_id": "0", - "last_batch_id": "0" - }, - "valsets": [], - "valset_confirms": [], - "batches": [], - "batch_confirms": [], - "logic_calls": [], - "logic_call_confirms": [], - "attestations": [], - "delegate_keys": [], - "erc20_to_denoms": [], - "unbatched_transfers": [] - }, - "ibc": { - "client_genesis": { - "clients": [], - "clients_consensus": [], - "clients_metadata": [], - "params": { - "allowed_clients": ["06-solomachine", "07-tendermint"] - }, - "create_localhost": false, - "next_client_sequence": "0" - }, - "connection_genesis": { - "connections": [], - "client_connection_paths": [], - "next_connection_sequence": "0", - "params": { - "max_expected_time_per_block": "30000000000" - } + { + "base_borrow_rate": "0.02", + "base_denom": "ibc/04F5F501207C3626A2C14BFEF654D51C2E0B8F7CA578AB8ED272A66FE4E48097", + "collateral_weight": "0.35", + "exponent": 6, + "kink_borrow_rate": "0.20", + "kink_utilization_rate": "0.80", + "liquidation_incentive": "0.10", + "liquidation_threshold": "0.40", + "max_borrow_rate": "0.80", + "reserve_factor": "0.10", + "symbol_denom": "JUNO" }, - "channel_genesis": { - "channels": [], - "acknowledgements": [], - "commitments": [], - "receipts": [], - "send_sequences": [], - "recv_sequences": [], - "ack_sequences": [], - "next_channel_sequence": "0" + { + "base_borrow_rate": "0.02", + "base_denom": "ibc/ED07A3391A112B175915CD8FAF43A2DA8E4790EDE12566649D0C2F97716B8518", + "collateral_weight": "0.60", + "exponent": 6, + "kink_borrow_rate": "0.20", + "kink_utilization_rate": "0.80", + "liquidation_incentive": "0.10", + "liquidation_threshold": "0.65", + "max_borrow_rate": "0.80", + "reserve_factor": "0.10", + "symbol_denom": "OSMO" + }, + { + "base_borrow_rate": "0.03", + "base_denom": "gravity0xd787Ec2b6C962f611300175603741Db8438674a0", + "collateral_weight": "0.75", + "exponent": 18, + "kink_borrow_rate": "0.15", + "kink_utilization_rate": "0.75", + "liquidation_incentive": "0.05", + "liquidation_threshold": "0.80", + "max_borrow_rate": "0.30", + "reserve_factor": "0.10", + "symbol_denom": "DAI" } + ], + "adjusted_borrows": [], + "collateral_settings": [], + "collateral": [], + "reserves": [], + "last_interest_time": "0", + "bad_debts": [], + "interest_scalars": [], + "utoken_supply": [] + }, + "mint": { + "minter": { + "inflation": "0.130000000000000000", + "annual_provisions": "0.000000000000000000" }, - "leverage": { - "params": { - "complete_liquidation_threshold": "0.100000000000000000", - "minimum_close_factor": "0.010000000000000000", - "oracle_reward_factor": "0.010000000000000000", - "small_liquidation_size": "100.000000000000000000" - }, - "registry": [ + "params": { + "mint_denom": "uumee", + "inflation_rate_change": "0.130000000000000000", + "inflation_max": "0.200000000000000000", + "inflation_min": "0.070000000000000000", + "goal_bonded": "0.670000000000000000", + "blocks_per_year": "6311520" + } + }, + "oracle": { + "params": { + "vote_period": "5", + "vote_threshold": "0.500000000000000000", + "reward_band": "0.020000000000000000", + "reward_distribution_window": "5256000", + "accept_list": [ { - "base_borrow_rate": "0.05", "base_denom": "uumee", - "collateral_weight": "0.10", - "exponent": 6, - "kink_borrow_rate": "0.30", - "kink_utilization_rate": "0.80", - "liquidation_incentive": "0.10", - "liquidation_threshold": "0.15", - "max_borrow_rate": "0.70", - "reserve_factor": "0.10", - "symbol_denom": "UMEE" - }, - { - "base_borrow_rate": "0.02", - "base_denom": "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2", - "collateral_weight": "0.50", - "exponent": 6, - "kink_borrow_rate": "0.20", - "kink_utilization_rate": "0.80", - "liquidation_incentive": "0.10", - "liquidation_threshold": "0.55", - "max_borrow_rate": "0.80", - "reserve_factor": "0.10", - "symbol_denom": "ATOM" - }, - { - "base_borrow_rate": "0.02", - "base_denom": "ibc/04F5F501207C3626A2C14BFEF654D51C2E0B8F7CA578AB8ED272A66FE4E48097", - "collateral_weight": "0.35", - "exponent": 6, - "kink_borrow_rate": "0.20", - "kink_utilization_rate": "0.80", - "liquidation_incentive": "0.10", - "liquidation_threshold": "0.40", - "max_borrow_rate": "0.80", - "reserve_factor": "0.10", - "symbol_denom": "JUNO" - }, - { - "base_borrow_rate": "0.02", - "base_denom": "ibc/ED07A3391A112B175915CD8FAF43A2DA8E4790EDE12566649D0C2F97716B8518", - "collateral_weight": "0.60", - "exponent": 6, - "kink_borrow_rate": "0.20", - "kink_utilization_rate": "0.80", - "liquidation_incentive": "0.10", - "liquidation_threshold": "0.65", - "max_borrow_rate": "0.80", - "reserve_factor": "0.10", - "symbol_denom": "OSMO" - }, - { - "base_borrow_rate": "0.03", - "base_denom": "gravity0xd787Ec2b6C962f611300175603741Db8438674a0", - "collateral_weight": "0.75", - "exponent": 18, - "kink_borrow_rate": "0.15", - "kink_utilization_rate": "0.75", - "liquidation_incentive": "0.05", - "liquidation_threshold": "0.80", - "max_borrow_rate": "0.30", - "reserve_factor": "0.10", - "symbol_denom": "DAI" + "symbol_denom": "umee", + "exponent": 6 } ], - "adjusted_borrows": [], - "collateral_settings": [], - "collateral": [], - "reserves": [], - "last_interest_time": "0", - "bad_debts": [], - "interest_scalars": [], - "utoken_supply": [] - }, - "mint": { - "minter": { - "inflation": "0.130000000000000000", - "annual_provisions": "0.000000000000000000" - }, - "params": { - "mint_denom": "uumee", - "inflation_rate_change": "0.130000000000000000", - "inflation_max": "0.200000000000000000", - "inflation_min": "0.070000000000000000", - "goal_bonded": "0.670000000000000000", - "blocks_per_year": "6311520" - } - }, - "oracle": { - "params": { - "vote_period": "5", - "vote_threshold": "0.500000000000000000", - "reward_band": "0.020000000000000000", - "reward_distribution_window": "5256000", - "accept_list": [ - { - "base_denom": "uumee", - "symbol_denom": "umee", - "exponent": 6 - } - ], - "slash_fraction": "0.000100000000000000", - "slash_window": "100800", - "min_valid_per_window": "0.050000000000000000" - }, - "feeder_delegations": [], - "exchange_rates": [], - "miss_counters": [], - "aggregate_exchange_rate_prevotes": [], - "aggregate_exchange_rate_votes": [] - }, - "params": null, - "slashing": { - "params": { - "signed_blocks_window": "10000", - "min_signed_per_window": "0.500000000000000000", - "downtime_jail_duration": "86400s", - "slash_fraction_double_sign": "0.050000000000000000", - "slash_fraction_downtime": "0.010000000000000000" - }, - "signing_infos": [], - "missed_blocks": [] + "slash_fraction": "0.000100000000000000", + "slash_window": "100800", + "min_valid_per_window": "0.050000000000000000" }, - "staking": { - "params": { - "unbonding_time": "1814400s", - "max_validators": 100, - "max_entries": 7, - "historical_entries": 10000, - "bond_denom": "uumee" - }, - "last_total_power": "0", - "last_validator_powers": [], - "validators": [], - "delegations": [], - "unbonding_delegations": [], - "redelegations": [], - "exported": false + "feeder_delegations": [], + "exchange_rates": [], + "miss_counters": [], + "aggregate_exchange_rate_prevotes": [], + "aggregate_exchange_rate_votes": [] + }, + "params": null, + "slashing": { + "params": { + "signed_blocks_window": "10000", + "min_signed_per_window": "0.500000000000000000", + "downtime_jail_duration": "86400s", + "slash_fraction_double_sign": "0.050000000000000000", + "slash_fraction_downtime": "0.010000000000000000" }, - "transfer": { - "port_id": "transfer", - "denom_traces": [], - "params": { - "send_enabled": true, - "receive_enabled": true - } + "signing_infos": [], + "missed_blocks": [] + }, + "staking": { + "params": { + "unbonding_time": "1814400s", + "max_validators": 100, + "max_entries": 7, + "historical_entries": 10000, + "bond_denom": "uumee" }, - "upgrade": {}, - "vesting": {} - } + "last_total_power": "0", + "last_validator_powers": [], + "validators": [], + "delegations": [], + "unbonding_delegations": [], + "redelegations": [], + "exported": false + }, + "transfer": { + "port_id": "transfer", + "denom_traces": [], + "params": { + "send_enabled": true, + "receive_enabled": true + } + }, + "upgrade": {}, + "vesting": {} } +} \ No newline at end of file diff --git a/proto/umee/leverage/v1/leverage.proto b/proto/umee/leverage/v1/leverage.proto index 0a1e158ed7..660346094c 100644 --- a/proto/umee/leverage/v1/leverage.proto +++ b/proto/umee/leverage/v1/leverage.proto @@ -41,6 +41,14 @@ message Params { (gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"small_liquidation_size\"" ]; + // Direct Liquidation Fee is the reduction in liquidation incentive experienced + // by liquidators who choose to receive base assets instead of uTokens as + // liquidation rewards. + string direct_liquidation_fee = 6 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", + (gogoproto.nullable) = false, + (gogoproto.moretags) = "yaml:\"direct_liquidation_fee\"" + ]; } // Token defines a token, along with its metadata and parameters, in the Umee diff --git a/proto/umee/leverage/v1/tx.proto b/proto/umee/leverage/v1/tx.proto index 95554057f0..9cc4df9c66 100644 --- a/proto/umee/leverage/v1/tx.proto +++ b/proto/umee/leverage/v1/tx.proto @@ -96,9 +96,10 @@ message MsgLiquidate { // Repayment is the maximum amount of base tokens that the liquidator is willing // to repay. cosmos.base.v1beta1.Coin repayment = 3 [(gogoproto.nullable) = false]; - // RewardDenom is the base token denom that the liquidator is willing to accept - // as a liquidation reward. The uToken equivalent of any base token rewards - // will be taken from the borrower's collateral. + // RewardDenom is the denom that the liquidator will receive liquidation reward. + // If it is a uToken, the liquidator will receive uTokens from the borrower's + // collateral. If it is a base token, the uTokens will be redeemed directly at + // a reduced Liquidation Incentive, and the liquidator will receive base tokens. string reward_denom = 4; } diff --git a/swagger/swagger.yaml b/swagger/swagger.yaml index 37688e598d..1abbe21c99 100644 --- a/swagger/swagger.yaml +++ b/swagger/swagger.yaml @@ -4,70 +4,19 @@ info: description: A REST interface for state queries version: 1.0.0 paths: - /umee/leverage/v1/borrow_limit: - get: - summary: BorrowLimit queries the borrow limit in USD of a given borrower. - operationId: BorrowLimit - responses: - '200': - description: A successful response. - schema: - type: object - properties: - borrow_limit: - type: string - description: >- - QueryBorrowLimitResponse defines the response structure for the - BorrowLimit - - gRPC service handler. - default: - description: An unexpected error response. - schema: - type: object - properties: - error: - type: string - code: - type: integer - format: int32 - message: - type: string - details: - type: array - items: - type: object - properties: - type_url: - type: string - value: - type: string - format: byte - parameters: - - name: address - in: query - required: false - type: string - tags: - - Query - /umee/leverage/v1/borrowed: + /umee/leverage/v1/account_balances: get: summary: >- - Borrowed queries for the borrowed amount of a user by token - denomination. - - If the denomination is not specified, the total for each borrowed token - is - - returned. - operationId: Borrowed + AccountBalances queries an account's current supply, collateral, and + borrow positions. + operationId: AccountBalances responses: '200': description: A successful response. schema: type: object properties: - borrowed: + supplied: type: array items: type: object @@ -84,116 +33,34 @@ paths: method signatures required by gogoproto. - description: >- - QueryBorrowedResponse defines the response structure for the - Borrowed gRPC - - service handler. - default: - description: An unexpected error response. - schema: - type: object - properties: - error: - type: string - code: - type: integer - format: int32 - message: - type: string - details: + description: >- + Supplied contains all tokens the account has supplied, + including interest earned. It is denominated in base tokens, + so exponent from each coin's registered_tokens entry must be + applied to convert to symbol denom. + collateral: type: array items: type: object properties: - type_url: + denom: type: string - value: + amount: type: string - format: byte - parameters: - - name: address - in: query - required: false - type: string - - name: denom - in: query - required: false - type: string - tags: - - Query - /umee/leverage/v1/borrowed_value: - get: - summary: >- - BorrowedValue queries for the usd value of the borrowed amount of a user + description: >- + Coin defines a token with a denomination and an amount. - by token denomination. If the denomination is not specified, the sum - across - all borrowed tokens is returned. - operationId: BorrowedValue - responses: - '200': - description: A successful response. - schema: - type: object - properties: - borrowed_value: - type: string - description: |- - QueryBorrowedValueResponse defines the response structure for the - BorrowedValue gRPC service handler. - default: - description: An unexpected error response. - schema: - type: object - properties: - error: - type: string - code: - type: integer - format: int32 - message: - type: string - details: - type: array - items: - type: object - properties: - type_url: - type: string - value: - type: string - format: byte - parameters: - - name: address - in: query - required: false - type: string - - name: denom - in: query - required: false - type: string - tags: - - Query - /umee/leverage/v1/collateral: - get: - summary: >- - Collateral queries the collateral amount of a user by token - denomination. - - If the denomination is not specified, all of the user's collateral - tokens + NOTE: The amount field is an Int which implements the custom + method - are returned. - operationId: Collateral - responses: - '200': - description: A successful response. - schema: - type: object - properties: - collateral: + signatures required by gogoproto. + description: >- + Collateral contains all uTokens the account has + collateralized. It is denominated in uTokens, so both exponent + and uToken exchange rate from each coin's market_summary must + be applied to convert to base token symbol denom. + borrowed: type: array items: type: object @@ -210,11 +77,14 @@ paths: method signatures required by gogoproto. + description: >- + Borrowed contains all tokens the account has borrowed, + including interest owed. It is denominated in base tokens, so + exponent from each coin's registered_tokens entry must be + applied to convert to symbol denom. description: >- - QueryCollateralResponse defines the response structure for the - Collateral - - gRPC service handler. + QueryAccountBalancesResponse defines the response structure for + the AccountBalances gRPC service handler. default: description: An unexpected error response. schema: @@ -242,34 +112,49 @@ paths: in: query required: false type: string - - name: denom - in: query - required: false - type: string tags: - Query - /umee/leverage/v1/collateral_value: + /umee/leverage/v1/account_summary: get: summary: >- - CollateralValue queries for the total USD value of a user's collateral, - or - - the USD value held as a given base asset's associated uToken - denomination. - operationId: CollateralValue + AccountSummary queries USD values representing an account's total + positions and borrowing limits. It requires oracle prices to return + successfully. + operationId: AccountSummary responses: '200': description: A successful response. schema: type: object properties: + supplied_value: + type: string + description: >- + Supplied Value is the sum of the USD value of all tokens the + account has supplied, includng interest earned. collateral_value: type: string + description: >- + Collateral Value is the sum of the USD value of all uTokens + the account has collateralized. + borrowed_value: + type: string + description: >- + Borrowed Value is the sum of the USD value of all tokens the + account has borrowed, including interest owed. + borrow_limit: + type: string + description: >- + Borrow Limit is the maximum Borrowed Value the account is + allowed to reach through direct borrowing. + liquidation_threshold: + type: string + description: >- + Liquidation Threshold is the Borrowed Value at which the + account becomes eligible for liquidation. description: >- - QueryCollateralValueResponse defines the response structure for - the - - CollateralValue gRPC service handler. + QueryAccountSummaryResponse defines the response structure for the + AccountSummary gRPC service handler. default: description: An unexpected error response. schema: @@ -297,17 +182,13 @@ paths: in: query required: false type: string - - name: denom - in: query - required: false - type: string tags: - Query /umee/leverage/v1/liquidation_targets: get: - summary: |- - LiquidationTargets queries a list of all borrower addresses eligible for - liquidation. + summary: >- + LiquidationTargets queries a list of all borrower account addresses + eligible for liquidation. operationId: LiquidationTargets responses: '200': @@ -319,6 +200,9 @@ paths: type: array items: type: string + description: >- + Targets are the addresses of borrowers eligible for + liquidation. description: >- QueryLiquidationTargetsResponse defines the response structure for the @@ -348,54 +232,6 @@ paths: format: byte tags: - Query - /umee/leverage/v1/liquidation_threshold: - get: - summary: |- - LiquidationThreshold returns a maximum borrow value in USD above which a - given borrower is eligible for liquidation. - operationId: LiquidationThreshold - responses: - '200': - description: A successful response. - schema: - type: object - properties: - liquidation_threshold: - type: string - description: >- - QueryLiquidationThresholdResponse defines the response structure - for the - - LiquidationThreshold gRPC service handler. - default: - description: An unexpected error response. - schema: - type: object - properties: - error: - type: string - code: - type: integer - format: int32 - message: - type: string - details: - type: array - items: - type: object - properties: - type_url: - type: string - value: - type: string - format: byte - parameters: - - name: address - in: query - required: false - type: string - tags: - - Query /umee/leverage/v1/market_summary: get: summary: >- @@ -636,6 +472,16 @@ paths: transaction, bypassing dynamic close factor. + direct_liquidation_fee: + type: string + description: >- + Direct Liquidation Fee is the reduction in liquidation + incentive experienced + + by liquidators who choose to receive base assets instead + of uTokens as + + liquidation rewards. description: Params defines the parameters for the leverage module. description: >- QueryParamsResponse defines the response structure for the Params @@ -882,130 +728,6 @@ paths: format: byte tags: - Query - /umee/leverage/v1/supplied: - get: - summary: >- - Supplied queries for the amount of tokens by a user by denomination. - - If the denomination is not specified, the total for each supplied token - is - - returned. - operationId: Supplied - responses: - '200': - description: A successful response. - schema: - type: object - properties: - supplied: - type: array - items: - type: object - properties: - denom: - type: string - amount: - type: string - description: >- - Coin defines a token with a denomination and an amount. - - - NOTE: The amount field is an Int which implements the custom - method - - signatures required by gogoproto. - description: >- - QuerySuppliedResponse defines the response structure for the - Supplied gRPC - - service handler. - default: - description: An unexpected error response. - schema: - type: object - properties: - error: - type: string - code: - type: integer - format: int32 - message: - type: string - details: - type: array - items: - type: object - properties: - type_url: - type: string - value: - type: string - format: byte - parameters: - - name: address - in: query - required: false - type: string - - name: denom - in: query - required: false - type: string - tags: - - Query - /umee/leverage/v1/supplied_value: - get: - summary: |- - SuppliedValue queries for the USD value supplied by a user by token - denomination. If the denomination is not specified, the sum across all - supplied tokens is returned. - operationId: SuppliedValue - responses: - '200': - description: A successful response. - schema: - type: object - properties: - supplied_value: - type: string - description: >- - QuerySuppliedValueResponse defines the response structure for the - SuppliedValue - - gRPC service handler. - default: - description: An unexpected error response. - schema: - type: object - properties: - error: - type: string - code: - type: integer - format: int32 - message: - type: string - details: - type: array - items: - type: object - properties: - type_url: - type: string - value: - type: string - format: byte - parameters: - - name: address - in: query - required: false - type: string - - name: denom - in: query - required: false - type: string - tags: - - Query /umee/oracle/v1/denoms/active_exchange_rates: get: summary: ActiveExchangeRates returns all active denoms @@ -1661,21 +1383,20 @@ definitions: bypassing dynamic close factor. - description: Params defines the parameters for the leverage module. - umee.leverage.v1.QueryBorrowLimitResponse: - type: object - properties: - borrow_limit: + direct_liquidation_fee: type: string - description: >- - QueryBorrowLimitResponse defines the response structure for the - BorrowLimit + description: >- + Direct Liquidation Fee is the reduction in liquidation incentive + experienced + + by liquidators who choose to receive base assets instead of uTokens as - gRPC service handler. - umee.leverage.v1.QueryBorrowedResponse: + liquidation rewards. + description: Params defines the parameters for the leverage module. + umee.leverage.v1.QueryAccountBalancesResponse: type: object properties: - borrowed: + supplied: type: array items: type: object @@ -1689,20 +1410,11 @@ definitions: NOTE: The amount field is an Int which implements the custom method signatures required by gogoproto. - description: |- - QueryBorrowedResponse defines the response structure for the Borrowed gRPC - service handler. - umee.leverage.v1.QueryBorrowedValueResponse: - type: object - properties: - borrowed_value: - type: string - description: |- - QueryBorrowedValueResponse defines the response structure for the - BorrowedValue gRPC service handler. - umee.leverage.v1.QueryCollateralResponse: - type: object - properties: + description: >- + Supplied contains all tokens the account has supplied, including + interest earned. It is denominated in base tokens, so exponent from + each coin's registered_tokens entry must be applied to convert to + symbol denom. collateral: type: array items: @@ -1717,17 +1429,64 @@ definitions: NOTE: The amount field is an Int which implements the custom method signatures required by gogoproto. - description: |- - QueryCollateralResponse defines the response structure for the Collateral - gRPC service handler. - umee.leverage.v1.QueryCollateralValueResponse: + description: >- + Collateral contains all uTokens the account has collateralized. It is + denominated in uTokens, so both exponent and uToken exchange rate from + each coin's market_summary must be applied to convert to base token + symbol denom. + borrowed: + type: array + items: + type: object + properties: + denom: + type: string + amount: + type: string + description: |- + Coin defines a token with a denomination and an amount. + + NOTE: The amount field is an Int which implements the custom method + signatures required by gogoproto. + description: >- + Borrowed contains all tokens the account has borrowed, including + interest owed. It is denominated in base tokens, so exponent from each + coin's registered_tokens entry must be applied to convert to symbol + denom. + description: >- + QueryAccountBalancesResponse defines the response structure for the + AccountBalances gRPC service handler. + umee.leverage.v1.QueryAccountSummaryResponse: type: object properties: + supplied_value: + type: string + description: >- + Supplied Value is the sum of the USD value of all tokens the account + has supplied, includng interest earned. collateral_value: type: string - description: |- - QueryCollateralValueResponse defines the response structure for the - CollateralValue gRPC service handler. + description: >- + Collateral Value is the sum of the USD value of all uTokens the + account has collateralized. + borrowed_value: + type: string + description: >- + Borrowed Value is the sum of the USD value of all tokens the account + has borrowed, including interest owed. + borrow_limit: + type: string + description: >- + Borrow Limit is the maximum Borrowed Value the account is allowed to + reach through direct borrowing. + liquidation_threshold: + type: string + description: >- + Liquidation Threshold is the Borrowed Value at which the account + becomes eligible for liquidation. + description: >- + QueryAccountSummaryResponse defines the response structure for the + AccountSummary gRPC service handler. umee.leverage.v1.QueryLiquidationTargetsResponse: type: object properties: @@ -1735,17 +1494,10 @@ definitions: type: array items: type: string + description: Targets are the addresses of borrowers eligible for liquidation. description: |- QueryLiquidationTargetsResponse defines the response structure for the LiquidationTargets gRPC service handler. - umee.leverage.v1.QueryLiquidationThresholdResponse: - type: object - properties: - liquidation_threshold: - type: string - description: |- - QueryLiquidationThresholdResponse defines the response structure for the - LiquidationThreshold gRPC service handler. umee.leverage.v1.QueryMarketSummaryResponse: type: object properties: @@ -1931,6 +1683,16 @@ definitions: bypassing dynamic close factor. + direct_liquidation_fee: + type: string + description: >- + Direct Liquidation Fee is the reduction in liquidation incentive + experienced + + by liquidators who choose to receive base assets instead of + uTokens as + + liquidation rewards. description: Params defines the parameters for the leverage module. description: |- QueryParamsResponse defines the response structure for the Params gRPC @@ -2105,36 +1867,6 @@ definitions: description: |- QueryRegisteredTokensResponse defines the response structure for the RegisteredTokens gRPC service handler. - umee.leverage.v1.QuerySuppliedResponse: - type: object - properties: - supplied: - type: array - items: - type: object - properties: - denom: - type: string - amount: - type: string - description: |- - Coin defines a token with a denomination and an amount. - - NOTE: The amount field is an Int which implements the custom method - signatures required by gogoproto. - description: |- - QuerySuppliedResponse defines the response structure for the Supplied gRPC - service handler. - umee.leverage.v1.QuerySuppliedValueResponse: - type: object - properties: - supplied_value: - type: string - description: >- - QuerySuppliedValueResponse defines the response structure for the - SuppliedValue - - gRPC service handler. umee.leverage.v1.Token: type: object properties: diff --git a/x/leverage/client/tests/tests.go b/x/leverage/client/tests/tests.go index f7bedbd573..eda90f1047 100644 --- a/x/leverage/client/tests/tests.go +++ b/x/leverage/client/tests/tests.go @@ -215,7 +215,7 @@ func (s *IntegrationTestSuite) TestLeverageScenario() { sdk.NewInt64Coin(umeeapp.BondDenom, 1000), ), Collateral: sdk.NewCoins( - sdk.NewInt64Coin(types.UTokenFromTokenDenom(umeeapp.BondDenom), 1000), + sdk.NewInt64Coin(types.ToUTokenDenom(umeeapp.BondDenom), 1000), ), Borrowed: sdk.NewCoins( sdk.NewInt64Coin(umeeapp.BondDenom, 51), diff --git a/x/leverage/keeper/borrows.go b/x/leverage/keeper/borrows.go index 975c46462e..767edf6211 100644 --- a/x/leverage/keeper/borrows.go +++ b/x/leverage/keeper/borrows.go @@ -26,6 +26,20 @@ func (k Keeper) GetBorrow(ctx sdk.Context, borrowerAddr sdk.AccAddress, denom st return owed } +// repayBorrow repays tokens borrowed by borrowAddr by sending coins in fromAddr to the module. This +// occurs during normal repayment (in which case fromAddr and borrowAddr are the same) and during +// liquidations, where fromAddr is the liquidator instead. +func (k Keeper) repayBorrow(ctx sdk.Context, fromAddr, borrowAddr sdk.AccAddress, repay sdk.Coin) error { + // send repayment from fromAddr to leverage module account + err := k.bankKeeper.SendCoinsFromAccountToModule(ctx, fromAddr, types.ModuleName, sdk.NewCoins(repay)) + if err != nil { + return err + } + // update borrower's remaining borrowed amount + newBorrow := k.GetBorrow(ctx, borrowAddr, repay.Denom).Sub(repay) + return k.setBorrow(ctx, borrowAddr, newBorrow) +} + // setBorrow sets the amount borrowed by an address in a given denom. // If the amount is zero, any stored value is cleared. func (k Keeper) setBorrow(ctx sdk.Context, borrowerAddr sdk.AccAddress, borrow sdk.Coin) error { diff --git a/x/leverage/keeper/borrows_test.go b/x/leverage/keeper/borrows_test.go index ac99f78e98..58f77fa18f 100644 --- a/x/leverage/keeper/borrows_test.go +++ b/x/leverage/keeper/borrows_test.go @@ -2,6 +2,8 @@ package keeper_test import ( sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/umee-network/umee/v2/x/leverage/types" ) func (s *IntegrationTestSuite) TestGetBorrow() { @@ -195,7 +197,7 @@ func (s *IntegrationTestSuite) TestCalculateBorrowLimit() { s.Require().EqualError(err, "abcd: invalid asset") // Create collateral uTokens (1k u/umee) - umeeCollatDenom := s.app.LeverageKeeper.FromTokenToUTokenDenom(s.ctx, umeeDenom) + umeeCollatDenom := types.ToUTokenDenom(umeeDenom) umeeCollateral := sdk.NewCoins(sdk.NewInt64Coin(umeeCollatDenom, 1000000000)) // Manually compute borrow limit using collateral weight of 0.25 @@ -210,7 +212,7 @@ func (s *IntegrationTestSuite) TestCalculateBorrowLimit() { s.Require().Equal(expectedUmeeLimit, borrowLimit) // Create collateral atom uTokens (1k u/uatom) - atomCollatDenom := s.app.LeverageKeeper.FromTokenToUTokenDenom(s.ctx, atomIBCDenom) + atomCollatDenom := types.ToUTokenDenom(atomIBCDenom) atomCollateral := sdk.NewCoins(sdk.NewInt64Coin(atomCollatDenom, 1000000000)) // Manually compute borrow limit using collateral weight of 0.25 diff --git a/x/leverage/keeper/collateral.go b/x/leverage/keeper/collateral.go index 6912c17362..de7a1c1f7c 100644 --- a/x/leverage/keeper/collateral.go +++ b/x/leverage/keeper/collateral.go @@ -7,6 +7,46 @@ import ( "github.com/umee-network/umee/v2/x/leverage/types" ) +// burnCollateral removes some uTokens from an account's collateral and burns them. This occurs +// during liquidations. +func (k Keeper) burnCollateral(ctx sdk.Context, addr sdk.AccAddress, collateral sdk.Coin) error { + if !types.HasUTokenPrefix(collateral.Denom) { + return types.ErrNotUToken.Wrap(collateral.Denom) + } + + // reduce account's collateral + oldCollateral := k.GetCollateralAmount(ctx, addr, collateral.Denom) + newCollateral := sdk.NewCoin(collateral.Denom, oldCollateral.Amount.Sub(collateral.Amount)) + if err := k.setCollateralAmount(ctx, addr, newCollateral); err != nil { + return err + } + // burn the uTokens + if err := k.bankKeeper.BurnCoins(ctx, types.ModuleName, sdk.NewCoins(collateral)); err != nil { + return err + } + // set the new total uToken supply + return k.setUTokenSupply(ctx, k.GetUTokenSupply(ctx, collateral.Denom).Sub(collateral)) +} + +// removeCollateral removes some uTokens in fromAddr's collateral and sends them to toAddr. This +// occurs when decollateralizing uTokens (in which case fromAddr and toAddr are the same) as well as +// during liquidations where the liquidator receives uToken rewards from the borrower's collateral. +func (k Keeper) removeCollateral(ctx sdk.Context, fromAddr, toAddr sdk.AccAddress, collateral sdk.Coin) error { + if !types.HasUTokenPrefix(collateral.Denom) { + return types.ErrNotUToken.Wrap(collateral.Denom) + } + + // reduce fromAddr's collateral + oldCollateral := k.GetCollateralAmount(ctx, fromAddr, collateral.Denom) + newCollateral := sdk.NewCoin(collateral.Denom, oldCollateral.Amount.Sub(collateral.Amount)) + if err := k.setCollateralAmount(ctx, fromAddr, newCollateral); err != nil { + return err + } + + // send the uTokens to toAddr + return k.bankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, toAddr, sdk.NewCoins(collateral)) +} + // GetCollateralAmount returns an sdk.Coin representing how much of a given denom the // x/leverage module account currently holds as collateral for a given borrower. func (k Keeper) GetCollateralAmount(ctx sdk.Context, borrowerAddr sdk.AccAddress, denom string) sdk.Coin { @@ -57,7 +97,7 @@ func (k Keeper) setCollateralAmount(ctx sdk.Context, borrowerAddr sdk.AccAddress // the x/leverage module account currently holds as collateral. Non-uTokens and invalid // assets return zero. func (k Keeper) GetTotalCollateral(ctx sdk.Context, denom string) sdk.Int { - if !k.IsAcceptedUToken(ctx, denom) { + if k.ValidateAcceptedUTokenDenom(ctx, denom) != nil { // non-uTokens cannot be collateral return sdk.ZeroInt() } diff --git a/x/leverage/keeper/collateral_test.go b/x/leverage/keeper/collateral_test.go index e643db62af..fd48279084 100644 --- a/x/leverage/keeper/collateral_test.go +++ b/x/leverage/keeper/collateral_test.go @@ -2,10 +2,12 @@ package keeper_test import ( sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/umee-network/umee/v2/x/leverage/types" ) func (s *IntegrationTestSuite) TestGetCollateralAmount() { - uDenom := s.tk.FromTokenToUTokenDenom(s.ctx, umeeDenom) + uDenom := types.ToUTokenDenom(umeeDenom) // get u/umee collateral amount of empty account address (zero) collateral := s.tk.GetCollateralAmount(s.ctx, sdk.AccAddress{}, uDenom) @@ -48,7 +50,7 @@ func (s *IntegrationTestSuite) TestGetCollateralAmount() { } func (s *IntegrationTestSuite) TestSetCollateralAmount() { - uDenom := s.tk.FromTokenToUTokenDenom(s.ctx, umeeDenom) + uDenom := types.ToUTokenDenom(umeeDenom) // set u/umee collateral amount of empty account address (error) err := s.tk.SetCollateralAmount(s.ctx, sdk.AccAddress{}, sdk.NewInt64Coin(uDenom, 0)) diff --git a/x/leverage/keeper/exchange_rate.go b/x/leverage/keeper/exchange_rate.go index cc775d1f45..fb82a6bc2a 100644 --- a/x/leverage/keeper/exchange_rate.go +++ b/x/leverage/keeper/exchange_rate.go @@ -14,7 +14,7 @@ func (k Keeper) ExchangeToken(ctx sdk.Context, token sdk.Coin) (sdk.Coin, error) return sdk.Coin{}, sdkerrors.Wrap(types.ErrInvalidAsset, token.String()) } - uTokenDenom := k.FromTokenToUTokenDenom(ctx, token.Denom) + uTokenDenom := types.ToUTokenDenom(token.Denom) if uTokenDenom == "" { return sdk.Coin{}, sdkerrors.Wrap(types.ErrInvalidAsset, token.Denom) } @@ -32,7 +32,7 @@ func (k Keeper) ExchangeUToken(ctx sdk.Context, uToken sdk.Coin) (sdk.Coin, erro return sdk.Coin{}, sdkerrors.Wrap(types.ErrInvalidAsset, uToken.String()) } - tokenDenom := k.FromUTokenToTokenDenom(ctx, uToken.Denom) + tokenDenom := types.ToTokenDenom(uToken.Denom) if tokenDenom == "" { return sdk.Coin{}, sdkerrors.Wrap(types.ErrInvalidAsset, uToken.Denom) } @@ -72,7 +72,7 @@ func (k Keeper) DeriveExchangeRate(ctx sdk.Context, denom string) sdk.Dec { moduleBalance := k.ModuleBalance(ctx, denom).ToDec() reserveAmount := k.GetReserveAmount(ctx, denom).ToDec() totalBorrowed := k.getAdjustedTotalBorrowed(ctx, denom).Mul(k.getInterestScalar(ctx, denom)) - uTokenSupply := k.GetUTokenSupply(ctx, k.FromTokenToUTokenDenom(ctx, denom)).Amount + uTokenSupply := k.GetUTokenSupply(ctx, types.ToUTokenDenom(denom)).Amount // Derive effective token supply tokenSupply := moduleBalance.Add(totalBorrowed).Sub(reserveAmount) diff --git a/x/leverage/keeper/filter.go b/x/leverage/keeper/filter.go index 864c958273..9b9193865b 100644 --- a/x/leverage/keeper/filter.go +++ b/x/leverage/keeper/filter.go @@ -20,7 +20,7 @@ func (k Keeper) filterAcceptedCoins(ctx sdk.Context, coins sdk.Coins) sdk.Coins return k.filterCoins( coins, func(c sdk.Coin) bool { - return k.validateAcceptedAsset(ctx, c) == nil + return k.ValidateAcceptedAsset(ctx, c) == nil }, ) } diff --git a/x/leverage/keeper/grpc_query.go b/x/leverage/keeper/grpc_query.go index e15deea254..c41e0bb3a1 100644 --- a/x/leverage/keeper/grpc_query.go +++ b/x/leverage/keeper/grpc_query.go @@ -82,7 +82,7 @@ func (q Querier) MarketSummary( reserved := q.Keeper.GetReserveAmount(ctx, req.Denom) borrowed := q.Keeper.GetTotalBorrowed(ctx, req.Denom) - uDenom := q.Keeper.FromTokenToUTokenDenom(ctx, req.Denom) + uDenom := types.ToUTokenDenom(req.Denom) uSupply := q.Keeper.GetUTokenSupply(ctx, uDenom) uCollateral := q.Keeper.GetTotalCollateral(ctx, uDenom) diff --git a/x/leverage/keeper/grpc_query_test.go b/x/leverage/keeper/grpc_query_test.go index 8b926819e7..ff3fcffd4e 100644 --- a/x/leverage/keeper/grpc_query_test.go +++ b/x/leverage/keeper/grpc_query_test.go @@ -65,7 +65,7 @@ func (s *IntegrationTestSuite) TestQuerier_AccountBalances() { sdk.NewCoin(umeeDenom, sdk.NewInt(1000000000)), ), Collateral: sdk.NewCoins( - sdk.NewCoin(types.UTokenFromTokenDenom(umeeDenom), sdk.NewInt(1000000000)), + sdk.NewCoin(types.ToUTokenDenom(umeeDenom), sdk.NewInt(1000000000)), ), Borrowed: nil, } diff --git a/x/leverage/keeper/iter.go b/x/leverage/keeper/iter.go index 7be4bc5c1e..21d2f69867 100644 --- a/x/leverage/keeper/iter.go +++ b/x/leverage/keeper/iter.go @@ -238,7 +238,7 @@ func (k Keeper) SweepBadDebts(ctx sdk.Context) error { // if collateral is still zero, attempt to repay a single address's debt in this denom if !done { var err error - done, err = k.RepayBadDebt(ctx, addr, denom) + done, err = k.repayBadDebt(ctx, addr, denom) if err != nil { return err } diff --git a/x/leverage/keeper/keeper.go b/x/leverage/keeper/keeper.go index fac6330095..e91fd518ce 100644 --- a/x/leverage/keeper/keeper.go +++ b/x/leverage/keeper/keeper.go @@ -77,7 +77,7 @@ func (k Keeper) ModuleBalance(ctx sdk.Context, denom string) sdk.Int { // exchange for uTokens. If asset type is invalid or account balance is // insufficient, we return an error. func (k Keeper) Supply(ctx sdk.Context, supplierAddr sdk.AccAddress, loan sdk.Coin) error { - if err := k.validateSupply(ctx, loan); err != nil { + if err := k.ValidateSupply(ctx, loan); err != nil { return err } @@ -113,13 +113,13 @@ func (k Keeper) Supply(ctx sdk.Context, supplierAddr sdk.AccAddress, loan sdk.Co // Withdraw attempts to deposit uTokens into the leverage module in exchange // for the original tokens supplied. Accepts a uToken amount to exchange for base tokens. // If the uToken denom is invalid or account or module balance insufficient, returns error. -func (k Keeper) Withdraw(ctx sdk.Context, supplierAddr sdk.AccAddress, coin sdk.Coin) error { - if !k.IsAcceptedUToken(ctx, coin.Denom) { - return sdkerrors.Wrap(types.ErrInvalidAsset, coin.String()) +func (k Keeper) Withdraw(ctx sdk.Context, supplierAddr sdk.AccAddress, uToken sdk.Coin) error { + if err := k.ValidateAcceptedUToken(ctx, uToken); err != nil { + return err } // calculate base asset amount to withdraw - token, err := k.ExchangeUToken(ctx, coin) + token, err := k.ExchangeUToken(ctx, uToken) if err != nil { return err } @@ -132,11 +132,11 @@ func (k Keeper) Withdraw(ctx sdk.Context, supplierAddr sdk.AccAddress, coin sdk. } // Withdraw will first attempt to use any uTokens in the supplier's wallet - amountFromWallet := sdk.MinInt(k.bankKeeper.SpendableCoins(ctx, supplierAddr).AmountOf(coin.Denom), coin.Amount) + uTokensFromWallet := sdk.MinInt(k.bankKeeper.SpendableCoins(ctx, supplierAddr).AmountOf(uToken.Denom), uToken.Amount) // Any additional uTokens must come from the supplier's collateral - amountFromCollateral := coin.Amount.Sub(amountFromWallet) + uTokensFromCollateral := uToken.Amount.Sub(uTokensFromWallet) - if amountFromCollateral.IsPositive() { + if uTokensFromCollateral.IsPositive() { // Calculate current borrowed value borrowed := k.GetBorrowerBorrows(ctx, supplierAddr) borrowedValue, err := k.TotalTokenValue(ctx, borrowed) @@ -146,12 +146,17 @@ func (k Keeper) Withdraw(ctx sdk.Context, supplierAddr sdk.AccAddress, coin sdk. // Check for sufficient collateral collateral := k.GetBorrowerCollateral(ctx, supplierAddr) - if collateral.AmountOf(coin.Denom).LT(amountFromCollateral) { - return sdkerrors.Wrap(types.ErrInsufficientBalance, coin.String()) + if collateral.AmountOf(uToken.Denom).LT(uTokensFromCollateral) { + info := fmt.Sprintf( + "\n spendable: %s\n addr: %s\n\n", + k.bankKeeper.SpendableCoins(ctx, supplierAddr).String(), + supplierAddr.String(), + ) + return sdkerrors.Wrap(types.ErrInsufficientBalance, uToken.String()+info) } // Calculate what borrow limit will be AFTER this withdrawal - collateralToWithdraw := sdk.NewCoins(sdk.NewCoin(coin.Denom, amountFromCollateral)) + collateralToWithdraw := sdk.NewCoins(sdk.NewCoin(uToken.Denom, uTokensFromCollateral)) newBorrowLimit, err := k.CalculateBorrowLimit(ctx, collateral.Sub(collateralToWithdraw)) if err != nil { return err @@ -164,14 +169,14 @@ func (k Keeper) Withdraw(ctx sdk.Context, supplierAddr sdk.AccAddress, coin sdk. } // reduce the supplier's collateral by amountFromCollateral - newCollateral := sdk.NewCoin(coin.Denom, collateral.AmountOf(coin.Denom).Sub(amountFromCollateral)) + newCollateral := sdk.NewCoin(uToken.Denom, collateral.AmountOf(uToken.Denom).Sub(uTokensFromCollateral)) if err = k.setCollateralAmount(ctx, supplierAddr, newCollateral); err != nil { return err } } - // transfer amountFromWallet uTokens to the module account - uTokens := sdk.NewCoins(sdk.NewCoin(coin.Denom, amountFromWallet)) + // transfer uTokensFromWallet to the module account + uTokens := sdk.NewCoins(sdk.NewCoin(uToken.Denom, uTokensFromWallet)) if err = k.bankKeeper.SendCoinsFromAccountToModule(ctx, supplierAddr, types.ModuleName, uTokens); err != nil { return err } @@ -183,10 +188,10 @@ func (k Keeper) Withdraw(ctx sdk.Context, supplierAddr sdk.AccAddress, coin sdk. } // burn the uTokens and set the new total uToken supply - if err = k.bankKeeper.BurnCoins(ctx, types.ModuleName, sdk.NewCoins(coin)); err != nil { + if err = k.bankKeeper.BurnCoins(ctx, types.ModuleName, sdk.NewCoins(uToken)); err != nil { return err } - if err = k.setUTokenSupply(ctx, k.GetUTokenSupply(ctx, coin.Denom).Sub(coin)); err != nil { + if err = k.setUTokenSupply(ctx, k.GetUTokenSupply(ctx, uToken.Denom).Sub(uToken)); err != nil { return err } @@ -197,7 +202,7 @@ func (k Keeper) Withdraw(ctx sdk.Context, supplierAddr sdk.AccAddress, coin sdk. // collateral uTokens. If asset type is invalid, collateral is insufficient, // or module balance is insufficient, we return an error. func (k Keeper) Borrow(ctx sdk.Context, borrowerAddr sdk.AccAddress, borrow sdk.Coin) error { - if err := k.validateBorrow(ctx, borrow); err != nil { + if err := k.ValidateBorrow(ctx, borrow); err != nil { return err } @@ -249,35 +254,21 @@ func (k Keeper) Borrow(ctx sdk.Context, borrowerAddr sdk.AccAddress, borrow sdk. // necessary amount is transferred. Because amount repaid may be less than the repayment attempted, // Repay returns the actual amount repaid. func (k Keeper) Repay(ctx sdk.Context, borrowerAddr sdk.AccAddress, payment sdk.Coin) (sdk.Int, error) { - if !payment.IsValid() { - return sdk.ZeroInt(), types.ErrInvalidAsset.Wrap(payment.String()) + if err := payment.Validate(); err != nil { + return sdk.ZeroInt(), err } - // Determine amount of selected denom currently owed + // determine amount of selected denom currently owed owed := k.GetBorrow(ctx, borrowerAddr, payment.Denom) if owed.IsZero() { - // Borrower has no open borrows in the denom presented as payment - return sdk.ZeroInt(), types.ErrInvalidRepayment.Wrap( - "Borrower doesn't have active position in " + payment.Denom) + return sdk.ZeroInt(), types.ErrInvalidRepayment.Wrapf("No %s borrowed ", payment.Denom) } - // Prevent overpaying + // prevent overpaying payment.Amount = sdk.MinInt(owed.Amount, payment.Amount) - if err := payment.Validate(); err != nil { - return sdk.ZeroInt(), types.ErrInvalidRepayment.Wrap(err.Error()) - } // send payment to leverage module account - if err := k.bankKeeper.SendCoinsFromAccountToModule( - ctx, borrowerAddr, - types.ModuleName, - sdk.NewCoins(payment), - ); err != nil { - return sdk.ZeroInt(), err - } - - owed.Amount = owed.Amount.Sub(payment.Amount) - if err := k.setBorrow(ctx, borrowerAddr, owed); err != nil { + if err := k.repayBorrow(ctx, borrowerAddr, borrowerAddr, payment); err != nil { return sdk.ZeroInt(), err } return payment.Amount, nil @@ -285,7 +276,7 @@ func (k Keeper) Repay(ctx sdk.Context, borrowerAddr sdk.AccAddress, payment sdk. // Collateralize enables selected uTokens for use as collateral by a single borrower. func (k Keeper) Collateralize(ctx sdk.Context, borrowerAddr sdk.AccAddress, coin sdk.Coin) error { - if err := k.validateCollateralAsset(ctx, coin); err != nil { + if err := k.ValidateCollateralize(ctx, coin); err != nil { return err } @@ -346,30 +337,40 @@ func (k Keeper) Decollateralize(ctx sdk.Context, borrowerAddr sdk.AccAddress, co return nil } -// Liquidate attempts to repay one of an eligible borrower's borrows (in part or in full) in exchange -// for a the base token equivalent of selected denomination of the borrower's uToken collateral. If the -// borrower is not over their liquidation limit, or the repayment or reward denominations are invalid, -// an error is returned. If the attempted repayment is greater than the amount owed or the maximum that -// can be repaid due to parameters or available balances, then a partial liquidation, equal to the maximum -// valid amount, is performed. Because partial liquidation is possible and exchange rates vary, Liquidate -// returns the actual amount of tokens repaid, uTokens consumed, and base tokens rewarded. +// Liquidate attempts to repay one of an eligible borrower's borrows (in part or in full) in exchange for +// some of the borrower's uToken collateral or associated base tokens. If the borrower is not over their +// liquidation limit, or the repayment or reward denominations are invalid, an error is returned. If the +// attempted repayment is greater than the amount owed or the maximum that can be repaid due to parameters +// or available balances, then a partial liquidation, equal to the maximum valid amount, is performed. +// Because partial liquidation is possible and exchange rates vary, Liquidate returns the actual amount of +// tokens repaid, collateral liquidated, and base tokens or uTokens rewarded. func (k Keeper) Liquidate( ctx sdk.Context, liquidatorAddr, borrowerAddr sdk.AccAddress, maxRepay sdk.Coin, rewardDenom string, -) (baseRepay sdk.Coin, collateralReward sdk.Coin, baseReward sdk.Coin, err error) { - if err := k.validateAcceptedAsset(ctx, maxRepay); err != nil { +) (repaid sdk.Coin, liquidated sdk.Coin, reward sdk.Coin, err error) { + if err := k.ValidateAcceptedAsset(ctx, maxRepay); err != nil { return sdk.Coin{}, sdk.Coin{}, sdk.Coin{}, err } - if err := k.validateAcceptedDenom(ctx, rewardDenom); err != nil { + + // detect if the user selected a base token reward instead of a uToken + directLiquidation := !types.HasUTokenPrefix(rewardDenom) + if !directLiquidation { + // convert rewardDenom to base token + rewardDenom = types.ToTokenDenom(rewardDenom) + } + // ensure that base reward is a registered token + if err := k.ValidateAcceptedDenom(ctx, rewardDenom); err != nil { return sdk.Coin{}, sdk.Coin{}, sdk.Coin{}, err } - // calculate borrowed Token repay, uToken collateral, and Token reward amounts allowed by liquidation rules and available balances - baseRepay, collateralReward, baseReward, err = k.getLiquidationAmounts( + // calculate borrowed Token repay, uToken liquidate, and Token reward amounts allowed by liquidation rules + // and available balances + baseRepay, collateralLiquidate, baseReward, err := k.getLiquidationAmounts( ctx, liquidatorAddr, borrowerAddr, maxRepay, rewardDenom, + directLiquidation, ) if err != nil { return sdk.Coin{}, sdk.Coin{}, sdk.Coin{}, err @@ -380,49 +381,36 @@ func (k Keeper) Liquidate( return sdk.Coin{}, sdk.Coin{}, sdk.Coin{}, types.ErrLiquidationInvalid } - // send repayment from liquidator to leverage module account - err = k.bankKeeper.SendCoinsFromAccountToModule(ctx, liquidatorAddr, types.ModuleName, sdk.NewCoins(baseRepay)) - if err != nil { - return sdk.Coin{}, sdk.Coin{}, sdk.Coin{}, err - } - // update borrower's remaining borrowed amount - newBorrow := k.GetBorrow(ctx, borrowerAddr, baseRepay.Denom).Amount.Sub(baseRepay.Amount) - if err = k.setBorrow(ctx, borrowerAddr, sdk.NewCoin(baseRepay.Denom, newBorrow)); err != nil { + // repay some of the borrower's debt using the liquidator's balance + if err = k.repayBorrow(ctx, liquidatorAddr, borrowerAddr, baseRepay); err != nil { return sdk.Coin{}, sdk.Coin{}, sdk.Coin{}, err } - // reduce borrower's collateral by collateral reward amount - oldCollateral := k.GetCollateralAmount(ctx, borrowerAddr, collateralReward.Denom) - newCollateral := sdk.NewCoin(collateralReward.Denom, oldCollateral.Amount.Sub(collateralReward.Amount)) - if err = k.setCollateralAmount(ctx, borrowerAddr, newCollateral); err != nil { - return sdk.Coin{}, sdk.Coin{}, sdk.Coin{}, err - } - // burn the collateral reward uTokens and set the new total uToken supply - if err = k.bankKeeper.BurnCoins(ctx, types.ModuleName, sdk.NewCoins(collateralReward)); err != nil { - return sdk.Coin{}, sdk.Coin{}, sdk.Coin{}, err - } - if err = k.setUTokenSupply(ctx, k.GetUTokenSupply(ctx, collateralReward.Denom).Sub(collateralReward)); err != nil { - return sdk.Coin{}, sdk.Coin{}, sdk.Coin{}, err + if directLiquidation { + // burn the uToken reward from borrower's collateral + if err = k.burnCollateral(ctx, borrowerAddr, collateralLiquidate); err != nil { + return sdk.Coin{}, sdk.Coin{}, sdk.Coin{}, err + } + // send base rewards from module to liquidator's account + err = k.bankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, liquidatorAddr, sdk.NewCoins(baseReward)) + if err != nil { + return sdk.Coin{}, sdk.Coin{}, sdk.Coin{}, err + } + } else { + // send uToken rewards from borrower collateral to liquidator's account + if err = k.removeCollateral(ctx, borrowerAddr, liquidatorAddr, collateralLiquidate); err != nil { + return sdk.Coin{}, sdk.Coin{}, sdk.Coin{}, err + } } - // send base rewards from module to liquidator's account - err = k.bankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, liquidatorAddr, sdk.NewCoins(baseReward)) - if err != nil { + // if borrower's collateral has reached zero, mark any remaining borrows as bad debt + if err := k.checkBadDebt(ctx, borrowerAddr); err != nil { return sdk.Coin{}, sdk.Coin{}, sdk.Coin{}, err } - // get remaining collateral, ignoring blacklisted - remainingCollateral := k.filterAcceptedCoins(ctx, k.GetBorrowerCollateral(ctx, borrowerAddr)) - - // detect bad debt if collateral is completely exhausted - if remainingCollateral.IsZero() { - for _, coin := range k.GetBorrowerBorrows(ctx, borrowerAddr) { - // set a bad debt flag for each borrowed denom - if err := k.setBadDebtAddress(ctx, borrowerAddr, coin.Denom, true); err != nil { - return sdk.Coin{}, sdk.Coin{}, sdk.Coin{}, err - } - } + // the last return value is the liquidator's selected reward + if directLiquidation { + return baseRepay, collateralLiquidate, baseReward, nil } - - return baseRepay, collateralReward, baseReward, nil + return baseRepay, collateralLiquidate, collateralLiquidate, nil } diff --git a/x/leverage/keeper/keeper_test.go b/x/leverage/keeper/keeper_test.go index 56e4976ed5..d8392ecce7 100644 --- a/x/leverage/keeper/keeper_test.go +++ b/x/leverage/keeper/keeper_test.go @@ -182,7 +182,7 @@ func (s *IntegrationTestSuite) TestSupply_Valid() { s.Require().NoError(err) // verify the total supply of the minted uToken - uTokenDenom := types.UTokenFromTokenDenom(umeeapp.BondDenom) + uTokenDenom := types.ToUTokenDenom(umeeapp.BondDenom) supply := s.app.LeverageKeeper.GetUTokenSupply(ctx, uTokenDenom) expected := sdk.NewInt64Coin(uTokenDenom, 1000000000) // 1k u/umee s.Require().Equal(expected, supply) @@ -211,7 +211,7 @@ func (s *IntegrationTestSuite) TestWithdraw_Valid() { s.Require().NoError(err) // verify the total supply of the minted uToken - uTokenDenom := types.UTokenFromTokenDenom(umeeapp.BondDenom) + uTokenDenom := types.ToUTokenDenom(umeeapp.BondDenom) supply := s.app.LeverageKeeper.GetUTokenSupply(ctx, uTokenDenom) expected := sdk.NewInt64Coin(uTokenDenom, 1000000000) // 1k u/umee s.Require().Equal(expected, supply) @@ -419,7 +419,7 @@ func (s *IntegrationTestSuite) TestBorrow_BorrowLimit() { // determine an amount of umee to borrow, such that the user will be at about 90% of their borrow limit token, _ := s.app.LeverageKeeper.GetTokenSettings(s.ctx, umeeapp.BondDenom) - uDenom := s.app.LeverageKeeper.FromTokenToUTokenDenom(s.ctx, umeeapp.BondDenom) + uDenom := types.ToUTokenDenom(umeeapp.BondDenom) collateral := s.app.LeverageKeeper.GetCollateralAmount(s.ctx, addr, uDenom) amountToBorrow := token.CollateralWeight.Mul(sdk.MustNewDecFromStr("0.9")).MulInt(collateral.Amount).TruncateInt() @@ -872,7 +872,7 @@ func (s *IntegrationTestSuite) TestCollateralAmountInvariant() { _, broken := keeper.CollateralAmountInvariant(s.app.LeverageKeeper)(s.ctx) s.Require().False(broken) - uTokenDenom := types.UTokenFromTokenDenom(umeeapp.BondDenom) + uTokenDenom := types.ToUTokenDenom(umeeapp.BondDenom) // withdraw the supplyed umee in the initBorrowScenario err := s.app.LeverageKeeper.Withdraw(s.ctx, addr, sdk.NewInt64Coin(uTokenDenom, 1000000000)) @@ -915,7 +915,7 @@ func (s *IntegrationTestSuite) TestWithdraw_InsufficientCollateral() { _ = s.setupAccount(umeeapp.BondDenom, 1000000, 1000000, 0, true) // verify collateral amount and total supply of minted uTokens - uTokenDenom := types.UTokenFromTokenDenom(umeeapp.BondDenom) + uTokenDenom := types.ToUTokenDenom(umeeapp.BondDenom) collateral := s.app.LeverageKeeper.GetCollateralAmount(s.ctx, supplierAddr, uTokenDenom) s.Require().Equal(sdk.NewInt64Coin(uTokenDenom, 1000000), collateral) // 1 u/umee supply := s.app.LeverageKeeper.GetUTokenSupply(s.ctx, uTokenDenom) @@ -929,7 +929,7 @@ func (s *IntegrationTestSuite) TestWithdraw_InsufficientCollateral() { func (s *IntegrationTestSuite) TestTotalCollateral() { // Test zero collateral - uDenom := types.UTokenFromTokenDenom(umeeDenom) + uDenom := types.ToUTokenDenom(umeeDenom) collateral := s.app.LeverageKeeper.GetTotalCollateral(s.ctx, uDenom) s.Require().Equal(sdk.ZeroInt(), collateral) diff --git a/x/leverage/keeper/liquidate.go b/x/leverage/keeper/liquidate.go index 26813f5ffc..bfe06ad201 100644 --- a/x/leverage/keeper/liquidate.go +++ b/x/leverage/keeper/liquidate.go @@ -8,16 +8,17 @@ import ( // getLiquidationAmounts takes a repayment and reward denom proposed by a liquidator and calculates // the actual repayment amount a target address is eligible for, and the corresponding collateral -// to burn and rewards to return to the liquidator. +// to liquidate and equivalent base rewards to send to the liquidator. func (k Keeper) getLiquidationAmounts( ctx sdk.Context, - liquidatorAddr sdk.AccAddress, + liquidatorAddr, targetAddr sdk.AccAddress, maxRepay sdk.Coin, rewardDenom string, -) (tokenRepay sdk.Coin, collateralBurn sdk.Coin, tokenReward sdk.Coin, err error) { + directLiquidation bool, +) (tokenRepay sdk.Coin, collateralLiquidate sdk.Coin, tokenReward sdk.Coin, err error) { repayDenom := maxRepay.Denom - collateralDenom := k.FromTokenToUTokenDenom(ctx, rewardDenom) + collateralDenom := types.ToUTokenDenom(rewardDenom) // get relevant liquidator, borrower, and module balances borrowerCollateral := k.GetBorrowerCollateral(ctx, targetAddr) @@ -67,6 +68,14 @@ func (k Keeper) getLiquidationAmounts( // get collateral uToken exchange rate exchangeRate := k.DeriveExchangeRate(ctx, rewardDenom) + // reduce liquidation incentive if directly receiving base assets. Since this fee also reduces the + // amount of collateral that must be burned, it is applied before any other computations as if the + // token itself had a smaller liquidation incentive + liqudationIncentive := ts.LiquidationIncentive + if directLiquidation { + liqudationIncentive = liqudationIncentive.Mul(sdk.OneDec().Sub(params.DirectLiquidationFee)) + } + // compute final liquidation amounts repay, burn, reward := ComputeLiquidation( sdk.MinInt(sdk.MinInt(availableRepay, maxRepay.Amount), totalBorrowed.AmountOf(repayDenom)), @@ -75,7 +84,7 @@ func (k Keeper) getLiquidationAmounts( repayTokenPrice, rewardTokenPrice, exchangeRate, - ts.LiquidationIncentive, + liqudationIncentive, closeFactor, borrowedValue, ) diff --git a/x/leverage/keeper/reserves.go b/x/leverage/keeper/reserves.go index 1d2443e8b3..e419c3bd33 100644 --- a/x/leverage/keeper/reserves.go +++ b/x/leverage/keeper/reserves.go @@ -46,9 +46,28 @@ func (k Keeper) setReserveAmount(ctx sdk.Context, coin sdk.Coin) error { return nil } -// RepayBadDebt uses reserves to repay borrower's debts of a given denom. +// checkBadDebt detects if a borrower has zero non-blacklisted collateral, +// and marks any remaining borrowed tokens as bad debt. +func (k Keeper) checkBadDebt(ctx sdk.Context, borrowerAddr sdk.AccAddress) error { + // get remaining collateral, ignoring blacklisted + remainingCollateral := k.filterAcceptedCoins(ctx, k.GetBorrowerCollateral(ctx, borrowerAddr)) + + // detect bad debt if collateral is completely exhausted + if remainingCollateral.IsZero() { + for _, coin := range k.GetBorrowerBorrows(ctx, borrowerAddr) { + // set a bad debt flag for each borrowed denom + if err := k.setBadDebtAddress(ctx, borrowerAddr, coin.Denom, true); err != nil { + return err + } + } + } + + return nil +} + +// repayBadDebt uses reserves to repay borrower's debts of a given denom. // It returns a boolean representing whether full repayment was achieved. -func (k Keeper) RepayBadDebt(ctx sdk.Context, borrowerAddr sdk.AccAddress, denom string) (bool, error) { +func (k Keeper) repayBadDebt(ctx sdk.Context, borrowerAddr sdk.AccAddress, denom string) (bool, error) { borrowed := k.GetBorrow(ctx, borrowerAddr, denom) reserved := k.GetReserveAmount(ctx, denom) diff --git a/x/leverage/keeper/store.go b/x/leverage/keeper/store.go index 95a3f5dcfc..e609d18968 100644 --- a/x/leverage/keeper/store.go +++ b/x/leverage/keeper/store.go @@ -134,8 +134,11 @@ func (k Keeper) GetUTokenSupply(ctx sdk.Context, denom string) sdk.Coin { // setUTokenSupply sets the total supply of a uToken. func (k Keeper) setUTokenSupply(ctx sdk.Context, coin sdk.Coin) error { - if !coin.IsValid() || !k.IsAcceptedUToken(ctx, coin.Denom) { - return sdkerrors.Wrap(types.ErrInvalidAsset, coin.String()) + if err := coin.Validate(); err != nil { + return err + } + if !types.HasUTokenPrefix(coin.Denom) { + return types.ErrNotUToken.Wrap(coin.Denom) } key := types.CreateUTokenSupplyKey(coin.Denom) diff --git a/x/leverage/keeper/loaned.go b/x/leverage/keeper/supply.go similarity index 80% rename from x/leverage/keeper/loaned.go rename to x/leverage/keeper/supply.go index 56cf5ed938..9f2184fc54 100644 --- a/x/leverage/keeper/loaned.go +++ b/x/leverage/keeper/supply.go @@ -2,7 +2,6 @@ package keeper import ( sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/umee-network/umee/v2/x/leverage/types" ) @@ -10,12 +9,12 @@ import ( // GetSupplied returns an sdk.Coin representing how much of a given denom a // user has supplied, including interest accrued. func (k Keeper) GetSupplied(ctx sdk.Context, supplierAddr sdk.AccAddress, denom string) (sdk.Coin, error) { - if !k.IsAcceptedToken(ctx, denom) { - return sdk.Coin{}, sdkerrors.Wrap(types.ErrInvalidAsset, denom) + if err := k.ValidateAcceptedDenom(ctx, denom); err != nil { + return sdk.Coin{}, err } // sum wallet-held and collateral-enabled uTokens in the associated uToken denom - uDenom := k.FromTokenToUTokenDenom(ctx, denom) + uDenom := types.ToUTokenDenom(denom) balance := k.bankKeeper.GetBalance(ctx, supplierAddr, uDenom) collateral := k.GetCollateralAmount(ctx, supplierAddr, uDenom) @@ -33,7 +32,7 @@ func (k Keeper) GetAllSupplied(ctx sdk.Context, supplierAddr sdk.AccAddress) (sd uTokens := sdk.Coins{} balance := k.bankKeeper.GetAllBalances(ctx, supplierAddr) for _, coin := range balance { - if k.IsAcceptedUToken(ctx, coin.Denom) { + if k.ValidateAcceptedUTokenDenom(ctx, coin.Denom) == nil { uTokens = uTokens.Add(coin) } } @@ -45,11 +44,11 @@ func (k Keeper) GetAllSupplied(ctx sdk.Context, supplierAddr sdk.AccAddress) (sd // GetTotalSupply returns the total supplied by all suppliers in a given denom, // including any interest accrued. func (k Keeper) GetTotalSupply(ctx sdk.Context, denom string) (sdk.Coin, error) { - if !k.IsAcceptedToken(ctx, denom) { - return sdk.Coin{}, sdkerrors.Wrap(types.ErrInvalidAsset, denom) + if err := k.ValidateAcceptedDenom(ctx, denom); err != nil { + return sdk.Coin{}, err } // convert associated uToken's total supply to base tokens - uTokenDenom := k.FromTokenToUTokenDenom(ctx, denom) + uTokenDenom := types.ToUTokenDenom(denom) return k.ExchangeUToken(ctx, k.GetUTokenSupply(ctx, uTokenDenom)) } diff --git a/x/leverage/keeper/token.go b/x/leverage/keeper/token.go index 688516fd73..411c6a6837 100644 --- a/x/leverage/keeper/token.go +++ b/x/leverage/keeper/token.go @@ -2,7 +2,6 @@ package keeper import ( "fmt" - "strings" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" @@ -10,38 +9,6 @@ import ( "github.com/umee-network/umee/v2/x/leverage/types" ) -// FromUTokenToTokenDenom strips the uToken prefix ("u/") from an input denom. -// An empty string is returned if the prefix is not present. -func (k Keeper) FromUTokenToTokenDenom(ctx sdk.Context, uTokenDenom string) string { - if strings.HasPrefix(uTokenDenom, types.UTokenPrefix) { - return strings.TrimPrefix(uTokenDenom, types.UTokenPrefix) - } - return "" -} - -// FromTokenToUTokenDenom adds the uToken prefix ("u/") to an input denom. -// An empty string is returned if the input token denom already has the prefix. -func (k Keeper) FromTokenToUTokenDenom(ctx sdk.Context, tokenDenom string) string { - if strings.HasPrefix(tokenDenom, types.UTokenPrefix) { - return "" - } - return types.UTokenPrefix + tokenDenom -} - -// IsAcceptedToken returns true if a given (non-UToken) token denom is an -// existing, non-blacklisted asset type. -func (k Keeper) IsAcceptedToken(ctx sdk.Context, tokenDenom string) bool { - t, err := k.GetTokenSettings(ctx, tokenDenom) - return err == nil && !t.Blacklist -} - -// IsAcceptedUToken returns true if a given uToken denom is associated with -// an accepted base asset type. -func (k Keeper) IsAcceptedUToken(ctx sdk.Context, uTokenDenom string) bool { - tokenDenom := k.FromUTokenToTokenDenom(ctx, uTokenDenom) - return k.IsAcceptedToken(ctx, tokenDenom) -} - // SetTokenSettings stores a Token into the x/leverage module's KVStore. func (k Keeper) SetTokenSettings(ctx sdk.Context, token types.Token) error { if err := token.Validate(); err != nil { diff --git a/x/leverage/keeper/validate.go b/x/leverage/keeper/validate.go index ff5c4b3d13..fa6ab6bf7d 100644 --- a/x/leverage/keeper/validate.go +++ b/x/leverage/keeper/validate.go @@ -6,9 +6,9 @@ import ( "github.com/umee-network/umee/v2/x/leverage/types" ) -// validateAcceptedDenom validates an sdk.Coin and ensures it is a registered Token +// ValidateAcceptedDenom validates an sdk.Coin and ensures it is a registered Token // with Blacklisted == false -func (k Keeper) validateAcceptedDenom(ctx sdk.Context, denom string) error { +func (k Keeper) ValidateAcceptedDenom(ctx sdk.Context, denom string) error { token, err := k.GetTokenSettings(ctx, denom) if err != nil { return err @@ -16,17 +16,35 @@ func (k Keeper) validateAcceptedDenom(ctx sdk.Context, denom string) error { return token.AssertNotBlacklisted() } -// validateAcceptedAsset validates an sdk.Coin and ensures it is a registered Token +// ValidateAcceptedUTokenDenom validates an sdk.Coin and ensures it is a uToken +// associated with a registered Token with Blacklisted == false +func (k Keeper) ValidateAcceptedUTokenDenom(ctx sdk.Context, udenom string) error { + if !types.HasUTokenPrefix(udenom) { + return types.ErrNotUToken.Wrap(udenom) + } + return k.ValidateAcceptedDenom(ctx, types.ToTokenDenom(udenom)) +} + +// ValidateAcceptedAsset validates an sdk.Coin and ensures it is a registered Token // with Blacklisted == false -func (k Keeper) validateAcceptedAsset(ctx sdk.Context, coin sdk.Coin) error { +func (k Keeper) ValidateAcceptedAsset(ctx sdk.Context, coin sdk.Coin) error { if err := coin.Validate(); err != nil { return err } - return k.validateAcceptedDenom(ctx, coin.Denom) + return k.ValidateAcceptedDenom(ctx, coin.Denom) } -// validateSupply validates an sdk.Coin and ensures its Denom is a Token with EnableMsgSupply -func (k Keeper) validateSupply(ctx sdk.Context, loan sdk.Coin) error { +// ValidateAcceptedUToken validates an sdk.Coin and ensures it is a uToken +// associated with a registered Token with Blacklisted == false +func (k Keeper) ValidateAcceptedUToken(ctx sdk.Context, coin sdk.Coin) error { + if err := coin.Validate(); err != nil { + return err + } + return k.ValidateAcceptedUTokenDenom(ctx, coin.Denom) +} + +// ValidateSupply validates an sdk.Coin and ensures its Denom is a Token with EnableMsgSupply +func (k Keeper) ValidateSupply(ctx sdk.Context, loan sdk.Coin) error { if err := loan.Validate(); err != nil { return err } @@ -37,8 +55,8 @@ func (k Keeper) validateSupply(ctx sdk.Context, loan sdk.Coin) error { return token.AssertSupplyEnabled() } -// validateBorrow validates an sdk.Coin and ensures its Denom is a Token with EnableMsgBorrow -func (k Keeper) validateBorrow(ctx sdk.Context, borrow sdk.Coin) error { +// ValidateBorrow validates an sdk.Coin and ensures its Denom is a Token with EnableMsgBorrow +func (k Keeper) ValidateBorrow(ctx sdk.Context, borrow sdk.Coin) error { if err := borrow.Validate(); err != nil { return err } @@ -49,14 +67,16 @@ func (k Keeper) validateBorrow(ctx sdk.Context, borrow sdk.Coin) error { return token.AssertBorrowEnabled() } -// validateCollateralAsset validates an sdk.Coin and ensures its Denom is a Token with EnableMsgSupply -// and CollateralWeight > 0 -func (k Keeper) validateCollateralAsset(ctx sdk.Context, collateral sdk.Coin) error { +// ValidateCollateralize validates an sdk.Coin and ensures it is a uToken of an accepted +// Token with EnableMsgSupply and CollateralWeight > 0 +func (k Keeper) ValidateCollateralize(ctx sdk.Context, collateral sdk.Coin) error { if err := collateral.Validate(); err != nil { return err } - tokenDenom := k.FromUTokenToTokenDenom(ctx, collateral.Denom) - token, err := k.GetTokenSettings(ctx, tokenDenom) + if !types.HasUTokenPrefix(collateral.Denom) { + return types.ErrNotUToken.Wrap(collateral.Denom) + } + token, err := k.GetTokenSettings(ctx, types.ToTokenDenom(collateral.Denom)) if err != nil { return err } diff --git a/x/leverage/simulation/genesis.go b/x/leverage/simulation/genesis.go index 8a00b54143..5f6c5ad5c5 100644 --- a/x/leverage/simulation/genesis.go +++ b/x/leverage/simulation/genesis.go @@ -16,6 +16,7 @@ const ( minimumCloseFactorKey = "minimum_close_factor" oracleRewardFactorKey = "oracle_reward_factor" smallLiquidationSizeKey = "small_liquidation_size" + directLiquidationFeeKey = "direct_liquidation_fee" ) // GenCompleteLiquidationThreshold produces a randomized CompleteLiquidationThreshold in the range of [0.050, 0.100] @@ -38,6 +39,11 @@ func GenSmallLiquidationSize(r *rand.Rand) sdk.Dec { return sdk.NewDec(int64(r.Intn(1000))) } +// GenDirectLiquidationFee produces a randomized DirectLiquidationFee in the range of [0, 1000] +func GenDirectLiquidationFee(r *rand.Rand) sdk.Dec { + return sdk.NewDec(int64(r.Intn(1000))) +} + // RandomizedGenState generates a random GenesisState for oracle func RandomizedGenState(simState *module.SimulationState) { var completeLiquidationThreshold sdk.Dec @@ -64,12 +70,19 @@ func RandomizedGenState(simState *module.SimulationState) { func(r *rand.Rand) { smallLiquidationSize = GenSmallLiquidationSize(r) }, ) + var directLiquidationFee sdk.Dec + simState.AppParams.GetOrGenerate( + simState.Cdc, directLiquidationFeeKey, &directLiquidationFee, simState.Rand, + func(r *rand.Rand) { smallLiquidationSize = GenDirectLiquidationFee(r) }, + ) + leverageGenesis := types.NewGenesisState( types.Params{ CompleteLiquidationThreshold: completeLiquidationThreshold, MinimumCloseFactor: minimumCloseFactor, OracleRewardFactor: oracleRewardFactor, SmallLiquidationSize: smallLiquidationSize, + DirectLiquidationFee: directLiquidationFee, }, []types.Token{}, []types.AdjustedBorrow{}, diff --git a/x/leverage/simulation/operations.go b/x/leverage/simulation/operations.go index e71bc180bc..5b4497420f 100644 --- a/x/leverage/simulation/operations.go +++ b/x/leverage/simulation/operations.go @@ -1,6 +1,7 @@ package simulation import ( + "fmt" "math/rand" "github.com/cosmos/cosmos-sdk/baseapp" @@ -227,7 +228,7 @@ func SimulateMsgCollateralize( return simtypes.NoOpMsg(types.ModuleName, types.EventTypeCollateralize, "skip all transfers"), nil, nil } - uDenom := lk.FromTokenToUTokenDenom(ctx, token.Denom) + uDenom := types.ToUTokenDenom(token.Denom) coin := sdk.NewCoin(uDenom, token.Amount) msg := types.NewMsgCollateralize(from.Address, coin) @@ -265,7 +266,7 @@ func SimulateMsgDecollateralize( return simtypes.NoOpMsg(types.ModuleName, types.EventTypeDecollateralize, "skip all transfers"), nil, nil } - uDenom := lk.FromTokenToUTokenDenom(ctx, token.Denom) + uDenom := types.ToUTokenDenom(token.Denom) coin := sdk.NewCoin(uDenom, token.Amount) msg := types.NewMsgDecollateralize(from.Address, coin) @@ -407,13 +408,19 @@ func randomWithdrawFields( uTokens := getSpendableUTokens(ctx, acc.Address, bk, lk) uTokens = uTokens.Add(lk.GetBorrowerCollateral(ctx, acc.Address)...) - uTokens = simtypes.RandSubsetCoins(r, uTokens) - if uTokens.Empty() { return acc, sdk.Coin{}, true } - return acc, randomCoin(r, uTokens), false + info := fmt.Sprintf( + "\n spendable: %s\n addr: %s\n\n", + bk.SpendableCoins(ctx, acc.Address).String(), + acc.Address.String(), + ) + fmt.Println(info) + + subset := randomCoin(r, simtypes.RandSubsetCoins(r, uTokens)) + return acc, subset, false } // getSpendableUTokens returns all uTokens from an account's spendable coins. @@ -423,7 +430,7 @@ func getSpendableUTokens( ) sdk.Coins { uTokens := sdk.NewCoins() for _, coin := range bk.SpendableCoins(ctx, addr) { - if lk.IsAcceptedUToken(ctx, coin.Denom) { + if lk.ValidateAcceptedUTokenDenom(ctx, coin.Denom) == nil { uTokens = uTokens.Add(coin) } } @@ -476,7 +483,7 @@ func randomLiquidateFields( return liquidator, borrower, sdk.Coin{}, "", true } - rewardDenom = lk.FromUTokenToTokenDenom(ctx, randomCoin(r, collateral).Denom) + rewardDenom = types.ToTokenDenom(randomCoin(r, collateral).Denom) return liquidator, borrower, randomCoin(r, borrowed), rewardDenom, false } diff --git a/x/leverage/simulation/operations_test.go b/x/leverage/simulation/operations_test.go index 092155f2c4..02d31f4c6b 100644 --- a/x/leverage/simulation/operations_test.go +++ b/x/leverage/simulation/operations_test.go @@ -50,53 +50,10 @@ func (s *SimTestSuite) SetupTest() { MaxSupplyUtilization: sdk.MustNewDecFromStr("0.9"), MinCollateralLiquidity: sdk.MustNewDecFromStr("0"), } - atomIBCToken := types.Token{ - BaseDenom: "ibc/CDC4587874B85BEA4FCEC3CEA5A1195139799A1FEE711A07D972537E18FDA39D", - ReserveFactor: sdk.MustNewDecFromStr("0.25"), - CollateralWeight: sdk.MustNewDecFromStr("0.8"), - LiquidationThreshold: sdk.MustNewDecFromStr("0.8"), - BaseBorrowRate: sdk.MustNewDecFromStr("0.05"), - KinkBorrowRate: sdk.MustNewDecFromStr("0.3"), - MaxBorrowRate: sdk.MustNewDecFromStr("0.9"), - KinkUtilization: sdk.MustNewDecFromStr("0.75"), - LiquidationIncentive: sdk.MustNewDecFromStr("0.11"), - SymbolDenom: "ATOM", - Exponent: 6, - EnableMsgSupply: true, - EnableMsgBorrow: true, - Blacklist: false, - MaxCollateralShare: sdk.MustNewDecFromStr("1"), - MaxSupplyUtilization: sdk.MustNewDecFromStr("0.9"), - MinCollateralLiquidity: sdk.MustNewDecFromStr("0"), - } - uabc := types.Token{ - BaseDenom: "uabc", - ReserveFactor: sdk.MustNewDecFromStr("0"), - CollateralWeight: sdk.MustNewDecFromStr("0.1"), - LiquidationThreshold: sdk.MustNewDecFromStr("0.1"), - BaseBorrowRate: sdk.MustNewDecFromStr("0.02"), - KinkBorrowRate: sdk.MustNewDecFromStr("0.22"), - MaxBorrowRate: sdk.MustNewDecFromStr("1.52"), - KinkUtilization: sdk.MustNewDecFromStr("0.87"), - LiquidationIncentive: sdk.MustNewDecFromStr("0.1"), - SymbolDenom: "ABC", - Exponent: 6, - EnableMsgSupply: true, - EnableMsgBorrow: true, - Blacklist: false, - MaxCollateralShare: sdk.MustNewDecFromStr("1"), - MaxSupplyUtilization: sdk.MustNewDecFromStr("0.9"), - MinCollateralLiquidity: sdk.MustNewDecFromStr("0"), - } - - tokens := []types.Token{umeeToken, atomIBCToken, uabc} leverage.InitGenesis(ctx, app.LeverageKeeper, *types.DefaultGenesis()) - - for _, token := range tokens { - s.Require().NoError(app.LeverageKeeper.SetTokenSettings(ctx, token)) - app.OracleKeeper.SetExchangeRate(ctx, token.SymbolDenom, sdk.MustNewDecFromStr("100.0")) - } + s.Require().NoError(app.LeverageKeeper.SetTokenSettings(ctx, umeeToken)) + app.OracleKeeper.SetExchangeRate(ctx, umeeToken.SymbolDenom, sdk.MustNewDecFromStr("100.0")) s.app = app s.ctx = ctx @@ -181,7 +138,7 @@ func (s *SimTestSuite) TestSimulateMsgSupply() { s.Require().True(operationMsg.OK) s.Require().Equal("umee1ghekyjucln7y67ntx7cf27m9dpuxxemn8w6h33", msg.Supplier) s.Require().Equal(types.EventTypeSupply, msg.Type()) - s.Require().Equal("185121068uumee", msg.Asset.String()) + s.Require().Equal("4896096uumee", msg.Asset.String()) s.Require().Len(futureOperations, 0) } @@ -256,7 +213,7 @@ func (s *SimTestSuite) TestSimulateMsgCollateralize() { s.Require().True(operationMsg.OK) s.Require().Equal("umee1ghekyjucln7y67ntx7cf27m9dpuxxemn8w6h33", msg.Borrower) s.Require().Equal(types.EventTypeCollateralize, msg.Type()) - s.Require().Equal("0u/uabc", msg.Coin.String()) + s.Require().Equal("0u/uumee", msg.Coin.String()) s.Require().Len(futureOperations, 0) } @@ -277,7 +234,7 @@ func (s *SimTestSuite) TestSimulateMsgDecollateralize() { s.Require().True(operationMsg.OK) s.Require().Equal("umee1ghekyjucln7y67ntx7cf27m9dpuxxemn8w6h33", msg.Borrower) s.Require().Equal(types.EventTypeDecollateralize, msg.Type()) - s.Require().Equal("0u/uabc", msg.Coin.String()) + s.Require().Equal("0u/uumee", msg.Coin.String()) s.Require().Len(futureOperations, 0) } diff --git a/x/leverage/simulation/params.go b/x/leverage/simulation/params.go index e202060fc2..ade2fbfec7 100644 --- a/x/leverage/simulation/params.go +++ b/x/leverage/simulation/params.go @@ -34,5 +34,10 @@ func ParamChanges(r *rand.Rand) []simtypes.ParamChange { return fmt.Sprintf("\"%s\"", GenSmallLiquidationSize(r)) }, ), + simulation.NewSimParamChange(types.ModuleName, string(types.KeyDirectLiquidationFee), + func(r *rand.Rand) string { + return fmt.Sprintf("\"%s\"", GenDirectLiquidationFee(r)) + }, + ), } } diff --git a/x/leverage/spec/07_params.md b/x/leverage/spec/07_params.md index 626809e990..ae24d83e26 100644 --- a/x/leverage/spec/07_params.md +++ b/x/leverage/spec/07_params.md @@ -8,6 +8,7 @@ The leverage module contains the following parameters: | MinimumCloseFactor | sdk.Dec | 0.01 | | OracleRewardFactor | sdk.Dec | 0.01 | | SmallLiquidationSize | sdk.Dec | 100.00 | +| DirectLiquidationFee | sdk.Dec | 0.1 | ## CompleteLiquidationThreshold @@ -28,4 +29,8 @@ the `x/oracle` reward pool. ## SmallLiquidationSize SmallLiquidationSize is the borrow value in USD below which [Close Factor](01_concepts.md#Close-Factor) -is always 1. \ No newline at end of file +is always 1. + +## DirectLiquidationFee + +DirectLiquidationFee is the reduction in Liquidation Incentive when liquidators choose to directly receive base assets instead of uTokens as liquidation rewards. \ No newline at end of file diff --git a/x/leverage/types/errors.go b/x/leverage/types/errors.go index 8dbed3cac6..89793cffbc 100644 --- a/x/leverage/types/errors.go +++ b/x/leverage/types/errors.go @@ -38,4 +38,5 @@ var ( 1126, "market total collateral would exceed MaxCollateralShare", ) + ErrNotUToken = sdkerrors.Register(ModuleName, 1127, "not a uToken denom") ) diff --git a/x/leverage/types/leverage.pb.go b/x/leverage/types/leverage.pb.go index a0d4a2f092..8391af001d 100644 --- a/x/leverage/types/leverage.pb.go +++ b/x/leverage/types/leverage.pb.go @@ -41,6 +41,10 @@ type Params struct { // considered small enough to be liquidated in a single transaction, bypassing // dynamic close factor. SmallLiquidationSize github_com_cosmos_cosmos_sdk_types.Dec `protobuf:"bytes,5,opt,name=small_liquidation_size,json=smallLiquidationSize,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Dec" json:"small_liquidation_size" yaml:"small_liquidation_size"` + // Direct Liquidation Fee is the reduction in liquidation incentive experienced + // by liquidators who choose to receive base assets instead of uTokens as + // liquidation rewards. + DirectLiquidationFee github_com_cosmos_cosmos_sdk_types.Dec `protobuf:"bytes,6,opt,name=direct_liquidation_fee,json=directLiquidationFee,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Dec" json:"direct_liquidation_fee" yaml:"direct_liquidation_fee"` } func (m *Params) Reset() { *m = Params{} } @@ -186,61 +190,62 @@ func init() { func init() { proto.RegisterFile("umee/leverage/v1/leverage.proto", fileDescriptor_8cb1bf9ea641ecc6) } var fileDescriptor_8cb1bf9ea641ecc6 = []byte{ - // 849 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x56, 0x4f, 0x6f, 0x1c, 0x35, - 0x1c, 0xdd, 0x81, 0x34, 0x24, 0x6e, 0x37, 0x9b, 0x4c, 0x93, 0x74, 0x04, 0x61, 0x27, 0xb2, 0x04, - 0xca, 0xa5, 0x59, 0x0a, 0x9c, 0x72, 0xdc, 0x56, 0xd0, 0xa2, 0xb6, 0x80, 0x53, 0x54, 0x89, 0xcb, - 0xc8, 0x3b, 0x6b, 0x76, 0xad, 0xb5, 0xc7, 0x8b, 0xed, 0xfd, 0x97, 0x0b, 0x07, 0xc4, 0x89, 0x0b, - 0x47, 0x2e, 0x48, 0xfd, 0x18, 0x1c, 0x39, 0xe6, 0xd8, 0x23, 0xe2, 0xb0, 0x82, 0xe4, 0xc2, 0x39, - 0x9f, 0x00, 0xd9, 0x9e, 0xd9, 0x99, 0x4d, 0x87, 0x4a, 0xa3, 0xed, 0x69, 0x67, 0xde, 0xef, 0xb7, - 0xef, 0x3d, 0xdb, 0xcf, 0xf6, 0x80, 0x70, 0xc4, 0x09, 0x69, 0x31, 0x32, 0x26, 0x12, 0xf7, 0x48, - 0x6b, 0x7c, 0x6f, 0xf1, 0x7c, 0x3c, 0x94, 0x42, 0x0b, 0x7f, 0xdb, 0x34, 0x1c, 0x2f, 0xc0, 0xf1, - 0xbd, 0x77, 0x77, 0x7b, 0xa2, 0x27, 0x6c, 0xb1, 0x65, 0x9e, 0x5c, 0x1f, 0xfc, 0x7d, 0x0d, 0xac, - 0x7f, 0x85, 0x25, 0xe6, 0xca, 0xff, 0xcd, 0x03, 0xcd, 0x58, 0xf0, 0x21, 0x23, 0x9a, 0x44, 0x8c, - 0x7e, 0x3f, 0xa2, 0x5d, 0xac, 0xa9, 0x48, 0x22, 0xdd, 0x97, 0x44, 0xf5, 0x05, 0xeb, 0x06, 0x6f, - 0x1d, 0x7a, 0x47, 0x9b, 0xed, 0xe7, 0xe7, 0xf3, 0xb0, 0xf6, 0xd7, 0x3c, 0xfc, 0xb0, 0x47, 0x75, - 0x7f, 0xd4, 0x39, 0x8e, 0x05, 0x6f, 0xc5, 0x42, 0x71, 0xa1, 0xd2, 0x9f, 0xbb, 0xaa, 0x3b, 0x68, - 0xe9, 0xd9, 0x90, 0xa8, 0xe3, 0x07, 0x24, 0xbe, 0x9a, 0x87, 0x1f, 0xcc, 0x30, 0x67, 0x27, 0xf0, - 0xf5, 0xec, 0x10, 0x1d, 0x64, 0x0d, 0x8f, 0xf3, 0xfa, 0xb3, 0xac, 0xec, 0xff, 0x00, 0x76, 0x39, - 0x4d, 0x28, 0x1f, 0xf1, 0x28, 0x66, 0x42, 0x91, 0xe8, 0x3b, 0x1c, 0x6b, 0x21, 0x83, 0xb7, 0xad, - 0xa9, 0x27, 0x95, 0x4d, 0xbd, 0xe7, 0x4c, 0x95, 0x71, 0x42, 0xe4, 0xa7, 0xf0, 0x7d, 0x83, 0x7e, - 0x66, 0x41, 0x63, 0x40, 0x48, 0x1c, 0x33, 0x12, 0x49, 0x32, 0xc1, 0xb2, 0x9b, 0x19, 0x58, 0x5b, - 0xcd, 0x40, 0x19, 0x27, 0x44, 0xbe, 0x83, 0x91, 0x45, 0x53, 0x03, 0x3f, 0x79, 0x60, 0x5f, 0x71, - 0xcc, 0xd8, 0xd2, 0x04, 0x2a, 0x7a, 0x46, 0x82, 0x1b, 0xd6, 0xc3, 0x97, 0x95, 0x3d, 0xbc, 0xef, - 0x3c, 0x94, 0xb3, 0x42, 0xb4, 0x6b, 0x0b, 0x85, 0xe5, 0x38, 0xa5, 0x67, 0xe4, 0x64, 0xed, 0xd7, - 0x17, 0x61, 0x0d, 0xfe, 0x51, 0x07, 0x37, 0x9e, 0x89, 0x01, 0x49, 0xfc, 0x4f, 0x01, 0xe8, 0x60, - 0x45, 0xa2, 0x2e, 0x49, 0x04, 0x0f, 0x3c, 0x6b, 0x65, 0xef, 0x6a, 0x1e, 0xee, 0x38, 0xf2, 0xbc, - 0x06, 0xd1, 0xa6, 0x79, 0x79, 0x60, 0x9e, 0xfd, 0x04, 0x6c, 0x49, 0xa2, 0x88, 0x1c, 0x2f, 0x56, - 0xd2, 0xc5, 0xeb, 0xf3, 0xca, 0x83, 0xd8, 0x73, 0x3a, 0xcb, 0x6c, 0x10, 0xd5, 0x53, 0x20, 0x9d, - 0xbd, 0x09, 0xd8, 0x89, 0x05, 0x63, 0x58, 0x13, 0x89, 0x59, 0x34, 0x21, 0xb4, 0xd7, 0xd7, 0x69, - 0x78, 0xbe, 0xa8, 0x2c, 0x19, 0x64, 0x89, 0xbe, 0x46, 0x08, 0xd1, 0x76, 0x8e, 0x3d, 0xb7, 0x90, - 0xff, 0xa3, 0x07, 0xf6, 0xca, 0xf7, 0x93, 0x4b, 0xce, 0xd3, 0xca, 0xea, 0x07, 0x4e, 0xfd, 0x7f, - 0xb6, 0xd1, 0x2e, 0x2b, 0xdb, 0x3e, 0x0a, 0x6c, 0xdb, 0x85, 0xe8, 0x08, 0x29, 0xc5, 0x24, 0x92, - 0x58, 0x67, 0xa9, 0x79, 0x54, 0x59, 0xff, 0x4e, 0x61, 0x61, 0x0b, 0x7c, 0x10, 0x6d, 0x19, 0xa8, - 0x6d, 0x11, 0x84, 0x35, 0x31, 0xa2, 0x03, 0x9a, 0x0c, 0x96, 0x44, 0xd7, 0x57, 0x13, 0xbd, 0xce, - 0x07, 0xd1, 0x96, 0x81, 0x0a, 0xa2, 0x43, 0xd0, 0xe0, 0x78, 0xba, 0xa4, 0xf9, 0x8e, 0xd5, 0x7c, - 0x58, 0x59, 0x73, 0x3f, 0x3d, 0x23, 0x96, 0xe9, 0x20, 0xaa, 0x73, 0x3c, 0x2d, 0x28, 0xea, 0x74, - 0x98, 0x23, 0x4d, 0x19, 0x3d, 0xb3, 0x13, 0x1f, 0x6c, 0xbc, 0x81, 0x61, 0x16, 0xf8, 0x20, 0x6a, - 0x18, 0xe8, 0x9b, 0x1c, 0x79, 0x25, 0x57, 0x34, 0x89, 0x49, 0xa2, 0xe9, 0x98, 0x04, 0x9b, 0x6f, - 0x2e, 0x57, 0x0b, 0xd2, 0xe5, 0x5c, 0x3d, 0xca, 0x60, 0xff, 0x04, 0xdc, 0x52, 0x33, 0xde, 0x11, - 0x2c, 0xdd, 0xfe, 0xc0, 0x6a, 0xdf, 0xb9, 0x9a, 0x87, 0xb7, 0xd3, 0xb3, 0xa5, 0x50, 0x85, 0xe8, - 0xa6, 0x7b, 0x75, 0x47, 0x40, 0x0b, 0x6c, 0x90, 0xe9, 0x50, 0x24, 0x24, 0xd1, 0xc1, 0xcd, 0x43, - 0xef, 0xa8, 0xde, 0xbe, 0x7d, 0x35, 0x0f, 0x1b, 0xee, 0x7f, 0x59, 0x05, 0xa2, 0x45, 0x93, 0xff, - 0x10, 0xec, 0x90, 0x04, 0x77, 0x18, 0x89, 0xb8, 0xea, 0x45, 0x6a, 0x34, 0x1c, 0xb2, 0x59, 0x70, - 0xeb, 0xd0, 0x3b, 0xda, 0x68, 0x1f, 0xe4, 0xbb, 0xf2, 0x95, 0x16, 0x88, 0x1a, 0x0e, 0x7b, 0xa2, - 0x7a, 0xa7, 0x16, 0xb9, 0xc6, 0xe4, 0x16, 0x37, 0xa8, 0xbf, 0x86, 0xc9, 0xb5, 0x14, 0x99, 0x5c, - 0x00, 0xfc, 0x03, 0xb0, 0xd9, 0x61, 0x38, 0x1e, 0x30, 0xaa, 0x74, 0xb0, 0x65, 0x18, 0x50, 0x0e, - 0xd8, 0x5b, 0x0b, 0x4f, 0xa3, 0xc2, 0x41, 0xa1, 0xfa, 0x58, 0x92, 0xa0, 0xb1, 0xe2, 0xad, 0x55, - 0xc2, 0x69, 0x6e, 0x2d, 0x3c, 0xbd, 0xbf, 0x40, 0x4f, 0x0d, 0x68, 0x2f, 0x0d, 0xd3, 0xed, 0x66, - 0x62, 0x29, 0xa2, 0xdb, 0xab, 0x5d, 0x1a, 0xe5, 0xac, 0x10, 0x99, 0x01, 0xbb, 0x59, 0x2e, 0xa6, - 0xf5, 0x67, 0x0f, 0x04, 0x9c, 0x26, 0x45, 0xd7, 0x2e, 0x4f, 0x54, 0xcf, 0x82, 0x1d, 0xeb, 0xe4, - 0xeb, 0xca, 0x4e, 0xc2, 0xc5, 0x1d, 0x5e, 0xca, 0x0b, 0xd1, 0x3e, 0xa7, 0x49, 0x3e, 0x23, 0x8f, - 0xb3, 0xc2, 0xc9, 0xda, 0xbf, 0x2f, 0x42, 0xaf, 0xfd, 0xf4, 0xfc, 0x9f, 0x66, 0xed, 0xfc, 0xa2, - 0xe9, 0xbd, 0xbc, 0x68, 0x7a, 0x7f, 0x5f, 0x34, 0xbd, 0x5f, 0x2e, 0x9b, 0xb5, 0x97, 0x97, 0xcd, - 0xda, 0x9f, 0x97, 0xcd, 0xda, 0xb7, 0x1f, 0x15, 0x6c, 0x98, 0xcf, 0xa9, 0xbb, 0x09, 0xd1, 0x13, - 0x21, 0x07, 0xf6, 0xa5, 0x35, 0xfe, 0xb8, 0x35, 0xcd, 0xbf, 0xc0, 0xac, 0xa9, 0xce, 0xba, 0xfd, - 0xa8, 0xfa, 0xe4, 0xbf, 0x00, 0x00, 0x00, 0xff, 0xff, 0x35, 0x72, 0xea, 0x08, 0x9f, 0x09, 0x00, - 0x00, + // 872 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x56, 0xbf, 0x6f, 0x1b, 0x37, + 0x14, 0xd6, 0xb5, 0xb6, 0x6b, 0x33, 0x91, 0x65, 0x5f, 0x64, 0xe7, 0xd0, 0xba, 0x3a, 0x83, 0x40, + 0x0b, 0x2f, 0xb1, 0x9a, 0xb6, 0x93, 0x47, 0x25, 0x48, 0x93, 0x22, 0x49, 0x5b, 0x3a, 0x45, 0x80, + 0x2e, 0x07, 0xea, 0xf4, 0x22, 0x11, 0x22, 0x8f, 0x2a, 0x49, 0xfd, 0xf2, 0xd2, 0xa1, 0xe8, 0xd4, + 0xa5, 0x63, 0x97, 0x02, 0xf9, 0x53, 0x32, 0x7a, 0xcc, 0x58, 0x74, 0x10, 0x5a, 0x7b, 0xe9, 0xec, + 0xbf, 0xa0, 0x38, 0xf2, 0x24, 0x9d, 0x9c, 0x6b, 0x00, 0x41, 0x99, 0x74, 0xfc, 0xde, 0xd3, 0xf7, + 0x7d, 0x24, 0xdf, 0x23, 0x89, 0xc2, 0xbe, 0x00, 0xa8, 0x73, 0x18, 0x80, 0xa2, 0x6d, 0xa8, 0x0f, + 0xee, 0xce, 0xbe, 0x8f, 0x7b, 0x4a, 0x1a, 0xe9, 0xef, 0xa4, 0x09, 0xc7, 0x33, 0x70, 0x70, 0xf7, + 0xc3, 0x6a, 0x5b, 0xb6, 0xa5, 0x0d, 0xd6, 0xd3, 0x2f, 0x97, 0x87, 0x5f, 0xad, 0xa3, 0x8d, 0x6f, + 0xa9, 0xa2, 0x42, 0xfb, 0x7f, 0x78, 0xa8, 0x16, 0x4b, 0xd1, 0xe3, 0x60, 0x20, 0xe2, 0xec, 0xc7, + 0x3e, 0x6b, 0x51, 0xc3, 0x64, 0x12, 0x99, 0x8e, 0x02, 0xdd, 0x91, 0xbc, 0x15, 0xbc, 0x77, 0xe8, + 0x1d, 0x6d, 0x35, 0x9e, 0x9f, 0x4f, 0xc2, 0xd2, 0x5f, 0x93, 0xf0, 0xd3, 0x36, 0x33, 0x9d, 0x7e, + 0xf3, 0x38, 0x96, 0xa2, 0x1e, 0x4b, 0x2d, 0xa4, 0xce, 0x7e, 0xee, 0xe8, 0x56, 0xb7, 0x6e, 0xc6, + 0x3d, 0xd0, 0xc7, 0xf7, 0x21, 0xbe, 0x9a, 0x84, 0x9f, 0x8c, 0xa9, 0xe0, 0x27, 0xf8, 0xed, 0xec, + 0x98, 0x1c, 0x4c, 0x13, 0x1e, 0xcf, 0xe3, 0xcf, 0xa6, 0x61, 0xff, 0x27, 0x54, 0x15, 0x2c, 0x61, + 0xa2, 0x2f, 0xa2, 0x98, 0x4b, 0x0d, 0xd1, 0x0b, 0x1a, 0x1b, 0xa9, 0x82, 0xf7, 0xad, 0xa9, 0x27, + 0x4b, 0x9b, 0xfa, 0xc8, 0x99, 0x2a, 0xe2, 0xc4, 0xc4, 0xcf, 0xe0, 0x7b, 0x29, 0xfa, 0xc0, 0x82, + 0xa9, 0x01, 0xa9, 0x68, 0xcc, 0x21, 0x52, 0x30, 0xa4, 0xaa, 0x35, 0x35, 0xb0, 0xb6, 0x9a, 0x81, + 0x22, 0x4e, 0x4c, 0x7c, 0x07, 0x13, 0x8b, 0x66, 0x06, 0x7e, 0xf1, 0xd0, 0xbe, 0x16, 0x94, 0xf3, + 0x85, 0x05, 0xd4, 0xec, 0x0c, 0x82, 0x75, 0xeb, 0xe1, 0x9b, 0xa5, 0x3d, 0x7c, 0xec, 0x3c, 0x14, + 0xb3, 0x62, 0x52, 0xb5, 0x81, 0xdc, 0x76, 0x9c, 0xb2, 0x33, 0xb0, 0x3e, 0x5a, 0x4c, 0x41, 0x6c, + 0x16, 0xfe, 0xf2, 0x02, 0x20, 0xd8, 0x58, 0xcd, 0x47, 0x31, 0x2b, 0x26, 0x55, 0x17, 0xc8, 0x19, + 0x79, 0x00, 0x70, 0xb2, 0xf6, 0xfb, 0xcb, 0xb0, 0x84, 0x5f, 0x95, 0xd1, 0xfa, 0x33, 0xd9, 0x85, + 0xc4, 0xff, 0x12, 0xa1, 0x26, 0xd5, 0x10, 0xb5, 0x20, 0x91, 0x22, 0xf0, 0xac, 0x95, 0xbd, 0xab, + 0x49, 0xb8, 0xeb, 0xc8, 0xe7, 0x31, 0x4c, 0xb6, 0xd2, 0xc1, 0xfd, 0xf4, 0xdb, 0x4f, 0xd0, 0xb6, + 0x02, 0x0d, 0x6a, 0x30, 0xab, 0x28, 0x57, 0xe6, 0x5f, 0x2d, 0x3d, 0x89, 0x3d, 0xa7, 0xb3, 0xc8, + 0x86, 0x49, 0x39, 0x03, 0xb2, 0x5d, 0x1c, 0xa2, 0xdd, 0x58, 0x72, 0x4e, 0x0d, 0x28, 0xca, 0xa3, + 0x21, 0xb0, 0x76, 0xc7, 0x64, 0x45, 0xfc, 0xf5, 0xd2, 0x92, 0xc1, 0xb4, 0xb3, 0xae, 0x11, 0x62, + 0xb2, 0x33, 0xc7, 0x9e, 0x5b, 0xc8, 0xff, 0xd9, 0x43, 0x7b, 0xc5, 0x7d, 0xed, 0x2a, 0xf8, 0xe9, + 0xd2, 0xea, 0x07, 0x4e, 0xfd, 0x7f, 0xda, 0xb9, 0xca, 0x8b, 0xda, 0x58, 0xa3, 0x1d, 0xbb, 0x11, + 0x4d, 0xa9, 0x94, 0x1c, 0x46, 0x8a, 0x9a, 0x69, 0xf5, 0x3e, 0x5a, 0x5a, 0xff, 0x76, 0x6e, 0x63, + 0x73, 0x7c, 0x98, 0x6c, 0xa7, 0x50, 0xc3, 0x22, 0x84, 0x1a, 0x48, 0x45, 0xbb, 0x2c, 0xe9, 0x2e, + 0x88, 0x6e, 0xac, 0x26, 0x7a, 0x9d, 0x0f, 0x93, 0xed, 0x14, 0xca, 0x89, 0xf6, 0x50, 0x45, 0xd0, + 0xd1, 0x82, 0xe6, 0x07, 0x56, 0xf3, 0xe1, 0xd2, 0x9a, 0xfb, 0xd9, 0x59, 0xb5, 0x48, 0x87, 0x49, + 0x59, 0xd0, 0x51, 0x4e, 0xd1, 0x64, 0xd3, 0xec, 0x1b, 0xc6, 0xd9, 0x99, 0x5d, 0xf8, 0x60, 0xf3, + 0x1d, 0x4c, 0x33, 0xc7, 0x87, 0x49, 0x25, 0x85, 0xbe, 0x9f, 0x23, 0x6f, 0xd4, 0x15, 0x4b, 0x62, + 0x48, 0x0c, 0x1b, 0x40, 0xb0, 0xf5, 0xee, 0xea, 0x6a, 0x46, 0xba, 0x58, 0x57, 0x8f, 0xa6, 0xb0, + 0x7f, 0x82, 0x6e, 0xea, 0xb1, 0x68, 0x4a, 0x9e, 0xb5, 0x3f, 0xb2, 0xda, 0xb7, 0xaf, 0x26, 0xe1, + 0xad, 0xec, 0x8c, 0xcb, 0x45, 0x31, 0xb9, 0xe1, 0x86, 0xee, 0x08, 0xa8, 0xa3, 0x4d, 0x18, 0xf5, + 0x64, 0x02, 0x89, 0x09, 0x6e, 0x1c, 0x7a, 0x47, 0xe5, 0xc6, 0xad, 0xab, 0x49, 0x58, 0x71, 0xff, + 0x9b, 0x46, 0x30, 0x99, 0x25, 0xf9, 0x0f, 0xd1, 0x2e, 0x24, 0xb4, 0xc9, 0x21, 0x12, 0xba, 0x1d, + 0xe9, 0x7e, 0xaf, 0xc7, 0xc7, 0xc1, 0xcd, 0x43, 0xef, 0x68, 0xb3, 0x71, 0x30, 0xef, 0xca, 0x37, + 0x52, 0x30, 0xa9, 0x38, 0xec, 0x89, 0x6e, 0x9f, 0x5a, 0xe4, 0x1a, 0x93, 0xdb, 0xdc, 0xa0, 0xfc, + 0x16, 0x26, 0x97, 0x92, 0x67, 0x72, 0x05, 0xe0, 0x1f, 0xa0, 0xad, 0x26, 0xa7, 0x71, 0x97, 0x33, + 0x6d, 0x82, 0xed, 0x94, 0x81, 0xcc, 0x01, 0x7b, 0x7b, 0xd2, 0x51, 0x94, 0x3b, 0x28, 0x74, 0x87, + 0x2a, 0x08, 0x2a, 0x2b, 0xde, 0x9e, 0x05, 0x9c, 0xe9, 0xed, 0x49, 0x47, 0xf7, 0x66, 0xe8, 0x69, + 0x0a, 0xda, 0x4b, 0x23, 0xcd, 0x76, 0x2b, 0xb1, 0x50, 0xa2, 0x3b, 0xab, 0x5d, 0x1a, 0xc5, 0xac, + 0x98, 0xa4, 0x13, 0x76, 0xab, 0x9c, 0xaf, 0xd6, 0x5f, 0x3d, 0x14, 0x08, 0x96, 0xe4, 0x5d, 0xbb, + 0x7a, 0x62, 0x66, 0x1c, 0xec, 0x5a, 0x27, 0xdf, 0x2d, 0xed, 0x24, 0x9c, 0xbd, 0x25, 0x0a, 0x79, + 0x31, 0xd9, 0x17, 0x2c, 0x99, 0xaf, 0xc8, 0xe3, 0x69, 0xe0, 0x64, 0xed, 0xdf, 0x97, 0xa1, 0xd7, + 0x78, 0x7a, 0xfe, 0x4f, 0xad, 0x74, 0x7e, 0x51, 0xf3, 0x5e, 0x5f, 0xd4, 0xbc, 0xbf, 0x2f, 0x6a, + 0xde, 0x6f, 0x97, 0xb5, 0xd2, 0xeb, 0xcb, 0x5a, 0xe9, 0xcf, 0xcb, 0x5a, 0xe9, 0x87, 0xcf, 0x72, + 0x36, 0xd2, 0x67, 0xdd, 0x9d, 0x04, 0xcc, 0x50, 0xaa, 0xae, 0x1d, 0xd4, 0x07, 0x9f, 0xd7, 0x47, + 0xf3, 0x97, 0xa0, 0x35, 0xd5, 0xdc, 0xb0, 0x8f, 0xbb, 0x2f, 0xfe, 0x0b, 0x00, 0x00, 0xff, 0xff, + 0xa5, 0x95, 0x59, 0x5a, 0x27, 0x0a, 0x00, 0x00, } func (this *Token) Equal(that interface{}) bool { @@ -335,6 +340,16 @@ func (m *Params) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + { + size := m.DirectLiquidationFee.Size() + i -= size + if _, err := m.DirectLiquidationFee.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintLeverage(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x32 { size := m.SmallLiquidationSize.Size() i -= size @@ -589,6 +604,8 @@ func (m *Params) Size() (n int) { n += 1 + l + sovLeverage(uint64(l)) l = m.SmallLiquidationSize.Size() n += 1 + l + sovLeverage(uint64(l)) + l = m.DirectLiquidationFee.Size() + n += 1 + l + sovLeverage(uint64(l)) return n } @@ -814,6 +831,40 @@ func (m *Params) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DirectLiquidationFee", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLeverage + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthLeverage + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthLeverage + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.DirectLiquidationFee.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipLeverage(dAtA[iNdEx:]) diff --git a/x/leverage/types/params.go b/x/leverage/types/params.go index 2641e3ac19..b59232c74e 100644 --- a/x/leverage/types/params.go +++ b/x/leverage/types/params.go @@ -15,6 +15,7 @@ var ( KeyMinimumCloseFactor = []byte("MinimumCloseFactor") KeyOracleRewardFactor = []byte("OracleRewardFactor") KeySmallLiquidationSize = []byte("SmallLiquidationSize") + KeyDirectLiquidationFee = []byte("DirectLiquidationFee") ) var ( @@ -22,6 +23,7 @@ var ( defaultMinimumCloseFactor = sdk.MustNewDecFromStr("0.01") defaultOracleRewardFactor = sdk.MustNewDecFromStr("0.01") defaultSmallLiquidationSize = sdk.MustNewDecFromStr("100.00") + defaultDirectLiquidationFee = sdk.MustNewDecFromStr("0.1") ) func NewParams() Params { @@ -52,6 +54,11 @@ func (p *Params) ParamSetPairs() paramtypes.ParamSetPairs { &p.SmallLiquidationSize, validateSmallLiquidationSize, ), + paramtypes.NewParamSetPair( + KeyDirectLiquidationFee, + &p.DirectLiquidationFee, + validateDirectLiquidationFee, + ), } } @@ -74,6 +81,7 @@ func DefaultParams() Params { MinimumCloseFactor: defaultMinimumCloseFactor, OracleRewardFactor: defaultOracleRewardFactor, SmallLiquidationSize: defaultSmallLiquidationSize, + DirectLiquidationFee: defaultDirectLiquidationFee, } } @@ -91,6 +99,9 @@ func (p Params) Validate() error { if err := validateSmallLiquidationSize(p.SmallLiquidationSize); err != nil { return err } + if err := validateDirectLiquidationFee(p.DirectLiquidationFee); err != nil { + return err + } return nil } @@ -151,3 +162,19 @@ func validateSmallLiquidationSize(i interface{}) error { return nil } + +func validateDirectLiquidationFee(i interface{}) error { + v, ok := i.(sdk.Dec) + if !ok { + return fmt.Errorf("invalid parameter type: %T", i) + } + + if v.IsNegative() { + return fmt.Errorf("direct liquidation fee cannot be negative: %d", v) + } + if v.GTE(sdk.OneDec()) { + return fmt.Errorf("direct liquidation fee must be less than 1: %d", v) + } + + return nil +} diff --git a/x/leverage/types/token.go b/x/leverage/types/token.go index 7bee142c1f..bdd1539dc1 100644 --- a/x/leverage/types/token.go +++ b/x/leverage/types/token.go @@ -13,9 +13,22 @@ const ( UTokenPrefix = "u/" ) -// UTokenFromTokenDenom returns the uToken denom given a token denom. -func UTokenFromTokenDenom(tokenDenom string) string { - return UTokenPrefix + tokenDenom +// HasUTokenPrefix detects the uToken prefix on a denom. +func HasUTokenPrefix(denom string) bool { + return strings.HasPrefix(denom, UTokenPrefix) +} + +// ToUTokenDenom adds the uToken prefix to a denom if not already present. +func ToUTokenDenom(denom string) string { + if HasUTokenPrefix(denom) { + return denom + } + return UTokenPrefix + denom +} + +// ToTokenDenom strips the uToken prefix from a denom if present. +func ToTokenDenom(denom string) string { + return strings.TrimPrefix(denom, UTokenPrefix) } // Validate performs validation on an Token type returning an error if the Token @@ -24,7 +37,7 @@ func (t Token) Validate() error { if err := sdk.ValidateDenom(t.BaseDenom); err != nil { return err } - if strings.HasPrefix(t.BaseDenom, UTokenPrefix) { + if HasUTokenPrefix(t.BaseDenom) { // prevent base asset denoms that start with "u/" return sdkerrors.Wrap(ErrInvalidAsset, t.BaseDenom) } @@ -32,8 +45,8 @@ func (t Token) Validate() error { if err := sdk.ValidateDenom(t.SymbolDenom); err != nil { return err } - if strings.HasPrefix(t.SymbolDenom, UTokenPrefix) { - // prevent symbol (ticker) denoms that start with "u/" + if HasUTokenPrefix(t.SymbolDenom) { + // prevent symbol denoms that start with "u/" return sdkerrors.Wrap(ErrInvalidAsset, t.SymbolDenom) } diff --git a/x/leverage/types/token_test.go b/x/leverage/types/token_test.go index e32296ebc2..71330c4347 100644 --- a/x/leverage/types/token_test.go +++ b/x/leverage/types/token_test.go @@ -9,11 +9,29 @@ import ( "github.com/umee-network/umee/v2/x/leverage/types" ) -func TestUTokenFromTokenDenom(t *testing.T) { - tokenDenom := "uumee" - uTokenDenom := types.UTokenFromTokenDenom(tokenDenom) - require.Equal(t, "u/"+tokenDenom, uTokenDenom) - require.NoError(t, sdk.ValidateDenom(uTokenDenom)) +func TestToTokenDenom(t *testing.T) { + // Used as intended + require.Equal(t, "uumee", types.ToTokenDenom("u/uumee")) + require.Equal(t, "uumee", types.ToTokenDenom("uumee")) + require.Equal(t, "ibc/abcd", types.ToTokenDenom("u/ibc/abcd")) + require.Equal(t, "ibc/abcd", types.ToTokenDenom("ibc/abcd")) + + // Does not ensure a valid output + require.Equal(t, "", types.ToTokenDenom("u/")) + require.Equal(t, "", types.ToTokenDenom("")) + require.Equal(t, "u/", types.ToTokenDenom("u/u/")) +} + +func TestToUTokenDenom(t *testing.T) { + // Used as intended + require.Equal(t, "u/uumee", types.ToUTokenDenom("uumee")) + require.Equal(t, "u/uumee", types.ToUTokenDenom("u/uumee")) + require.Equal(t, "u/ibc/abcd", types.ToUTokenDenom("ibc/abcd")) + require.Equal(t, "u/ibc/abcd", types.ToUTokenDenom("u/ibc/abcd")) + + // Does not ensure a valid output + require.Equal(t, "u/", types.ToUTokenDenom("")) + require.Equal(t, "u/", types.ToUTokenDenom("u/")) } func TestUpdateRegistryProposal_String(t *testing.T) { diff --git a/x/leverage/types/tx.pb.go b/x/leverage/types/tx.pb.go index 9a3c7fda1f..b225ebd2ba 100644 --- a/x/leverage/types/tx.pb.go +++ b/x/leverage/types/tx.pb.go @@ -287,9 +287,10 @@ type MsgLiquidate struct { // Repayment is the maximum amount of base tokens that the liquidator is willing // to repay. Repayment types.Coin `protobuf:"bytes,3,opt,name=repayment,proto3" json:"repayment"` - // RewardDenom is the base token denom that the liquidator is willing to accept - // as a liquidation reward. The uToken equivalent of any base token rewards - // will be taken from the borrower's collateral. + // RewardDenom is the denom that the liquidator will receive liquidation reward. + // If it is a uToken, the liquidator will receive uTokens from the borrower's + // collateral. If it is a base token, the uTokens will be redeemed directly at + // a reduced Liquidation Incentive, and the liquidator will receive base tokens. RewardDenom string `protobuf:"bytes,4,opt,name=reward_denom,json=rewardDenom,proto3" json:"reward_denom,omitempty"` } From 0d4cbdf761649bcbc7e74fababce33dbd1a4589f Mon Sep 17 00:00:00 2001 From: toteki <63419657+toteki@users.noreply.github.com> Date: Sun, 7 Aug 2022 16:10:19 -0700 Subject: [PATCH 02/38] cl++ --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3e50e529cd..b0eb18418d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -61,7 +61,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ - [1140](https://github.com/umee-network/umee/pull/1140) Rename MarketSize query to TotalSuppliedValue, and TokenMarketSize to TotalSupplied. - [1188](https://github.com/umee-network/umee/pull/1188) Remove all individual queries which duplicate market_summary fields. - [1199](https://github.com/umee-network/umee/pull/1199) Move all queries which require address input (e.g. `supplied`, `collateral_value`, `borrow_limit`) into aggregate queries `acccount_summary` or `account_balances`. -- [XXXX](https://github.com/umee-network/umee/pull/XXXX) Add leverage parameter DirectLiquidationFee +- [1222](https://github.com/umee-network/umee/pull/1222) Add leverage parameter DirectLiquidationFee. ### Features @@ -80,7 +80,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ - [1203](https://github.com/umee-network/umee/pull/1203) Add Swagger docs. - [1212](https://github.com/umee-network/umee/pull/1212) Add `util/checkers` utility package providing common check / validation functions. - [1220](https://github.com/umee-network/umee/pull/1220) Submit oracle prevotes / vote txs via the CLI. -- [XXXX](https://github.com/umee-network/umee/pull/XXXX) Liquidation reward_denom can now be either token or uToken +- [1222](https://github.com/umee-network/umee/pull/1222) Liquidation reward_denom can now be either token or uToken ### Improvements From 4eb78bab2a20bc7a808fd01f0ee2b63f9900b079 Mon Sep 17 00:00:00 2001 From: toteki <63419657+toteki@users.noreply.github.com> Date: Sun, 7 Aug 2022 16:13:58 -0700 Subject: [PATCH 03/38] -- --- networks/umeemania-1/genesis.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/networks/umeemania-1/genesis.json b/networks/umeemania-1/genesis.json index 8d693acdfe..efc12a3178 100644 --- a/networks/umeemania-1/genesis.json +++ b/networks/umeemania-1/genesis.json @@ -720,8 +720,7 @@ "complete_liquidation_threshold": "0.100000000000000000", "minimum_close_factor": "0.010000000000000000", "oracle_reward_factor": "0.010000000000000000", - "small_liquidation_size": "100.000000000000000000", - "direct_liquidation_fee": "0.100000000000000000" + "small_liquidation_size": "100.000000000000000000" }, "registry": [ { From 9f124b44b04f7f8662afd3072ab4744453e69757 Mon Sep 17 00:00:00 2001 From: toteki <63419657+toteki@users.noreply.github.com> Date: Sun, 7 Aug 2022 16:16:06 -0700 Subject: [PATCH 04/38] -- --- networks/umeemania-1/genesis.json | 1633 ++++++++++++++--------------- 1 file changed, 810 insertions(+), 823 deletions(-) diff --git a/networks/umeemania-1/genesis.json b/networks/umeemania-1/genesis.json index efc12a3178..c7408ded7b 100644 --- a/networks/umeemania-1/genesis.json +++ b/networks/umeemania-1/genesis.json @@ -1,877 +1,864 @@ { - "genesis_time": "2022-04-05T15:39:08.904252Z", - "chain_id": "umeemania-1", - "initial_height": "1", - "consensus_params": { - "block": { - "max_bytes": "22020096", - "max_gas": "-1", - "time_iota_ms": "1000" - }, - "evidence": { - "max_age_num_blocks": "100000", - "max_age_duration": "172800000000000", - "max_bytes": "1048576" - }, - "validator": { - "pub_key_types": [ - "ed25519" - ] - }, - "version": {} - }, - "app_hash": "", - "app_state": { - "auth": { - "params": { - "max_memo_characters": "256", - "tx_sig_limit": "7", - "tx_size_cost_per_byte": "10", - "sig_verify_cost_ed25519": "590", - "sig_verify_cost_secp256k1": "1000" + "genesis_time": "2022-04-05T15:39:08.904252Z", + "chain_id": "umeemania-1", + "initial_height": "1", + "consensus_params": { + "block": { + "max_bytes": "22020096", + "max_gas": "-1", + "time_iota_ms": "1000" }, - "accounts": [ - { - "@type": "/cosmos.auth.v1beta1.BaseAccount", - "address": "umee1xpyya7quj4rx2pm3xclspyvh5gmfxwn2hwlt80", - "pub_key": null, - "account_number": "0", - "sequence": "0" - }, - { - "@type": "/cosmos.auth.v1beta1.BaseAccount", - "address": "umee1kpqyxsafxzw3k07rmhycf3jtwtmntrlgvdnj67", - "pub_key": null, - "account_number": "0", - "sequence": "0" - }, - { - "@type": "/cosmos.auth.v1beta1.BaseAccount", - "address": "umee1zl7pjgzfmcgwa0c7vgxklmkykuvmu77yxj75mp", - "pub_key": null, - "account_number": "0", - "sequence": "0" - }, - { - "@type": "/cosmos.auth.v1beta1.BaseAccount", - "address": "umee1e98evcznumc2njy9z36x5cv2p75lsagzfkwyqv", - "pub_key": null, - "account_number": "0", - "sequence": "0" - }, - { - "@type": "/cosmos.auth.v1beta1.BaseAccount", - "address": "umee1dak0cr5ha793jsrxkn3lghyc9ptkdj322m5xvr", - "pub_key": null, - "account_number": "0", - "sequence": "0" - }, - { - "@type": "/cosmos.auth.v1beta1.BaseAccount", - "address": "umee15a0n83ggtlzlu5jyny7ecdx2a5g4az47um3whs", - "pub_key": null, - "account_number": "0", - "sequence": "0" - }, - { - "@type": "/cosmos.auth.v1beta1.BaseAccount", - "address": "umee1ca6z887q92cmrwx0jxcpm52fh8gf7j0kgjgvhf", - "pub_key": null, - "account_number": "0", - "sequence": "0" - }, - { - "@type": "/cosmos.auth.v1beta1.BaseAccount", - "address": "umee1gczvdlfhljmvt7yzm3l5654fmjp4ucaael9jvr", - "pub_key": null, - "account_number": "0", - "sequence": "0" - }, - { - "@type": "/cosmos.auth.v1beta1.BaseAccount", - "address": "umee1fmca3ew9r5qfy3c66w69swwpm8czhccnulu6fp", - "pub_key": null, - "account_number": "0", - "sequence": "0" - }, - { - "@type": "/cosmos.auth.v1beta1.BaseAccount", - "address": "umee1hwn8tc4z4e43jt02tdjza0jzylqy4t4ry4ung5", - "pub_key": null, - "account_number": "0", - "sequence": "0" - }, - { - "@type": "/cosmos.auth.v1beta1.BaseAccount", - "address": "umee1385dtql6vzs9uqjz5grmf9ls49lsx88lehcgmv", - "pub_key": null, - "account_number": "0", - "sequence": "0" - }, - { - "@type": "/cosmos.auth.v1beta1.BaseAccount", - "address": "umee1a7aydr659p03j4svzqs5kkddmz7w70slu5hgch", - "pub_key": null, - "account_number": "0", - "sequence": "0" - }, - { - "@type": "/cosmos.auth.v1beta1.BaseAccount", - "address": "umee1jp90x75tnv87h8faz98jzjdp4hkgtrzraa3k9z", - "pub_key": null, - "account_number": "0", - "sequence": "0" - }, - { - "@type": "/cosmos.auth.v1beta1.BaseAccount", - "address": "umee1nc26yck588gpskuenf4y693vpgqjk590f9q594", - "pub_key": null, - "account_number": "0", - "sequence": "0" - }, - { - "@type": "/cosmos.auth.v1beta1.BaseAccount", - "address": "umee1l6tmph3s9vhg5m5t5akcc5yap2cmqh6emjv5uz", - "pub_key": null, - "account_number": "0", - "sequence": "0" - } - ] - }, - "authz": { - "authorization": [] - }, - "bank": { - "params": { - "send_enabled": [], - "default_send_enabled": true + "evidence": { + "max_age_num_blocks": "100000", + "max_age_duration": "172800000000000", + "max_bytes": "1048576" }, - "balances": [ - { - "address": "umee1zl7pjgzfmcgwa0c7vgxklmkykuvmu77yxj75mp", - "coins": [ - { - "denom": "uumee", - "amount": "90000000000000000" - } - ] - }, - { - "address": "umee1xpyya7quj4rx2pm3xclspyvh5gmfxwn2hwlt80", - "coins": [ - { - "denom": "uumee", - "amount": "90000000000000000" - } - ] - }, - { - "address": "umee1gczvdlfhljmvt7yzm3l5654fmjp4ucaael9jvr", - "coins": [ - { - "denom": "uumee", - "amount": "90000000000000000" - } - ] - }, - { - "address": "umee1fmca3ew9r5qfy3c66w69swwpm8czhccnulu6fp", - "coins": [ - { - "denom": "uumee", - "amount": "90000000000000000" - } - ] - }, - { - "address": "umee1dak0cr5ha793jsrxkn3lghyc9ptkdj322m5xvr", - "coins": [ - { - "denom": "uumee", - "amount": "90000000000000000" - } - ] - }, - { - "address": "umee1385dtql6vzs9uqjz5grmf9ls49lsx88lehcgmv", - "coins": [ - { - "denom": "uumee", - "amount": "90000000000000000" - } - ] - }, - { - "address": "umee1jp90x75tnv87h8faz98jzjdp4hkgtrzraa3k9z", - "coins": [ - { - "denom": "uumee", - "amount": "90000000000000000" - } - ] - }, - { - "address": "umee1nc26yck588gpskuenf4y693vpgqjk590f9q594", - "coins": [ - { - "denom": "uumee", - "amount": "90000000000000000" - } - ] - }, - { - "address": "umee15a0n83ggtlzlu5jyny7ecdx2a5g4az47um3whs", - "coins": [ - { - "denom": "uumee", - "amount": "90000000000000000" - } - ] - }, - { - "address": "umee1kpqyxsafxzw3k07rmhycf3jtwtmntrlgvdnj67", - "coins": [ - { - "denom": "uumee", - "amount": "90000000000000000" - } - ] - }, - { - "address": "umee1hwn8tc4z4e43jt02tdjza0jzylqy4t4ry4ung5", - "coins": [ - { - "denom": "uumee", - "amount": "90000000000000000" - } - ] - }, - { - "address": "umee1ca6z887q92cmrwx0jxcpm52fh8gf7j0kgjgvhf", - "coins": [ - { - "denom": "uumee", - "amount": "90000000000000000" - } - ] - }, - { - "address": "umee1e98evcznumc2njy9z36x5cv2p75lsagzfkwyqv", - "coins": [ - { - "denom": "uumee", - "amount": "90000000000000000" - } - ] - }, - { - "address": "umee1a7aydr659p03j4svzqs5kkddmz7w70slu5hgch", - "coins": [ - { - "denom": "uumee", - "amount": "90000000000000000" - } - ] - }, - { - "address": "umee1l6tmph3s9vhg5m5t5akcc5yap2cmqh6emjv5uz", - "coins": [ - { - "denom": "uumee", - "amount": "90000000000000000" - } - ] - } - ], - "supply": [], - "denom_metadata": [ - { - "base": "uumee", - "denom_units": [ - { - "aliases": [ - "microumee" - ], - "denom": "uumee", - "exponent": 0 - }, - { - "aliases": [], - "denom": "UMEE", - "exponent": 6 - } - ], - "description": "The native staking token of the Umee network.", - "display": "UMEE", - "name": "UMEE", - "symbol": "UMEE" - }, - { - "base": "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2", - "denom_units": [ - { - "aliases": [ - "microatom" - ], - "denom": "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2", - "exponent": 0 - }, - { - "aliases": [], - "denom": "ATOM", - "exponent": 6 - } - ], - "description": "The native staking and governance token of Gaia", - "display": "ATOM", - "name": "Cosmos", - "symbol": "ATOM" - }, - { - "base": "ibc/04F5F501207C3626A2C14BFEF654D51C2E0B8F7CA578AB8ED272A66FE4E48097", - "denom_units": [ - { - "aliases": [ - "microjuno" - ], - "denom": "ibc/04F5F501207C3626A2C14BFEF654D51C2E0B8F7CA578AB8ED272A66FE4E48097", - "exponent": 0 - }, - { - "aliases": [], - "denom": "JUNO", - "exponent": 6 - } - ], - "description": "The native staking and governance token of Juno", - "display": "JUNO", - "name": "Juno", - "symbol": "JUNO" - }, - { - "base": "ibc/ED07A3391A112B175915CD8FAF43A2DA8E4790EDE12566649D0C2F97716B8518", - "denom_units": [ - { - "aliases": [ - "microatom" - ], - "denom": "ibc/ED07A3391A112B175915CD8FAF43A2DA8E4790EDE12566649D0C2F97716B8518", - "exponent": 0 - }, - { - "aliases": [], - "denom": "OSMO", - "exponent": 6 - } - ], - "description": "The native staking and governance token of Osmosis", - "display": "OSMO", - "name": "Osmosis", - "symbol": "OSMO" - } - ] - }, - "bech32ibc": { - "nativeHRP": "osmo", - "hrpIBCRecords": [] - }, - "capability": { - "index": "1", - "owners": [] - }, - "crisis": { - "constant_fee": { - "denom": "uumee", - "amount": "1000" - } + "validator": { + "pub_key_types": ["ed25519"] + }, + "version": {} }, - "distribution": { - "params": { - "community_tax": "0.020000000000000000", - "base_proposer_reward": "0.010000000000000000", - "bonus_proposer_reward": "0.040000000000000000", - "withdraw_addr_enabled": true + "app_hash": "", + "app_state": { + "auth": { + "params": { + "max_memo_characters": "256", + "tx_sig_limit": "7", + "tx_size_cost_per_byte": "10", + "sig_verify_cost_ed25519": "590", + "sig_verify_cost_secp256k1": "1000" + }, + "accounts": [ + { + "@type": "/cosmos.auth.v1beta1.BaseAccount", + "address": "umee1xpyya7quj4rx2pm3xclspyvh5gmfxwn2hwlt80", + "pub_key": null, + "account_number": "0", + "sequence": "0" + }, + { + "@type": "/cosmos.auth.v1beta1.BaseAccount", + "address": "umee1kpqyxsafxzw3k07rmhycf3jtwtmntrlgvdnj67", + "pub_key": null, + "account_number": "0", + "sequence": "0" + }, + { + "@type": "/cosmos.auth.v1beta1.BaseAccount", + "address": "umee1zl7pjgzfmcgwa0c7vgxklmkykuvmu77yxj75mp", + "pub_key": null, + "account_number": "0", + "sequence": "0" + }, + { + "@type": "/cosmos.auth.v1beta1.BaseAccount", + "address": "umee1e98evcznumc2njy9z36x5cv2p75lsagzfkwyqv", + "pub_key": null, + "account_number": "0", + "sequence": "0" + }, + { + "@type": "/cosmos.auth.v1beta1.BaseAccount", + "address": "umee1dak0cr5ha793jsrxkn3lghyc9ptkdj322m5xvr", + "pub_key": null, + "account_number": "0", + "sequence": "0" + }, + { + "@type": "/cosmos.auth.v1beta1.BaseAccount", + "address": "umee15a0n83ggtlzlu5jyny7ecdx2a5g4az47um3whs", + "pub_key": null, + "account_number": "0", + "sequence": "0" + }, + { + "@type": "/cosmos.auth.v1beta1.BaseAccount", + "address": "umee1ca6z887q92cmrwx0jxcpm52fh8gf7j0kgjgvhf", + "pub_key": null, + "account_number": "0", + "sequence": "0" + }, + { + "@type": "/cosmos.auth.v1beta1.BaseAccount", + "address": "umee1gczvdlfhljmvt7yzm3l5654fmjp4ucaael9jvr", + "pub_key": null, + "account_number": "0", + "sequence": "0" + }, + { + "@type": "/cosmos.auth.v1beta1.BaseAccount", + "address": "umee1fmca3ew9r5qfy3c66w69swwpm8czhccnulu6fp", + "pub_key": null, + "account_number": "0", + "sequence": "0" + }, + { + "@type": "/cosmos.auth.v1beta1.BaseAccount", + "address": "umee1hwn8tc4z4e43jt02tdjza0jzylqy4t4ry4ung5", + "pub_key": null, + "account_number": "0", + "sequence": "0" + }, + { + "@type": "/cosmos.auth.v1beta1.BaseAccount", + "address": "umee1385dtql6vzs9uqjz5grmf9ls49lsx88lehcgmv", + "pub_key": null, + "account_number": "0", + "sequence": "0" + }, + { + "@type": "/cosmos.auth.v1beta1.BaseAccount", + "address": "umee1a7aydr659p03j4svzqs5kkddmz7w70slu5hgch", + "pub_key": null, + "account_number": "0", + "sequence": "0" + }, + { + "@type": "/cosmos.auth.v1beta1.BaseAccount", + "address": "umee1jp90x75tnv87h8faz98jzjdp4hkgtrzraa3k9z", + "pub_key": null, + "account_number": "0", + "sequence": "0" + }, + { + "@type": "/cosmos.auth.v1beta1.BaseAccount", + "address": "umee1nc26yck588gpskuenf4y693vpgqjk590f9q594", + "pub_key": null, + "account_number": "0", + "sequence": "0" + }, + { + "@type": "/cosmos.auth.v1beta1.BaseAccount", + "address": "umee1l6tmph3s9vhg5m5t5akcc5yap2cmqh6emjv5uz", + "pub_key": null, + "account_number": "0", + "sequence": "0" + } + ] }, - "fee_pool": { - "community_pool": [] + "authz": { + "authorization": [] }, - "delegator_withdraw_infos": [], - "previous_proposer": "", - "outstanding_rewards": [], - "validator_accumulated_commissions": [], - "validator_historical_rewards": [], - "validator_current_rewards": [], - "delegator_starting_infos": [], - "validator_slash_events": [] - }, - "evidence": { - "evidence": [] - }, - "feegrant": { - "allowances": [] - }, - "genutil": { - "gen_txs": [ - { - "body": { - "messages": [ + "bank": { + "params": { + "send_enabled": [], + "default_send_enabled": true + }, + "balances": [ + { + "address": "umee1zl7pjgzfmcgwa0c7vgxklmkykuvmu77yxj75mp", + "coins": [ { - "@type": "/cosmos.staking.v1beta1.MsgCreateValidator", - "description": { - "moniker": "banana", - "identity": "", - "website": "", - "security_contact": "", - "details": "" - }, - "commission": { - "rate": "0.100000000000000000", - "max_rate": "0.200000000000000000", - "max_change_rate": "0.010000000000000000" - }, - "min_self_delegation": "1", - "delegator_address": "umee15a0n83ggtlzlu5jyny7ecdx2a5g4az47um3whs", - "validator_address": "umeevaloper15a0n83ggtlzlu5jyny7ecdx2a5g4az47ulkpx6", - "pubkey": { - "@type": "/cosmos.crypto.ed25519.PubKey", - "key": "Q0c69lMlQRagx+Nk7pylSJyVp4Vfck/5TzeJ2z1G9Vg=" - }, - "value": { - "denom": "uumee", - "amount": "100000000000" - } - }, + "denom": "uumee", + "amount": "90000000000000000" + } + ] + }, + { + "address": "umee1xpyya7quj4rx2pm3xclspyvh5gmfxwn2hwlt80", + "coins": [ { - "@type": "/gravity.v1.MsgSetOrchestratorAddress", - "validator": "umeevaloper15a0n83ggtlzlu5jyny7ecdx2a5g4az47ulkpx6", - "orchestrator": "umee1gczvdlfhljmvt7yzm3l5654fmjp4ucaael9jvr", - "eth_address": "0xfac87ECc6009Be4a856CB30846F82ea0B94d8C01" + "denom": "uumee", + "amount": "90000000000000000" } - ], - "memo": "08840edc6775bd7a298269f3343c554e5af0359f@172.17.0.2:26656", - "timeout_height": "0", - "extension_options": [], - "non_critical_extension_options": [] + ] }, - "auth_info": { - "signer_infos": [ + { + "address": "umee1gczvdlfhljmvt7yzm3l5654fmjp4ucaael9jvr", + "coins": [ { - "public_key": { - "@type": "/cosmos.crypto.secp256k1.PubKey", - "key": "A5MfFNWQUPRuw0GDlKwSr8p3ptDjYl5UyZkxQabWwaP5" - }, - "mode_info": { - "single": { - "mode": "SIGN_MODE_DIRECT" - } - }, - "sequence": "0" + "denom": "uumee", + "amount": "90000000000000000" } - ], - "fee": { - "amount": [], - "gas_limit": "200000", - "payer": "", - "granter": "" - } + ] }, - "signatures": [ - "KnVWLes4AWR+YN6QChksZLRJ5q7vW5Fq0Gg5WmuvvIdaJ0V8Xz9mvsbfaFoyA8Lmh9N4sbPX/Cq/PzD5QrI/wA==" - ] - }, - { - "body": { - "messages": [ + { + "address": "umee1fmca3ew9r5qfy3c66w69swwpm8czhccnulu6fp", + "coins": [ { - "@type": "/cosmos.staking.v1beta1.MsgCreateValidator", - "description": { - "moniker": "alley", - "identity": "", - "website": "", - "security_contact": "", - "details": "" - }, - "commission": { - "rate": "0.100000000000000000", - "max_rate": "0.200000000000000000", - "max_change_rate": "0.010000000000000000" - }, - "min_self_delegation": "1", - "delegator_address": "umee1jp90x75tnv87h8faz98jzjdp4hkgtrzraa3k9z", - "validator_address": "umeevaloper1jp90x75tnv87h8faz98jzjdp4hkgtrzraeke5g", - "pubkey": { - "@type": "/cosmos.crypto.ed25519.PubKey", - "key": "yarR/0r9WW9IuFOIxj5oLIMMZKamUid6DnkyMp9J9nU=" - }, - "value": { - "denom": "uumee", - "amount": "100000000000" - } + "denom": "uumee", + "amount": "90000000000000000" + } + ] + }, + { + "address": "umee1dak0cr5ha793jsrxkn3lghyc9ptkdj322m5xvr", + "coins": [ + { + "denom": "uumee", + "amount": "90000000000000000" + } + ] + }, + { + "address": "umee1385dtql6vzs9uqjz5grmf9ls49lsx88lehcgmv", + "coins": [ + { + "denom": "uumee", + "amount": "90000000000000000" + } + ] + }, + { + "address": "umee1jp90x75tnv87h8faz98jzjdp4hkgtrzraa3k9z", + "coins": [ + { + "denom": "uumee", + "amount": "90000000000000000" + } + ] + }, + { + "address": "umee1nc26yck588gpskuenf4y693vpgqjk590f9q594", + "coins": [ + { + "denom": "uumee", + "amount": "90000000000000000" + } + ] + }, + { + "address": "umee15a0n83ggtlzlu5jyny7ecdx2a5g4az47um3whs", + "coins": [ + { + "denom": "uumee", + "amount": "90000000000000000" + } + ] + }, + { + "address": "umee1kpqyxsafxzw3k07rmhycf3jtwtmntrlgvdnj67", + "coins": [ + { + "denom": "uumee", + "amount": "90000000000000000" + } + ] + }, + { + "address": "umee1hwn8tc4z4e43jt02tdjza0jzylqy4t4ry4ung5", + "coins": [ + { + "denom": "uumee", + "amount": "90000000000000000" + } + ] + }, + { + "address": "umee1ca6z887q92cmrwx0jxcpm52fh8gf7j0kgjgvhf", + "coins": [ + { + "denom": "uumee", + "amount": "90000000000000000" + } + ] + }, + { + "address": "umee1e98evcznumc2njy9z36x5cv2p75lsagzfkwyqv", + "coins": [ + { + "denom": "uumee", + "amount": "90000000000000000" + } + ] + }, + { + "address": "umee1a7aydr659p03j4svzqs5kkddmz7w70slu5hgch", + "coins": [ + { + "denom": "uumee", + "amount": "90000000000000000" + } + ] + }, + { + "address": "umee1l6tmph3s9vhg5m5t5akcc5yap2cmqh6emjv5uz", + "coins": [ + { + "denom": "uumee", + "amount": "90000000000000000" + } + ] + } + ], + "supply": [], + "denom_metadata": [ + { + "base": "uumee", + "denom_units": [ + { + "aliases": ["microumee"], + "denom": "uumee", + "exponent": 0 }, { - "@type": "/gravity.v1.MsgSetOrchestratorAddress", - "validator": "umeevaloper1jp90x75tnv87h8faz98jzjdp4hkgtrzraeke5g", - "orchestrator": "umee1l6tmph3s9vhg5m5t5akcc5yap2cmqh6emjv5uz", - "eth_address": "0xfacEFb63F7CDFf0ca7a7A064639b3956Ad9f3acC" + "aliases": [], + "denom": "UMEE", + "exponent": 6 } ], - "memo": "08f30f7119ae1317082d50fac6a08e6f2400126f@172.17.0.2:26656", - "timeout_height": "0", - "extension_options": [], - "non_critical_extension_options": [] + "description": "The native staking token of the Umee network.", + "display": "UMEE", + "name": "UMEE", + "symbol": "UMEE" }, - "auth_info": { - "signer_infos": [ + { + "base": "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2", + "denom_units": [ { - "public_key": { - "@type": "/cosmos.crypto.secp256k1.PubKey", - "key": "Aoux/zmfmVaIEzmpjgV6k2oXbqVVP7iXKPng0XxiiHB6" - }, - "mode_info": { - "single": { - "mode": "SIGN_MODE_DIRECT" - } - }, - "sequence": "0" + "aliases": ["microatom"], + "denom": "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2", + "exponent": 0 + }, + { + "aliases": [], + "denom": "ATOM", + "exponent": 6 } ], - "fee": { - "amount": [], - "gas_limit": "200000", - "payer": "", - "granter": "" - } + "description": "The native staking and governance token of Gaia", + "display": "ATOM", + "name": "Cosmos", + "symbol": "ATOM" }, - "signatures": [ - "4lhhQT1SS+ycJ583K3K5KiTP6moiEhscFrAF5N69NXxj1yiMy7v2N64rH7bYhoDmxs9QXAx8mAtrHQAsGJDPgQ==" - ] - }, - { - "body": { - "messages": [ + { + "base": "ibc/04F5F501207C3626A2C14BFEF654D51C2E0B8F7CA578AB8ED272A66FE4E48097", + "denom_units": [ { - "@type": "/cosmos.staking.v1beta1.MsgCreateValidator", - "description": { - "moniker": "festival", - "identity": "", - "website": "", - "security_contact": "", - "details": "" - }, - "commission": { - "rate": "0.100000000000000000", - "max_rate": "0.200000000000000000", - "max_change_rate": "0.010000000000000000" - }, - "min_self_delegation": "1", - "delegator_address": "umee1xpyya7quj4rx2pm3xclspyvh5gmfxwn2hwlt80", - "validator_address": "umeevaloper1xpyya7quj4rx2pm3xclspyvh5gmfxwn2h2cyk9", - "pubkey": { - "@type": "/cosmos.crypto.ed25519.PubKey", - "key": "pppHqyu2KFGb08WVT/wE7aW3FxKM36taED+cyLt7dEE=" - }, - "value": { - "denom": "uumee", - "amount": "100000000000" - } + "aliases": ["microjuno"], + "denom": "ibc/04F5F501207C3626A2C14BFEF654D51C2E0B8F7CA578AB8ED272A66FE4E48097", + "exponent": 0 }, { - "@type": "/gravity.v1.MsgSetOrchestratorAddress", - "validator": "umeevaloper1xpyya7quj4rx2pm3xclspyvh5gmfxwn2h2cyk9", - "orchestrator": "umee1zl7pjgzfmcgwa0c7vgxklmkykuvmu77yxj75mp", - "eth_address": "0xfacf66789DD2fA6d80A36353f900922cb6D990F1" + "aliases": [], + "denom": "JUNO", + "exponent": 6 } ], - "memo": "67cd50cac0eff503a7c9efd537d369cc7a3ebe96@172.17.0.2:26656", - "timeout_height": "0", - "extension_options": [], - "non_critical_extension_options": [] + "description": "The native staking and governance token of Juno", + "display": "JUNO", + "name": "Juno", + "symbol": "JUNO" }, - "auth_info": { - "signer_infos": [ + { + "base": "ibc/ED07A3391A112B175915CD8FAF43A2DA8E4790EDE12566649D0C2F97716B8518", + "denom_units": [ { - "public_key": { - "@type": "/cosmos.crypto.secp256k1.PubKey", - "key": "A7ikWhnGN/ayRth06lI8+Dr71Irs9EM2W6ZmdHCulIz7" - }, - "mode_info": { - "single": { - "mode": "SIGN_MODE_DIRECT" - } - }, - "sequence": "0" + "aliases": ["microatom"], + "denom": "ibc/ED07A3391A112B175915CD8FAF43A2DA8E4790EDE12566649D0C2F97716B8518", + "exponent": 0 + }, + { + "aliases": [], + "denom": "OSMO", + "exponent": 6 } ], - "fee": { - "amount": [], - "gas_limit": "200000", - "payer": "", - "granter": "" - } - }, - "signatures": [ - "Rwm/C5SKEJlyzPJs2h2t+t2GORYQ1OiWqayz26Y0yXRN+p6wZBQHWYnf3BxBNZlhEmE+R3QzI5gI4UuvNFxl6g==" - ] - } - ] - }, - "gov": { - "starting_proposal_id": "1", - "deposits": [], - "votes": [], - "proposals": [], - "deposit_params": { - "min_deposit": [ - { - "denom": "uumee", - "amount": "10000000" + "description": "The native staking and governance token of Osmosis", + "display": "OSMO", + "name": "Osmosis", + "symbol": "OSMO" } - ], - "max_deposit_period": "172800s" + ] }, - "voting_params": { - "voting_period": "600s" + "bech32ibc": { + "nativeHRP": "osmo", + "hrpIBCRecords": [] }, - "tally_params": { - "quorum": "0.334000000000000000", - "threshold": "0.500000000000000000", - "veto_threshold": "0.334000000000000000" - } - }, - "gravity": { - "params": { - "gravity_id": "defaultgravityid", - "contract_source_hash": "", - "bridge_ethereum_address": "0x0000000000000000000000000000000000000000", - "bridge_chain_id": "5", - "signed_valsets_window": "10000", - "signed_batches_window": "10000", - "signed_logic_calls_window": "10000", - "target_batch_timeout": "43200000", - "average_block_time": "5000", - "average_ethereum_block_time": "15000", - "slash_fraction_valset": "0.001000000000000000", - "slash_fraction_batch": "0.001000000000000000", - "slash_fraction_logic_call": "0.001000000000000000", - "unbond_slashing_valsets_window": "10000", - "slash_fraction_bad_eth_signature": "0.001000000000000000", - "valset_reward": { - "denom": "", - "amount": "0" - }, - "bridge_active": true, - "ethereum_blacklist": [] + "capability": { + "index": "1", + "owners": [] }, - "gravity_nonces": { - "latest_valset_nonce": "0", - "last_observed_nonce": "0", - "last_slashed_valset_nonce": "0", - "last_slashed_batch_block": "0", - "last_slashed_logic_call_block": "0", - "last_tx_pool_id": "0", - "last_batch_id": "0" + "crisis": { + "constant_fee": { + "denom": "uumee", + "amount": "1000" + } }, - "valsets": [], - "valset_confirms": [], - "batches": [], - "batch_confirms": [], - "logic_calls": [], - "logic_call_confirms": [], - "attestations": [], - "delegate_keys": [], - "erc20_to_denoms": [], - "unbatched_transfers": [] - }, - "ibc": { - "client_genesis": { - "clients": [], - "clients_consensus": [], - "clients_metadata": [], + "distribution": { "params": { - "allowed_clients": [ - "06-solomachine", - "07-tendermint" - ] - }, - "create_localhost": false, - "next_client_sequence": "0" + "community_tax": "0.020000000000000000", + "base_proposer_reward": "0.010000000000000000", + "bonus_proposer_reward": "0.040000000000000000", + "withdraw_addr_enabled": true + }, + "fee_pool": { + "community_pool": [] + }, + "delegator_withdraw_infos": [], + "previous_proposer": "", + "outstanding_rewards": [], + "validator_accumulated_commissions": [], + "validator_historical_rewards": [], + "validator_current_rewards": [], + "delegator_starting_infos": [], + "validator_slash_events": [] }, - "connection_genesis": { - "connections": [], - "client_connection_paths": [], - "next_connection_sequence": "0", - "params": { - "max_expected_time_per_block": "30000000000" - } + "evidence": { + "evidence": [] }, - "channel_genesis": { - "channels": [], - "acknowledgements": [], - "commitments": [], - "receipts": [], - "send_sequences": [], - "recv_sequences": [], - "ack_sequences": [], - "next_channel_sequence": "0" - } - }, - "leverage": { - "params": { - "complete_liquidation_threshold": "0.100000000000000000", - "minimum_close_factor": "0.010000000000000000", - "oracle_reward_factor": "0.010000000000000000", - "small_liquidation_size": "100.000000000000000000" + "feegrant": { + "allowances": [] }, - "registry": [ - { - "base_borrow_rate": "0.05", - "base_denom": "uumee", - "collateral_weight": "0.10", - "exponent": 6, - "kink_borrow_rate": "0.30", - "kink_utilization_rate": "0.80", - "liquidation_incentive": "0.10", - "liquidation_threshold": "0.15", - "max_borrow_rate": "0.70", - "reserve_factor": "0.10", - "symbol_denom": "UMEE" - }, - { - "base_borrow_rate": "0.02", - "base_denom": "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2", - "collateral_weight": "0.50", - "exponent": 6, - "kink_borrow_rate": "0.20", - "kink_utilization_rate": "0.80", - "liquidation_incentive": "0.10", - "liquidation_threshold": "0.55", - "max_borrow_rate": "0.80", - "reserve_factor": "0.10", - "symbol_denom": "ATOM" + "genutil": { + "gen_txs": [ + { + "body": { + "messages": [ + { + "@type": "/cosmos.staking.v1beta1.MsgCreateValidator", + "description": { + "moniker": "banana", + "identity": "", + "website": "", + "security_contact": "", + "details": "" + }, + "commission": { + "rate": "0.100000000000000000", + "max_rate": "0.200000000000000000", + "max_change_rate": "0.010000000000000000" + }, + "min_self_delegation": "1", + "delegator_address": "umee15a0n83ggtlzlu5jyny7ecdx2a5g4az47um3whs", + "validator_address": "umeevaloper15a0n83ggtlzlu5jyny7ecdx2a5g4az47ulkpx6", + "pubkey": { + "@type": "/cosmos.crypto.ed25519.PubKey", + "key": "Q0c69lMlQRagx+Nk7pylSJyVp4Vfck/5TzeJ2z1G9Vg=" + }, + "value": { + "denom": "uumee", + "amount": "100000000000" + } + }, + { + "@type": "/gravity.v1.MsgSetOrchestratorAddress", + "validator": "umeevaloper15a0n83ggtlzlu5jyny7ecdx2a5g4az47ulkpx6", + "orchestrator": "umee1gczvdlfhljmvt7yzm3l5654fmjp4ucaael9jvr", + "eth_address": "0xfac87ECc6009Be4a856CB30846F82ea0B94d8C01" + } + ], + "memo": "08840edc6775bd7a298269f3343c554e5af0359f@172.17.0.2:26656", + "timeout_height": "0", + "extension_options": [], + "non_critical_extension_options": [] + }, + "auth_info": { + "signer_infos": [ + { + "public_key": { + "@type": "/cosmos.crypto.secp256k1.PubKey", + "key": "A5MfFNWQUPRuw0GDlKwSr8p3ptDjYl5UyZkxQabWwaP5" + }, + "mode_info": { + "single": { + "mode": "SIGN_MODE_DIRECT" + } + }, + "sequence": "0" + } + ], + "fee": { + "amount": [], + "gas_limit": "200000", + "payer": "", + "granter": "" + } + }, + "signatures": [ + "KnVWLes4AWR+YN6QChksZLRJ5q7vW5Fq0Gg5WmuvvIdaJ0V8Xz9mvsbfaFoyA8Lmh9N4sbPX/Cq/PzD5QrI/wA==" + ] + }, + { + "body": { + "messages": [ + { + "@type": "/cosmos.staking.v1beta1.MsgCreateValidator", + "description": { + "moniker": "alley", + "identity": "", + "website": "", + "security_contact": "", + "details": "" + }, + "commission": { + "rate": "0.100000000000000000", + "max_rate": "0.200000000000000000", + "max_change_rate": "0.010000000000000000" + }, + "min_self_delegation": "1", + "delegator_address": "umee1jp90x75tnv87h8faz98jzjdp4hkgtrzraa3k9z", + "validator_address": "umeevaloper1jp90x75tnv87h8faz98jzjdp4hkgtrzraeke5g", + "pubkey": { + "@type": "/cosmos.crypto.ed25519.PubKey", + "key": "yarR/0r9WW9IuFOIxj5oLIMMZKamUid6DnkyMp9J9nU=" + }, + "value": { + "denom": "uumee", + "amount": "100000000000" + } + }, + { + "@type": "/gravity.v1.MsgSetOrchestratorAddress", + "validator": "umeevaloper1jp90x75tnv87h8faz98jzjdp4hkgtrzraeke5g", + "orchestrator": "umee1l6tmph3s9vhg5m5t5akcc5yap2cmqh6emjv5uz", + "eth_address": "0xfacEFb63F7CDFf0ca7a7A064639b3956Ad9f3acC" + } + ], + "memo": "08f30f7119ae1317082d50fac6a08e6f2400126f@172.17.0.2:26656", + "timeout_height": "0", + "extension_options": [], + "non_critical_extension_options": [] + }, + "auth_info": { + "signer_infos": [ + { + "public_key": { + "@type": "/cosmos.crypto.secp256k1.PubKey", + "key": "Aoux/zmfmVaIEzmpjgV6k2oXbqVVP7iXKPng0XxiiHB6" + }, + "mode_info": { + "single": { + "mode": "SIGN_MODE_DIRECT" + } + }, + "sequence": "0" + } + ], + "fee": { + "amount": [], + "gas_limit": "200000", + "payer": "", + "granter": "" + } + }, + "signatures": [ + "4lhhQT1SS+ycJ583K3K5KiTP6moiEhscFrAF5N69NXxj1yiMy7v2N64rH7bYhoDmxs9QXAx8mAtrHQAsGJDPgQ==" + ] + }, + { + "body": { + "messages": [ + { + "@type": "/cosmos.staking.v1beta1.MsgCreateValidator", + "description": { + "moniker": "festival", + "identity": "", + "website": "", + "security_contact": "", + "details": "" + }, + "commission": { + "rate": "0.100000000000000000", + "max_rate": "0.200000000000000000", + "max_change_rate": "0.010000000000000000" + }, + "min_self_delegation": "1", + "delegator_address": "umee1xpyya7quj4rx2pm3xclspyvh5gmfxwn2hwlt80", + "validator_address": "umeevaloper1xpyya7quj4rx2pm3xclspyvh5gmfxwn2h2cyk9", + "pubkey": { + "@type": "/cosmos.crypto.ed25519.PubKey", + "key": "pppHqyu2KFGb08WVT/wE7aW3FxKM36taED+cyLt7dEE=" + }, + "value": { + "denom": "uumee", + "amount": "100000000000" + } + }, + { + "@type": "/gravity.v1.MsgSetOrchestratorAddress", + "validator": "umeevaloper1xpyya7quj4rx2pm3xclspyvh5gmfxwn2h2cyk9", + "orchestrator": "umee1zl7pjgzfmcgwa0c7vgxklmkykuvmu77yxj75mp", + "eth_address": "0xfacf66789DD2fA6d80A36353f900922cb6D990F1" + } + ], + "memo": "67cd50cac0eff503a7c9efd537d369cc7a3ebe96@172.17.0.2:26656", + "timeout_height": "0", + "extension_options": [], + "non_critical_extension_options": [] + }, + "auth_info": { + "signer_infos": [ + { + "public_key": { + "@type": "/cosmos.crypto.secp256k1.PubKey", + "key": "A7ikWhnGN/ayRth06lI8+Dr71Irs9EM2W6ZmdHCulIz7" + }, + "mode_info": { + "single": { + "mode": "SIGN_MODE_DIRECT" + } + }, + "sequence": "0" + } + ], + "fee": { + "amount": [], + "gas_limit": "200000", + "payer": "", + "granter": "" + } + }, + "signatures": [ + "Rwm/C5SKEJlyzPJs2h2t+t2GORYQ1OiWqayz26Y0yXRN+p6wZBQHWYnf3BxBNZlhEmE+R3QzI5gI4UuvNFxl6g==" + ] + } + ] + }, + "gov": { + "starting_proposal_id": "1", + "deposits": [], + "votes": [], + "proposals": [], + "deposit_params": { + "min_deposit": [ + { + "denom": "uumee", + "amount": "10000000" + } + ], + "max_deposit_period": "172800s" }, - { - "base_borrow_rate": "0.02", - "base_denom": "ibc/04F5F501207C3626A2C14BFEF654D51C2E0B8F7CA578AB8ED272A66FE4E48097", - "collateral_weight": "0.35", - "exponent": 6, - "kink_borrow_rate": "0.20", - "kink_utilization_rate": "0.80", - "liquidation_incentive": "0.10", - "liquidation_threshold": "0.40", - "max_borrow_rate": "0.80", - "reserve_factor": "0.10", - "symbol_denom": "JUNO" + "voting_params": { + "voting_period": "600s" }, - { - "base_borrow_rate": "0.02", - "base_denom": "ibc/ED07A3391A112B175915CD8FAF43A2DA8E4790EDE12566649D0C2F97716B8518", - "collateral_weight": "0.60", - "exponent": 6, - "kink_borrow_rate": "0.20", - "kink_utilization_rate": "0.80", - "liquidation_incentive": "0.10", - "liquidation_threshold": "0.65", - "max_borrow_rate": "0.80", - "reserve_factor": "0.10", - "symbol_denom": "OSMO" + "tally_params": { + "quorum": "0.334000000000000000", + "threshold": "0.500000000000000000", + "veto_threshold": "0.334000000000000000" + } + }, + "gravity": { + "params": { + "gravity_id": "defaultgravityid", + "contract_source_hash": "", + "bridge_ethereum_address": "0x0000000000000000000000000000000000000000", + "bridge_chain_id": "5", + "signed_valsets_window": "10000", + "signed_batches_window": "10000", + "signed_logic_calls_window": "10000", + "target_batch_timeout": "43200000", + "average_block_time": "5000", + "average_ethereum_block_time": "15000", + "slash_fraction_valset": "0.001000000000000000", + "slash_fraction_batch": "0.001000000000000000", + "slash_fraction_logic_call": "0.001000000000000000", + "unbond_slashing_valsets_window": "10000", + "slash_fraction_bad_eth_signature": "0.001000000000000000", + "valset_reward": { + "denom": "", + "amount": "0" + }, + "bridge_active": true, + "ethereum_blacklist": [] + }, + "gravity_nonces": { + "latest_valset_nonce": "0", + "last_observed_nonce": "0", + "last_slashed_valset_nonce": "0", + "last_slashed_batch_block": "0", + "last_slashed_logic_call_block": "0", + "last_tx_pool_id": "0", + "last_batch_id": "0" + }, + "valsets": [], + "valset_confirms": [], + "batches": [], + "batch_confirms": [], + "logic_calls": [], + "logic_call_confirms": [], + "attestations": [], + "delegate_keys": [], + "erc20_to_denoms": [], + "unbatched_transfers": [] + }, + "ibc": { + "client_genesis": { + "clients": [], + "clients_consensus": [], + "clients_metadata": [], + "params": { + "allowed_clients": ["06-solomachine", "07-tendermint"] + }, + "create_localhost": false, + "next_client_sequence": "0" + }, + "connection_genesis": { + "connections": [], + "client_connection_paths": [], + "next_connection_sequence": "0", + "params": { + "max_expected_time_per_block": "30000000000" + } }, - { - "base_borrow_rate": "0.03", - "base_denom": "gravity0xd787Ec2b6C962f611300175603741Db8438674a0", - "collateral_weight": "0.75", - "exponent": 18, - "kink_borrow_rate": "0.15", - "kink_utilization_rate": "0.75", - "liquidation_incentive": "0.05", - "liquidation_threshold": "0.80", - "max_borrow_rate": "0.30", - "reserve_factor": "0.10", - "symbol_denom": "DAI" + "channel_genesis": { + "channels": [], + "acknowledgements": [], + "commitments": [], + "receipts": [], + "send_sequences": [], + "recv_sequences": [], + "ack_sequences": [], + "next_channel_sequence": "0" } - ], - "adjusted_borrows": [], - "collateral_settings": [], - "collateral": [], - "reserves": [], - "last_interest_time": "0", - "bad_debts": [], - "interest_scalars": [], - "utoken_supply": [] - }, - "mint": { - "minter": { - "inflation": "0.130000000000000000", - "annual_provisions": "0.000000000000000000" }, - "params": { - "mint_denom": "uumee", - "inflation_rate_change": "0.130000000000000000", - "inflation_max": "0.200000000000000000", - "inflation_min": "0.070000000000000000", - "goal_bonded": "0.670000000000000000", - "blocks_per_year": "6311520" - } - }, - "oracle": { - "params": { - "vote_period": "5", - "vote_threshold": "0.500000000000000000", - "reward_band": "0.020000000000000000", - "reward_distribution_window": "5256000", - "accept_list": [ + "leverage": { + "params": { + "complete_liquidation_threshold": "0.100000000000000000", + "minimum_close_factor": "0.010000000000000000", + "oracle_reward_factor": "0.010000000000000000", + "small_liquidation_size": "100.000000000000000000" + }, + "registry": [ { + "base_borrow_rate": "0.05", "base_denom": "uumee", - "symbol_denom": "umee", - "exponent": 6 + "collateral_weight": "0.10", + "exponent": 6, + "kink_borrow_rate": "0.30", + "kink_utilization_rate": "0.80", + "liquidation_incentive": "0.10", + "liquidation_threshold": "0.15", + "max_borrow_rate": "0.70", + "reserve_factor": "0.10", + "symbol_denom": "UMEE" + }, + { + "base_borrow_rate": "0.02", + "base_denom": "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2", + "collateral_weight": "0.50", + "exponent": 6, + "kink_borrow_rate": "0.20", + "kink_utilization_rate": "0.80", + "liquidation_incentive": "0.10", + "liquidation_threshold": "0.55", + "max_borrow_rate": "0.80", + "reserve_factor": "0.10", + "symbol_denom": "ATOM" + }, + { + "base_borrow_rate": "0.02", + "base_denom": "ibc/04F5F501207C3626A2C14BFEF654D51C2E0B8F7CA578AB8ED272A66FE4E48097", + "collateral_weight": "0.35", + "exponent": 6, + "kink_borrow_rate": "0.20", + "kink_utilization_rate": "0.80", + "liquidation_incentive": "0.10", + "liquidation_threshold": "0.40", + "max_borrow_rate": "0.80", + "reserve_factor": "0.10", + "symbol_denom": "JUNO" + }, + { + "base_borrow_rate": "0.02", + "base_denom": "ibc/ED07A3391A112B175915CD8FAF43A2DA8E4790EDE12566649D0C2F97716B8518", + "collateral_weight": "0.60", + "exponent": 6, + "kink_borrow_rate": "0.20", + "kink_utilization_rate": "0.80", + "liquidation_incentive": "0.10", + "liquidation_threshold": "0.65", + "max_borrow_rate": "0.80", + "reserve_factor": "0.10", + "symbol_denom": "OSMO" + }, + { + "base_borrow_rate": "0.03", + "base_denom": "gravity0xd787Ec2b6C962f611300175603741Db8438674a0", + "collateral_weight": "0.75", + "exponent": 18, + "kink_borrow_rate": "0.15", + "kink_utilization_rate": "0.75", + "liquidation_incentive": "0.05", + "liquidation_threshold": "0.80", + "max_borrow_rate": "0.30", + "reserve_factor": "0.10", + "symbol_denom": "DAI" } ], - "slash_fraction": "0.000100000000000000", - "slash_window": "100800", - "min_valid_per_window": "0.050000000000000000" + "adjusted_borrows": [], + "collateral_settings": [], + "collateral": [], + "reserves": [], + "last_interest_time": "0", + "bad_debts": [], + "interest_scalars": [], + "utoken_supply": [] }, - "feeder_delegations": [], - "exchange_rates": [], - "miss_counters": [], - "aggregate_exchange_rate_prevotes": [], - "aggregate_exchange_rate_votes": [] - }, - "params": null, - "slashing": { - "params": { - "signed_blocks_window": "10000", - "min_signed_per_window": "0.500000000000000000", - "downtime_jail_duration": "86400s", - "slash_fraction_double_sign": "0.050000000000000000", - "slash_fraction_downtime": "0.010000000000000000" + "mint": { + "minter": { + "inflation": "0.130000000000000000", + "annual_provisions": "0.000000000000000000" + }, + "params": { + "mint_denom": "uumee", + "inflation_rate_change": "0.130000000000000000", + "inflation_max": "0.200000000000000000", + "inflation_min": "0.070000000000000000", + "goal_bonded": "0.670000000000000000", + "blocks_per_year": "6311520" + } }, - "signing_infos": [], - "missed_blocks": [] - }, - "staking": { - "params": { - "unbonding_time": "1814400s", - "max_validators": 100, - "max_entries": 7, - "historical_entries": 10000, - "bond_denom": "uumee" + "oracle": { + "params": { + "vote_period": "5", + "vote_threshold": "0.500000000000000000", + "reward_band": "0.020000000000000000", + "reward_distribution_window": "5256000", + "accept_list": [ + { + "base_denom": "uumee", + "symbol_denom": "umee", + "exponent": 6 + } + ], + "slash_fraction": "0.000100000000000000", + "slash_window": "100800", + "min_valid_per_window": "0.050000000000000000" + }, + "feeder_delegations": [], + "exchange_rates": [], + "miss_counters": [], + "aggregate_exchange_rate_prevotes": [], + "aggregate_exchange_rate_votes": [] }, - "last_total_power": "0", - "last_validator_powers": [], - "validators": [], - "delegations": [], - "unbonding_delegations": [], - "redelegations": [], - "exported": false - }, - "transfer": { - "port_id": "transfer", - "denom_traces": [], - "params": { - "send_enabled": true, - "receive_enabled": true - } - }, - "upgrade": {}, - "vesting": {} + "params": null, + "slashing": { + "params": { + "signed_blocks_window": "10000", + "min_signed_per_window": "0.500000000000000000", + "downtime_jail_duration": "86400s", + "slash_fraction_double_sign": "0.050000000000000000", + "slash_fraction_downtime": "0.010000000000000000" + }, + "signing_infos": [], + "missed_blocks": [] + }, + "staking": { + "params": { + "unbonding_time": "1814400s", + "max_validators": 100, + "max_entries": 7, + "historical_entries": 10000, + "bond_denom": "uumee" + }, + "last_total_power": "0", + "last_validator_powers": [], + "validators": [], + "delegations": [], + "unbonding_delegations": [], + "redelegations": [], + "exported": false + }, + "transfer": { + "port_id": "transfer", + "denom_traces": [], + "params": { + "send_enabled": true, + "receive_enabled": true + } + }, + "upgrade": {}, + "vesting": {} + } } -} \ No newline at end of file From abade2bc284bf1afc94add5a305e15acb853973f Mon Sep 17 00:00:00 2001 From: toteki <63419657+toteki@users.noreply.github.com> Date: Sun, 7 Aug 2022 16:21:07 -0700 Subject: [PATCH 05/38] ++ --- proto/umee/leverage/v1/tx.proto | 2 +- x/leverage/types/tx.pb.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/proto/umee/leverage/v1/tx.proto b/proto/umee/leverage/v1/tx.proto index 9cc4df9c66..59ac0840dd 100644 --- a/proto/umee/leverage/v1/tx.proto +++ b/proto/umee/leverage/v1/tx.proto @@ -96,7 +96,7 @@ message MsgLiquidate { // Repayment is the maximum amount of base tokens that the liquidator is willing // to repay. cosmos.base.v1beta1.Coin repayment = 3 [(gogoproto.nullable) = false]; - // RewardDenom is the denom that the liquidator will receive liquidation reward. + // RewardDenom is the denom that the liquidator will receive as a liquidation reward. // If it is a uToken, the liquidator will receive uTokens from the borrower's // collateral. If it is a base token, the uTokens will be redeemed directly at // a reduced Liquidation Incentive, and the liquidator will receive base tokens. diff --git a/x/leverage/types/tx.pb.go b/x/leverage/types/tx.pb.go index b225ebd2ba..3c518eff3f 100644 --- a/x/leverage/types/tx.pb.go +++ b/x/leverage/types/tx.pb.go @@ -287,7 +287,7 @@ type MsgLiquidate struct { // Repayment is the maximum amount of base tokens that the liquidator is willing // to repay. Repayment types.Coin `protobuf:"bytes,3,opt,name=repayment,proto3" json:"repayment"` - // RewardDenom is the denom that the liquidator will receive liquidation reward. + // RewardDenom is the denom that the liquidator will receive as a liquidation reward. // If it is a uToken, the liquidator will receive uTokens from the borrower's // collateral. If it is a base token, the uTokens will be redeemed directly at // a reduced Liquidation Incentive, and the liquidator will receive base tokens. From fdaec95fa687ffb9e4ca8c4ec82fc89d5e74e513 Mon Sep 17 00:00:00 2001 From: toteki <63419657+toteki@users.noreply.github.com> Date: Sun, 7 Aug 2022 16:30:11 -0700 Subject: [PATCH 06/38] -- --- x/leverage/keeper/collateral.go | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/x/leverage/keeper/collateral.go b/x/leverage/keeper/collateral.go index de7a1c1f7c..411858574c 100644 --- a/x/leverage/keeper/collateral.go +++ b/x/leverage/keeper/collateral.go @@ -10,10 +10,6 @@ import ( // burnCollateral removes some uTokens from an account's collateral and burns them. This occurs // during liquidations. func (k Keeper) burnCollateral(ctx sdk.Context, addr sdk.AccAddress, collateral sdk.Coin) error { - if !types.HasUTokenPrefix(collateral.Denom) { - return types.ErrNotUToken.Wrap(collateral.Denom) - } - // reduce account's collateral oldCollateral := k.GetCollateralAmount(ctx, addr, collateral.Denom) newCollateral := sdk.NewCoin(collateral.Denom, oldCollateral.Amount.Sub(collateral.Amount)) @@ -30,12 +26,8 @@ func (k Keeper) burnCollateral(ctx sdk.Context, addr sdk.AccAddress, collateral // removeCollateral removes some uTokens in fromAddr's collateral and sends them to toAddr. This // occurs when decollateralizing uTokens (in which case fromAddr and toAddr are the same) as well as -// during liquidations where the liquidator receives uToken rewards from the borrower's collateral. +// during liquidations, where toAddr is the liquidator. func (k Keeper) removeCollateral(ctx sdk.Context, fromAddr, toAddr sdk.AccAddress, collateral sdk.Coin) error { - if !types.HasUTokenPrefix(collateral.Denom) { - return types.ErrNotUToken.Wrap(collateral.Denom) - } - // reduce fromAddr's collateral oldCollateral := k.GetCollateralAmount(ctx, fromAddr, collateral.Denom) newCollateral := sdk.NewCoin(collateral.Denom, oldCollateral.Amount.Sub(collateral.Amount)) From 29309afca20e8b805f3eeee1cc9323035cfc037c Mon Sep 17 00:00:00 2001 From: toteki <63419657+toteki@users.noreply.github.com> Date: Sun, 7 Aug 2022 16:34:57 -0700 Subject: [PATCH 07/38] -- --- x/leverage/keeper/collateral.go | 25 +++++++++++-------------- x/leverage/keeper/collateral_test.go | 22 +++++++++++----------- x/leverage/keeper/keeper.go | 2 +- x/leverage/keeper/keeper_test.go | 12 ++++++------ x/leverage/keeper/supply.go | 2 +- 5 files changed, 30 insertions(+), 33 deletions(-) diff --git a/x/leverage/keeper/collateral.go b/x/leverage/keeper/collateral.go index 411858574c..9d6fdbf09a 100644 --- a/x/leverage/keeper/collateral.go +++ b/x/leverage/keeper/collateral.go @@ -9,39 +9,36 @@ import ( // burnCollateral removes some uTokens from an account's collateral and burns them. This occurs // during liquidations. -func (k Keeper) burnCollateral(ctx sdk.Context, addr sdk.AccAddress, collateral sdk.Coin) error { +func (k Keeper) burnCollateral(ctx sdk.Context, addr sdk.AccAddress, coin sdk.Coin) error { // reduce account's collateral - oldCollateral := k.GetCollateralAmount(ctx, addr, collateral.Denom) - newCollateral := sdk.NewCoin(collateral.Denom, oldCollateral.Amount.Sub(collateral.Amount)) - if err := k.setCollateralAmount(ctx, addr, newCollateral); err != nil { + err := k.setCollateralAmount(ctx, addr, k.GetCollateral(ctx, addr, coin.Denom).Sub(coin)) + if err != nil { return err } // burn the uTokens - if err := k.bankKeeper.BurnCoins(ctx, types.ModuleName, sdk.NewCoins(collateral)); err != nil { + if err = k.bankKeeper.BurnCoins(ctx, types.ModuleName, sdk.NewCoins(coin)); err != nil { return err } // set the new total uToken supply - return k.setUTokenSupply(ctx, k.GetUTokenSupply(ctx, collateral.Denom).Sub(collateral)) + return k.setUTokenSupply(ctx, k.GetUTokenSupply(ctx, coin.Denom).Sub(coin)) } // removeCollateral removes some uTokens in fromAddr's collateral and sends them to toAddr. This // occurs when decollateralizing uTokens (in which case fromAddr and toAddr are the same) as well as // during liquidations, where toAddr is the liquidator. -func (k Keeper) removeCollateral(ctx sdk.Context, fromAddr, toAddr sdk.AccAddress, collateral sdk.Coin) error { +func (k Keeper) removeCollateral(ctx sdk.Context, fromAddr, toAddr sdk.AccAddress, coin sdk.Coin) error { // reduce fromAddr's collateral - oldCollateral := k.GetCollateralAmount(ctx, fromAddr, collateral.Denom) - newCollateral := sdk.NewCoin(collateral.Denom, oldCollateral.Amount.Sub(collateral.Amount)) - if err := k.setCollateralAmount(ctx, fromAddr, newCollateral); err != nil { + err := k.setCollateralAmount(ctx, fromAddr, k.GetCollateral(ctx, fromAddr, coin.Denom).Sub(coin)) + if err != nil { return err } - // send the uTokens to toAddr - return k.bankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, toAddr, sdk.NewCoins(collateral)) + return k.bankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, toAddr, sdk.NewCoins(coin)) } -// GetCollateralAmount returns an sdk.Coin representing how much of a given denom the +// GetCollateral returns an sdk.Coin representing how much of a given denom the // x/leverage module account currently holds as collateral for a given borrower. -func (k Keeper) GetCollateralAmount(ctx sdk.Context, borrowerAddr sdk.AccAddress, denom string) sdk.Coin { +func (k Keeper) GetCollateral(ctx sdk.Context, borrowerAddr sdk.AccAddress, denom string) sdk.Coin { store := ctx.KVStore(k.storeKey) collateral := sdk.NewCoin(denom, sdk.ZeroInt()) key := types.CreateCollateralAmountKey(borrowerAddr, denom) diff --git a/x/leverage/keeper/collateral_test.go b/x/leverage/keeper/collateral_test.go index fd48279084..efd54623a5 100644 --- a/x/leverage/keeper/collateral_test.go +++ b/x/leverage/keeper/collateral_test.go @@ -10,40 +10,40 @@ func (s *IntegrationTestSuite) TestGetCollateralAmount() { uDenom := types.ToUTokenDenom(umeeDenom) // get u/umee collateral amount of empty account address (zero) - collateral := s.tk.GetCollateralAmount(s.ctx, sdk.AccAddress{}, uDenom) + collateral := s.tk.GetCollateral(s.ctx, sdk.AccAddress{}, uDenom) s.Require().Equal(sdk.NewInt64Coin(uDenom, 0), collateral) // get u/umee collateral amount of non-empty account address (zero) - collateral = s.tk.GetCollateralAmount(s.ctx, sdk.AccAddress{0x01}, uDenom) + collateral = s.tk.GetCollateral(s.ctx, sdk.AccAddress{0x01}, uDenom) s.Require().Equal(sdk.NewInt64Coin(uDenom, 0), collateral) // creates account which has 1000 u/umee but not enabled as collateral addr := s.setupAccount(umeeDenom, 1000, 1000, 0, false) // confirm collateral amount is 0 u/uumee - collateral = s.tk.GetCollateralAmount(s.ctx, addr, uDenom) + collateral = s.tk.GetCollateral(s.ctx, addr, uDenom) s.Require().Equal(sdk.NewInt64Coin(uDenom, 0), collateral) // enable u/umee as collateral s.Require().NoError(s.tk.Collateralize(s.ctx, addr, sdk.NewInt64Coin(uDenom, 1000))) // confirm collateral amount is 1000 u/uumee - collateral = s.tk.GetCollateralAmount(s.ctx, addr, uDenom) + collateral = s.tk.GetCollateral(s.ctx, addr, uDenom) s.Require().Equal(sdk.NewInt64Coin(uDenom, 1000), collateral) // collateral amount of non-utoken denom (zero) - collateral = s.tk.GetCollateralAmount(s.ctx, addr, umeeDenom) + collateral = s.tk.GetCollateral(s.ctx, addr, umeeDenom) s.Require().Equal(sdk.NewInt64Coin(umeeDenom, 0), collateral) // collateral amount of unregistered denom (zero) - collateral = s.tk.GetCollateralAmount(s.ctx, addr, "abcd") + collateral = s.tk.GetCollateral(s.ctx, addr, "abcd") s.Require().Equal(sdk.NewInt64Coin("abcd", 0), collateral) // disable u/umee as collateral s.Require().NoError(s.tk.Decollateralize(s.ctx, addr, sdk.NewInt64Coin(uDenom, 1000))) // confirm collateral amount is 0 u/uumee - collateral = s.tk.GetCollateralAmount(s.ctx, addr, uDenom) + collateral = s.tk.GetCollateral(s.ctx, addr, uDenom) s.Require().Equal(sdk.NewInt64Coin(uDenom, 0), collateral) // we do not test empty denom, as that will cause a panic @@ -66,28 +66,28 @@ func (s *IntegrationTestSuite) TestSetCollateralAmount() { s.Require().NoError(s.tk.SetCollateralAmount(s.ctx, addr, sdk.NewInt64Coin(uDenom, 10))) // confirm effect - collateral := s.tk.GetCollateralAmount(s.ctx, addr, uDenom) + collateral := s.tk.GetCollateral(s.ctx, addr, uDenom) s.Require().Equal(sdk.NewInt64Coin(uDenom, 10), collateral) // set u/umee collateral amount to zero s.Require().NoError(s.tk.SetCollateralAmount(s.ctx, addr, sdk.NewInt64Coin(uDenom, 0))) // confirm effect - collateral = s.tk.GetCollateralAmount(s.ctx, addr, uDenom) + collateral = s.tk.GetCollateral(s.ctx, addr, uDenom) s.Require().Equal(sdk.NewInt64Coin(uDenom, 0), collateral) // set unregistered token collateral amount s.Require().NoError(s.tk.SetCollateralAmount(s.ctx, addr, sdk.NewInt64Coin("abcd", 10))) // confirm effect - collateral = s.tk.GetCollateralAmount(s.ctx, addr, "abcd") + collateral = s.tk.GetCollateral(s.ctx, addr, "abcd") s.Require().Equal(sdk.NewInt64Coin("abcd", 10), collateral) // set unregistered token collateral amount to zero s.Require().NoError(s.tk.SetCollateralAmount(s.ctx, addr, sdk.NewInt64Coin("abcd", 0))) // confirm effect - collateral = s.tk.GetCollateralAmount(s.ctx, addr, "abcd") + collateral = s.tk.GetCollateral(s.ctx, addr, "abcd") s.Require().Equal(sdk.NewInt64Coin("abcd", 0), collateral) // we do not test empty denom, as that will cause a panic diff --git a/x/leverage/keeper/keeper.go b/x/leverage/keeper/keeper.go index e91fd518ce..b2e72bdacf 100644 --- a/x/leverage/keeper/keeper.go +++ b/x/leverage/keeper/keeper.go @@ -280,7 +280,7 @@ func (k Keeper) Collateralize(ctx sdk.Context, borrowerAddr sdk.AccAddress, coin return err } - currentCollateral := k.GetCollateralAmount(ctx, borrowerAddr, coin.Denom) + currentCollateral := k.GetCollateral(ctx, borrowerAddr, coin.Denom) if err := k.setCollateralAmount(ctx, borrowerAddr, currentCollateral.Add(coin)); err != nil { return err } diff --git a/x/leverage/keeper/keeper_test.go b/x/leverage/keeper/keeper_test.go index d8392ecce7..2dd0460dff 100644 --- a/x/leverage/keeper/keeper_test.go +++ b/x/leverage/keeper/keeper_test.go @@ -407,7 +407,7 @@ func (s *IntegrationTestSuite) TestBorrow_Valid() { s.Require().Equal(uTokenBalance, sdk.NewInt64Coin("u/"+umeeapp.BondDenom, 0)) // verify user's uToken collateral remains at 1000 u/umee from initial conditions - collateralBalance := s.app.LeverageKeeper.GetCollateralAmount(s.ctx, addr, "u/"+umeeapp.BondDenom) + collateralBalance := s.app.LeverageKeeper.GetCollateral(s.ctx, addr, "u/"+umeeapp.BondDenom) s.Require().Equal(collateralBalance, sdk.NewInt64Coin("u/"+umeeapp.BondDenom, 1000000000)) } @@ -420,7 +420,7 @@ func (s *IntegrationTestSuite) TestBorrow_BorrowLimit() { // determine an amount of umee to borrow, such that the user will be at about 90% of their borrow limit token, _ := s.app.LeverageKeeper.GetTokenSettings(s.ctx, umeeapp.BondDenom) uDenom := types.ToUTokenDenom(umeeapp.BondDenom) - collateral := s.app.LeverageKeeper.GetCollateralAmount(s.ctx, addr, uDenom) + collateral := s.app.LeverageKeeper.GetCollateral(s.ctx, addr, uDenom) amountToBorrow := token.CollateralWeight.Mul(sdk.MustNewDecFromStr("0.9")).MulInt(collateral.Amount).TruncateInt() // user borrows umee up to 90% of their borrow limit @@ -494,7 +494,7 @@ func (s *IntegrationTestSuite) TestRepay_Valid() { s.Require().Equal(uTokenBalance, sdk.NewInt64Coin("u/"+umeeapp.BondDenom, 0)) // verify user's uToken collateral remains at 1000 u/umee from initial conditions - collateralBalance := s.app.LeverageKeeper.GetCollateralAmount(s.ctx, addr, "u/"+umeeapp.BondDenom) + collateralBalance := s.app.LeverageKeeper.GetCollateral(s.ctx, addr, "u/"+umeeapp.BondDenom) s.Require().Equal(collateralBalance, sdk.NewInt64Coin("u/"+umeeapp.BondDenom, 1000000000)) // user repays 12 umee (loan repaid in full) @@ -515,7 +515,7 @@ func (s *IntegrationTestSuite) TestRepay_Valid() { s.Require().Equal(uTokenBalance, sdk.NewInt64Coin("u/"+umeeapp.BondDenom, 0)) // verify user's uToken collateral remains at 1000 u/umee from initial conditions - collateralBalance = s.app.LeverageKeeper.GetCollateralAmount(s.ctx, addr, "u/"+umeeapp.BondDenom) + collateralBalance = s.app.LeverageKeeper.GetCollateral(s.ctx, addr, "u/"+umeeapp.BondDenom) s.Require().Equal(collateralBalance, sdk.NewInt64Coin("u/"+umeeapp.BondDenom, 1000000000)) } @@ -551,7 +551,7 @@ func (s *IntegrationTestSuite) TestRepay_Overpay() { s.Require().Equal(uTokenBalance, sdk.NewInt64Coin("u/"+umeeapp.BondDenom, 0)) // verify user's uToken collateral remains at 1000 u/umee from initial conditions - collateralBalance := s.app.LeverageKeeper.GetCollateralAmount(s.ctx, addr, "u/"+umeeapp.BondDenom) + collateralBalance := s.app.LeverageKeeper.GetCollateral(s.ctx, addr, "u/"+umeeapp.BondDenom) s.Require().Equal(collateralBalance, sdk.NewInt64Coin("u/"+umeeapp.BondDenom, 1000000000)) // user repays 50 umee - this time it fails because the loan no longer exists @@ -916,7 +916,7 @@ func (s *IntegrationTestSuite) TestWithdraw_InsufficientCollateral() { // verify collateral amount and total supply of minted uTokens uTokenDenom := types.ToUTokenDenom(umeeapp.BondDenom) - collateral := s.app.LeverageKeeper.GetCollateralAmount(s.ctx, supplierAddr, uTokenDenom) + collateral := s.app.LeverageKeeper.GetCollateral(s.ctx, supplierAddr, uTokenDenom) s.Require().Equal(sdk.NewInt64Coin(uTokenDenom, 1000000), collateral) // 1 u/umee supply := s.app.LeverageKeeper.GetUTokenSupply(s.ctx, uTokenDenom) s.Require().Equal(sdk.NewInt64Coin(uTokenDenom, 2000000), supply) // 2 u/umee diff --git a/x/leverage/keeper/supply.go b/x/leverage/keeper/supply.go index 9f2184fc54..9f057a5d12 100644 --- a/x/leverage/keeper/supply.go +++ b/x/leverage/keeper/supply.go @@ -16,7 +16,7 @@ func (k Keeper) GetSupplied(ctx sdk.Context, supplierAddr sdk.AccAddress, denom // sum wallet-held and collateral-enabled uTokens in the associated uToken denom uDenom := types.ToUTokenDenom(denom) balance := k.bankKeeper.GetBalance(ctx, supplierAddr, uDenom) - collateral := k.GetCollateralAmount(ctx, supplierAddr, uDenom) + collateral := k.GetCollateral(ctx, supplierAddr, uDenom) // convert uTokens to tokens return k.ExchangeUToken(ctx, balance.Add(collateral)) From 32ae949728b563ace5f290726fb3cadcff432661 Mon Sep 17 00:00:00 2001 From: toteki <63419657+toteki@users.noreply.github.com> Date: Sun, 7 Aug 2022 16:36:15 -0700 Subject: [PATCH 08/38] -- --- x/leverage/keeper/collateral.go | 8 ++++---- x/leverage/keeper/collateral_test.go | 22 +++++++++++----------- x/leverage/keeper/keeper.go | 2 +- x/leverage/keeper/keeper_test.go | 12 ++++++------ x/leverage/keeper/supply.go | 2 +- 5 files changed, 23 insertions(+), 23 deletions(-) diff --git a/x/leverage/keeper/collateral.go b/x/leverage/keeper/collateral.go index 9d6fdbf09a..6bce14b4bf 100644 --- a/x/leverage/keeper/collateral.go +++ b/x/leverage/keeper/collateral.go @@ -11,7 +11,7 @@ import ( // during liquidations. func (k Keeper) burnCollateral(ctx sdk.Context, addr sdk.AccAddress, coin sdk.Coin) error { // reduce account's collateral - err := k.setCollateralAmount(ctx, addr, k.GetCollateral(ctx, addr, coin.Denom).Sub(coin)) + err := k.setCollateralAmount(ctx, addr, k.GetCollateralAmount(ctx, addr, coin.Denom).Sub(coin)) if err != nil { return err } @@ -28,7 +28,7 @@ func (k Keeper) burnCollateral(ctx sdk.Context, addr sdk.AccAddress, coin sdk.Co // during liquidations, where toAddr is the liquidator. func (k Keeper) removeCollateral(ctx sdk.Context, fromAddr, toAddr sdk.AccAddress, coin sdk.Coin) error { // reduce fromAddr's collateral - err := k.setCollateralAmount(ctx, fromAddr, k.GetCollateral(ctx, fromAddr, coin.Denom).Sub(coin)) + err := k.setCollateralAmount(ctx, fromAddr, k.GetCollateralAmount(ctx, fromAddr, coin.Denom).Sub(coin)) if err != nil { return err } @@ -36,9 +36,9 @@ func (k Keeper) removeCollateral(ctx sdk.Context, fromAddr, toAddr sdk.AccAddres return k.bankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, toAddr, sdk.NewCoins(coin)) } -// GetCollateral returns an sdk.Coin representing how much of a given denom the +// GetCollateralAmount returns an sdk.Coin representing how much of a given denom the // x/leverage module account currently holds as collateral for a given borrower. -func (k Keeper) GetCollateral(ctx sdk.Context, borrowerAddr sdk.AccAddress, denom string) sdk.Coin { +func (k Keeper) GetCollateralAmount(ctx sdk.Context, borrowerAddr sdk.AccAddress, denom string) sdk.Coin { store := ctx.KVStore(k.storeKey) collateral := sdk.NewCoin(denom, sdk.ZeroInt()) key := types.CreateCollateralAmountKey(borrowerAddr, denom) diff --git a/x/leverage/keeper/collateral_test.go b/x/leverage/keeper/collateral_test.go index efd54623a5..fd48279084 100644 --- a/x/leverage/keeper/collateral_test.go +++ b/x/leverage/keeper/collateral_test.go @@ -10,40 +10,40 @@ func (s *IntegrationTestSuite) TestGetCollateralAmount() { uDenom := types.ToUTokenDenom(umeeDenom) // get u/umee collateral amount of empty account address (zero) - collateral := s.tk.GetCollateral(s.ctx, sdk.AccAddress{}, uDenom) + collateral := s.tk.GetCollateralAmount(s.ctx, sdk.AccAddress{}, uDenom) s.Require().Equal(sdk.NewInt64Coin(uDenom, 0), collateral) // get u/umee collateral amount of non-empty account address (zero) - collateral = s.tk.GetCollateral(s.ctx, sdk.AccAddress{0x01}, uDenom) + collateral = s.tk.GetCollateralAmount(s.ctx, sdk.AccAddress{0x01}, uDenom) s.Require().Equal(sdk.NewInt64Coin(uDenom, 0), collateral) // creates account which has 1000 u/umee but not enabled as collateral addr := s.setupAccount(umeeDenom, 1000, 1000, 0, false) // confirm collateral amount is 0 u/uumee - collateral = s.tk.GetCollateral(s.ctx, addr, uDenom) + collateral = s.tk.GetCollateralAmount(s.ctx, addr, uDenom) s.Require().Equal(sdk.NewInt64Coin(uDenom, 0), collateral) // enable u/umee as collateral s.Require().NoError(s.tk.Collateralize(s.ctx, addr, sdk.NewInt64Coin(uDenom, 1000))) // confirm collateral amount is 1000 u/uumee - collateral = s.tk.GetCollateral(s.ctx, addr, uDenom) + collateral = s.tk.GetCollateralAmount(s.ctx, addr, uDenom) s.Require().Equal(sdk.NewInt64Coin(uDenom, 1000), collateral) // collateral amount of non-utoken denom (zero) - collateral = s.tk.GetCollateral(s.ctx, addr, umeeDenom) + collateral = s.tk.GetCollateralAmount(s.ctx, addr, umeeDenom) s.Require().Equal(sdk.NewInt64Coin(umeeDenom, 0), collateral) // collateral amount of unregistered denom (zero) - collateral = s.tk.GetCollateral(s.ctx, addr, "abcd") + collateral = s.tk.GetCollateralAmount(s.ctx, addr, "abcd") s.Require().Equal(sdk.NewInt64Coin("abcd", 0), collateral) // disable u/umee as collateral s.Require().NoError(s.tk.Decollateralize(s.ctx, addr, sdk.NewInt64Coin(uDenom, 1000))) // confirm collateral amount is 0 u/uumee - collateral = s.tk.GetCollateral(s.ctx, addr, uDenom) + collateral = s.tk.GetCollateralAmount(s.ctx, addr, uDenom) s.Require().Equal(sdk.NewInt64Coin(uDenom, 0), collateral) // we do not test empty denom, as that will cause a panic @@ -66,28 +66,28 @@ func (s *IntegrationTestSuite) TestSetCollateralAmount() { s.Require().NoError(s.tk.SetCollateralAmount(s.ctx, addr, sdk.NewInt64Coin(uDenom, 10))) // confirm effect - collateral := s.tk.GetCollateral(s.ctx, addr, uDenom) + collateral := s.tk.GetCollateralAmount(s.ctx, addr, uDenom) s.Require().Equal(sdk.NewInt64Coin(uDenom, 10), collateral) // set u/umee collateral amount to zero s.Require().NoError(s.tk.SetCollateralAmount(s.ctx, addr, sdk.NewInt64Coin(uDenom, 0))) // confirm effect - collateral = s.tk.GetCollateral(s.ctx, addr, uDenom) + collateral = s.tk.GetCollateralAmount(s.ctx, addr, uDenom) s.Require().Equal(sdk.NewInt64Coin(uDenom, 0), collateral) // set unregistered token collateral amount s.Require().NoError(s.tk.SetCollateralAmount(s.ctx, addr, sdk.NewInt64Coin("abcd", 10))) // confirm effect - collateral = s.tk.GetCollateral(s.ctx, addr, "abcd") + collateral = s.tk.GetCollateralAmount(s.ctx, addr, "abcd") s.Require().Equal(sdk.NewInt64Coin("abcd", 10), collateral) // set unregistered token collateral amount to zero s.Require().NoError(s.tk.SetCollateralAmount(s.ctx, addr, sdk.NewInt64Coin("abcd", 0))) // confirm effect - collateral = s.tk.GetCollateral(s.ctx, addr, "abcd") + collateral = s.tk.GetCollateralAmount(s.ctx, addr, "abcd") s.Require().Equal(sdk.NewInt64Coin("abcd", 0), collateral) // we do not test empty denom, as that will cause a panic diff --git a/x/leverage/keeper/keeper.go b/x/leverage/keeper/keeper.go index b2e72bdacf..e91fd518ce 100644 --- a/x/leverage/keeper/keeper.go +++ b/x/leverage/keeper/keeper.go @@ -280,7 +280,7 @@ func (k Keeper) Collateralize(ctx sdk.Context, borrowerAddr sdk.AccAddress, coin return err } - currentCollateral := k.GetCollateral(ctx, borrowerAddr, coin.Denom) + currentCollateral := k.GetCollateralAmount(ctx, borrowerAddr, coin.Denom) if err := k.setCollateralAmount(ctx, borrowerAddr, currentCollateral.Add(coin)); err != nil { return err } diff --git a/x/leverage/keeper/keeper_test.go b/x/leverage/keeper/keeper_test.go index 2dd0460dff..d8392ecce7 100644 --- a/x/leverage/keeper/keeper_test.go +++ b/x/leverage/keeper/keeper_test.go @@ -407,7 +407,7 @@ func (s *IntegrationTestSuite) TestBorrow_Valid() { s.Require().Equal(uTokenBalance, sdk.NewInt64Coin("u/"+umeeapp.BondDenom, 0)) // verify user's uToken collateral remains at 1000 u/umee from initial conditions - collateralBalance := s.app.LeverageKeeper.GetCollateral(s.ctx, addr, "u/"+umeeapp.BondDenom) + collateralBalance := s.app.LeverageKeeper.GetCollateralAmount(s.ctx, addr, "u/"+umeeapp.BondDenom) s.Require().Equal(collateralBalance, sdk.NewInt64Coin("u/"+umeeapp.BondDenom, 1000000000)) } @@ -420,7 +420,7 @@ func (s *IntegrationTestSuite) TestBorrow_BorrowLimit() { // determine an amount of umee to borrow, such that the user will be at about 90% of their borrow limit token, _ := s.app.LeverageKeeper.GetTokenSettings(s.ctx, umeeapp.BondDenom) uDenom := types.ToUTokenDenom(umeeapp.BondDenom) - collateral := s.app.LeverageKeeper.GetCollateral(s.ctx, addr, uDenom) + collateral := s.app.LeverageKeeper.GetCollateralAmount(s.ctx, addr, uDenom) amountToBorrow := token.CollateralWeight.Mul(sdk.MustNewDecFromStr("0.9")).MulInt(collateral.Amount).TruncateInt() // user borrows umee up to 90% of their borrow limit @@ -494,7 +494,7 @@ func (s *IntegrationTestSuite) TestRepay_Valid() { s.Require().Equal(uTokenBalance, sdk.NewInt64Coin("u/"+umeeapp.BondDenom, 0)) // verify user's uToken collateral remains at 1000 u/umee from initial conditions - collateralBalance := s.app.LeverageKeeper.GetCollateral(s.ctx, addr, "u/"+umeeapp.BondDenom) + collateralBalance := s.app.LeverageKeeper.GetCollateralAmount(s.ctx, addr, "u/"+umeeapp.BondDenom) s.Require().Equal(collateralBalance, sdk.NewInt64Coin("u/"+umeeapp.BondDenom, 1000000000)) // user repays 12 umee (loan repaid in full) @@ -515,7 +515,7 @@ func (s *IntegrationTestSuite) TestRepay_Valid() { s.Require().Equal(uTokenBalance, sdk.NewInt64Coin("u/"+umeeapp.BondDenom, 0)) // verify user's uToken collateral remains at 1000 u/umee from initial conditions - collateralBalance = s.app.LeverageKeeper.GetCollateral(s.ctx, addr, "u/"+umeeapp.BondDenom) + collateralBalance = s.app.LeverageKeeper.GetCollateralAmount(s.ctx, addr, "u/"+umeeapp.BondDenom) s.Require().Equal(collateralBalance, sdk.NewInt64Coin("u/"+umeeapp.BondDenom, 1000000000)) } @@ -551,7 +551,7 @@ func (s *IntegrationTestSuite) TestRepay_Overpay() { s.Require().Equal(uTokenBalance, sdk.NewInt64Coin("u/"+umeeapp.BondDenom, 0)) // verify user's uToken collateral remains at 1000 u/umee from initial conditions - collateralBalance := s.app.LeverageKeeper.GetCollateral(s.ctx, addr, "u/"+umeeapp.BondDenom) + collateralBalance := s.app.LeverageKeeper.GetCollateralAmount(s.ctx, addr, "u/"+umeeapp.BondDenom) s.Require().Equal(collateralBalance, sdk.NewInt64Coin("u/"+umeeapp.BondDenom, 1000000000)) // user repays 50 umee - this time it fails because the loan no longer exists @@ -916,7 +916,7 @@ func (s *IntegrationTestSuite) TestWithdraw_InsufficientCollateral() { // verify collateral amount and total supply of minted uTokens uTokenDenom := types.ToUTokenDenom(umeeapp.BondDenom) - collateral := s.app.LeverageKeeper.GetCollateral(s.ctx, supplierAddr, uTokenDenom) + collateral := s.app.LeverageKeeper.GetCollateralAmount(s.ctx, supplierAddr, uTokenDenom) s.Require().Equal(sdk.NewInt64Coin(uTokenDenom, 1000000), collateral) // 1 u/umee supply := s.app.LeverageKeeper.GetUTokenSupply(s.ctx, uTokenDenom) s.Require().Equal(sdk.NewInt64Coin(uTokenDenom, 2000000), supply) // 2 u/umee diff --git a/x/leverage/keeper/supply.go b/x/leverage/keeper/supply.go index 9f057a5d12..9f2184fc54 100644 --- a/x/leverage/keeper/supply.go +++ b/x/leverage/keeper/supply.go @@ -16,7 +16,7 @@ func (k Keeper) GetSupplied(ctx sdk.Context, supplierAddr sdk.AccAddress, denom // sum wallet-held and collateral-enabled uTokens in the associated uToken denom uDenom := types.ToUTokenDenom(denom) balance := k.bankKeeper.GetBalance(ctx, supplierAddr, uDenom) - collateral := k.GetCollateral(ctx, supplierAddr, uDenom) + collateral := k.GetCollateralAmount(ctx, supplierAddr, uDenom) // convert uTokens to tokens return k.ExchangeUToken(ctx, balance.Add(collateral)) From 9df396e002c93205830e39a6b37a66470f67740a Mon Sep 17 00:00:00 2001 From: toteki <63419657+toteki@users.noreply.github.com> Date: Sun, 7 Aug 2022 16:45:31 -0700 Subject: [PATCH 09/38] test++ --- x/leverage/types/token_test.go | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/x/leverage/types/token_test.go b/x/leverage/types/token_test.go index 71330c4347..c214bf878b 100644 --- a/x/leverage/types/token_test.go +++ b/x/leverage/types/token_test.go @@ -10,10 +10,12 @@ import ( ) func TestToTokenDenom(t *testing.T) { - // Used as intended + // Turns uToken denoms into base tokens require.Equal(t, "uumee", types.ToTokenDenom("u/uumee")) - require.Equal(t, "uumee", types.ToTokenDenom("uumee")) require.Equal(t, "ibc/abcd", types.ToTokenDenom("u/ibc/abcd")) + + // Does not alter base tokens + require.Equal(t, "uumee", types.ToTokenDenom("uumee")) require.Equal(t, "ibc/abcd", types.ToTokenDenom("ibc/abcd")) // Does not ensure a valid output @@ -23,15 +25,18 @@ func TestToTokenDenom(t *testing.T) { } func TestToUTokenDenom(t *testing.T) { - // Used as intended + // Turns base token denoms into base uTokens require.Equal(t, "u/uumee", types.ToUTokenDenom("uumee")) - require.Equal(t, "u/uumee", types.ToUTokenDenom("u/uumee")) require.Equal(t, "u/ibc/abcd", types.ToUTokenDenom("ibc/abcd")) + + // Does not alter uTokens + require.Equal(t, "u/uumee", types.ToUTokenDenom("u/uumee")) require.Equal(t, "u/ibc/abcd", types.ToUTokenDenom("u/ibc/abcd")) // Does not ensure a valid output require.Equal(t, "u/", types.ToUTokenDenom("")) require.Equal(t, "u/", types.ToUTokenDenom("u/")) + require.Equal(t, "u/u/", types.ToUTokenDenom("u/u/")) } func TestUpdateRegistryProposal_String(t *testing.T) { From 2257744af27a190062d9085027e879b243d3416f Mon Sep 17 00:00:00 2001 From: toteki <63419657+toteki@users.noreply.github.com> Date: Sun, 7 Aug 2022 16:49:37 -0700 Subject: [PATCH 10/38] ++ --- x/leverage/keeper/borrows.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/x/leverage/keeper/borrows.go b/x/leverage/keeper/borrows.go index 767edf6211..89ba226a86 100644 --- a/x/leverage/keeper/borrows.go +++ b/x/leverage/keeper/borrows.go @@ -36,8 +36,7 @@ func (k Keeper) repayBorrow(ctx sdk.Context, fromAddr, borrowAddr sdk.AccAddress return err } // update borrower's remaining borrowed amount - newBorrow := k.GetBorrow(ctx, borrowAddr, repay.Denom).Sub(repay) - return k.setBorrow(ctx, borrowAddr, newBorrow) + return k.setBorrow(ctx, borrowAddr, k.GetBorrow(ctx, borrowAddr, repay.Denom).Sub(repay)) } // setBorrow sets the amount borrowed by an address in a given denom. From 84c4aac04f7ebcc5792961419040bd56c97c0f78 Mon Sep 17 00:00:00 2001 From: toteki <63419657+toteki@users.noreply.github.com> Date: Sun, 7 Aug 2022 17:11:44 -0700 Subject: [PATCH 11/38] stricter ToTokenDenom --- x/leverage/types/token.go | 19 +++++++++++++++---- x/leverage/types/token_test.go | 22 +++++++++++----------- 2 files changed, 26 insertions(+), 15 deletions(-) diff --git a/x/leverage/types/token.go b/x/leverage/types/token.go index bdd1539dc1..f212e43d38 100644 --- a/x/leverage/types/token.go +++ b/x/leverage/types/token.go @@ -18,17 +18,28 @@ func HasUTokenPrefix(denom string) bool { return strings.HasPrefix(denom, UTokenPrefix) } -// ToUTokenDenom adds the uToken prefix to a denom if not already present. +// ToUTokenDenom adds the uToken prefix to a denom. Returns an empty string +// instead if the prefix was already present. func ToUTokenDenom(denom string) string { if HasUTokenPrefix(denom) { - return denom + return "" } return UTokenPrefix + denom } -// ToTokenDenom strips the uToken prefix from a denom if present. +// ToTokenDenom strips the uToken prefix from a denom, or returns an empty +// string if it was not present. Also returns an empty string if the prefix +// was repeated multiple times. func ToTokenDenom(denom string) string { - return strings.TrimPrefix(denom, UTokenPrefix) + if !HasUTokenPrefix(denom) { + return "" + } + s := strings.TrimPrefix(denom, UTokenPrefix) + if HasUTokenPrefix(s) { + // denom started with "u/u/" + return "" + } + return s } // Validate performs validation on an Token type returning an error if the Token diff --git a/x/leverage/types/token_test.go b/x/leverage/types/token_test.go index c214bf878b..a59038bed1 100644 --- a/x/leverage/types/token_test.go +++ b/x/leverage/types/token_test.go @@ -14,14 +14,16 @@ func TestToTokenDenom(t *testing.T) { require.Equal(t, "uumee", types.ToTokenDenom("u/uumee")) require.Equal(t, "ibc/abcd", types.ToTokenDenom("u/ibc/abcd")) - // Does not alter base tokens - require.Equal(t, "uumee", types.ToTokenDenom("uumee")) - require.Equal(t, "ibc/abcd", types.ToTokenDenom("ibc/abcd")) + // Empty return for base tokens + require.Equal(t, "", types.ToTokenDenom("uumee")) + require.Equal(t, "", types.ToTokenDenom("ibc/abcd")) - // Does not ensure a valid output + // Empty return on repreated prefix + require.Equal(t, "", types.ToTokenDenom("u/u/abcd")) + + // Edge cases require.Equal(t, "", types.ToTokenDenom("u/")) require.Equal(t, "", types.ToTokenDenom("")) - require.Equal(t, "u/", types.ToTokenDenom("u/u/")) } func TestToUTokenDenom(t *testing.T) { @@ -29,14 +31,12 @@ func TestToUTokenDenom(t *testing.T) { require.Equal(t, "u/uumee", types.ToUTokenDenom("uumee")) require.Equal(t, "u/ibc/abcd", types.ToUTokenDenom("ibc/abcd")) - // Does not alter uTokens - require.Equal(t, "u/uumee", types.ToUTokenDenom("u/uumee")) - require.Equal(t, "u/ibc/abcd", types.ToUTokenDenom("u/ibc/abcd")) + // Empty return for uTokens + require.Equal(t, "", types.ToUTokenDenom("u/uumee")) + require.Equal(t, "", types.ToUTokenDenom("u/ibc/abcd")) - // Does not ensure a valid output + // Edge cases require.Equal(t, "u/", types.ToUTokenDenom("")) - require.Equal(t, "u/", types.ToUTokenDenom("u/")) - require.Equal(t, "u/u/", types.ToUTokenDenom("u/u/")) } func TestUpdateRegistryProposal_String(t *testing.T) { From 185841666a521ac81e46aa56193c08d5531fd984 Mon Sep 17 00:00:00 2001 From: toteki <63419657+toteki@users.noreply.github.com> Date: Sun, 7 Aug 2022 17:15:18 -0700 Subject: [PATCH 12/38] -- --- x/leverage/keeper/collateral.go | 2 +- x/leverage/keeper/filter.go | 2 +- x/leverage/keeper/keeper.go | 12 +++++----- x/leverage/keeper/supply.go | 6 ++--- x/leverage/keeper/validate.go | 34 ++++++++++++++--------------- x/leverage/simulation/operations.go | 2 +- 6 files changed, 29 insertions(+), 29 deletions(-) diff --git a/x/leverage/keeper/collateral.go b/x/leverage/keeper/collateral.go index 6bce14b4bf..37f939352f 100644 --- a/x/leverage/keeper/collateral.go +++ b/x/leverage/keeper/collateral.go @@ -86,7 +86,7 @@ func (k Keeper) setCollateralAmount(ctx sdk.Context, borrowerAddr sdk.AccAddress // the x/leverage module account currently holds as collateral. Non-uTokens and invalid // assets return zero. func (k Keeper) GetTotalCollateral(ctx sdk.Context, denom string) sdk.Int { - if k.ValidateAcceptedUTokenDenom(ctx, denom) != nil { + if k.validateAcceptedUTokenDenom(ctx, denom) != nil { // non-uTokens cannot be collateral return sdk.ZeroInt() } diff --git a/x/leverage/keeper/filter.go b/x/leverage/keeper/filter.go index 9b9193865b..864c958273 100644 --- a/x/leverage/keeper/filter.go +++ b/x/leverage/keeper/filter.go @@ -20,7 +20,7 @@ func (k Keeper) filterAcceptedCoins(ctx sdk.Context, coins sdk.Coins) sdk.Coins return k.filterCoins( coins, func(c sdk.Coin) bool { - return k.ValidateAcceptedAsset(ctx, c) == nil + return k.validateAcceptedAsset(ctx, c) == nil }, ) } diff --git a/x/leverage/keeper/keeper.go b/x/leverage/keeper/keeper.go index e91fd518ce..795299b92f 100644 --- a/x/leverage/keeper/keeper.go +++ b/x/leverage/keeper/keeper.go @@ -77,7 +77,7 @@ func (k Keeper) ModuleBalance(ctx sdk.Context, denom string) sdk.Int { // exchange for uTokens. If asset type is invalid or account balance is // insufficient, we return an error. func (k Keeper) Supply(ctx sdk.Context, supplierAddr sdk.AccAddress, loan sdk.Coin) error { - if err := k.ValidateSupply(ctx, loan); err != nil { + if err := k.validateSupply(ctx, loan); err != nil { return err } @@ -114,7 +114,7 @@ func (k Keeper) Supply(ctx sdk.Context, supplierAddr sdk.AccAddress, loan sdk.Co // for the original tokens supplied. Accepts a uToken amount to exchange for base tokens. // If the uToken denom is invalid or account or module balance insufficient, returns error. func (k Keeper) Withdraw(ctx sdk.Context, supplierAddr sdk.AccAddress, uToken sdk.Coin) error { - if err := k.ValidateAcceptedUToken(ctx, uToken); err != nil { + if err := k.validateAcceptedUToken(ctx, uToken); err != nil { return err } @@ -202,7 +202,7 @@ func (k Keeper) Withdraw(ctx sdk.Context, supplierAddr sdk.AccAddress, uToken sd // collateral uTokens. If asset type is invalid, collateral is insufficient, // or module balance is insufficient, we return an error. func (k Keeper) Borrow(ctx sdk.Context, borrowerAddr sdk.AccAddress, borrow sdk.Coin) error { - if err := k.ValidateBorrow(ctx, borrow); err != nil { + if err := k.validateBorrow(ctx, borrow); err != nil { return err } @@ -276,7 +276,7 @@ func (k Keeper) Repay(ctx sdk.Context, borrowerAddr sdk.AccAddress, payment sdk. // Collateralize enables selected uTokens for use as collateral by a single borrower. func (k Keeper) Collateralize(ctx sdk.Context, borrowerAddr sdk.AccAddress, coin sdk.Coin) error { - if err := k.ValidateCollateralize(ctx, coin); err != nil { + if err := k.validateCollateral(ctx, coin); err != nil { return err } @@ -347,7 +347,7 @@ func (k Keeper) Decollateralize(ctx sdk.Context, borrowerAddr sdk.AccAddress, co func (k Keeper) Liquidate( ctx sdk.Context, liquidatorAddr, borrowerAddr sdk.AccAddress, maxRepay sdk.Coin, rewardDenom string, ) (repaid sdk.Coin, liquidated sdk.Coin, reward sdk.Coin, err error) { - if err := k.ValidateAcceptedAsset(ctx, maxRepay); err != nil { + if err := k.validateAcceptedAsset(ctx, maxRepay); err != nil { return sdk.Coin{}, sdk.Coin{}, sdk.Coin{}, err } @@ -358,7 +358,7 @@ func (k Keeper) Liquidate( rewardDenom = types.ToTokenDenom(rewardDenom) } // ensure that base reward is a registered token - if err := k.ValidateAcceptedDenom(ctx, rewardDenom); err != nil { + if err := k.validateAcceptedDenom(ctx, rewardDenom); err != nil { return sdk.Coin{}, sdk.Coin{}, sdk.Coin{}, err } diff --git a/x/leverage/keeper/supply.go b/x/leverage/keeper/supply.go index 9f2184fc54..158de5d205 100644 --- a/x/leverage/keeper/supply.go +++ b/x/leverage/keeper/supply.go @@ -9,7 +9,7 @@ import ( // GetSupplied returns an sdk.Coin representing how much of a given denom a // user has supplied, including interest accrued. func (k Keeper) GetSupplied(ctx sdk.Context, supplierAddr sdk.AccAddress, denom string) (sdk.Coin, error) { - if err := k.ValidateAcceptedDenom(ctx, denom); err != nil { + if err := k.validateAcceptedDenom(ctx, denom); err != nil { return sdk.Coin{}, err } @@ -32,7 +32,7 @@ func (k Keeper) GetAllSupplied(ctx sdk.Context, supplierAddr sdk.AccAddress) (sd uTokens := sdk.Coins{} balance := k.bankKeeper.GetAllBalances(ctx, supplierAddr) for _, coin := range balance { - if k.ValidateAcceptedUTokenDenom(ctx, coin.Denom) == nil { + if k.validateAcceptedUTokenDenom(ctx, coin.Denom) == nil { uTokens = uTokens.Add(coin) } } @@ -44,7 +44,7 @@ func (k Keeper) GetAllSupplied(ctx sdk.Context, supplierAddr sdk.AccAddress) (sd // GetTotalSupply returns the total supplied by all suppliers in a given denom, // including any interest accrued. func (k Keeper) GetTotalSupply(ctx sdk.Context, denom string) (sdk.Coin, error) { - if err := k.ValidateAcceptedDenom(ctx, denom); err != nil { + if err := k.validateAcceptedDenom(ctx, denom); err != nil { return sdk.Coin{}, err } diff --git a/x/leverage/keeper/validate.go b/x/leverage/keeper/validate.go index fa6ab6bf7d..4b21e948fb 100644 --- a/x/leverage/keeper/validate.go +++ b/x/leverage/keeper/validate.go @@ -6,9 +6,9 @@ import ( "github.com/umee-network/umee/v2/x/leverage/types" ) -// ValidateAcceptedDenom validates an sdk.Coin and ensures it is a registered Token +// validateAcceptedDenom validates an sdk.Coin and ensures it is a registered Token // with Blacklisted == false -func (k Keeper) ValidateAcceptedDenom(ctx sdk.Context, denom string) error { +func (k Keeper) validateAcceptedDenom(ctx sdk.Context, denom string) error { token, err := k.GetTokenSettings(ctx, denom) if err != nil { return err @@ -16,35 +16,35 @@ func (k Keeper) ValidateAcceptedDenom(ctx sdk.Context, denom string) error { return token.AssertNotBlacklisted() } -// ValidateAcceptedUTokenDenom validates an sdk.Coin and ensures it is a uToken +// validateAcceptedUTokenDenom validates an sdk.Coin and ensures it is a uToken // associated with a registered Token with Blacklisted == false -func (k Keeper) ValidateAcceptedUTokenDenom(ctx sdk.Context, udenom string) error { +func (k Keeper) validateAcceptedUTokenDenom(ctx sdk.Context, udenom string) error { if !types.HasUTokenPrefix(udenom) { return types.ErrNotUToken.Wrap(udenom) } - return k.ValidateAcceptedDenom(ctx, types.ToTokenDenom(udenom)) + return k.validateAcceptedDenom(ctx, types.ToTokenDenom(udenom)) } -// ValidateAcceptedAsset validates an sdk.Coin and ensures it is a registered Token +// validateAcceptedAsset validates an sdk.Coin and ensures it is a registered Token // with Blacklisted == false -func (k Keeper) ValidateAcceptedAsset(ctx sdk.Context, coin sdk.Coin) error { +func (k Keeper) validateAcceptedAsset(ctx sdk.Context, coin sdk.Coin) error { if err := coin.Validate(); err != nil { return err } - return k.ValidateAcceptedDenom(ctx, coin.Denom) + return k.validateAcceptedDenom(ctx, coin.Denom) } -// ValidateAcceptedUToken validates an sdk.Coin and ensures it is a uToken +// validateAcceptedUToken validates an sdk.Coin and ensures it is a uToken // associated with a registered Token with Blacklisted == false -func (k Keeper) ValidateAcceptedUToken(ctx sdk.Context, coin sdk.Coin) error { +func (k Keeper) validateAcceptedUToken(ctx sdk.Context, coin sdk.Coin) error { if err := coin.Validate(); err != nil { return err } - return k.ValidateAcceptedUTokenDenom(ctx, coin.Denom) + return k.validateAcceptedUTokenDenom(ctx, coin.Denom) } -// ValidateSupply validates an sdk.Coin and ensures its Denom is a Token with EnableMsgSupply -func (k Keeper) ValidateSupply(ctx sdk.Context, loan sdk.Coin) error { +// validateSupply validates an sdk.Coin and ensures its Denom is a Token with EnableMsgSupply +func (k Keeper) validateSupply(ctx sdk.Context, loan sdk.Coin) error { if err := loan.Validate(); err != nil { return err } @@ -55,8 +55,8 @@ func (k Keeper) ValidateSupply(ctx sdk.Context, loan sdk.Coin) error { return token.AssertSupplyEnabled() } -// ValidateBorrow validates an sdk.Coin and ensures its Denom is a Token with EnableMsgBorrow -func (k Keeper) ValidateBorrow(ctx sdk.Context, borrow sdk.Coin) error { +// validateBorrow validates an sdk.Coin and ensures its Denom is a Token with EnableMsgBorrow +func (k Keeper) validateBorrow(ctx sdk.Context, borrow sdk.Coin) error { if err := borrow.Validate(); err != nil { return err } @@ -67,9 +67,9 @@ func (k Keeper) ValidateBorrow(ctx sdk.Context, borrow sdk.Coin) error { return token.AssertBorrowEnabled() } -// ValidateCollateralize validates an sdk.Coin and ensures it is a uToken of an accepted +// validateCollateral validates an sdk.Coin and ensures it is a uToken of an accepted // Token with EnableMsgSupply and CollateralWeight > 0 -func (k Keeper) ValidateCollateralize(ctx sdk.Context, collateral sdk.Coin) error { +func (k Keeper) validateCollateral(ctx sdk.Context, collateral sdk.Coin) error { if err := collateral.Validate(); err != nil { return err } diff --git a/x/leverage/simulation/operations.go b/x/leverage/simulation/operations.go index 5b4497420f..0711c9cdfe 100644 --- a/x/leverage/simulation/operations.go +++ b/x/leverage/simulation/operations.go @@ -430,7 +430,7 @@ func getSpendableUTokens( ) sdk.Coins { uTokens := sdk.NewCoins() for _, coin := range bk.SpendableCoins(ctx, addr) { - if lk.ValidateAcceptedUTokenDenom(ctx, coin.Denom) == nil { + if types.HasUTokenPrefix(coin.Denom) { uTokens = uTokens.Add(coin) } } From 1723306fa49619e56404175d6ed9552b1fb7e490 Mon Sep 17 00:00:00 2001 From: toteki <63419657+toteki@users.noreply.github.com> Date: Sun, 7 Aug 2022 17:17:40 -0700 Subject: [PATCH 13/38] -- --- x/leverage/keeper/collateral.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x/leverage/keeper/collateral.go b/x/leverage/keeper/collateral.go index 37f939352f..455c89d368 100644 --- a/x/leverage/keeper/collateral.go +++ b/x/leverage/keeper/collateral.go @@ -86,7 +86,7 @@ func (k Keeper) setCollateralAmount(ctx sdk.Context, borrowerAddr sdk.AccAddress // the x/leverage module account currently holds as collateral. Non-uTokens and invalid // assets return zero. func (k Keeper) GetTotalCollateral(ctx sdk.Context, denom string) sdk.Int { - if k.validateAcceptedUTokenDenom(ctx, denom) != nil { + if !types.HasUTokenPrefix(denom) { // non-uTokens cannot be collateral return sdk.ZeroInt() } From 5362356084ca7e7878ee2b3a2c4f3bafe09a3534 Mon Sep 17 00:00:00 2001 From: toteki <63419657+toteki@users.noreply.github.com> Date: Sun, 7 Aug 2022 17:18:53 -0700 Subject: [PATCH 14/38] ++ --- x/leverage/keeper/collateral.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/x/leverage/keeper/collateral.go b/x/leverage/keeper/collateral.go index 455c89d368..5356e739cd 100644 --- a/x/leverage/keeper/collateral.go +++ b/x/leverage/keeper/collateral.go @@ -83,8 +83,8 @@ func (k Keeper) setCollateralAmount(ctx sdk.Context, borrowerAddr sdk.AccAddress } // GetTotalCollateral returns an sdk.Coin representing how much of a given uToken -// the x/leverage module account currently holds as collateral. Non-uTokens and invalid -// assets return zero. +// the x/leverage module account currently holds as collateral. Non-uTokens return +// zero. func (k Keeper) GetTotalCollateral(ctx sdk.Context, denom string) sdk.Int { if !types.HasUTokenPrefix(denom) { // non-uTokens cannot be collateral From 8444aab5d3751816e80589b5990dc891cb15befa Mon Sep 17 00:00:00 2001 From: toteki <63419657+toteki@users.noreply.github.com> Date: Sun, 7 Aug 2022 17:23:08 -0700 Subject: [PATCH 15/38] -- --- x/leverage/keeper/iter.go | 2 +- x/leverage/keeper/reserves.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/x/leverage/keeper/iter.go b/x/leverage/keeper/iter.go index 21d2f69867..7be4bc5c1e 100644 --- a/x/leverage/keeper/iter.go +++ b/x/leverage/keeper/iter.go @@ -238,7 +238,7 @@ func (k Keeper) SweepBadDebts(ctx sdk.Context) error { // if collateral is still zero, attempt to repay a single address's debt in this denom if !done { var err error - done, err = k.repayBadDebt(ctx, addr, denom) + done, err = k.RepayBadDebt(ctx, addr, denom) if err != nil { return err } diff --git a/x/leverage/keeper/reserves.go b/x/leverage/keeper/reserves.go index e419c3bd33..4a267571b6 100644 --- a/x/leverage/keeper/reserves.go +++ b/x/leverage/keeper/reserves.go @@ -65,9 +65,9 @@ func (k Keeper) checkBadDebt(ctx sdk.Context, borrowerAddr sdk.AccAddress) error return nil } -// repayBadDebt uses reserves to repay borrower's debts of a given denom. +// RepayBadDebt uses reserves to repay borrower's debts of a given denom. // It returns a boolean representing whether full repayment was achieved. -func (k Keeper) repayBadDebt(ctx sdk.Context, borrowerAddr sdk.AccAddress, denom string) (bool, error) { +func (k Keeper) RepayBadDebt(ctx sdk.Context, borrowerAddr sdk.AccAddress, denom string) (bool, error) { borrowed := k.GetBorrow(ctx, borrowerAddr, denom) reserved := k.GetReserveAmount(ctx, denom) From 1b79c6341b4ec70c27697449bf0c8ce8e949326a Mon Sep 17 00:00:00 2001 From: toteki <63419657+toteki@users.noreply.github.com> Date: Sun, 7 Aug 2022 17:27:44 -0700 Subject: [PATCH 16/38] ++ --- x/leverage/keeper/keeper.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/x/leverage/keeper/keeper.go b/x/leverage/keeper/keeper.go index 795299b92f..b359aee745 100644 --- a/x/leverage/keeper/keeper.go +++ b/x/leverage/keeper/keeper.go @@ -110,9 +110,10 @@ func (k Keeper) Supply(ctx sdk.Context, supplierAddr sdk.AccAddress, loan sdk.Co return nil } -// Withdraw attempts to deposit uTokens into the leverage module in exchange -// for the original tokens supplied. Accepts a uToken amount to exchange for base tokens. -// If the uToken denom is invalid or account or module balance insufficient, returns error. +// Withdraw attempts to deposit uTokens into the leverage module in exchange for base tokens. +// If there are not enough uTokens in balance, Withdraw will attempt to withdraw uToken collateral +// to make up the difference (as long as borrow limit allows). If the uToken denom is invalid or +// balances are insufficient to withdraw the full amount requested, returns an error. func (k Keeper) Withdraw(ctx sdk.Context, supplierAddr sdk.AccAddress, uToken sdk.Coin) error { if err := k.validateAcceptedUToken(ctx, uToken); err != nil { return err From c15e4d60ca2605192ac47f5ca4899515e783dbf0 Mon Sep 17 00:00:00 2001 From: toteki <63419657+toteki@users.noreply.github.com> Date: Sun, 7 Aug 2022 17:28:47 -0700 Subject: [PATCH 17/38] -- --- x/leverage/keeper/keeper.go | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/x/leverage/keeper/keeper.go b/x/leverage/keeper/keeper.go index b359aee745..19c0de388d 100644 --- a/x/leverage/keeper/keeper.go +++ b/x/leverage/keeper/keeper.go @@ -114,13 +114,13 @@ func (k Keeper) Supply(ctx sdk.Context, supplierAddr sdk.AccAddress, loan sdk.Co // If there are not enough uTokens in balance, Withdraw will attempt to withdraw uToken collateral // to make up the difference (as long as borrow limit allows). If the uToken denom is invalid or // balances are insufficient to withdraw the full amount requested, returns an error. -func (k Keeper) Withdraw(ctx sdk.Context, supplierAddr sdk.AccAddress, uToken sdk.Coin) error { - if err := k.validateAcceptedUToken(ctx, uToken); err != nil { +func (k Keeper) Withdraw(ctx sdk.Context, supplierAddr sdk.AccAddress, coin sdk.Coin) error { + if err := k.validateAcceptedUToken(ctx, coin); err != nil { return err } // calculate base asset amount to withdraw - token, err := k.ExchangeUToken(ctx, uToken) + token, err := k.ExchangeUToken(ctx, coin) if err != nil { return err } @@ -133,9 +133,9 @@ func (k Keeper) Withdraw(ctx sdk.Context, supplierAddr sdk.AccAddress, uToken sd } // Withdraw will first attempt to use any uTokens in the supplier's wallet - uTokensFromWallet := sdk.MinInt(k.bankKeeper.SpendableCoins(ctx, supplierAddr).AmountOf(uToken.Denom), uToken.Amount) + uTokensFromWallet := sdk.MinInt(k.bankKeeper.SpendableCoins(ctx, supplierAddr).AmountOf(coin.Denom), coin.Amount) // Any additional uTokens must come from the supplier's collateral - uTokensFromCollateral := uToken.Amount.Sub(uTokensFromWallet) + uTokensFromCollateral := coin.Amount.Sub(uTokensFromWallet) if uTokensFromCollateral.IsPositive() { // Calculate current borrowed value @@ -147,17 +147,17 @@ func (k Keeper) Withdraw(ctx sdk.Context, supplierAddr sdk.AccAddress, uToken sd // Check for sufficient collateral collateral := k.GetBorrowerCollateral(ctx, supplierAddr) - if collateral.AmountOf(uToken.Denom).LT(uTokensFromCollateral) { + if collateral.AmountOf(coin.Denom).LT(uTokensFromCollateral) { info := fmt.Sprintf( "\n spendable: %s\n addr: %s\n\n", k.bankKeeper.SpendableCoins(ctx, supplierAddr).String(), supplierAddr.String(), ) - return sdkerrors.Wrap(types.ErrInsufficientBalance, uToken.String()+info) + return sdkerrors.Wrap(types.ErrInsufficientBalance, coin.String()+info) } // Calculate what borrow limit will be AFTER this withdrawal - collateralToWithdraw := sdk.NewCoins(sdk.NewCoin(uToken.Denom, uTokensFromCollateral)) + collateralToWithdraw := sdk.NewCoins(sdk.NewCoin(coin.Denom, uTokensFromCollateral)) newBorrowLimit, err := k.CalculateBorrowLimit(ctx, collateral.Sub(collateralToWithdraw)) if err != nil { return err @@ -170,14 +170,14 @@ func (k Keeper) Withdraw(ctx sdk.Context, supplierAddr sdk.AccAddress, uToken sd } // reduce the supplier's collateral by amountFromCollateral - newCollateral := sdk.NewCoin(uToken.Denom, collateral.AmountOf(uToken.Denom).Sub(uTokensFromCollateral)) + newCollateral := sdk.NewCoin(coin.Denom, collateral.AmountOf(coin.Denom).Sub(uTokensFromCollateral)) if err = k.setCollateralAmount(ctx, supplierAddr, newCollateral); err != nil { return err } } // transfer uTokensFromWallet to the module account - uTokens := sdk.NewCoins(sdk.NewCoin(uToken.Denom, uTokensFromWallet)) + uTokens := sdk.NewCoins(sdk.NewCoin(coin.Denom, uTokensFromWallet)) if err = k.bankKeeper.SendCoinsFromAccountToModule(ctx, supplierAddr, types.ModuleName, uTokens); err != nil { return err } @@ -189,10 +189,10 @@ func (k Keeper) Withdraw(ctx sdk.Context, supplierAddr sdk.AccAddress, uToken sd } // burn the uTokens and set the new total uToken supply - if err = k.bankKeeper.BurnCoins(ctx, types.ModuleName, sdk.NewCoins(uToken)); err != nil { + if err = k.bankKeeper.BurnCoins(ctx, types.ModuleName, sdk.NewCoins(coin)); err != nil { return err } - if err = k.setUTokenSupply(ctx, k.GetUTokenSupply(ctx, uToken.Denom).Sub(uToken)); err != nil { + if err = k.setUTokenSupply(ctx, k.GetUTokenSupply(ctx, coin.Denom).Sub(coin)); err != nil { return err } From f3b6d78a016b4ce9b1d7900bc4700947ab571f9e Mon Sep 17 00:00:00 2001 From: toteki <63419657+toteki@users.noreply.github.com> Date: Sun, 7 Aug 2022 17:30:59 -0700 Subject: [PATCH 18/38] -- --- x/leverage/keeper/keeper.go | 23 +++++++++-------------- x/leverage/keeper/validate.go | 4 ++-- 2 files changed, 11 insertions(+), 16 deletions(-) diff --git a/x/leverage/keeper/keeper.go b/x/leverage/keeper/keeper.go index 19c0de388d..e832ea7173 100644 --- a/x/leverage/keeper/keeper.go +++ b/x/leverage/keeper/keeper.go @@ -133,11 +133,11 @@ func (k Keeper) Withdraw(ctx sdk.Context, supplierAddr sdk.AccAddress, coin sdk. } // Withdraw will first attempt to use any uTokens in the supplier's wallet - uTokensFromWallet := sdk.MinInt(k.bankKeeper.SpendableCoins(ctx, supplierAddr).AmountOf(coin.Denom), coin.Amount) + amountFromWallet := sdk.MinInt(k.bankKeeper.SpendableCoins(ctx, supplierAddr).AmountOf(coin.Denom), coin.Amount) // Any additional uTokens must come from the supplier's collateral - uTokensFromCollateral := coin.Amount.Sub(uTokensFromWallet) + amountFromCollateral := coin.Amount.Sub(amountFromWallet) - if uTokensFromCollateral.IsPositive() { + if amountFromCollateral.IsPositive() { // Calculate current borrowed value borrowed := k.GetBorrowerBorrows(ctx, supplierAddr) borrowedValue, err := k.TotalTokenValue(ctx, borrowed) @@ -147,17 +147,12 @@ func (k Keeper) Withdraw(ctx sdk.Context, supplierAddr sdk.AccAddress, coin sdk. // Check for sufficient collateral collateral := k.GetBorrowerCollateral(ctx, supplierAddr) - if collateral.AmountOf(coin.Denom).LT(uTokensFromCollateral) { - info := fmt.Sprintf( - "\n spendable: %s\n addr: %s\n\n", - k.bankKeeper.SpendableCoins(ctx, supplierAddr).String(), - supplierAddr.String(), - ) - return sdkerrors.Wrap(types.ErrInsufficientBalance, coin.String()+info) + if collateral.AmountOf(coin.Denom).LT(amountFromCollateral) { + return sdkerrors.Wrap(types.ErrInsufficientBalance, coin.String()) } // Calculate what borrow limit will be AFTER this withdrawal - collateralToWithdraw := sdk.NewCoins(sdk.NewCoin(coin.Denom, uTokensFromCollateral)) + collateralToWithdraw := sdk.NewCoins(sdk.NewCoin(coin.Denom, amountFromCollateral)) newBorrowLimit, err := k.CalculateBorrowLimit(ctx, collateral.Sub(collateralToWithdraw)) if err != nil { return err @@ -170,14 +165,14 @@ func (k Keeper) Withdraw(ctx sdk.Context, supplierAddr sdk.AccAddress, coin sdk. } // reduce the supplier's collateral by amountFromCollateral - newCollateral := sdk.NewCoin(coin.Denom, collateral.AmountOf(coin.Denom).Sub(uTokensFromCollateral)) + newCollateral := sdk.NewCoin(coin.Denom, collateral.AmountOf(coin.Denom).Sub(amountFromCollateral)) if err = k.setCollateralAmount(ctx, supplierAddr, newCollateral); err != nil { return err } } // transfer uTokensFromWallet to the module account - uTokens := sdk.NewCoins(sdk.NewCoin(coin.Denom, uTokensFromWallet)) + uTokens := sdk.NewCoins(sdk.NewCoin(coin.Denom, amountFromWallet)) if err = k.bankKeeper.SendCoinsFromAccountToModule(ctx, supplierAddr, types.ModuleName, uTokens); err != nil { return err } @@ -277,7 +272,7 @@ func (k Keeper) Repay(ctx sdk.Context, borrowerAddr sdk.AccAddress, payment sdk. // Collateralize enables selected uTokens for use as collateral by a single borrower. func (k Keeper) Collateralize(ctx sdk.Context, borrowerAddr sdk.AccAddress, coin sdk.Coin) error { - if err := k.validateCollateral(ctx, coin); err != nil { + if err := k.validateCollateralAsset(ctx, coin); err != nil { return err } diff --git a/x/leverage/keeper/validate.go b/x/leverage/keeper/validate.go index 4b21e948fb..0693e06e71 100644 --- a/x/leverage/keeper/validate.go +++ b/x/leverage/keeper/validate.go @@ -67,9 +67,9 @@ func (k Keeper) validateBorrow(ctx sdk.Context, borrow sdk.Coin) error { return token.AssertBorrowEnabled() } -// validateCollateral validates an sdk.Coin and ensures it is a uToken of an accepted +// validateCollateralAsset validates an sdk.Coin and ensures it is a uToken of an accepted // Token with EnableMsgSupply and CollateralWeight > 0 -func (k Keeper) validateCollateral(ctx sdk.Context, collateral sdk.Coin) error { +func (k Keeper) validateCollateralAsset(ctx sdk.Context, collateral sdk.Coin) error { if err := collateral.Validate(); err != nil { return err } From b3964ebe2c22e633f3d91cf41ea5a62492f04477 Mon Sep 17 00:00:00 2001 From: toteki <63419657+toteki@users.noreply.github.com> Date: Sun, 7 Aug 2022 17:40:12 -0700 Subject: [PATCH 19/38] ++ --- x/leverage/keeper/filter.go | 10 ++++++++++ x/leverage/keeper/iter.go | 1 - x/leverage/keeper/keeper.go | 2 +- x/leverage/keeper/reserves.go | 4 ++-- 4 files changed, 13 insertions(+), 4 deletions(-) diff --git a/x/leverage/keeper/filter.go b/x/leverage/keeper/filter.go index 864c958273..e965e90a4a 100644 --- a/x/leverage/keeper/filter.go +++ b/x/leverage/keeper/filter.go @@ -24,3 +24,13 @@ func (k Keeper) filterAcceptedCoins(ctx sdk.Context, coins sdk.Coins) sdk.Coins }, ) } + +// filterAcceptedCoins returns the subset of an sdk.Coins that are accepted, non-blacklisted uTokens +func (k Keeper) filterAcceptedUTokens(ctx sdk.Context, coins sdk.Coins) sdk.Coins { + return k.filterCoins( + coins, + func(c sdk.Coin) bool { + return k.validateAcceptedUToken(ctx, c) == nil + }, + ) +} diff --git a/x/leverage/keeper/iter.go b/x/leverage/keeper/iter.go index 7be4bc5c1e..535c0b177b 100644 --- a/x/leverage/keeper/iter.go +++ b/x/leverage/keeper/iter.go @@ -146,7 +146,6 @@ func (k Keeper) GetBorrowerCollateral(ctx sdk.Context, borrowerAddr sdk.AccAddre return err } - // add to totalBorrowed totalCollateral = totalCollateral.Add(sdk.NewCoin(denom, amount)) return nil } diff --git a/x/leverage/keeper/keeper.go b/x/leverage/keeper/keeper.go index e832ea7173..e72b9a1652 100644 --- a/x/leverage/keeper/keeper.go +++ b/x/leverage/keeper/keeper.go @@ -171,7 +171,7 @@ func (k Keeper) Withdraw(ctx sdk.Context, supplierAddr sdk.AccAddress, coin sdk. } } - // transfer uTokensFromWallet to the module account + // transfer amountFromWallet uTokens to the module account uTokens := sdk.NewCoins(sdk.NewCoin(coin.Denom, amountFromWallet)) if err = k.bankKeeper.SendCoinsFromAccountToModule(ctx, supplierAddr, types.ModuleName, uTokens); err != nil { return err diff --git a/x/leverage/keeper/reserves.go b/x/leverage/keeper/reserves.go index 4a267571b6..cf01fb2af6 100644 --- a/x/leverage/keeper/reserves.go +++ b/x/leverage/keeper/reserves.go @@ -49,8 +49,8 @@ func (k Keeper) setReserveAmount(ctx sdk.Context, coin sdk.Coin) error { // checkBadDebt detects if a borrower has zero non-blacklisted collateral, // and marks any remaining borrowed tokens as bad debt. func (k Keeper) checkBadDebt(ctx sdk.Context, borrowerAddr sdk.AccAddress) error { - // get remaining collateral, ignoring blacklisted - remainingCollateral := k.filterAcceptedCoins(ctx, k.GetBorrowerCollateral(ctx, borrowerAddr)) + // get remaining collateral uTokens, ignoring blacklisted + remainingCollateral := k.filterAcceptedUTokens(ctx, k.GetBorrowerCollateral(ctx, borrowerAddr)) // detect bad debt if collateral is completely exhausted if remainingCollateral.IsZero() { From 93ed1849f73e0b4e1ed8001c0723a2e0d4969a10 Mon Sep 17 00:00:00 2001 From: toteki <63419657+toteki@users.noreply.github.com> Date: Sun, 7 Aug 2022 17:40:55 -0700 Subject: [PATCH 20/38] ++ --- x/leverage/keeper/filter.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x/leverage/keeper/filter.go b/x/leverage/keeper/filter.go index e965e90a4a..0b1a276c41 100644 --- a/x/leverage/keeper/filter.go +++ b/x/leverage/keeper/filter.go @@ -25,7 +25,7 @@ func (k Keeper) filterAcceptedCoins(ctx sdk.Context, coins sdk.Coins) sdk.Coins ) } -// filterAcceptedCoins returns the subset of an sdk.Coins that are accepted, non-blacklisted uTokens +// filterAcceptedUTokens returns the subset of an sdk.Coins that are accepted, non-blacklisted uTokens func (k Keeper) filterAcceptedUTokens(ctx sdk.Context, coins sdk.Coins) sdk.Coins { return k.filterCoins( coins, From 18793950b35d3b95653852f53350fa79e6ae246f Mon Sep 17 00:00:00 2001 From: toteki <63419657+toteki@users.noreply.github.com> Date: Sun, 7 Aug 2022 17:51:48 -0700 Subject: [PATCH 21/38] GetSupply blacklisted behavior --- x/leverage/keeper/supply.go | 10 +++++----- x/leverage/types/errors.go | 3 ++- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/x/leverage/keeper/supply.go b/x/leverage/keeper/supply.go index 158de5d205..f51737e342 100644 --- a/x/leverage/keeper/supply.go +++ b/x/leverage/keeper/supply.go @@ -9,8 +9,8 @@ import ( // GetSupplied returns an sdk.Coin representing how much of a given denom a // user has supplied, including interest accrued. func (k Keeper) GetSupplied(ctx sdk.Context, supplierAddr sdk.AccAddress, denom string) (sdk.Coin, error) { - if err := k.validateAcceptedDenom(ctx, denom); err != nil { - return sdk.Coin{}, err + if types.HasUTokenPrefix(denom) { + return sdk.Coin{}, types.ErrUToken.Wrap(denom) } // sum wallet-held and collateral-enabled uTokens in the associated uToken denom @@ -32,7 +32,7 @@ func (k Keeper) GetAllSupplied(ctx sdk.Context, supplierAddr sdk.AccAddress) (sd uTokens := sdk.Coins{} balance := k.bankKeeper.GetAllBalances(ctx, supplierAddr) for _, coin := range balance { - if k.validateAcceptedUTokenDenom(ctx, coin.Denom) == nil { + if types.HasUTokenPrefix(coin.Denom) { uTokens = uTokens.Add(coin) } } @@ -44,8 +44,8 @@ func (k Keeper) GetAllSupplied(ctx sdk.Context, supplierAddr sdk.AccAddress) (sd // GetTotalSupply returns the total supplied by all suppliers in a given denom, // including any interest accrued. func (k Keeper) GetTotalSupply(ctx sdk.Context, denom string) (sdk.Coin, error) { - if err := k.validateAcceptedDenom(ctx, denom); err != nil { - return sdk.Coin{}, err + if types.HasUTokenPrefix(denom) { + return sdk.Coin{}, types.ErrUToken.Wrap(denom) } // convert associated uToken's total supply to base tokens diff --git a/x/leverage/types/errors.go b/x/leverage/types/errors.go index 89793cffbc..659744f538 100644 --- a/x/leverage/types/errors.go +++ b/x/leverage/types/errors.go @@ -38,5 +38,6 @@ var ( 1126, "market total collateral would exceed MaxCollateralShare", ) - ErrNotUToken = sdkerrors.Register(ModuleName, 1127, "not a uToken denom") + ErrNotUToken = sdkerrors.Register(ModuleName, 1127, "denom is not a uToken") + ErrUToken = sdkerrors.Register(ModuleName, 1128, "denom is a uToken") ) From 6460bbb541b3911e6bed030d39d6b5c816ecd4ee Mon Sep 17 00:00:00 2001 From: toteki <63419657+toteki@users.noreply.github.com> Date: Sun, 7 Aug 2022 18:09:20 -0700 Subject: [PATCH 22/38] -- --- x/leverage/simulation/operations.go | 8 -------- 1 file changed, 8 deletions(-) diff --git a/x/leverage/simulation/operations.go b/x/leverage/simulation/operations.go index 0711c9cdfe..e20de462ca 100644 --- a/x/leverage/simulation/operations.go +++ b/x/leverage/simulation/operations.go @@ -1,7 +1,6 @@ package simulation import ( - "fmt" "math/rand" "github.com/cosmos/cosmos-sdk/baseapp" @@ -412,13 +411,6 @@ func randomWithdrawFields( return acc, sdk.Coin{}, true } - info := fmt.Sprintf( - "\n spendable: %s\n addr: %s\n\n", - bk.SpendableCoins(ctx, acc.Address).String(), - acc.Address.String(), - ) - fmt.Println(info) - subset := randomCoin(r, simtypes.RandSubsetCoins(r, uTokens)) return acc, subset, false } From 5e406d9ac5010730e0bab8a22b22837cd1e6a30c Mon Sep 17 00:00:00 2001 From: toteki <63419657+toteki@users.noreply.github.com> Date: Sun, 7 Aug 2022 18:11:20 -0700 Subject: [PATCH 23/38] -- --- x/leverage/simulation/operations.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/x/leverage/simulation/operations.go b/x/leverage/simulation/operations.go index e20de462ca..d2d0a0be97 100644 --- a/x/leverage/simulation/operations.go +++ b/x/leverage/simulation/operations.go @@ -407,12 +407,13 @@ func randomWithdrawFields( uTokens := getSpendableUTokens(ctx, acc.Address, bk, lk) uTokens = uTokens.Add(lk.GetBorrowerCollateral(ctx, acc.Address)...) + uTokens = simtypes.RandSubsetCoins(r, uTokens) + if uTokens.Empty() { return acc, sdk.Coin{}, true } - subset := randomCoin(r, simtypes.RandSubsetCoins(r, uTokens)) - return acc, subset, false + return acc, randomCoin(r, uTokens), false } // getSpendableUTokens returns all uTokens from an account's spendable coins. From 886f6ee0d34a932a3b4cd1f4b2510371bffd81cd Mon Sep 17 00:00:00 2001 From: toteki <63419657+toteki@users.noreply.github.com> Date: Sun, 7 Aug 2022 18:19:21 -0700 Subject: [PATCH 24/38] Withdraw blacklisted behavior --- x/leverage/keeper/keeper.go | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/x/leverage/keeper/keeper.go b/x/leverage/keeper/keeper.go index e72b9a1652..5b70e472fb 100644 --- a/x/leverage/keeper/keeper.go +++ b/x/leverage/keeper/keeper.go @@ -114,13 +114,16 @@ func (k Keeper) Supply(ctx sdk.Context, supplierAddr sdk.AccAddress, loan sdk.Co // If there are not enough uTokens in balance, Withdraw will attempt to withdraw uToken collateral // to make up the difference (as long as borrow limit allows). If the uToken denom is invalid or // balances are insufficient to withdraw the full amount requested, returns an error. -func (k Keeper) Withdraw(ctx sdk.Context, supplierAddr sdk.AccAddress, coin sdk.Coin) error { - if err := k.validateAcceptedUToken(ctx, coin); err != nil { +func (k Keeper) Withdraw(ctx sdk.Context, supplierAddr sdk.AccAddress, uToken sdk.Coin) error { + if err := uToken.Validate(); err != nil { return err } + if !types.HasUTokenPrefix(uToken.Denom) { + return types.ErrNotUToken.Wrap(uToken.Denom) + } // calculate base asset amount to withdraw - token, err := k.ExchangeUToken(ctx, coin) + token, err := k.ExchangeUToken(ctx, uToken) if err != nil { return err } @@ -133,9 +136,9 @@ func (k Keeper) Withdraw(ctx sdk.Context, supplierAddr sdk.AccAddress, coin sdk. } // Withdraw will first attempt to use any uTokens in the supplier's wallet - amountFromWallet := sdk.MinInt(k.bankKeeper.SpendableCoins(ctx, supplierAddr).AmountOf(coin.Denom), coin.Amount) + amountFromWallet := sdk.MinInt(k.bankKeeper.SpendableCoins(ctx, supplierAddr).AmountOf(uToken.Denom), uToken.Amount) // Any additional uTokens must come from the supplier's collateral - amountFromCollateral := coin.Amount.Sub(amountFromWallet) + amountFromCollateral := uToken.Amount.Sub(amountFromWallet) if amountFromCollateral.IsPositive() { // Calculate current borrowed value @@ -147,12 +150,12 @@ func (k Keeper) Withdraw(ctx sdk.Context, supplierAddr sdk.AccAddress, coin sdk. // Check for sufficient collateral collateral := k.GetBorrowerCollateral(ctx, supplierAddr) - if collateral.AmountOf(coin.Denom).LT(amountFromCollateral) { - return sdkerrors.Wrap(types.ErrInsufficientBalance, coin.String()) + if collateral.AmountOf(uToken.Denom).LT(amountFromCollateral) { + return sdkerrors.Wrap(types.ErrInsufficientBalance, uToken.String()) } // Calculate what borrow limit will be AFTER this withdrawal - collateralToWithdraw := sdk.NewCoins(sdk.NewCoin(coin.Denom, amountFromCollateral)) + collateralToWithdraw := sdk.NewCoins(sdk.NewCoin(uToken.Denom, amountFromCollateral)) newBorrowLimit, err := k.CalculateBorrowLimit(ctx, collateral.Sub(collateralToWithdraw)) if err != nil { return err @@ -165,14 +168,14 @@ func (k Keeper) Withdraw(ctx sdk.Context, supplierAddr sdk.AccAddress, coin sdk. } // reduce the supplier's collateral by amountFromCollateral - newCollateral := sdk.NewCoin(coin.Denom, collateral.AmountOf(coin.Denom).Sub(amountFromCollateral)) + newCollateral := sdk.NewCoin(uToken.Denom, collateral.AmountOf(uToken.Denom).Sub(amountFromCollateral)) if err = k.setCollateralAmount(ctx, supplierAddr, newCollateral); err != nil { return err } } // transfer amountFromWallet uTokens to the module account - uTokens := sdk.NewCoins(sdk.NewCoin(coin.Denom, amountFromWallet)) + uTokens := sdk.NewCoins(sdk.NewCoin(uToken.Denom, amountFromWallet)) if err = k.bankKeeper.SendCoinsFromAccountToModule(ctx, supplierAddr, types.ModuleName, uTokens); err != nil { return err } @@ -184,10 +187,10 @@ func (k Keeper) Withdraw(ctx sdk.Context, supplierAddr sdk.AccAddress, coin sdk. } // burn the uTokens and set the new total uToken supply - if err = k.bankKeeper.BurnCoins(ctx, types.ModuleName, sdk.NewCoins(coin)); err != nil { + if err = k.bankKeeper.BurnCoins(ctx, types.ModuleName, sdk.NewCoins(uToken)); err != nil { return err } - if err = k.setUTokenSupply(ctx, k.GetUTokenSupply(ctx, coin.Denom).Sub(coin)); err != nil { + if err = k.setUTokenSupply(ctx, k.GetUTokenSupply(ctx, uToken.Denom).Sub(uToken)); err != nil { return err } From b969fd028e8c516eee25baac1bbc3cdb1f134cc0 Mon Sep 17 00:00:00 2001 From: toteki <63419657+toteki@users.noreply.github.com> Date: Sun, 7 Aug 2022 22:20:27 -0700 Subject: [PATCH 25/38] remaining todos --- x/leverage/keeper/iter.go | 1 + x/leverage/simulation/operations_test.go | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/x/leverage/keeper/iter.go b/x/leverage/keeper/iter.go index 535c0b177b..8d1694f03c 100644 --- a/x/leverage/keeper/iter.go +++ b/x/leverage/keeper/iter.go @@ -233,6 +233,7 @@ func (k Keeper) SweepBadDebts(ctx sdk.Context) error { // first check if the borrower has gained collateral since the bad debt was identified done := k.HasCollateral(ctx, addr) + // TODO: Non-blacklisted collateral only // if collateral is still zero, attempt to repay a single address's debt in this denom if !done { diff --git a/x/leverage/simulation/operations_test.go b/x/leverage/simulation/operations_test.go index 02d31f4c6b..40b629f01b 100644 --- a/x/leverage/simulation/operations_test.go +++ b/x/leverage/simulation/operations_test.go @@ -147,7 +147,7 @@ func (s *SimTestSuite) TestSimulateMsgWithdraw() { supplyToken := sdk.NewCoin(umeeapp.BondDenom, sdk.NewInt(100)) accs := s.getTestingAccounts(r, 3, func(fundedAccount simtypes.Account) { - s.app.LeverageKeeper.Supply(s.ctx, fundedAccount.Address, supplyToken) + s.Require().NoError(s.app.LeverageKeeper.Supply(s.ctx, fundedAccount.Address, supplyToken)) }) s.app.BeginBlock(abci.RequestBeginBlock{Header: tmproto.Header{Height: s.app.LastBlockHeight() + 1, AppHash: s.app.LastCommitID().Hash}}) @@ -159,6 +159,8 @@ func (s *SimTestSuite) TestSimulateMsgWithdraw() { var msg types.MsgWithdraw types.ModuleCdc.UnmarshalJSON(operationMsg.Msg, &msg) + // todo: message order execution? + s.Require().True(operationMsg.OK) s.Require().Equal("umee1ghekyjucln7y67ntx7cf27m9dpuxxemn8w6h33", msg.Supplier) s.Require().Equal(types.EventTypeWithdraw, msg.Type()) From 64cc8f1e60e7f690b3930659f5d69405e1390751 Mon Sep 17 00:00:00 2001 From: toteki <63419657+toteki@users.noreply.github.com> Date: Mon, 8 Aug 2022 08:41:19 -0700 Subject: [PATCH 26/38] improve error message --- x/leverage/keeper/keeper.go | 5 ++++- x/leverage/keeper/keeper_test.go | 2 +- x/leverage/simulation/operations_test.go | 28 +++++++++++++++++++++--- 3 files changed, 30 insertions(+), 5 deletions(-) diff --git a/x/leverage/keeper/keeper.go b/x/leverage/keeper/keeper.go index 5b70e472fb..d091795137 100644 --- a/x/leverage/keeper/keeper.go +++ b/x/leverage/keeper/keeper.go @@ -151,7 +151,10 @@ func (k Keeper) Withdraw(ctx sdk.Context, supplierAddr sdk.AccAddress, uToken sd // Check for sufficient collateral collateral := k.GetBorrowerCollateral(ctx, supplierAddr) if collateral.AmountOf(uToken.Denom).LT(amountFromCollateral) { - return sdkerrors.Wrap(types.ErrInsufficientBalance, uToken.String()) + return types.ErrInsufficientBalance.Wrapf("%s balance + %s collateral / %s withdraw", + amountFromWallet.String(), + collateral.AmountOf(uToken.Denom).String(), + uToken.String()) } // Calculate what borrow limit will be AFTER this withdrawal diff --git a/x/leverage/keeper/keeper_test.go b/x/leverage/keeper/keeper_test.go index d8392ecce7..1a54ab249b 100644 --- a/x/leverage/keeper/keeper_test.go +++ b/x/leverage/keeper/keeper_test.go @@ -924,7 +924,7 @@ func (s *IntegrationTestSuite) TestWithdraw_InsufficientCollateral() { // withdraw more collateral than available uToken := collateral.Add(sdk.NewInt64Coin(uTokenDenom, 1)) err := s.app.LeverageKeeper.Withdraw(s.ctx, supplierAddr, uToken) - s.Require().EqualError(err, "1000001u/uumee: insufficient balance") + s.Require().EqualError(err, "0 balance + 1000000 collateral / 1000001u/uumee withdraw: insufficient balance") } func (s *IntegrationTestSuite) TestTotalCollateral() { diff --git a/x/leverage/simulation/operations_test.go b/x/leverage/simulation/operations_test.go index 40b629f01b..c4c86d96e2 100644 --- a/x/leverage/simulation/operations_test.go +++ b/x/leverage/simulation/operations_test.go @@ -1,6 +1,7 @@ package simulation_test import ( + "fmt" "math/rand" "testing" @@ -122,6 +123,7 @@ func (s *SimTestSuite) TestWeightedOperations() { } } +/* func (s *SimTestSuite) TestSimulateMsgSupply() { r := rand.New(rand.NewSource(1)) accs := s.getTestingAccounts(r, 3, func(fundedAccount simtypes.Account) {}) @@ -141,33 +143,52 @@ func (s *SimTestSuite) TestSimulateMsgSupply() { s.Require().Equal("4896096uumee", msg.Asset.String()) s.Require().Len(futureOperations, 0) } +*/ func (s *SimTestSuite) TestSimulateMsgWithdraw() { r := rand.New(rand.NewSource(1)) supplyToken := sdk.NewCoin(umeeapp.BondDenom, sdk.NewInt(100)) + withdrawToken := sdk.NewCoin("u/"+umeeapp.BondDenom, sdk.NewInt(80)) - accs := s.getTestingAccounts(r, 3, func(fundedAccount simtypes.Account) { + accs := s.getTestingAccounts(r, 18, func(fundedAccount simtypes.Account) { s.Require().NoError(s.app.LeverageKeeper.Supply(s.ctx, fundedAccount.Address, supplyToken)) + s.Require().NoError(s.app.LeverageKeeper.Withdraw(s.ctx, fundedAccount.Address, withdrawToken)) }) s.app.BeginBlock(abci.RequestBeginBlock{Header: tmproto.Header{Height: s.app.LastBlockHeight() + 1, AppHash: s.app.LastCommitID().Hash}}) + fmt.Println("\n>") + for i, a := range accs { + fmt.Printf("+%d+ %s %s\n", i, a.Address.String(), s.app.BankKeeper.SpendableCoins(s.ctx, a.Address).AmountOf("u/uumee")) + } + fmt.Println("\n ") + op := simulation.SimulateMsgWithdraw(s.app.AccountKeeper, s.app.BankKeeper, s.app.LeverageKeeper) operationMsg, futureOperations, err := op(r, s.app.BaseApp, s.ctx, accs, "") + fmt.Println("\n>") + for i, a := range accs { + fmt.Printf("+%d+ %s %s\n", i, a.Address.String(), s.app.BankKeeper.SpendableCoins(s.ctx, a.Address).AmountOf("u/uumee")) + } + fmt.Println("\n ") s.Require().NoError(err) var msg types.MsgWithdraw types.ModuleCdc.UnmarshalJSON(operationMsg.Msg, &msg) - // todo: message order execution? + fmt.Println("\n>") + for i, a := range accs { + fmt.Printf("+%d+ %s %s\n", i, a.Address.String(), s.app.BankKeeper.SpendableCoins(s.ctx, a.Address).AmountOf("u/uumee")) + } + fmt.Println("\n ") s.Require().True(operationMsg.OK) - s.Require().Equal("umee1ghekyjucln7y67ntx7cf27m9dpuxxemn8w6h33", msg.Supplier) + // s.Require().Equal("umee1ghekyjucln7y67ntx7cf27m9dpuxxemn8w6h33", msg.Supplier) s.Require().Equal(types.EventTypeWithdraw, msg.Type()) s.Require().Equal("73u/uumee", msg.Asset.String()) s.Require().Len(futureOperations, 0) } +/* func (s *SimTestSuite) TestSimulateMsgBorrow() { r := rand.New(rand.NewSource(8)) supplyToken := sdk.NewCoin(umeeapp.BondDenom, sdk.NewInt(1000)) @@ -301,6 +322,7 @@ func (s *SimTestSuite) TestSimulateMsgLiquidate() { s.Require().Equal(types.EventTypeLiquidate, msg.Type()) s.Require().Len(futureOperations, 0) } +*/ func TestSimTestSuite(t *testing.T) { suite.Run(t, new(SimTestSuite)) From 743f0b1a344c0e79a778c9c936d111f815a9afbe Mon Sep 17 00:00:00 2001 From: toteki <63419657+toteki@users.noreply.github.com> Date: Mon, 8 Aug 2022 10:06:29 -0700 Subject: [PATCH 27/38] fix ops tests --- x/leverage/simulation/operations.go | 233 ++++++++++++++--------- x/leverage/simulation/operations_test.go | 48 ++--- 2 files changed, 157 insertions(+), 124 deletions(-) diff --git a/x/leverage/simulation/operations.go b/x/leverage/simulation/operations.go index d2d0a0be97..25f04a4683 100644 --- a/x/leverage/simulation/operations.go +++ b/x/leverage/simulation/operations.go @@ -20,7 +20,7 @@ const ( DefaultWeightMsgWithdraw int = 85 DefaultWeightMsgBorrow int = 80 DefaultWeightMsgCollateralize int = 60 - DefaultWeightMsgDecollateralize int = 0 + DefaultWeightMsgDecollateralize int = 60 DefaultWeightMsgRepay int = 70 DefaultWeightMsgLiquidate int = 75 OperationWeightMsgSupply = "op_weight_msg_supply" @@ -121,7 +121,7 @@ func SimulateMsgSupply(ak simulation.AccountKeeper, bk types.BankKeeper) simtype r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simtypes.Account, chainID string, ) (simtypes.OperationMsg, []simtypes.FutureOperation, error) { - from, coin, skip := randomSpendableFields(r, ctx, accs, bk) + from, coin, skip := randomSupplyFields(r, ctx, accs, bk) if skip { return simtypes.NoOpMsg(types.ModuleName, types.EventTypeSupply, "skip all transfers"), nil, nil } @@ -162,17 +162,18 @@ func SimulateMsgWithdraw(ak simulation.AccountKeeper, bk types.BankKeeper, lk ke msg := types.NewMsgWithdraw(from.Address, withdrawUToken) txCtx := simulation.OperationInput{ - R: r, - App: app, - TxGen: simappparams.MakeTestEncodingConfig().TxConfig, - Cdc: nil, - Msg: msg, - MsgType: types.EventTypeWithdraw, - Context: ctx, - SimAccount: from, - AccountKeeper: ak, - Bankkeeper: bk, - ModuleName: types.ModuleName, + R: r, + App: app, + TxGen: simappparams.MakeTestEncodingConfig().TxConfig, + Cdc: nil, + Msg: msg, + MsgType: types.EventTypeWithdraw, + Context: ctx, + SimAccount: from, + AccountKeeper: ak, + Bankkeeper: bk, + ModuleName: types.ModuleName, + CoinsSpentInMsg: sdk.NewCoins(withdrawUToken), } return simulation.GenAndDeliverTxWithRandFees(txCtx) @@ -186,7 +187,7 @@ func SimulateMsgBorrow(ak simulation.AccountKeeper, bk types.BankKeeper, lk keep r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simtypes.Account, chainID string, ) (simtypes.OperationMsg, []simtypes.FutureOperation, error) { - from, token, skip := randomTokenFields(r, ctx, accs, lk) + from, token, skip := randomBorrowFields(r, ctx, accs, lk) if skip { return simtypes.NoOpMsg(types.ModuleName, types.EventTypeBorrow, "skip all transfers"), nil, nil } @@ -222,27 +223,26 @@ func SimulateMsgCollateralize( r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simtypes.Account, chainID string, ) (simtypes.OperationMsg, []simtypes.FutureOperation, error) { - from, token, skip := randomTokenFields(r, ctx, accs, lk) + from, collateral, skip := randomCollateralizeFields(r, ctx, accs, bk) if skip { return simtypes.NoOpMsg(types.ModuleName, types.EventTypeCollateralize, "skip all transfers"), nil, nil } - uDenom := types.ToUTokenDenom(token.Denom) - coin := sdk.NewCoin(uDenom, token.Amount) - msg := types.NewMsgCollateralize(from.Address, coin) + msg := types.NewMsgCollateralize(from.Address, collateral) txCtx := simulation.OperationInput{ - R: r, - App: app, - TxGen: simappparams.MakeTestEncodingConfig().TxConfig, - Cdc: nil, - Msg: msg, - MsgType: types.EventTypeCollateralize, - Context: ctx, - SimAccount: from, - AccountKeeper: ak, - Bankkeeper: bk, - ModuleName: types.ModuleName, + R: r, + App: app, + TxGen: simappparams.MakeTestEncodingConfig().TxConfig, + Cdc: nil, + Msg: msg, + MsgType: types.EventTypeCollateralize, + Context: ctx, + SimAccount: from, + AccountKeeper: ak, + Bankkeeper: bk, + ModuleName: types.ModuleName, + CoinsSpentInMsg: sdk.NewCoins(collateral), } return simulation.GenAndDeliverTxWithRandFees(txCtx) @@ -260,14 +260,12 @@ func SimulateMsgDecollateralize( r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simtypes.Account, chainID string, ) (simtypes.OperationMsg, []simtypes.FutureOperation, error) { - from, token, skip := randomTokenFields(r, ctx, accs, lk) + from, collateral, skip := randomDecollateralizeFields(r, ctx, accs, lk) if skip { return simtypes.NoOpMsg(types.ModuleName, types.EventTypeDecollateralize, "skip all transfers"), nil, nil } - uDenom := types.ToUTokenDenom(token.Denom) - coin := sdk.NewCoin(uDenom, token.Amount) - msg := types.NewMsgDecollateralize(from.Address, coin) + msg := types.NewMsgDecollateralize(from.Address, collateral) txCtx := simulation.OperationInput{ R: r, @@ -294,25 +292,26 @@ func SimulateMsgRepay(ak simulation.AccountKeeper, bk types.BankKeeper, lk keepe r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simtypes.Account, chainID string, ) (simtypes.OperationMsg, []simtypes.FutureOperation, error) { - from, borrowToken, skip := randomBorrowedFields(r, ctx, accs, lk) + from, repayToken, skip := randomRepayFields(r, ctx, accs, lk) if skip { return simtypes.NoOpMsg(types.ModuleName, types.EventTypeRepayBorrowedAsset, "skip all transfers"), nil, nil } - msg := types.NewMsgRepay(from.Address, borrowToken) + msg := types.NewMsgRepay(from.Address, repayToken) txCtx := simulation.OperationInput{ - R: r, - App: app, - TxGen: simappparams.MakeTestEncodingConfig().TxConfig, - Cdc: nil, - Msg: msg, - MsgType: types.EventTypeRepayBorrowedAsset, - Context: ctx, - SimAccount: from, - AccountKeeper: ak, - Bankkeeper: bk, - ModuleName: types.ModuleName, + R: r, + App: app, + TxGen: simappparams.MakeTestEncodingConfig().TxConfig, + Cdc: nil, + Msg: msg, + MsgType: types.EventTypeRepayBorrowedAsset, + Context: ctx, + SimAccount: from, + AccountKeeper: ak, + Bankkeeper: bk, + ModuleName: types.ModuleName, + CoinsSpentInMsg: sdk.NewCoins(repayToken), } return simulation.GenAndDeliverTxWithRandFees(txCtx) @@ -334,17 +333,18 @@ func SimulateMsgLiquidate(ak simulation.AccountKeeper, bk types.BankKeeper, lk k msg := types.NewMsgLiquidate(liquidator.Address, borrower.Address, repaymentToken, rewardDenom) txCtx := simulation.OperationInput{ - R: r, - App: app, - TxGen: simappparams.MakeTestEncodingConfig().TxConfig, - Cdc: nil, - Msg: msg, - MsgType: types.EventTypeLiquidate, - Context: ctx, - SimAccount: liquidator, - AccountKeeper: ak, - Bankkeeper: bk, - ModuleName: types.ModuleName, + R: r, + App: app, + TxGen: simappparams.MakeTestEncodingConfig().TxConfig, + Cdc: nil, + Msg: msg, + MsgType: types.EventTypeLiquidate, + Context: ctx, + SimAccount: liquidator, + AccountKeeper: ak, + Bankkeeper: bk, + ModuleName: types.ModuleName, + CoinsSpentInMsg: sdk.NewCoins(repaymentToken), } return simulation.GenAndDeliverTxWithRandFees(txCtx) @@ -359,40 +359,45 @@ func randomCoin(r *rand.Rand, coins sdk.Coins) sdk.Coin { return coins[r.Int31n(int32(coins.Len()))] } -// randomSpendableFields returns a random account and an sdk.Coin from its spendable -// coins. It returns skip=true if the account has zero spendable coins. -func randomSpendableFields( - r *rand.Rand, ctx sdk.Context, accs []simtypes.Account, bk types.BankKeeper, -) (acc simtypes.Account, spendableToken sdk.Coin, skip bool) { - acc, _ = simtypes.RandomAcc(r, accs) +// getSpendableTokens returns all non-uTokens from an account's spendable coins. +func getSpendableTokens(ctx sdk.Context, addr sdk.AccAddress, bk types.BankKeeper) sdk.Coins { + tokens := sdk.NewCoins() + for _, coin := range bk.SpendableCoins(ctx, addr) { + if !types.HasUTokenPrefix(coin.Denom) { + tokens = tokens.Add(coin) + } + } - spendableBalances := bk.SpendableCoins(ctx, acc.Address) + return tokens +} - spendableTokens := simtypes.RandSubsetCoins(r, spendableBalances) - if spendableTokens.Empty() { - return acc, sdk.Coin{}, true +// getSpendableUTokens returns all uTokens from an account's spendable coins. +func getSpendableUTokens(ctx sdk.Context, addr sdk.AccAddress, bk types.BankKeeper) sdk.Coins { + uTokens := sdk.NewCoins() + for _, coin := range bk.SpendableCoins(ctx, addr) { + if types.HasUTokenPrefix(coin.Denom) { + uTokens = uTokens.Add(coin) + } } - return acc, randomCoin(r, spendableTokens), false + return uTokens } -// randomTokenFields returns a random account and an sdk.Coin from all -// the registered tokens with an random amount [0, 150]. -// It returns skip=true if no registered token was found. -func randomTokenFields( - r *rand.Rand, ctx sdk.Context, accs []simtypes.Account, lk keeper.Keeper, -) (acc simtypes.Account, token sdk.Coin, skip bool) { +// randomSupplyFields returns a random account and a non-uToken from its spendable +// coins. It returns skip=true if the account has zero spendable base tokens. +func randomSupplyFields( + r *rand.Rand, ctx sdk.Context, accs []simtypes.Account, bk types.BankKeeper, +) (acc simtypes.Account, spendableToken sdk.Coin, skip bool) { acc, _ = simtypes.RandomAcc(r, accs) - allTokens := lk.GetAllRegisteredTokens(ctx) - if len(allTokens) == 0 { + tokens := getSpendableTokens(ctx, acc.Address, bk) + + spendableTokens := simtypes.RandSubsetCoins(r, tokens) + if spendableTokens.Empty() { return acc, sdk.Coin{}, true } - registeredToken := allTokens[r.Int31n(int32(len(allTokens)))] - token = sdk.NewCoin(registeredToken.BaseDenom, simtypes.RandomAmount(r, sdk.NewInt(150))) - - return acc, token, false + return acc, randomCoin(r, spendableTokens), false } // randomWithdrawFields returns a random account and an sdk.Coin from its uTokens @@ -404,7 +409,7 @@ func randomWithdrawFields( ) (acc simtypes.Account, withdrawal sdk.Coin, skip bool) { acc, _ = simtypes.RandomAcc(r, accs) - uTokens := getSpendableUTokens(ctx, acc.Address, bk, lk) + uTokens := getSpendableUTokens(ctx, acc.Address, bk) uTokens = uTokens.Add(lk.GetBorrowerCollateral(ctx, acc.Address)...) uTokens = simtypes.RandSubsetCoins(r, uTokens) @@ -416,24 +421,64 @@ func randomWithdrawFields( return acc, randomCoin(r, uTokens), false } -// getSpendableUTokens returns all uTokens from an account's spendable coins. -func getSpendableUTokens( - ctx sdk.Context, addr sdk.AccAddress, - bk types.BankKeeper, lk keeper.Keeper, -) sdk.Coins { - uTokens := sdk.NewCoins() - for _, coin := range bk.SpendableCoins(ctx, addr) { - if types.HasUTokenPrefix(coin.Denom) { - uTokens = uTokens.Add(coin) - } +// randomCollateralizeFields returns a random account and a uToken from its spendable +// coins. It returns skip=true if the account has zero spendable uTokens. +func randomCollateralizeFields( + r *rand.Rand, ctx sdk.Context, accs []simtypes.Account, bk types.BankKeeper, +) (acc simtypes.Account, spendableToken sdk.Coin, skip bool) { + acc, _ = simtypes.RandomAcc(r, accs) + + uTokens := getSpendableUTokens(ctx, acc.Address, bk) + + uTokens = simtypes.RandSubsetCoins(r, uTokens) + + if uTokens.Empty() { + return acc, sdk.Coin{}, true } - return uTokens + return acc, randomCoin(r, uTokens), false +} + +// randomDecollateralizeFields returns a random account and a uToken from its collateral +// coins. It returns skip=true if the account has zero collateral. +func randomDecollateralizeFields( + r *rand.Rand, ctx sdk.Context, accs []simtypes.Account, lk keeper.Keeper, +) (acc simtypes.Account, spendableToken sdk.Coin, skip bool) { + acc, _ = simtypes.RandomAcc(r, accs) + + uTokens := lk.GetBorrowerCollateral(ctx, acc.Address) + + uTokens = simtypes.RandSubsetCoins(r, uTokens) + + if uTokens.Empty() { + return acc, sdk.Coin{}, true + } + + return acc, randomCoin(r, uTokens), false +} + +// randomBorrowFields returns a random account and an sdk.Coin from all +// the registered tokens with an random amount [0, 150]. +// It returns skip=true if no registered token was found. +func randomBorrowFields( + r *rand.Rand, ctx sdk.Context, accs []simtypes.Account, lk keeper.Keeper, +) (acc simtypes.Account, token sdk.Coin, skip bool) { + acc, _ = simtypes.RandomAcc(r, accs) + + allTokens := lk.GetAllRegisteredTokens(ctx) + if len(allTokens) == 0 { + return acc, sdk.Coin{}, true + } + + registeredToken := allTokens[r.Int31n(int32(len(allTokens)))] + token = sdk.NewCoin(registeredToken.BaseDenom, simtypes.RandomAmount(r, sdk.NewInt(150))) + + return acc, token, false } -// randomBorrowedFields returns a random account and an sdk.Coin from an open borrow position. +// randomRepayFields returns a random account and an sdk.Coin from an open borrow position. // It returns skip=true if no borrow position was open. -func randomBorrowedFields( +func randomRepayFields( r *rand.Rand, ctx sdk.Context, accs []simtypes.Account, lk keeper.Keeper, ) (acc simtypes.Account, borrowToken sdk.Coin, skip bool) { acc, _ = simtypes.RandomAcc(r, accs) diff --git a/x/leverage/simulation/operations_test.go b/x/leverage/simulation/operations_test.go index c4c86d96e2..bfd8fafbf4 100644 --- a/x/leverage/simulation/operations_test.go +++ b/x/leverage/simulation/operations_test.go @@ -1,7 +1,6 @@ package simulation_test import ( - "fmt" "math/rand" "testing" @@ -123,7 +122,6 @@ func (s *SimTestSuite) TestWeightedOperations() { } } -/* func (s *SimTestSuite) TestSimulateMsgSupply() { r := rand.New(rand.NewSource(1)) accs := s.getTestingAccounts(r, 3, func(fundedAccount simtypes.Account) {}) @@ -143,52 +141,31 @@ func (s *SimTestSuite) TestSimulateMsgSupply() { s.Require().Equal("4896096uumee", msg.Asset.String()) s.Require().Len(futureOperations, 0) } -*/ func (s *SimTestSuite) TestSimulateMsgWithdraw() { r := rand.New(rand.NewSource(1)) supplyToken := sdk.NewCoin(umeeapp.BondDenom, sdk.NewInt(100)) - withdrawToken := sdk.NewCoin("u/"+umeeapp.BondDenom, sdk.NewInt(80)) - accs := s.getTestingAccounts(r, 18, func(fundedAccount simtypes.Account) { + accs := s.getTestingAccounts(r, 3, func(fundedAccount simtypes.Account) { s.Require().NoError(s.app.LeverageKeeper.Supply(s.ctx, fundedAccount.Address, supplyToken)) - s.Require().NoError(s.app.LeverageKeeper.Withdraw(s.ctx, fundedAccount.Address, withdrawToken)) }) s.app.BeginBlock(abci.RequestBeginBlock{Header: tmproto.Header{Height: s.app.LastBlockHeight() + 1, AppHash: s.app.LastCommitID().Hash}}) - fmt.Println("\n>") - for i, a := range accs { - fmt.Printf("+%d+ %s %s\n", i, a.Address.String(), s.app.BankKeeper.SpendableCoins(s.ctx, a.Address).AmountOf("u/uumee")) - } - fmt.Println("\n ") - op := simulation.SimulateMsgWithdraw(s.app.AccountKeeper, s.app.BankKeeper, s.app.LeverageKeeper) operationMsg, futureOperations, err := op(r, s.app.BaseApp, s.ctx, accs, "") - fmt.Println("\n>") - for i, a := range accs { - fmt.Printf("+%d+ %s %s\n", i, a.Address.String(), s.app.BankKeeper.SpendableCoins(s.ctx, a.Address).AmountOf("u/uumee")) - } - fmt.Println("\n ") s.Require().NoError(err) var msg types.MsgWithdraw types.ModuleCdc.UnmarshalJSON(operationMsg.Msg, &msg) - fmt.Println("\n>") - for i, a := range accs { - fmt.Printf("+%d+ %s %s\n", i, a.Address.String(), s.app.BankKeeper.SpendableCoins(s.ctx, a.Address).AmountOf("u/uumee")) - } - fmt.Println("\n ") - s.Require().True(operationMsg.OK) - // s.Require().Equal("umee1ghekyjucln7y67ntx7cf27m9dpuxxemn8w6h33", msg.Supplier) + s.Require().Equal("umee1ghekyjucln7y67ntx7cf27m9dpuxxemn8w6h33", msg.Supplier) s.Require().Equal(types.EventTypeWithdraw, msg.Type()) s.Require().Equal("73u/uumee", msg.Asset.String()) s.Require().Len(futureOperations, 0) } -/* func (s *SimTestSuite) TestSimulateMsgBorrow() { r := rand.New(rand.NewSource(8)) supplyToken := sdk.NewCoin(umeeapp.BondDenom, sdk.NewInt(1000)) @@ -221,8 +198,11 @@ func (s *SimTestSuite) TestSimulateMsgBorrow() { func (s *SimTestSuite) TestSimulateMsgCollateralize() { r := rand.New(rand.NewSource(1)) + supplyToken := sdk.NewInt64Coin(umeeapp.BondDenom, 100) - accs := s.getTestingAccounts(r, 3, func(fundedAccount simtypes.Account) {}) + accs := s.getTestingAccounts(r, 3, func(fundedAccount simtypes.Account) { + s.Require().NoError(s.app.LeverageKeeper.Supply(s.ctx, fundedAccount.Address, supplyToken)) + }) s.app.BeginBlock(abci.RequestBeginBlock{Header: tmproto.Header{Height: s.app.LastBlockHeight() + 1, AppHash: s.app.LastCommitID().Hash}}) @@ -236,14 +216,23 @@ func (s *SimTestSuite) TestSimulateMsgCollateralize() { s.Require().True(operationMsg.OK) s.Require().Equal("umee1ghekyjucln7y67ntx7cf27m9dpuxxemn8w6h33", msg.Borrower) s.Require().Equal(types.EventTypeCollateralize, msg.Type()) - s.Require().Equal("0u/uumee", msg.Coin.String()) + s.Require().Equal("73u/uumee", msg.Coin.String()) s.Require().Len(futureOperations, 0) } func (s *SimTestSuite) TestSimulateMsgDecollateralize() { r := rand.New(rand.NewSource(1)) + supplyToken := sdk.NewInt64Coin(umeeapp.BondDenom, 100) - accs := s.getTestingAccounts(r, 3, func(fundedAccount simtypes.Account) {}) + accs := s.getTestingAccounts(r, 3, func(fundedAccount simtypes.Account) { + uToken, err := s.app.LeverageKeeper.ExchangeToken(s.ctx, supplyToken) + if err != nil { + s.Require().NoError(err) + } + + s.Require().NoError(s.app.LeverageKeeper.Supply(s.ctx, fundedAccount.Address, supplyToken)) + s.Require().NoError(s.app.LeverageKeeper.Collateralize(s.ctx, fundedAccount.Address, uToken)) + }) s.app.BeginBlock(abci.RequestBeginBlock{Header: tmproto.Header{Height: s.app.LastBlockHeight() + 1, AppHash: s.app.LastCommitID().Hash}}) @@ -257,7 +246,7 @@ func (s *SimTestSuite) TestSimulateMsgDecollateralize() { s.Require().True(operationMsg.OK) s.Require().Equal("umee1ghekyjucln7y67ntx7cf27m9dpuxxemn8w6h33", msg.Borrower) s.Require().Equal(types.EventTypeDecollateralize, msg.Type()) - s.Require().Equal("0u/uumee", msg.Coin.String()) + s.Require().Equal("73u/uumee", msg.Coin.String()) s.Require().Len(futureOperations, 0) } @@ -322,7 +311,6 @@ func (s *SimTestSuite) TestSimulateMsgLiquidate() { s.Require().Equal(types.EventTypeLiquidate, msg.Type()) s.Require().Len(futureOperations, 0) } -*/ func TestSimTestSuite(t *testing.T) { suite.Run(t, new(SimTestSuite)) From 012208c00fef82ddce319f304e3d75f5cd18ffaa Mon Sep 17 00:00:00 2001 From: toteki <63419657+toteki@users.noreply.github.com> Date: Mon, 8 Aug 2022 10:12:40 -0700 Subject: [PATCH 28/38] ++ --- x/leverage/simulation/operations_test.go | 47 ++++++++++++++++++++++-- 1 file changed, 44 insertions(+), 3 deletions(-) diff --git a/x/leverage/simulation/operations_test.go b/x/leverage/simulation/operations_test.go index bfd8fafbf4..e1c2e87b26 100644 --- a/x/leverage/simulation/operations_test.go +++ b/x/leverage/simulation/operations_test.go @@ -50,10 +50,51 @@ func (s *SimTestSuite) SetupTest() { MaxSupplyUtilization: sdk.MustNewDecFromStr("0.9"), MinCollateralLiquidity: sdk.MustNewDecFromStr("0"), } + atomIBCToken := types.Token{ + BaseDenom: "ibc/CDC4587874B85BEA4FCEC3CEA5A1195139799A1FEE711A07D972537E18FDA39D", + ReserveFactor: sdk.MustNewDecFromStr("0.25"), + CollateralWeight: sdk.MustNewDecFromStr("0.8"), + LiquidationThreshold: sdk.MustNewDecFromStr("0.8"), + BaseBorrowRate: sdk.MustNewDecFromStr("0.05"), + KinkBorrowRate: sdk.MustNewDecFromStr("0.3"), + MaxBorrowRate: sdk.MustNewDecFromStr("0.9"), + KinkUtilization: sdk.MustNewDecFromStr("0.75"), + LiquidationIncentive: sdk.MustNewDecFromStr("0.11"), + SymbolDenom: "ATOM", + Exponent: 6, + EnableMsgSupply: true, + EnableMsgBorrow: true, + Blacklist: false, + MaxCollateralShare: sdk.MustNewDecFromStr("1"), + MaxSupplyUtilization: sdk.MustNewDecFromStr("0.9"), + MinCollateralLiquidity: sdk.MustNewDecFromStr("0"), + } + uabc := types.Token{ + BaseDenom: "uabc", + ReserveFactor: sdk.MustNewDecFromStr("0"), + CollateralWeight: sdk.MustNewDecFromStr("0.1"), + LiquidationThreshold: sdk.MustNewDecFromStr("0.1"), + BaseBorrowRate: sdk.MustNewDecFromStr("0.02"), + KinkBorrowRate: sdk.MustNewDecFromStr("0.22"), + MaxBorrowRate: sdk.MustNewDecFromStr("1.52"), + KinkUtilization: sdk.MustNewDecFromStr("0.87"), + LiquidationIncentive: sdk.MustNewDecFromStr("0.1"), + SymbolDenom: "ABC", + Exponent: 6, + EnableMsgSupply: true, + EnableMsgBorrow: true, + Blacklist: false, + MaxCollateralShare: sdk.MustNewDecFromStr("1"), + MaxSupplyUtilization: sdk.MustNewDecFromStr("0.9"), + MinCollateralLiquidity: sdk.MustNewDecFromStr("0"), + } + tokens := []types.Token{umeeToken, atomIBCToken, uabc} leverage.InitGenesis(ctx, app.LeverageKeeper, *types.DefaultGenesis()) - s.Require().NoError(app.LeverageKeeper.SetTokenSettings(ctx, umeeToken)) - app.OracleKeeper.SetExchangeRate(ctx, umeeToken.SymbolDenom, sdk.MustNewDecFromStr("100.0")) + for _, token := range tokens { + s.Require().NoError(app.LeverageKeeper.SetTokenSettings(ctx, token)) + app.OracleKeeper.SetExchangeRate(ctx, token.SymbolDenom, sdk.MustNewDecFromStr("100.0")) + } s.app = app s.ctx = ctx @@ -138,7 +179,7 @@ func (s *SimTestSuite) TestSimulateMsgSupply() { s.Require().True(operationMsg.OK) s.Require().Equal("umee1ghekyjucln7y67ntx7cf27m9dpuxxemn8w6h33", msg.Supplier) s.Require().Equal(types.EventTypeSupply, msg.Type()) - s.Require().Equal("4896096uumee", msg.Asset.String()) + s.Require().Equal("185121068uumee", msg.Asset.String()) s.Require().Len(futureOperations, 0) } From f693c136d23cb50f6377770c0e32920467d1c4af Mon Sep 17 00:00:00 2001 From: toteki <63419657+toteki@users.noreply.github.com> Date: Mon, 8 Aug 2022 10:36:49 -0700 Subject: [PATCH 29/38] TODO++ --- x/leverage/keeper/iter.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x/leverage/keeper/iter.go b/x/leverage/keeper/iter.go index 8d1694f03c..a0a1923229 100644 --- a/x/leverage/keeper/iter.go +++ b/x/leverage/keeper/iter.go @@ -233,7 +233,7 @@ func (k Keeper) SweepBadDebts(ctx sdk.Context) error { // first check if the borrower has gained collateral since the bad debt was identified done := k.HasCollateral(ctx, addr) - // TODO: Non-blacklisted collateral only + // TODO #1223: Decollateralize any blacklisted collateral and proceed // if collateral is still zero, attempt to repay a single address's debt in this denom if !done { From 87f6468f1ea06fce8fb3bcdfadc965027103cc59 Mon Sep 17 00:00:00 2001 From: toteki <63419657+toteki@users.noreply.github.com> Date: Wed, 10 Aug 2022 11:07:38 -0700 Subject: [PATCH 30/38] lint++ --- x/leverage/keeper/keeper.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x/leverage/keeper/keeper.go b/x/leverage/keeper/keeper.go index 5aea097d8c..53906f1545 100644 --- a/x/leverage/keeper/keeper.go +++ b/x/leverage/keeper/keeper.go @@ -364,7 +364,7 @@ func (k Keeper) Liquidate( return sdk.Coin{}, sdk.Coin{}, sdk.Coin{}, err } - // calculate borrowed Token repay, uToken liquidate, and Token reward amounts allowed by + // calculate borrowed Token repay, uToken liquidate, and Token reward amounts allowed by // liquidation rules and available balances baseRepay, collateralLiquidate, baseReward, err := k.getLiquidationAmounts( ctx, From 6dcf6fc704b74aa8d36346934b3299f474a00c01 Mon Sep 17 00:00:00 2001 From: toteki <63419657+toteki@users.noreply.github.com> Date: Wed, 10 Aug 2022 13:55:06 -0700 Subject: [PATCH 31/38] suggestion++ Co-authored-by: Adam Wozniak <29418299+adamewozniak@users.noreply.github.com> --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b0eb18418d..96715c056d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -80,7 +80,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ - [1203](https://github.com/umee-network/umee/pull/1203) Add Swagger docs. - [1212](https://github.com/umee-network/umee/pull/1212) Add `util/checkers` utility package providing common check / validation functions. - [1220](https://github.com/umee-network/umee/pull/1220) Submit oracle prevotes / vote txs via the CLI. -- [1222](https://github.com/umee-network/umee/pull/1222) Liquidation reward_denom can now be either token or uToken +- [1222](https://github.com/umee-network/umee/pull/1222) Liquidation reward_denom can now be either token or uToken. ### Improvements From fa861823e4ada3c79a51aeb5d06b4f2848d89b47 Mon Sep 17 00:00:00 2001 From: toteki <63419657+toteki@users.noreply.github.com> Date: Wed, 10 Aug 2022 13:56:19 -0700 Subject: [PATCH 32/38] suggestion++ Co-authored-by: Adam Wozniak <29418299+adamewozniak@users.noreply.github.com> --- x/leverage/keeper/keeper.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/x/leverage/keeper/keeper.go b/x/leverage/keeper/keeper.go index 53906f1545..9956f13721 100644 --- a/x/leverage/keeper/keeper.go +++ b/x/leverage/keeper/keeper.go @@ -393,9 +393,9 @@ func (k Keeper) Liquidate( if err = k.burnCollateral(ctx, borrowerAddr, collateralLiquidate); err != nil { return sdk.Coin{}, sdk.Coin{}, sdk.Coin{}, err } + // send base rewards from module to liquidator's account - err = k.bankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, liquidatorAddr, sdk.NewCoins(baseReward)) - if err != nil { + if err = k.bankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, liquidatorAddr, sdk.NewCoins(baseReward)); err != nil { return sdk.Coin{}, sdk.Coin{}, sdk.Coin{}, err } } else { From 4d4ee85428aee53a0c91665591a74406e33b4165 Mon Sep 17 00:00:00 2001 From: toteki <63419657+toteki@users.noreply.github.com> Date: Wed, 10 Aug 2022 13:58:27 -0700 Subject: [PATCH 33/38] suggestion++ Co-authored-by: Adam Wozniak <29418299+adamewozniak@users.noreply.github.com> --- x/leverage/keeper/borrows.go | 2 -- x/leverage/keeper/collateral.go | 5 ----- 2 files changed, 7 deletions(-) diff --git a/x/leverage/keeper/borrows.go b/x/leverage/keeper/borrows.go index 89ba226a86..f6c29d0599 100644 --- a/x/leverage/keeper/borrows.go +++ b/x/leverage/keeper/borrows.go @@ -30,12 +30,10 @@ func (k Keeper) GetBorrow(ctx sdk.Context, borrowerAddr sdk.AccAddress, denom st // occurs during normal repayment (in which case fromAddr and borrowAddr are the same) and during // liquidations, where fromAddr is the liquidator instead. func (k Keeper) repayBorrow(ctx sdk.Context, fromAddr, borrowAddr sdk.AccAddress, repay sdk.Coin) error { - // send repayment from fromAddr to leverage module account err := k.bankKeeper.SendCoinsFromAccountToModule(ctx, fromAddr, types.ModuleName, sdk.NewCoins(repay)) if err != nil { return err } - // update borrower's remaining borrowed amount return k.setBorrow(ctx, borrowAddr, k.GetBorrow(ctx, borrowAddr, repay.Denom).Sub(repay)) } diff --git a/x/leverage/keeper/collateral.go b/x/leverage/keeper/collateral.go index 5356e739cd..80f236ce94 100644 --- a/x/leverage/keeper/collateral.go +++ b/x/leverage/keeper/collateral.go @@ -10,16 +10,13 @@ import ( // burnCollateral removes some uTokens from an account's collateral and burns them. This occurs // during liquidations. func (k Keeper) burnCollateral(ctx sdk.Context, addr sdk.AccAddress, coin sdk.Coin) error { - // reduce account's collateral err := k.setCollateralAmount(ctx, addr, k.GetCollateralAmount(ctx, addr, coin.Denom).Sub(coin)) if err != nil { return err } - // burn the uTokens if err = k.bankKeeper.BurnCoins(ctx, types.ModuleName, sdk.NewCoins(coin)); err != nil { return err } - // set the new total uToken supply return k.setUTokenSupply(ctx, k.GetUTokenSupply(ctx, coin.Denom).Sub(coin)) } @@ -27,12 +24,10 @@ func (k Keeper) burnCollateral(ctx sdk.Context, addr sdk.AccAddress, coin sdk.Co // occurs when decollateralizing uTokens (in which case fromAddr and toAddr are the same) as well as // during liquidations, where toAddr is the liquidator. func (k Keeper) removeCollateral(ctx sdk.Context, fromAddr, toAddr sdk.AccAddress, coin sdk.Coin) error { - // reduce fromAddr's collateral err := k.setCollateralAmount(ctx, fromAddr, k.GetCollateralAmount(ctx, fromAddr, coin.Denom).Sub(coin)) if err != nil { return err } - // send the uTokens to toAddr return k.bankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, toAddr, sdk.NewCoins(coin)) } From 65a25e42711a0c8ab39f8ac8b6ef003af3b0bde5 Mon Sep 17 00:00:00 2001 From: toteki <63419657+toteki@users.noreply.github.com> Date: Wed, 10 Aug 2022 13:58:57 -0700 Subject: [PATCH 34/38] suggestion++ Co-authored-by: Adam Wozniak <29418299+adamewozniak@users.noreply.github.com> --- x/leverage/types/errors.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/x/leverage/types/errors.go b/x/leverage/types/errors.go index 659744f538..357fb61401 100644 --- a/x/leverage/types/errors.go +++ b/x/leverage/types/errors.go @@ -38,6 +38,6 @@ var ( 1126, "market total collateral would exceed MaxCollateralShare", ) - ErrNotUToken = sdkerrors.Register(ModuleName, 1127, "denom is not a uToken") - ErrUToken = sdkerrors.Register(ModuleName, 1128, "denom is a uToken") + ErrNotUToken = sdkerrors.Register(ModuleName, 1127, "denom should be a uToken") + ErrUToken = sdkerrors.Register(ModuleName, 1128, "denom should not be a uToken") ) From 5262362fdfc350e845cf4d5ebfc413ad534695e9 Mon Sep 17 00:00:00 2001 From: toteki <63419657+toteki@users.noreply.github.com> Date: Wed, 10 Aug 2022 13:59:12 -0700 Subject: [PATCH 35/38] suggestion++ Co-authored-by: Adam Wozniak <29418299+adamewozniak@users.noreply.github.com> --- x/leverage/keeper/liquidate.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/x/leverage/keeper/liquidate.go b/x/leverage/keeper/liquidate.go index dd662d9e8f..f461baedb2 100644 --- a/x/leverage/keeper/liquidate.go +++ b/x/leverage/keeper/liquidate.go @@ -68,9 +68,9 @@ func (k Keeper) getLiquidationAmounts( // get collateral uToken exchange rate exchangeRate := k.DeriveExchangeRate(ctx, rewardDenom) - // reduce liquidation incentive if directly receiving base assets. Since this fee also reduces the - // amount of collateral that must be burned, it is applied before any other computations as if the - // token itself had a smaller liquidation incentive + // Reduce liquidation incentive if the liquidator has specified they would like to directly receive base assets. Since this fee also reduces the + // amount of collateral that must be burned, it is applied before any other computations, as if the + // token itself had a smaller liquidation incentive. liqudationIncentive := ts.LiquidationIncentive if directLiquidation { liqudationIncentive = liqudationIncentive.Mul(sdk.OneDec().Sub(params.DirectLiquidationFee)) From 39d7565809ef6792a96d86fcd24fe702ea0f917e Mon Sep 17 00:00:00 2001 From: toteki <63419657+toteki@users.noreply.github.com> Date: Wed, 10 Aug 2022 14:03:00 -0700 Subject: [PATCH 36/38] lint++ --- x/leverage/keeper/keeper.go | 6 ++++-- x/leverage/keeper/liquidate.go | 6 +++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/x/leverage/keeper/keeper.go b/x/leverage/keeper/keeper.go index 9956f13721..1e21c697d0 100644 --- a/x/leverage/keeper/keeper.go +++ b/x/leverage/keeper/keeper.go @@ -393,9 +393,11 @@ func (k Keeper) Liquidate( if err = k.burnCollateral(ctx, borrowerAddr, collateralLiquidate); err != nil { return sdk.Coin{}, sdk.Coin{}, sdk.Coin{}, err } - + // send base rewards from module to liquidator's account - if err = k.bankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, liquidatorAddr, sdk.NewCoins(baseReward)); err != nil { + if err = k.bankKeeper.SendCoinsFromModuleToAccount( + ctx, types.ModuleName, liquidatorAddr, sdk.NewCoins(baseReward), + ); err != nil { return sdk.Coin{}, sdk.Coin{}, sdk.Coin{}, err } } else { diff --git a/x/leverage/keeper/liquidate.go b/x/leverage/keeper/liquidate.go index f461baedb2..e80b8a6897 100644 --- a/x/leverage/keeper/liquidate.go +++ b/x/leverage/keeper/liquidate.go @@ -68,9 +68,9 @@ func (k Keeper) getLiquidationAmounts( // get collateral uToken exchange rate exchangeRate := k.DeriveExchangeRate(ctx, rewardDenom) - // Reduce liquidation incentive if the liquidator has specified they would like to directly receive base assets. Since this fee also reduces the - // amount of collateral that must be burned, it is applied before any other computations, as if the - // token itself had a smaller liquidation incentive. + // Reduce liquidation incentive if the liquidator has specified they would like to directly receive base assets. + // Since this fee also reduces the amount of collateral that must be burned, it is applied before any other + // computations, as if the token itself had a smaller liquidation incentive. liqudationIncentive := ts.LiquidationIncentive if directLiquidation { liqudationIncentive = liqudationIncentive.Mul(sdk.OneDec().Sub(params.DirectLiquidationFee)) From 9298521e7f3db01fa3bd7dbbc8671cac82b21ab2 Mon Sep 17 00:00:00 2001 From: toteki <63419657+toteki@users.noreply.github.com> Date: Wed, 10 Aug 2022 14:07:59 -0700 Subject: [PATCH 37/38] clarify error message --- x/leverage/keeper/keeper.go | 2 +- x/leverage/keeper/keeper_test.go | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/x/leverage/keeper/keeper.go b/x/leverage/keeper/keeper.go index 1e21c697d0..3ca72dac81 100644 --- a/x/leverage/keeper/keeper.go +++ b/x/leverage/keeper/keeper.go @@ -151,7 +151,7 @@ func (k Keeper) Withdraw(ctx sdk.Context, supplierAddr sdk.AccAddress, uToken sd // Check for sufficient collateral collateral := k.GetBorrowerCollateral(ctx, supplierAddr) if collateral.AmountOf(uToken.Denom).LT(amountFromCollateral) { - return types.ErrInsufficientBalance.Wrapf("%s balance + %s collateral / %s withdraw", + return types.ErrInsufficientBalance.Wrapf("%s uToken balance + %s from collateral is less than %s to withdraw", amountFromWallet.String(), collateral.AmountOf(uToken.Denom).String(), uToken.String()) diff --git a/x/leverage/keeper/keeper_test.go b/x/leverage/keeper/keeper_test.go index 1a54ab249b..9b7c6af22e 100644 --- a/x/leverage/keeper/keeper_test.go +++ b/x/leverage/keeper/keeper_test.go @@ -924,7 +924,9 @@ func (s *IntegrationTestSuite) TestWithdraw_InsufficientCollateral() { // withdraw more collateral than available uToken := collateral.Add(sdk.NewInt64Coin(uTokenDenom, 1)) err := s.app.LeverageKeeper.Withdraw(s.ctx, supplierAddr, uToken) - s.Require().EqualError(err, "0 balance + 1000000 collateral / 1000001u/uumee withdraw: insufficient balance") + s.Require().EqualError(err, + "0 uToken balance + 1000000 from collateral is less than 1000001u/uumee to withdraw: insufficient balance", + ) } func (s *IntegrationTestSuite) TestTotalCollateral() { From 88d2947b7db7425bcbd694b7ff313e708aa09284 Mon Sep 17 00:00:00 2001 From: toteki <63419657+toteki@users.noreply.github.com> Date: Wed, 10 Aug 2022 14:14:06 -0700 Subject: [PATCH 38/38] make proto-gen and make proto-swagger-gen --- swagger/swagger.yaml | 56 ++++++++++++++-- x/leverage/types/leverage.pb.go | 111 ++++++++++++++++---------------- 2 files changed, 106 insertions(+), 61 deletions(-) diff --git a/swagger/swagger.yaml b/swagger/swagger.yaml index 1abbe21c99..79a418d465 100644 --- a/swagger/swagger.yaml +++ b/swagger/swagger.yaml @@ -619,8 +619,11 @@ paths: Enable Msg Supply allows supplying for lending or collateral using this - token. Note that withdrawing is always enabled. - Disabling supplying would + token. `false` means that a token can no longer be + supplied. + + Note that withdrawing is always enabled. Disabling + supply would be one step in phasing out an asset type. enable_msg_borrow: @@ -694,6 +697,20 @@ paths: value due to interest or liquidations. + max_supply: + type: string + description: >- + Max Supply is the maximum amount of tokens the protocol + can hold. + + Adding more supply of the given token to the protocol + will return an error. + + Must be a non negative value. 0 means that there is no + limit. + + To mark a token as not valid for supply, `msg_supply` + must be set to false. description: >- Token defines a token, along with its metadata and parameters, in the Umee @@ -1785,8 +1802,9 @@ definitions: Enable Msg Supply allows supplying for lending or collateral using this - token. Note that withdrawing is always enabled. Disabling - supplying would + token. `false` means that a token can no longer be supplied. + + Note that withdrawing is always enabled. Disabling supply would be one step in phasing out an asset type. enable_msg_borrow: @@ -1859,6 +1877,19 @@ definitions: to interest or liquidations. + max_supply: + type: string + description: >- + Max Supply is the maximum amount of tokens the protocol can + hold. + + Adding more supply of the given token to the protocol will + return an error. + + Must be a non negative value. 0 means that there is no limit. + + To mark a token as not valid for supply, `msg_supply` must be + set to false. description: >- Token defines a token, along with its metadata and parameters, in the Umee @@ -1942,8 +1973,9 @@ definitions: Enable Msg Supply allows supplying for lending or collateral using this - token. Note that withdrawing is always enabled. Disabling supplying - would + token. `false` means that a token can no longer be supplied. + + Note that withdrawing is always enabled. Disabling supply would be one step in phasing out an asset type. enable_msg_borrow: @@ -2016,6 +2048,18 @@ definitions: interest or liquidations. + max_supply: + type: string + description: >- + Max Supply is the maximum amount of tokens the protocol can hold. + + Adding more supply of the given token to the protocol will return an + error. + + Must be a non negative value. 0 means that there is no limit. + + To mark a token as not valid for supply, `msg_supply` must be set to + false. description: |- Token defines a token, along with its metadata and parameters, in the Umee capital facility that can be supplied and borrowed. diff --git a/x/leverage/types/leverage.pb.go b/x/leverage/types/leverage.pb.go index 0dbe42dc5d..98e8e8be45 100644 --- a/x/leverage/types/leverage.pb.go +++ b/x/leverage/types/leverage.pb.go @@ -196,62 +196,63 @@ func init() { func init() { proto.RegisterFile("umee/leverage/v1/leverage.proto", fileDescriptor_8cb1bf9ea641ecc6) } var fileDescriptor_8cb1bf9ea641ecc6 = []byte{ - // 872 bytes of a gzipped FileDescriptorProto + // 896 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x56, 0xbf, 0x6f, 0x1b, 0x37, - 0x14, 0xd6, 0xb5, 0xb6, 0x6b, 0x33, 0x91, 0x65, 0x5f, 0x64, 0xe7, 0xd0, 0xba, 0x3a, 0x83, 0x40, - 0x0b, 0x2f, 0xb1, 0x9a, 0xb6, 0x93, 0x47, 0x25, 0x48, 0x93, 0x22, 0x49, 0x5b, 0x3a, 0x45, 0x80, - 0x2e, 0x07, 0xea, 0xf4, 0x22, 0x11, 0x22, 0x8f, 0x2a, 0x49, 0xfd, 0xf2, 0xd2, 0xa1, 0xe8, 0xd4, - 0xa5, 0x63, 0x97, 0x02, 0xf9, 0x53, 0x32, 0x7a, 0xcc, 0x58, 0x74, 0x10, 0x5a, 0x7b, 0xe9, 0xec, - 0xbf, 0xa0, 0x38, 0xf2, 0x24, 0x9d, 0x9c, 0x6b, 0x00, 0x41, 0x99, 0x74, 0xfc, 0xde, 0xd3, 0xf7, - 0x7d, 0x24, 0xdf, 0x23, 0x89, 0xc2, 0xbe, 0x00, 0xa8, 0x73, 0x18, 0x80, 0xa2, 0x6d, 0xa8, 0x0f, - 0xee, 0xce, 0xbe, 0x8f, 0x7b, 0x4a, 0x1a, 0xe9, 0xef, 0xa4, 0x09, 0xc7, 0x33, 0x70, 0x70, 0xf7, - 0xc3, 0x6a, 0x5b, 0xb6, 0xa5, 0x0d, 0xd6, 0xd3, 0x2f, 0x97, 0x87, 0x5f, 0xad, 0xa3, 0x8d, 0x6f, - 0xa9, 0xa2, 0x42, 0xfb, 0x7f, 0x78, 0xa8, 0x16, 0x4b, 0xd1, 0xe3, 0x60, 0x20, 0xe2, 0xec, 0xc7, - 0x3e, 0x6b, 0x51, 0xc3, 0x64, 0x12, 0x99, 0x8e, 0x02, 0xdd, 0x91, 0xbc, 0x15, 0xbc, 0x77, 0xe8, - 0x1d, 0x6d, 0x35, 0x9e, 0x9f, 0x4f, 0xc2, 0xd2, 0x5f, 0x93, 0xf0, 0xd3, 0x36, 0x33, 0x9d, 0x7e, - 0xf3, 0x38, 0x96, 0xa2, 0x1e, 0x4b, 0x2d, 0xa4, 0xce, 0x7e, 0xee, 0xe8, 0x56, 0xb7, 0x6e, 0xc6, - 0x3d, 0xd0, 0xc7, 0xf7, 0x21, 0xbe, 0x9a, 0x84, 0x9f, 0x8c, 0xa9, 0xe0, 0x27, 0xf8, 0xed, 0xec, - 0x98, 0x1c, 0x4c, 0x13, 0x1e, 0xcf, 0xe3, 0xcf, 0xa6, 0x61, 0xff, 0x27, 0x54, 0x15, 0x2c, 0x61, - 0xa2, 0x2f, 0xa2, 0x98, 0x4b, 0x0d, 0xd1, 0x0b, 0x1a, 0x1b, 0xa9, 0x82, 0xf7, 0xad, 0xa9, 0x27, - 0x4b, 0x9b, 0xfa, 0xc8, 0x99, 0x2a, 0xe2, 0xc4, 0xc4, 0xcf, 0xe0, 0x7b, 0x29, 0xfa, 0xc0, 0x82, - 0xa9, 0x01, 0xa9, 0x68, 0xcc, 0x21, 0x52, 0x30, 0xa4, 0xaa, 0x35, 0x35, 0xb0, 0xb6, 0x9a, 0x81, - 0x22, 0x4e, 0x4c, 0x7c, 0x07, 0x13, 0x8b, 0x66, 0x06, 0x7e, 0xf1, 0xd0, 0xbe, 0x16, 0x94, 0xf3, - 0x85, 0x05, 0xd4, 0xec, 0x0c, 0x82, 0x75, 0xeb, 0xe1, 0x9b, 0xa5, 0x3d, 0x7c, 0xec, 0x3c, 0x14, - 0xb3, 0x62, 0x52, 0xb5, 0x81, 0xdc, 0x76, 0x9c, 0xb2, 0x33, 0xb0, 0x3e, 0x5a, 0x4c, 0x41, 0x6c, - 0x16, 0xfe, 0xf2, 0x02, 0x20, 0xd8, 0x58, 0xcd, 0x47, 0x31, 0x2b, 0x26, 0x55, 0x17, 0xc8, 0x19, - 0x79, 0x00, 0x70, 0xb2, 0xf6, 0xfb, 0xcb, 0xb0, 0x84, 0x5f, 0x95, 0xd1, 0xfa, 0x33, 0xd9, 0x85, - 0xc4, 0xff, 0x12, 0xa1, 0x26, 0xd5, 0x10, 0xb5, 0x20, 0x91, 0x22, 0xf0, 0xac, 0x95, 0xbd, 0xab, - 0x49, 0xb8, 0xeb, 0xc8, 0xe7, 0x31, 0x4c, 0xb6, 0xd2, 0xc1, 0xfd, 0xf4, 0xdb, 0x4f, 0xd0, 0xb6, - 0x02, 0x0d, 0x6a, 0x30, 0xab, 0x28, 0x57, 0xe6, 0x5f, 0x2d, 0x3d, 0x89, 0x3d, 0xa7, 0xb3, 0xc8, - 0x86, 0x49, 0x39, 0x03, 0xb2, 0x5d, 0x1c, 0xa2, 0xdd, 0x58, 0x72, 0x4e, 0x0d, 0x28, 0xca, 0xa3, - 0x21, 0xb0, 0x76, 0xc7, 0x64, 0x45, 0xfc, 0xf5, 0xd2, 0x92, 0xc1, 0xb4, 0xb3, 0xae, 0x11, 0x62, - 0xb2, 0x33, 0xc7, 0x9e, 0x5b, 0xc8, 0xff, 0xd9, 0x43, 0x7b, 0xc5, 0x7d, 0xed, 0x2a, 0xf8, 0xe9, - 0xd2, 0xea, 0x07, 0x4e, 0xfd, 0x7f, 0xda, 0xb9, 0xca, 0x8b, 0xda, 0x58, 0xa3, 0x1d, 0xbb, 0x11, - 0x4d, 0xa9, 0x94, 0x1c, 0x46, 0x8a, 0x9a, 0x69, 0xf5, 0x3e, 0x5a, 0x5a, 0xff, 0x76, 0x6e, 0x63, - 0x73, 0x7c, 0x98, 0x6c, 0xa7, 0x50, 0xc3, 0x22, 0x84, 0x1a, 0x48, 0x45, 0xbb, 0x2c, 0xe9, 0x2e, - 0x88, 0x6e, 0xac, 0x26, 0x7a, 0x9d, 0x0f, 0x93, 0xed, 0x14, 0xca, 0x89, 0xf6, 0x50, 0x45, 0xd0, - 0xd1, 0x82, 0xe6, 0x07, 0x56, 0xf3, 0xe1, 0xd2, 0x9a, 0xfb, 0xd9, 0x59, 0xb5, 0x48, 0x87, 0x49, - 0x59, 0xd0, 0x51, 0x4e, 0xd1, 0x64, 0xd3, 0xec, 0x1b, 0xc6, 0xd9, 0x99, 0x5d, 0xf8, 0x60, 0xf3, - 0x1d, 0x4c, 0x33, 0xc7, 0x87, 0x49, 0x25, 0x85, 0xbe, 0x9f, 0x23, 0x6f, 0xd4, 0x15, 0x4b, 0x62, - 0x48, 0x0c, 0x1b, 0x40, 0xb0, 0xf5, 0xee, 0xea, 0x6a, 0x46, 0xba, 0x58, 0x57, 0x8f, 0xa6, 0xb0, - 0x7f, 0x82, 0x6e, 0xea, 0xb1, 0x68, 0x4a, 0x9e, 0xb5, 0x3f, 0xb2, 0xda, 0xb7, 0xaf, 0x26, 0xe1, - 0xad, 0xec, 0x8c, 0xcb, 0x45, 0x31, 0xb9, 0xe1, 0x86, 0xee, 0x08, 0xa8, 0xa3, 0x4d, 0x18, 0xf5, - 0x64, 0x02, 0x89, 0x09, 0x6e, 0x1c, 0x7a, 0x47, 0xe5, 0xc6, 0xad, 0xab, 0x49, 0x58, 0x71, 0xff, - 0x9b, 0x46, 0x30, 0x99, 0x25, 0xf9, 0x0f, 0xd1, 0x2e, 0x24, 0xb4, 0xc9, 0x21, 0x12, 0xba, 0x1d, - 0xe9, 0x7e, 0xaf, 0xc7, 0xc7, 0xc1, 0xcd, 0x43, 0xef, 0x68, 0xb3, 0x71, 0x30, 0xef, 0xca, 0x37, - 0x52, 0x30, 0xa9, 0x38, 0xec, 0x89, 0x6e, 0x9f, 0x5a, 0xe4, 0x1a, 0x93, 0xdb, 0xdc, 0xa0, 0xfc, - 0x16, 0x26, 0x97, 0x92, 0x67, 0x72, 0x05, 0xe0, 0x1f, 0xa0, 0xad, 0x26, 0xa7, 0x71, 0x97, 0x33, - 0x6d, 0x82, 0xed, 0x94, 0x81, 0xcc, 0x01, 0x7b, 0x7b, 0xd2, 0x51, 0x94, 0x3b, 0x28, 0x74, 0x87, - 0x2a, 0x08, 0x2a, 0x2b, 0xde, 0x9e, 0x05, 0x9c, 0xe9, 0xed, 0x49, 0x47, 0xf7, 0x66, 0xe8, 0x69, - 0x0a, 0xda, 0x4b, 0x23, 0xcd, 0x76, 0x2b, 0xb1, 0x50, 0xa2, 0x3b, 0xab, 0x5d, 0x1a, 0xc5, 0xac, - 0x98, 0xa4, 0x13, 0x76, 0xab, 0x9c, 0xaf, 0xd6, 0x5f, 0x3d, 0x14, 0x08, 0x96, 0xe4, 0x5d, 0xbb, - 0x7a, 0x62, 0x66, 0x1c, 0xec, 0x5a, 0x27, 0xdf, 0x2d, 0xed, 0x24, 0x9c, 0xbd, 0x25, 0x0a, 0x79, - 0x31, 0xd9, 0x17, 0x2c, 0x99, 0xaf, 0xc8, 0xe3, 0x69, 0xe0, 0x64, 0xed, 0xdf, 0x97, 0xa1, 0xd7, - 0x78, 0x7a, 0xfe, 0x4f, 0xad, 0x74, 0x7e, 0x51, 0xf3, 0x5e, 0x5f, 0xd4, 0xbc, 0xbf, 0x2f, 0x6a, - 0xde, 0x6f, 0x97, 0xb5, 0xd2, 0xeb, 0xcb, 0x5a, 0xe9, 0xcf, 0xcb, 0x5a, 0xe9, 0x87, 0xcf, 0x72, - 0x36, 0xd2, 0x67, 0xdd, 0x9d, 0x04, 0xcc, 0x50, 0xaa, 0xae, 0x1d, 0xd4, 0x07, 0x9f, 0xd7, 0x47, - 0xf3, 0x97, 0xa0, 0x35, 0xd5, 0xdc, 0xb0, 0x8f, 0xbb, 0x2f, 0xfe, 0x0b, 0x00, 0x00, 0xff, 0xff, - 0xa5, 0x95, 0x59, 0x5a, 0x27, 0x0a, 0x00, 0x00, + 0x14, 0xd6, 0xb5, 0xb6, 0x6b, 0x31, 0xb1, 0x64, 0x5f, 0x64, 0xe7, 0xd0, 0xba, 0x3a, 0x83, 0x40, + 0x0b, 0x2f, 0xb1, 0x9a, 0xb6, 0x93, 0x47, 0x39, 0x48, 0xe3, 0x22, 0x49, 0x5b, 0x3a, 0x45, 0x80, + 0x2e, 0x07, 0xea, 0xf4, 0x22, 0x11, 0xe2, 0x1d, 0x55, 0x92, 0xfa, 0xe5, 0xa5, 0x43, 0xd1, 0xa9, + 0x4b, 0xc7, 0x2e, 0x05, 0x32, 0xf4, 0x0f, 0xe9, 0xe8, 0x31, 0x63, 0xd1, 0x41, 0x68, 0xed, 0xa5, + 0xb3, 0xff, 0x82, 0xe2, 0xc8, 0x93, 0xee, 0xe4, 0xa8, 0x01, 0x0e, 0xca, 0xa4, 0xe3, 0xf7, 0x9e, + 0xbe, 0xef, 0x23, 0xf9, 0xc8, 0x47, 0xe4, 0x0f, 0x22, 0x80, 0x06, 0x87, 0x21, 0x48, 0xda, 0x81, + 0xc6, 0xf0, 0xfe, 0xfc, 0xfb, 0xa8, 0x2f, 0x85, 0x16, 0xee, 0x76, 0x92, 0x70, 0x34, 0x07, 0x87, + 0xf7, 0xdf, 0xaf, 0x75, 0x44, 0x47, 0x98, 0x60, 0x23, 0xf9, 0xb2, 0x79, 0xf8, 0x8f, 0x75, 0xb4, + 0xf1, 0x35, 0x95, 0x34, 0x52, 0xee, 0x6f, 0x0e, 0xaa, 0x87, 0x22, 0xea, 0x73, 0xd0, 0x10, 0x70, + 0xf6, 0xfd, 0x80, 0xb5, 0xa9, 0x66, 0x22, 0x0e, 0x74, 0x57, 0x82, 0xea, 0x0a, 0xde, 0xf6, 0xde, + 0x39, 0x70, 0x0e, 0xcb, 0xcd, 0xe7, 0x17, 0x53, 0xbf, 0xf4, 0xd7, 0xd4, 0xff, 0xb8, 0xc3, 0x74, + 0x77, 0xd0, 0x3a, 0x0a, 0x45, 0xd4, 0x08, 0x85, 0x8a, 0x84, 0x4a, 0x7f, 0xee, 0xa9, 0x76, 0xaf, + 0xa1, 0x27, 0x7d, 0x50, 0x47, 0x0f, 0x20, 0xbc, 0x9e, 0xfa, 0x1f, 0x4d, 0x68, 0xc4, 0x8f, 0xf1, + 0x9b, 0xd9, 0x31, 0xd9, 0x9f, 0x25, 0x3c, 0xce, 0xe2, 0xcf, 0x66, 0x61, 0xf7, 0x07, 0x54, 0x8b, + 0x58, 0xcc, 0xa2, 0x41, 0x14, 0x84, 0x5c, 0x28, 0x08, 0x5e, 0xd0, 0x50, 0x0b, 0xe9, 0xbd, 0x6b, + 0x4c, 0x3d, 0x29, 0x6c, 0xea, 0x03, 0x6b, 0x6a, 0x19, 0x27, 0x26, 0x6e, 0x0a, 0x9f, 0x24, 0xe8, + 0x43, 0x03, 0x26, 0x06, 0x84, 0xa4, 0x21, 0x87, 0x40, 0xc2, 0x88, 0xca, 0xf6, 0xcc, 0xc0, 0xda, + 0x6a, 0x06, 0x96, 0x71, 0x62, 0xe2, 0x5a, 0x98, 0x18, 0x34, 0x35, 0xf0, 0x93, 0x83, 0xf6, 0x54, + 0x44, 0x39, 0x5f, 0x58, 0x40, 0xc5, 0xce, 0xc1, 0x5b, 0x37, 0x1e, 0xbe, 0x2a, 0xec, 0xe1, 0x43, + 0xeb, 0x61, 0x39, 0x2b, 0x26, 0x35, 0x13, 0xc8, 0x6d, 0xc7, 0x19, 0x3b, 0x07, 0xe3, 0xa3, 0xcd, + 0x24, 0x84, 0x7a, 0xe1, 0x2f, 0x2f, 0x00, 0xbc, 0x8d, 0xd5, 0x7c, 0x2c, 0x67, 0xc5, 0xa4, 0x66, + 0x03, 0x39, 0x23, 0x0f, 0x01, 0x8e, 0xd7, 0x7e, 0x7d, 0xe9, 0x97, 0xf0, 0xef, 0x15, 0xb4, 0xfe, + 0x4c, 0xf4, 0x20, 0x76, 0x3f, 0x47, 0xa8, 0x45, 0x15, 0x04, 0x6d, 0x88, 0x45, 0xe4, 0x39, 0xc6, + 0xca, 0xee, 0xf5, 0xd4, 0xdf, 0xb1, 0xe4, 0x59, 0x0c, 0x93, 0x72, 0x32, 0x78, 0x90, 0x7c, 0xbb, + 0x31, 0xaa, 0x48, 0x50, 0x20, 0x87, 0xf3, 0x8a, 0xb2, 0x65, 0xfe, 0x45, 0xe1, 0x49, 0xec, 0x5a, + 0x9d, 0x45, 0x36, 0x4c, 0xb6, 0x52, 0x20, 0xdd, 0xc5, 0x11, 0xda, 0x09, 0x05, 0xe7, 0x54, 0x83, + 0xa4, 0x3c, 0x18, 0x01, 0xeb, 0x74, 0x75, 0x5a, 0xc4, 0x5f, 0x16, 0x96, 0xf4, 0x66, 0x27, 0xeb, + 0x06, 0x21, 0x26, 0xdb, 0x19, 0xf6, 0xdc, 0x40, 0xee, 0x8f, 0x0e, 0xda, 0x5d, 0x7e, 0xae, 0x6d, + 0x05, 0x3f, 0x2d, 0xac, 0xbe, 0x6f, 0xd5, 0xff, 0xe7, 0x38, 0xd7, 0xf8, 0xb2, 0x63, 0xac, 0xd0, + 0xb6, 0xd9, 0x88, 0x96, 0x90, 0x52, 0x8c, 0x02, 0x49, 0xf5, 0xac, 0x7a, 0x4f, 0x0b, 0xeb, 0xdf, + 0xcd, 0x6d, 0x6c, 0x8e, 0x0f, 0x93, 0x4a, 0x02, 0x35, 0x0d, 0x42, 0xa8, 0x86, 0x44, 0xb4, 0xc7, + 0xe2, 0xde, 0x82, 0xe8, 0xc6, 0x6a, 0xa2, 0x37, 0xf9, 0x30, 0xa9, 0x24, 0x50, 0x4e, 0xb4, 0x8f, + 0xaa, 0x11, 0x1d, 0x2f, 0x68, 0xbe, 0x67, 0x34, 0x1f, 0x15, 0xd6, 0xdc, 0x4b, 0xef, 0xaa, 0x45, + 0x3a, 0x4c, 0xb6, 0x22, 0x3a, 0xce, 0x29, 0xea, 0x74, 0x9a, 0x03, 0xcd, 0x38, 0x3b, 0x37, 0x0b, + 0xef, 0x6d, 0xbe, 0x85, 0x69, 0xe6, 0xf8, 0x30, 0xa9, 0x26, 0xd0, 0xb7, 0x19, 0xf2, 0x5a, 0x5d, + 0xb1, 0x38, 0x84, 0x58, 0xb3, 0x21, 0x78, 0xe5, 0xb7, 0x57, 0x57, 0x73, 0xd2, 0xc5, 0xba, 0x3a, + 0x9d, 0xc1, 0xee, 0x31, 0xba, 0xad, 0x26, 0x51, 0x4b, 0xf0, 0xf4, 0xf8, 0x23, 0xa3, 0x7d, 0xf7, + 0x7a, 0xea, 0xdf, 0x49, 0xef, 0xb8, 0x5c, 0x14, 0x93, 0x5b, 0x76, 0x68, 0xaf, 0x80, 0x06, 0xda, + 0x84, 0x71, 0x5f, 0xc4, 0x10, 0x6b, 0xef, 0xd6, 0x81, 0x73, 0xb8, 0xd5, 0xbc, 0x73, 0x3d, 0xf5, + 0xab, 0xf6, 0x7f, 0xb3, 0x08, 0x26, 0xf3, 0x24, 0xf7, 0x11, 0xda, 0x81, 0x98, 0xb6, 0x38, 0x04, + 0x91, 0xea, 0x04, 0x6a, 0xd0, 0xef, 0xf3, 0x89, 0x77, 0xfb, 0xc0, 0x39, 0xdc, 0x6c, 0xee, 0x67, + 0xa7, 0xf2, 0xb5, 0x14, 0x4c, 0xaa, 0x16, 0x7b, 0xa2, 0x3a, 0x67, 0x06, 0xb9, 0xc1, 0x64, 0x37, + 0xd7, 0xdb, 0x7a, 0x03, 0x93, 0x4d, 0xc9, 0x33, 0xd9, 0x02, 0x70, 0xf7, 0x51, 0xb9, 0xc5, 0x69, + 0xd8, 0xe3, 0x4c, 0x69, 0xaf, 0x92, 0x30, 0x90, 0x0c, 0x30, 0xdd, 0x93, 0x8e, 0x83, 0xdc, 0x45, + 0xa1, 0xba, 0x54, 0x82, 0x57, 0x5d, 0xb1, 0x7b, 0x2e, 0xe1, 0x4c, 0xba, 0x27, 0x1d, 0x9f, 0xcc, + 0xd1, 0xb3, 0x04, 0x34, 0x4d, 0x23, 0xc9, 0xb6, 0x2b, 0xb1, 0x50, 0xa2, 0xdb, 0xab, 0x35, 0x8d, + 0xe5, 0xac, 0x98, 0x24, 0x13, 0xb6, 0xab, 0x9c, 0xaf, 0xd6, 0x9f, 0x1d, 0xe4, 0x45, 0x2c, 0xce, + 0xbb, 0xb6, 0xf5, 0xc4, 0xf4, 0xc4, 0xdb, 0x31, 0x4e, 0xbe, 0x29, 0xec, 0xc4, 0x9f, 0xbf, 0x25, + 0x96, 0xf2, 0x62, 0xb2, 0x17, 0xb1, 0x38, 0x5b, 0x91, 0xc7, 0xb3, 0x80, 0xdb, 0x42, 0x28, 0xb3, + 0xef, 0xb9, 0x46, 0xfe, 0xa4, 0x80, 0xfc, 0x69, 0xac, 0xb3, 0x06, 0x97, 0x31, 0x61, 0x52, 0x9e, + 0x4f, 0xfe, 0x78, 0xed, 0xdf, 0x97, 0xbe, 0xd3, 0x7c, 0x7a, 0xf1, 0x4f, 0xbd, 0x74, 0x71, 0x59, + 0x77, 0x5e, 0x5d, 0xd6, 0x9d, 0xbf, 0x2f, 0xeb, 0xce, 0x2f, 0x57, 0xf5, 0xd2, 0xab, 0xab, 0x7a, + 0xe9, 0xcf, 0xab, 0x7a, 0xe9, 0xbb, 0x4f, 0x72, 0x5a, 0xc9, 0xd3, 0xf1, 0x5e, 0x0c, 0x7a, 0x24, + 0x64, 0xcf, 0x0c, 0x1a, 0xc3, 0x4f, 0x1b, 0xe3, 0xec, 0xb5, 0x69, 0x94, 0x5b, 0x1b, 0xe6, 0x01, + 0xf9, 0xd9, 0x7f, 0x01, 0x00, 0x00, 0xff, 0xff, 0xb0, 0xe6, 0xcb, 0xbb, 0x8b, 0x0a, 0x00, 0x00, } func (this *Token) Equal(that interface{}) bool {