diff --git a/Cargo.lock b/Cargo.lock index 5a24a722a..fcc702395 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10554,6 +10554,25 @@ dependencies = [ "valence-test-icq-lib", ] +[[package]] +name = "valence-elys-lending" +version = "0.2.0" +dependencies = [ + "cosmos-sdk-proto 0.20.0", + "cosmwasm-schema 2.2.1", + "cosmwasm-std 2.2.1", + "cw-ownable", + "cw-storage-plus 2.0.0", + "schemars", + "serde", + "thiserror 1.0.69", + "valence-account-utils", + "valence-lending-utils", + "valence-library-base", + "valence-library-utils", + "valence-macros", +] + [[package]] name = "valence-encoder-broker" version = "0.2.0" @@ -10729,6 +10748,9 @@ version = "0.2.0" dependencies = [ "cosmwasm-schema 2.2.1", "cosmwasm-std 2.2.1", + "prost 0.12.6", + "schemars", + "serde", ] [[package]] @@ -11087,6 +11109,7 @@ dependencies = [ "valence-authorization-utils", "valence-drop-liquid-staker", "valence-drop-liquid-unstaker", + "valence-elys-lending", "valence-forwarder-library", "valence-generic-ibc-transfer-library", "valence-library-base", diff --git a/Cargo.toml b/Cargo.toml index aa8a99cf7..a375457a5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,21 +1,21 @@ [workspace] members = [ - "contracts/accounts/*", - "contracts/libraries/*", - "contracts/encoders/*", - "contracts/authorization", - "contracts/processor", - "contracts/testing/*", - "contracts/program-registry", - "contracts/middleware/type-registries/osmosis/osmo-26-0-0", - "contracts/middleware/broker", - "contracts/middleware/asserter", - "contracts/middleware/verification-gateway", - "packages/*", - "program-manager", - "e2e", - "examples/*", - "deployment/scripts/*", + "contracts/accounts/*", + "contracts/libraries/*", + "contracts/encoders/*", + "contracts/authorization", + "contracts/processor", + "contracts/testing/*", + "contracts/program-registry", + "contracts/middleware/type-registries/osmosis/osmo-26-0-0", + "contracts/middleware/broker", + "contracts/middleware/asserter", + "contracts/middleware/verification-gateway", + "packages/*", + "program-manager", + "e2e", + "examples/*", + "deployment/scripts/*", ] resolver = "2" @@ -95,11 +95,12 @@ valence-clearing-queue = { path = "contracts/libraries/clearing-qu # middleware valence-middleware-osmosis = { path = "contracts/middleware/type-registries/osmosis/osmo-26-0-0", features = [ - "library", + "library", ] } valence-middleware-broker = { path = "contracts/middleware/broker", features = ["library"] } valence-middleware-asserter = { path = "contracts/middleware/asserter", features = ["library"] } valence-mars-lending = { path = "contracts/libraries/mars-lending", features = ["library"] } +valence-elys-lending = { path = "contracts/libraries/elys-lending", features = ["library"] } # our packages valence-account-utils = { path = "packages/account-utils" } diff --git a/contracts/libraries/elys-lending/.cargo/config.toml b/contracts/libraries/elys-lending/.cargo/config.toml new file mode 100644 index 000000000..e03b96d17 --- /dev/null +++ b/contracts/libraries/elys-lending/.cargo/config.toml @@ -0,0 +1,3 @@ +[alias] +wasm = "build --release --lib --target wasm32-unknown-unknown" +schema = "run --bin schema" diff --git a/contracts/libraries/elys-lending/Cargo.toml b/contracts/libraries/elys-lending/Cargo.toml new file mode 100644 index 000000000..4d9560d8b --- /dev/null +++ b/contracts/libraries/elys-lending/Cargo.toml @@ -0,0 +1,29 @@ +[package] +name = "valence-elys-lending" +authors.workspace = true +edition.workspace = true +license.workspace = true +version.workspace = true +repository.workspace = true + +[lib] +crate-type = ["cdylib", "rlib"] + +[features] +# use library feature to disable all instantiate/execute/query exports +library = [] + +[dependencies] +cosmwasm-schema = { workspace = true } +cosmwasm-std = { workspace = true } +cw-ownable = { workspace = true } +cosmos-sdk-proto = { workspace = true } +cw-storage-plus = { workspace = true } +schemars = { workspace = true } +serde = { workspace = true } +thiserror = { workspace = true } +valence-macros = { workspace = true } +valence-library-utils = { workspace = true } +valence-library-base = { workspace = true } +valence-lending-utils = { workspace = true } +valence-account-utils = { workspace = true } diff --git a/contracts/libraries/elys-lending/README.md b/contracts/libraries/elys-lending/README.md new file mode 100644 index 000000000..8e494156a --- /dev/null +++ b/contracts/libraries/elys-lending/README.md @@ -0,0 +1,18 @@ +# Elys Lending library + +The **Valence Elys Lending** library enables lending on Elys from a **input account**. Also, the library allows **withdrawing lent assets** from the **input account** on Elys and depositing the withdrawed tokens into an **output account**. Additionally, users can **claim rewards** from the **input account**, and the rewards will also be sent to the **output account** upon claiming them. + +## Configuration + +The library is configured on instantiation via the `LibraryConfig` type. + +```rust +pub struct LibraryConfig { + /// Address of the input account + pub input_addr: LibraryAccountType, + /// Address of the output account + pub output_addr: LibraryAccountType, + /// ID of the pool we are going to lend to + pub pool_id: Uint64, +} +``` diff --git a/contracts/libraries/elys-lending/schema/valence-elys-lending.json b/contracts/libraries/elys-lending/schema/valence-elys-lending.json new file mode 100644 index 000000000..904bcb82b --- /dev/null +++ b/contracts/libraries/elys-lending/schema/valence-elys-lending.json @@ -0,0 +1,728 @@ +{ + "contract_name": "valence-elys-lending", + "contract_version": "0.2.0", + "idl_version": "1.0.0", + "instantiate": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "InstantiateMsg", + "type": "object", + "required": [ + "config", + "owner", + "processor" + ], + "properties": { + "config": { + "$ref": "#/definitions/LibraryConfig" + }, + "owner": { + "type": "string" + }, + "processor": { + "type": "string" + } + }, + "additionalProperties": false, + "definitions": { + "LibraryAccountType": { + "description": "A helper type that is used to associate an account or library with an id When a program is not instantiated yet, ids will be used to reference accounts and libraries When a program is instantiated, the ids will be replaced by the instantiated addresses", + "oneOf": [ + { + "type": "object", + "required": [ + "|library_account_addr|" + ], + "properties": { + "|library_account_addr|": { + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "|account_id|" + ], + "properties": { + "|account_id|": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "|library_id|" + ], + "properties": { + "|library_id|": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + ] + }, + "LibraryConfig": { + "type": "object", + "required": [ + "input_addr", + "output_addr", + "pool_id" + ], + "properties": { + "input_addr": { + "description": "Address of the input account", + "allOf": [ + { + "$ref": "#/definitions/LibraryAccountType" + } + ] + }, + "output_addr": { + "description": "Address of the output account", + "allOf": [ + { + "$ref": "#/definitions/LibraryAccountType" + } + ] + }, + "pool_id": { + "description": "ID of the pool we are going to lend to", + "allOf": [ + { + "$ref": "#/definitions/Uint64" + } + ] + } + }, + "additionalProperties": false + }, + "Uint64": { + "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", + "type": "string" + } + } + }, + "execute": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ExecuteMsg", + "oneOf": [ + { + "type": "object", + "required": [ + "process_function" + ], + "properties": { + "process_function": { + "$ref": "#/definitions/FunctionMsgs" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "update_config" + ], + "properties": { + "update_config": { + "type": "object", + "required": [ + "new_config" + ], + "properties": { + "new_config": { + "$ref": "#/definitions/LibraryConfigUpdate" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "update_processor" + ], + "properties": { + "update_processor": { + "type": "object", + "required": [ + "processor" + ], + "properties": { + "processor": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Update the contract's ownership. The `action` to be provided can be either to propose transferring ownership to an account, accept a pending ownership transfer, or renounce the ownership permanently.", + "type": "object", + "required": [ + "update_ownership" + ], + "properties": { + "update_ownership": { + "$ref": "#/definitions/Action" + } + }, + "additionalProperties": false + } + ], + "definitions": { + "Action": { + "description": "Actions that can be taken to alter the contract's ownership", + "oneOf": [ + { + "description": "Propose to transfer the contract's ownership to another account, optionally with an expiry time.\n\nCan only be called by the contract's current owner.\n\nAny existing pending ownership transfer is overwritten.", + "type": "object", + "required": [ + "transfer_ownership" + ], + "properties": { + "transfer_ownership": { + "type": "object", + "required": [ + "new_owner" + ], + "properties": { + "expiry": { + "anyOf": [ + { + "$ref": "#/definitions/Expiration" + }, + { + "type": "null" + } + ] + }, + "new_owner": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Accept the pending ownership transfer.\n\nCan only be called by the pending owner.", + "type": "string", + "enum": [ + "accept_ownership" + ] + }, + { + "description": "Give up the contract's ownership and the possibility of appointing a new owner.\n\nCan only be invoked by the contract's current owner.\n\nAny existing pending ownership transfer is canceled.", + "type": "string", + "enum": [ + "renounce_ownership" + ] + } + ] + }, + "Expiration": { + "description": "Expiration represents a point in time when some event happens. It can compare with a BlockInfo and will return is_expired() == true once the condition is hit (and for every block in the future)", + "oneOf": [ + { + "description": "AtHeight will expire when `env.block.height` >= height", + "type": "object", + "required": [ + "at_height" + ], + "properties": { + "at_height": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + { + "description": "AtTime will expire when `env.block.time` >= time", + "type": "object", + "required": [ + "at_time" + ], + "properties": { + "at_time": { + "$ref": "#/definitions/Timestamp" + } + }, + "additionalProperties": false + }, + { + "description": "Never will never expire. Used to express the empty variant", + "type": "object", + "required": [ + "never" + ], + "properties": { + "never": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] + }, + "FunctionMsgs": { + "oneOf": [ + { + "description": "Message to lend tokens.", + "type": "object", + "required": [ + "lend" + ], + "properties": { + "lend": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Message to withdraw tokens. If amount is not specified, full amount will be withdrawn.", + "type": "object", + "required": [ + "withdraw" + ], + "properties": { + "withdraw": { + "type": "object", + "properties": { + "amount": { + "anyOf": [ + { + "$ref": "#/definitions/Uint128" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Message to claim reward tokens.", + "type": "object", + "required": [ + "claim_rewards" + ], + "properties": { + "claim_rewards": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] + }, + "LibraryAccountType": { + "description": "A helper type that is used to associate an account or library with an id When a program is not instantiated yet, ids will be used to reference accounts and libraries When a program is instantiated, the ids will be replaced by the instantiated addresses", + "oneOf": [ + { + "type": "object", + "required": [ + "|library_account_addr|" + ], + "properties": { + "|library_account_addr|": { + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "|account_id|" + ], + "properties": { + "|account_id|": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "|library_id|" + ], + "properties": { + "|library_id|": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + ] + }, + "LibraryConfigUpdate": { + "type": "object", + "properties": { + "input_addr": { + "anyOf": [ + { + "$ref": "#/definitions/LibraryAccountType" + }, + { + "type": "null" + } + ] + }, + "output_addr": { + "anyOf": [ + { + "$ref": "#/definitions/LibraryAccountType" + }, + { + "type": "null" + } + ] + }, + "pool_id": { + "anyOf": [ + { + "$ref": "#/definitions/Uint64" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + "Timestamp": { + "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", + "allOf": [ + { + "$ref": "#/definitions/Uint64" + } + ] + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + }, + "Uint64": { + "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", + "type": "string" + } + } + }, + "query": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "QueryMsg", + "description": "Enum representing the different query messages that can be sent.", + "oneOf": [ + { + "description": "Query to get the processor address.", + "type": "object", + "required": [ + "get_processor" + ], + "properties": { + "get_processor": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Query to get the library configuration.", + "type": "object", + "required": [ + "get_library_config" + ], + "properties": { + "get_library_config": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "get_raw_library_config" + ], + "properties": { + "get_raw_library_config": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Query the contract's ownership information", + "type": "object", + "required": [ + "ownership" + ], + "properties": { + "ownership": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] + }, + "migrate": null, + "sudo": null, + "responses": { + "get_library_config": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Config", + "type": "object", + "required": [ + "input_addr", + "output_addr", + "pool_id" + ], + "properties": { + "input_addr": { + "$ref": "#/definitions/Addr" + }, + "output_addr": { + "$ref": "#/definitions/Addr" + }, + "pool_id": { + "$ref": "#/definitions/Uint64" + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "Uint64": { + "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", + "type": "string" + } + } + }, + "get_processor": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Addr", + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "get_raw_library_config": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "LibraryConfig", + "type": "object", + "required": [ + "input_addr", + "output_addr", + "pool_id" + ], + "properties": { + "input_addr": { + "description": "Address of the input account", + "allOf": [ + { + "$ref": "#/definitions/LibraryAccountType" + } + ] + }, + "output_addr": { + "description": "Address of the output account", + "allOf": [ + { + "$ref": "#/definitions/LibraryAccountType" + } + ] + }, + "pool_id": { + "description": "ID of the pool we are going to lend to", + "allOf": [ + { + "$ref": "#/definitions/Uint64" + } + ] + } + }, + "additionalProperties": false, + "definitions": { + "LibraryAccountType": { + "description": "A helper type that is used to associate an account or library with an id When a program is not instantiated yet, ids will be used to reference accounts and libraries When a program is instantiated, the ids will be replaced by the instantiated addresses", + "oneOf": [ + { + "type": "object", + "required": [ + "|library_account_addr|" + ], + "properties": { + "|library_account_addr|": { + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "|account_id|" + ], + "properties": { + "|account_id|": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "|library_id|" + ], + "properties": { + "|library_id|": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + ] + }, + "Uint64": { + "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", + "type": "string" + } + } + }, + "ownership": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Ownership_for_String", + "description": "The contract's ownership info", + "type": "object", + "properties": { + "owner": { + "description": "The contract's current owner. `None` if the ownership has been renounced.", + "type": [ + "string", + "null" + ] + }, + "pending_expiry": { + "description": "The deadline for the pending owner to accept the ownership. `None` if there isn't a pending ownership transfer, or if a transfer exists and it doesn't have a deadline.", + "anyOf": [ + { + "$ref": "#/definitions/Expiration" + }, + { + "type": "null" + } + ] + }, + "pending_owner": { + "description": "The account who has been proposed to take over the ownership. `None` if there isn't a pending ownership transfer.", + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false, + "definitions": { + "Expiration": { + "description": "Expiration represents a point in time when some event happens. It can compare with a BlockInfo and will return is_expired() == true once the condition is hit (and for every block in the future)", + "oneOf": [ + { + "description": "AtHeight will expire when `env.block.height` >= height", + "type": "object", + "required": [ + "at_height" + ], + "properties": { + "at_height": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + { + "description": "AtTime will expire when `env.block.time` >= time", + "type": "object", + "required": [ + "at_time" + ], + "properties": { + "at_time": { + "$ref": "#/definitions/Timestamp" + } + }, + "additionalProperties": false + }, + { + "description": "Never will never expire. Used to express the empty variant", + "type": "object", + "required": [ + "never" + ], + "properties": { + "never": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] + }, + "Timestamp": { + "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", + "allOf": [ + { + "$ref": "#/definitions/Uint64" + } + ] + }, + "Uint64": { + "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", + "type": "string" + } + } + } + } +} diff --git a/contracts/libraries/elys-lending/src/bin/schema.rs b/contracts/libraries/elys-lending/src/bin/schema.rs new file mode 100644 index 000000000..0815deaf0 --- /dev/null +++ b/contracts/libraries/elys-lending/src/bin/schema.rs @@ -0,0 +1,12 @@ +use cosmwasm_schema::write_api; + +use valence_elys_lending::msg::{FunctionMsgs, LibraryConfig, LibraryConfigUpdate, QueryMsg}; +use valence_library_utils::msg::{ExecuteMsg, InstantiateMsg}; + +fn main() { + write_api! { + instantiate: InstantiateMsg, + execute: ExecuteMsg, + query: QueryMsg, + } +} diff --git a/contracts/libraries/elys-lending/src/contract.rs b/contracts/libraries/elys-lending/src/contract.rs new file mode 100644 index 000000000..98290a8a2 --- /dev/null +++ b/contracts/libraries/elys-lending/src/contract.rs @@ -0,0 +1,237 @@ +use crate::msg::{Config, FunctionMsgs, LibraryConfig, LibraryConfigUpdate, QueryMsg}; +use cosmos_sdk_proto::traits::MessageExt; +#[cfg(not(feature = "library"))] +use cosmwasm_std::entry_point; +use cosmwasm_std::{ + to_json_binary, to_json_string, Binary, Coin, CosmosMsg, Deps, DepsMut, Env, MessageInfo, + Reply, Response, StdError, StdResult, SubMsg, +}; +use valence_lending_utils::elys::{ + ElysQuery, MsgBond, MsgClaimRewards, MsgUnbond, QueryCommittedTokensLockedResponse, +}; +use valence_library_utils::{ + error::LibraryError, + execute_on_behalf_of, execute_submsgs_on_behalf_of, + msg::{ExecuteMsg, InstantiateMsg}, +}; + +// version info for migration info +const CONTRACT_NAME: &str = env!("CARGO_PKG_NAME"); +const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); + +// Unique ID for reply handling +const WITHDRAW_REPLY_ID: u64 = 1; +const CLAIM_REPLY_ID: u64 = 2; + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn instantiate( + deps: DepsMut, + _env: Env, + _info: MessageInfo, + msg: InstantiateMsg, +) -> Result { + valence_library_base::instantiate(deps, CONTRACT_NAME, CONTRACT_VERSION, msg) +} + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn execute( + deps: DepsMut, + env: Env, + info: MessageInfo, + msg: ExecuteMsg, +) -> Result { + valence_library_base::execute(deps, env, info, msg, process_function, update_config) +} + +pub fn process_function( + deps: DepsMut, + _env: Env, + _info: MessageInfo, + msg: FunctionMsgs, + cfg: Config, +) -> Result { + match msg { + FunctionMsgs::Lend {} => { + // Query the pool to get the deposit denom + let pool = valence_lending_utils::elys::query_pool(&deps, cfg.pool_id.u64())?; + + // Query account balance + let balance = deps + .querier + .query_balance(cfg.input_addr.clone(), pool.deposit_denom.clone())?; + + if balance.amount.is_zero() { + return Err(LibraryError::ExecutionError("No funds to lend".to_string())); + } + + // Prepare lend message + let lend_cosmos_msg = MsgBond { + amount: balance.clone().amount.to_string(), + pool_id: cfg.pool_id.into(), + creator: cfg.input_addr.to_string(), + }; + + #[allow(deprecated)] + let lend_msg = CosmosMsg::Stargate { + type_url: "/elys.stablestake.MsgBond".to_string(), + value: Binary::from(lend_cosmos_msg.to_bytes().unwrap()), + }; + + // Execute on behalf of input_addr + let execute_msg = execute_on_behalf_of(vec![lend_msg], &cfg.input_addr)?; + + Ok(Response::new() + .add_message(execute_msg) + .add_attribute("method", "lend") + .add_attribute("amount", balance.to_string()) + .add_attribute("input_addr", cfg.input_addr.to_string())) + } + FunctionMsgs::Withdraw { amount } => { + // Check the balance of the input address (lender) for locked tokens + let query = ElysQuery::QueryCommittedTokensLocked { + address: cfg.input_addr.to_string(), + }; + let tokens_locked: QueryCommittedTokensLockedResponse = + deps.querier.query(&query.into())?; + + // We know there will be only one locked coin because we are locking tokens specifically for this pool. + // If no coins are found, it means there are no funds locked for the specified pool, and an error is returned. + let balance_locked: Coin = tokens_locked + .locked_committed + .into_iter() + .next() // Take the first coin + .ok_or_else(|| { + LibraryError::ExecutionError( + "No funds locked for the specified denom".to_string(), + ) + })?; + + // Check if amount_locked is greater than zero + if balance_locked.amount.is_zero() { + return Err(LibraryError::ExecutionError( + "Available amount for withdrawal is zero".to_string(), + )); + } + + // Check withdrawal amount + let withdrawal_amount = match amount { + // withdraw exact amount + Some(amt) => { + if amt > balance_locked.amount || amt.is_zero() { + return Err(LibraryError::ExecutionError( + "Withdraw amount is either zero or bigger than balance".to_string(), + )); + } + amt.to_string() + } + // if no amount is specified, we withdraw the entire position + None => balance_locked.amount.to_string(), + }; + + // Prepare withdraw message + let withdraw_cosmos_msg = MsgUnbond { + amount: withdrawal_amount.to_string(), + pool_id: cfg.pool_id.into(), + creator: cfg.input_addr.to_string(), + }; + + #[allow(deprecated)] + let withdraw_msg = CosmosMsg::Stargate { + type_url: "/elys.stablestake.MsgUnbond".to_string(), + value: Binary::from(withdraw_cosmos_msg.to_bytes().unwrap()), + }; + + // Execute on behalf of input_addr with reply. On reply we will send the funds to the output address + let execute_msg = execute_submsgs_on_behalf_of( + vec![SubMsg::reply_on_success(withdraw_msg, WITHDRAW_REPLY_ID)], + Some(to_json_string(&cfg)?), + &cfg.input_addr, + )?; + + Ok(Response::new() + .add_submessage(SubMsg::reply_on_success(execute_msg, WITHDRAW_REPLY_ID))) + } + FunctionMsgs::ClaimRewards {} => { + // Prepare claim message + let claim_cosmos_msg = MsgClaimRewards { + sender: cfg.input_addr.to_string(), + pool_ids: vec![cfg.pool_id.into()], + }; + #[allow(deprecated)] + let claim_msg = CosmosMsg::Stargate { + type_url: "/elys.masterchef.MsgClaimRewards".to_string(), + value: Binary::from(claim_cosmos_msg.to_bytes().unwrap()), + }; + // Execute on behalf of input_addr with reply to handle reward transfer + let execute_msg = execute_submsgs_on_behalf_of( + vec![SubMsg::reply_on_success(claim_msg, CLAIM_REPLY_ID)], + Some(to_json_string(&cfg)?), + &cfg.input_addr, + )?; + Ok(Response::new() + .add_submessage(SubMsg::reply_on_success(execute_msg, CLAIM_REPLY_ID))) + } + } +} + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn reply(deps: DepsMut, _env: Env, msg: Reply) -> Result { + let fn_name = match msg.id { + WITHDRAW_REPLY_ID => "withdraw", + CLAIM_REPLY_ID => "claim_rewards", + _ => return Err(LibraryError::Std(StdError::generic_err("unknown reply id"))), + }; + + // Extract configuration from the reply payload + let cfg: Config = valence_account_utils::msg::parse_valence_payload(&msg.result)?; + + // Query account balances of input account + #[allow(deprecated)] + let balances = deps + .querier + .query_all_balances(cfg.input_addr.clone()) + .unwrap(); + + // Transfer the funds to the output address + let send_msg = CosmosMsg::Bank(cosmwasm_std::BankMsg::Send { + to_address: cfg.output_addr.to_string(), + amount: balances.clone(), + }); + + let execute_msg = execute_on_behalf_of(vec![send_msg], &cfg.input_addr)?; + + Ok(Response::new() + .add_message(execute_msg) + .add_attribute("method", fn_name) + .add_attribute("output_addr", cfg.output_addr.to_string())) +} + +pub fn update_config( + deps: DepsMut, + _env: Env, + _info: MessageInfo, + new_config: LibraryConfigUpdate, +) -> Result<(), LibraryError> { + new_config.update_config(deps) +} + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { + match msg { + QueryMsg::Ownership {} => { + to_json_binary(&valence_library_base::get_ownership(deps.storage)?) + } + QueryMsg::GetProcessor {} => { + to_json_binary(&valence_library_base::get_processor(deps.storage)?) + } + QueryMsg::GetLibraryConfig {} => { + let config: Config = valence_library_base::load_config(deps.storage)?; + to_json_binary(&config) + } + QueryMsg::GetRawLibraryConfig {} => { + let raw_config: LibraryConfig = + valence_library_utils::raw_config::query_raw_library_config(deps.storage)?; + to_json_binary(&raw_config) + } + } +} diff --git a/contracts/libraries/elys-lending/src/lib.rs b/contracts/libraries/elys-lending/src/lib.rs new file mode 100644 index 000000000..112ecadc8 --- /dev/null +++ b/contracts/libraries/elys-lending/src/lib.rs @@ -0,0 +1,2 @@ +pub mod contract; +pub mod msg; diff --git a/contracts/libraries/elys-lending/src/msg.rs b/contracts/libraries/elys-lending/src/msg.rs new file mode 100644 index 000000000..96d6dfb2b --- /dev/null +++ b/contracts/libraries/elys-lending/src/msg.rs @@ -0,0 +1,116 @@ +use cosmwasm_schema::{cw_serde, QueryResponses}; +use cosmwasm_std::{Addr, Deps, DepsMut, Uint128, Uint64}; +use cw_ownable::cw_ownable_query; +use valence_lending_utils::elys::ElysQuery; +use valence_library_utils::LibraryAccountType; +use valence_library_utils::{error::LibraryError, msg::LibraryConfigValidation}; +use valence_macros::{valence_library_query, ValenceLibraryInterface}; + +#[cw_serde] +pub enum FunctionMsgs { + /// Message to lend tokens. + Lend {}, + /// Message to withdraw tokens. If amount is not specified, full amount will be withdrawn. + Withdraw { amount: Option }, + /// Message to claim reward tokens. + ClaimRewards {}, +} + +#[valence_library_query] +#[cw_ownable_query] +#[cw_serde] +#[derive(QueryResponses)] +/// Enum representing the different query messages that can be sent. +pub enum QueryMsg {} + +#[cw_serde] +#[derive(ValenceLibraryInterface)] +pub struct LibraryConfig { + /// Address of the input account + pub input_addr: LibraryAccountType, + /// Address of the output account + pub output_addr: LibraryAccountType, + /// ID of the pool we are going to lend to + pub pool_id: Uint64, +} + +impl LibraryConfig { + pub fn new( + input_addr: impl Into, + output_addr: impl Into, + pool: Uint64, + ) -> Self { + LibraryConfig { + input_addr: input_addr.into(), + output_addr: output_addr.into(), + pool_id: pool, + } + } + + fn do_validate( + &self, + api: &dyn cosmwasm_std::Api, + ) -> Result<(Addr, Addr, Uint64), LibraryError> { + let input_addr = self.input_addr.to_addr(api)?; + let output_addr = self.output_addr.to_addr(api)?; + + Ok((input_addr, output_addr, self.pool_id)) + } +} + +impl LibraryConfigValidation for LibraryConfig { + #[cfg(not(target_arch = "wasm32"))] + fn pre_validate(&self, api: &dyn cosmwasm_std::Api) -> Result<(), LibraryError> { + self.do_validate(api)?; + Ok(()) + } + + fn validate(&self, deps: Deps) -> Result { + let (input_addr, output_addr, pool) = self.do_validate(deps.api)?; + + Ok(Config { + input_addr, + output_addr, + pool_id: pool, + }) + } +} + +impl LibraryConfigUpdate { + pub fn update_config(self, deps: DepsMut) -> Result<(), LibraryError> { + let mut config: Config = valence_library_base::load_config(deps.storage)?; + + if let Some(input_addr) = self.input_addr { + config.input_addr = input_addr.to_addr(deps.api)?; + } + + if let Some(output_addr) = self.output_addr { + config.output_addr = output_addr.to_addr(deps.api)?; + } + + if let Some(pool_id) = self.pool_id { + valence_lending_utils::elys::query_pool(&deps, pool_id.u64())?; + config.pool_id = pool_id; + } + + valence_library_base::save_config(deps.storage, &config)?; + Ok(()) + } +} + +#[cw_serde] +pub struct Config { + pub input_addr: Addr, + pub output_addr: Addr, + pub pool_id: Uint64, +} + +impl Config { + pub fn new(input_addr: Addr, output_addr: Addr, pool: Uint64) -> Self { + Config { + input_addr, + output_addr, + pool_id: pool, + } + } +} diff --git a/docs/src/SUMMARY.md b/docs/src/SUMMARY.md index 5df37a205..6078d9767 100644 --- a/docs/src/SUMMARY.md +++ b/docs/src/SUMMARY.md @@ -36,6 +36,7 @@ - [Astroport LPer](./libraries/cosmwasm/astroport_lper.md) - [Astroport Withdrawer](./libraries/cosmwasm/astroport_withdrawer.md) - [Clearing Queue](./libraries/cosmwasm/clearing_queue.md) + - [Elys Lending](./libraries/cosmwasm/elys_lending.md) - [Forwarder](./libraries/cosmwasm/forwarder.md) - [Generic IBC Transfer](./libraries/cosmwasm/generic_ibc_transfer.md) - [Neutron IBC Transfer](./libraries/cosmwasm/neutron_ibc_transfer.md) diff --git a/docs/src/libraries/cosmwasm/elys_lending.md b/docs/src/libraries/cosmwasm/elys_lending.md new file mode 100644 index 000000000..188b716fe --- /dev/null +++ b/docs/src/libraries/cosmwasm/elys_lending.md @@ -0,0 +1,78 @@ +# Valence Elys Lending library + +The Valence Elys Lending library facilitates lending operations through [Elys Earn Vaults](https://elys-network.gitbook.io/docs/core-features/earn/vaults) on the [Elys Protocol](https://app.elys.network/) from an input account and manages withdrawal of lent assets to an output account. Additionally, users can claim rewards earned from lending operations. This library enables Valence Programs to earn yield on deposited assets through Elys Protocol's lending markets while maintaining full control over the lending positions directly from the input account. + +## High Level Flow + +```mermaid +--- +title: Elys Lending Library +--- +graph LR + IA((Input + Account)) + OA((Output + Account)) + P[Processor] + S[Elys Lending + Library] + P -- 1/Lend, Withdraw, or Claim --> S + S -- 2/Query balances --> IA + S -- 3/Execute Lending --> IA + S -- 4/Execute Withdrawal --> IA + S -- 5/Execute Claim Rewards --> IA + IA -- 6/Transfer Assets --> OA +``` + +## Functions + +| Function | Parameters | Description | +|----------|------------|-------------| +| **Lend** | - | Lends the entire balance of the vault token (determined by the pool ID) from the input account to the Elys Protocol. | +| **Withdraw** | `amount: Option` | Withdraws lent assets from the input account to the output account. If no amount is specified, the entire position is withdrawn. Rewards in vault tokens are automatically added to the balance upon withdrawal, while rewards in other denoms must be claimed separately. | +| **ClaimRewards** | - | Claims rewards earned from lending operations. Rewards include fees paid in USDC and bonus EDEN rewards, which are upon claiming transferred to the output account. | + +## Configuration + +The library is configured on instantiation via the `LibraryConfig` type. + +```rust +pub struct LibraryConfig { + /// Address of the input account + pub input_addr: LibraryAccountType, + /// Address of the output account + pub output_addr: LibraryAccountType, + /// ID of the pool we are going to lend to + pub pool_id: Uint64, +} +``` + +## Implementation Details + +### Lending Process + +1. **Balance Check**: Queries the input account balance for the vault token, which is determined by the pool ID. The library queries the pool to retrieve the denom of the vault token. +2. **Lend**: Executes a `MsgBond` message to deposit assets into the vault. Borrowers use these assets to open Leverage LP positions, paying fees that are transformed into rewards for the lender. + +### Withdrawal Process + +1. **Balance Check**: Queries the locked (lent) token amount in the vault for the input account. +2. **Amount Calculation**: Uses the exact amount if specified; otherwise, withdraws the entire balance. +3. **Withdraw**: Executes a `MsgUnbond` message to withdraw the lent position back to the input account. +4. **Reply Handling**: Uses the CosmWasm reply mechanism to handle the two-step process of withdrawal. Upon successful withdrawal, the funds will be transferred to the Valence output account. + +### Claim Rewards Process + +1. **Claim**: Executes a `MsgClaimRewards` message to claim rewards earned from lending operations. Rewards include fees paid in USDC and bonus EDEN rewards, which are transferred to the input account. However, EDEN rewards are not transferable upon claiming. Instead, the input account will remain the owner of the EDEN tokens. Currently, the library does not implement functions for managing EDEN rewards, such as vesting or committing them. + +2. **Reply Handling**: Uses the CosmWasm reply mechanism to send the transferable rewards (e.g., USDC) from the input Valence account to the output account. EDEN rewards are excluded from this transfer and remain in the input account. + +#### Summary of EDEN Rewards + +Claiming EDEN rewards does not make them immediately transferable. EDEN tokens function as vesting reward tokens within the Elys ecosystem, designed to encourage long-term participation. Users can commit EDEN back into the network to earn additional rewards or vest EDEN to convert them into transferable ELYS tokens over a 90-day linear vesting period. For more details, visit the [Elys Tokenomics and Rewards System Documentation](https://elys-network.gitbook.io/docs/tokenomic-and-rewards-system/eden-and-eden-boost). + +### Error Handling + +- **No Funds**: Returns an error if attempting to lend, withdraw, or claim rewards with a zero balance. +- **Elys Integration**: Propagates Elys Protocol errors during lending, withdrawal, or reward claim operations. + diff --git a/packages/lending-utils/Cargo.toml b/packages/lending-utils/Cargo.toml index 333f71aa9..37fc91879 100644 --- a/packages/lending-utils/Cargo.toml +++ b/packages/lending-utils/Cargo.toml @@ -8,3 +8,6 @@ description = "Helpers for lending integrations" [dependencies] cosmwasm-std = { workspace = true } cosmwasm-schema = { workspace = true } +serde = { workspace = true } +schemars = { workspace = true } +prost = "0.12" diff --git a/packages/lending-utils/src/elys.rs b/packages/lending-utils/src/elys.rs new file mode 100644 index 000000000..8aa12e6fa --- /dev/null +++ b/packages/lending-utils/src/elys.rs @@ -0,0 +1,122 @@ +// Since Elys is using an old CosmWasm version, to make it compatible with our packages, we are going to redefine the messages here using Cosmwasm 2.x that we need +// for our library +// The content here is from elys-std 0.1.0 +use cosmwasm_std::{Coin, CustomQuery, DepsMut, StdError, StdResult}; + +#[derive( + ::serde::Serialize, ::serde::Deserialize, Clone, Debug, PartialEq, Eq, ::schemars::JsonSchema, +)] +#[serde(rename_all = "snake_case")] +pub enum ElysQuery { + QueryCommittedTokensLocked { address: String }, + QueryGetPool { pool_id: u64 }, +} + +impl CustomQuery for ElysQuery {} + +#[derive( + ::serde::Serialize, ::serde::Deserialize, Clone, Debug, PartialEq, Eq, ::schemars::JsonSchema, +)] +#[serde(rename_all = "snake_case")] +pub struct QueryGetPoolResponse { + pub pool: ::core::option::Option, +} +#[derive( + ::serde::Serialize, ::serde::Deserialize, Clone, Debug, PartialEq, Eq, ::schemars::JsonSchema, +)] +#[serde(rename_all = "snake_case")] +pub struct PoolResponse { + pub deposit_denom: String, + pub redemption_rate: String, + pub interest_rate: String, + pub interest_rate_max: String, + pub interest_rate_min: String, + pub interest_rate_increase: String, + pub interest_rate_decrease: String, + pub health_gain_factor: String, + pub total_value: String, + pub max_leverage_ratio: String, + pub pool_id: u64, + pub total_deposit: String, + pub total_borrow: String, + pub borrow_ratio: String, + pub max_withdraw_ratio: String, +} +#[derive( + ::serde::Serialize, ::serde::Deserialize, Clone, Debug, PartialEq, Eq, ::schemars::JsonSchema, +)] +#[serde(rename_all = "snake_case")] +pub struct QueryCommittedTokensLockedResponse { + pub address: String, + pub locked_committed: Vec, + pub total_committed: Vec, +} + +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive( + Clone, + PartialEq, + Eq, + ::prost::Message, + ::serde::Serialize, + ::serde::Deserialize, + ::schemars::JsonSchema, +)] +pub struct MsgBond { + #[prost(string, tag = "1")] + pub creator: String, + #[prost(string, tag = "2")] + pub amount: String, + #[prost(uint64, tag = "3")] + #[serde(alias = "poolID")] + pub pool_id: u64, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive( + Clone, + PartialEq, + Eq, + ::prost::Message, + ::serde::Serialize, + ::serde::Deserialize, + ::schemars::JsonSchema, +)] +pub struct MsgUnbond { + #[prost(string, tag = "1")] + pub creator: String, + #[prost(string, tag = "2")] + pub amount: String, + #[prost(uint64, tag = "3")] + #[serde(alias = "poolID")] + pub pool_id: u64, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive( + Clone, + PartialEq, + Eq, + ::prost::Message, + ::serde::Serialize, + ::serde::Deserialize, + ::schemars::JsonSchema, +)] +pub struct MsgClaimRewards { + #[prost(string, tag = "1")] + pub sender: String, + #[prost(uint64, repeated, tag = "2")] + #[serde(alias = "poolIDs")] + pub pool_ids: Vec, +} + +/// Queries pool information by pool ID from the Elys network +pub fn query_pool(deps: &DepsMut, pool_id: u64) -> StdResult { + let query = ElysQuery::QueryGetPool { pool_id }; + let query_pool_response: QueryGetPoolResponse = deps + .querier + .query(&query.into()) + .map_err(|e| StdError::generic_err(format!("Failed to query pool {}: {}", pool_id, e)))?; + let pool = query_pool_response + .pool + .ok_or_else(|| StdError::generic_err(format!("Pool {} not found", pool_id)))?; + Ok(pool) +} diff --git a/packages/lending-utils/src/lib.rs b/packages/lending-utils/src/lib.rs index 1957afaa5..520b408c2 100644 --- a/packages/lending-utils/src/lib.rs +++ b/packages/lending-utils/src/lib.rs @@ -1 +1,2 @@ +pub mod elys; pub mod mars; diff --git a/program-manager/Cargo.toml b/program-manager/Cargo.toml index 26974b32c..d24b263ce 100644 --- a/program-manager/Cargo.toml +++ b/program-manager/Cargo.toml @@ -45,6 +45,7 @@ valence-neutron-ibc-transfer-library = { workspace = true } valence-drop-liquid-staker = { workspace = true } valence-drop-liquid-unstaker = { workspace = true } valence-mars-lending = { workspace = true } +valence-elys-lending = { workspace = true } tokio = { workspace = true } aho-corasick = "1.1" diff --git a/program-manager/schema/valence-program-manager.json b/program-manager/schema/valence-program-manager.json index 885853ea2..5078e259b 100644 --- a/program-manager/schema/valence-program-manager.json +++ b/program-manager/schema/valence-program-manager.json @@ -926,6 +926,18 @@ } }, "additionalProperties": false + }, + { + "type": "object", + "required": [ + "ValenceElysLending" + ], + "properties": { + "ValenceElysLending": { + "$ref": "#/definitions/LibraryConfig15" + } + }, + "additionalProperties": false } ] }, @@ -1054,6 +1066,41 @@ }, "additionalProperties": false }, + "LibraryConfig15": { + "type": "object", + "required": [ + "input_addr", + "output_addr", + "pool_id" + ], + "properties": { + "input_addr": { + "description": "Address of the input account", + "allOf": [ + { + "$ref": "#/definitions/LibraryAccountType" + } + ] + }, + "output_addr": { + "description": "Address of the output account", + "allOf": [ + { + "$ref": "#/definitions/LibraryAccountType" + } + ] + }, + "pool_id": { + "description": "ID of the pool we are going to lend to", + "allOf": [ + { + "$ref": "#/definitions/Uint64" + } + ] + } + }, + "additionalProperties": false + }, "LibraryConfig2": { "description": "Struct representing the library configuration.", "type": "object", @@ -1450,6 +1497,18 @@ } }, "additionalProperties": false + }, + { + "type": "object", + "required": [ + "ValenceElysLending" + ], + "properties": { + "ValenceElysLending": { + "$ref": "#/definitions/LibraryConfigUpdate15" + } + }, + "additionalProperties": false } ] }, @@ -1651,6 +1710,42 @@ }, "additionalProperties": false }, + "LibraryConfigUpdate15": { + "type": "object", + "properties": { + "input_addr": { + "anyOf": [ + { + "$ref": "#/definitions/LibraryAccountType" + }, + { + "type": "null" + } + ] + }, + "output_addr": { + "anyOf": [ + { + "$ref": "#/definitions/LibraryAccountType" + }, + { + "type": "null" + } + ] + }, + "pool_id": { + "anyOf": [ + { + "$ref": "#/definitions/Uint64" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, "LibraryConfigUpdate2": { "type": "object", "properties": { diff --git a/program-manager/src/library.rs b/program-manager/src/library.rs index f86ad8cbe..8d0454efd 100644 --- a/program-manager/src/library.rs +++ b/program-manager/src/library.rs @@ -98,6 +98,7 @@ pub enum LibraryConfig { ValenceDropLiquidStaker(valence_drop_liquid_staker::msg::LibraryConfig), ValenceDropLiquidUnstaker(valence_drop_liquid_unstaker::msg::LibraryConfig), ValenceMarsLending(valence_mars_lending::msg::LibraryConfig), + ValenceElysLending(valence_elys_lending::msg::LibraryConfig), } impl LibraryConfig {