feature: nympool contract (#5464)
* squashed nym-pool commits initialised nym-pool contract and updated all bls12_381 to make it possible create scaffolding for tests ability to control the contract admin introducing contract grants grant type validation basic grant operations + stubs for other messages added queries use transaction stubs added expiration information to grant queries setting initial grant state based on the current environment allowance logic for attempting to spend part of a grant implemented all remaining transactions made public api for coin locking perform validation tests for locked tokens storage nympool storage tests added messages for changing granter set tests and fixes for sufficient tokens when inserting grants tests for initial state + more bugfixes queries tests additional tests for transactions and fixes post rebase fixes updated contract dependencies removed redundant wasm constructor dont ask me why this suddenly became an issue - no clue removed redundant wasm constructor dont ask me why this suddenly became an issue - no clue * missing schema + added nym_pool to the main Makefile
This commit is contained in:
committed by
GitHub
parent
f04cb6f6a6
commit
2de8f8bc21
Generated
+30
@@ -1264,6 +1264,36 @@ dependencies = [
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-pool-contract"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"cosmwasm-schema",
|
||||
"cosmwasm-std",
|
||||
"cw-controllers",
|
||||
"cw-multi-test",
|
||||
"cw-storage-plus",
|
||||
"cw2",
|
||||
"nym-contracts-common",
|
||||
"nym-pool-contract-common",
|
||||
"rand",
|
||||
"rand_chacha",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-pool-contract-common"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"cosmwasm-schema",
|
||||
"cosmwasm-std",
|
||||
"cw-controllers",
|
||||
"schemars",
|
||||
"serde",
|
||||
"thiserror 2.0.12",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-sphinx-types"
|
||||
version = "0.2.0"
|
||||
|
||||
@@ -5,6 +5,7 @@ members = [
|
||||
"ecash",
|
||||
"mixnet",
|
||||
"mixnet-vesting-integration-tests",
|
||||
"nym-pool",
|
||||
"multisig/cw3-flex-multisig",
|
||||
"multisig/cw4-group",
|
||||
"vesting",
|
||||
@@ -46,6 +47,8 @@ cw3-fixed-multisig = "=2.0.0"
|
||||
cw4 = "=2.0.0"
|
||||
cw20 = "=2.0.0"
|
||||
cw20-base = "2.0.0"
|
||||
rand = "0.8.5"
|
||||
rand_chacha = "0.3.1"
|
||||
semver = "1.0.21"
|
||||
serde = "1.0.196"
|
||||
sylvia = "1.3.3"
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
[alias]
|
||||
wasm = "build --release --lib --target wasm32-unknown-unknown"
|
||||
unit-test = "test --lib"
|
||||
schema = "run --bin schema --features=schema-gen"
|
||||
@@ -0,0 +1,37 @@
|
||||
[package]
|
||||
name = "nym-pool-contract"
|
||||
version = "0.1.0"
|
||||
edition = { workspace = true }
|
||||
authors = { workspace = true }
|
||||
license = { workspace = true }
|
||||
repository = { workspace = true }
|
||||
|
||||
[[bin]]
|
||||
name = "schema"
|
||||
required-features = ["schema-gen"]
|
||||
|
||||
[lib]
|
||||
name = "nym_pool_contract"
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[dependencies]
|
||||
cosmwasm-std = { workspace = true }
|
||||
cw2 = { workspace = true }
|
||||
cw-storage-plus = { workspace = true }
|
||||
cw-controllers = { workspace = true }
|
||||
|
||||
cosmwasm-schema = { workspace = true, optional = true }
|
||||
|
||||
nym-contracts-common = { path = "../../common/cosmwasm-smart-contracts/contracts-common" }
|
||||
nym-pool-contract-common = { path = "../../common/cosmwasm-smart-contracts/nym-pool-contract" }
|
||||
|
||||
|
||||
[dev-dependencies]
|
||||
anyhow = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
rand_chacha = { workspace = true }
|
||||
rand = { workspace = true }
|
||||
cw-multi-test = { workspace = true }
|
||||
|
||||
[features]
|
||||
schema-gen = ["nym-pool-contract-common/schema", "cosmwasm-schema"]
|
||||
@@ -0,0 +1,5 @@
|
||||
wasm:
|
||||
RUSTFLAGS='-C link-arg=-s' cargo build --release --target wasm32-unknown-unknown
|
||||
|
||||
generate-schema:
|
||||
cargo schema
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,544 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "ExecuteMsg",
|
||||
"oneOf": [
|
||||
{
|
||||
"description": "Change the admin",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"update_admin"
|
||||
],
|
||||
"properties": {
|
||||
"update_admin": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"admin"
|
||||
],
|
||||
"properties": {
|
||||
"admin": {
|
||||
"type": "string"
|
||||
},
|
||||
"update_granter_set": {
|
||||
"type": [
|
||||
"boolean",
|
||||
"null"
|
||||
]
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"description": "Attempt to grant new allowance to the specified grantee",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"grant_allowance"
|
||||
],
|
||||
"properties": {
|
||||
"grant_allowance": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"allowance",
|
||||
"grantee"
|
||||
],
|
||||
"properties": {
|
||||
"allowance": {
|
||||
"$ref": "#/definitions/Allowance"
|
||||
},
|
||||
"grantee": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"description": "Attempt to revoke previously granted allowance",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"revoke_allowance"
|
||||
],
|
||||
"properties": {
|
||||
"revoke_allowance": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"grantee"
|
||||
],
|
||||
"properties": {
|
||||
"grantee": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"description": "Attempt to use allowance",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"use_allowance"
|
||||
],
|
||||
"properties": {
|
||||
"use_allowance": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"recipients"
|
||||
],
|
||||
"properties": {
|
||||
"recipients": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/TransferRecipient"
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"description": "Attempt to withdraw the specified amount into the grantee's account",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"withdraw_allowance"
|
||||
],
|
||||
"properties": {
|
||||
"withdraw_allowance": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"amount"
|
||||
],
|
||||
"properties": {
|
||||
"amount": {
|
||||
"$ref": "#/definitions/Coin"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"description": "Attempt to lock part of existing allowance for future use",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"lock_allowance"
|
||||
],
|
||||
"properties": {
|
||||
"lock_allowance": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"amount"
|
||||
],
|
||||
"properties": {
|
||||
"amount": {
|
||||
"$ref": "#/definitions/Coin"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"description": "Attempt to unlock previously locked allowance",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"unlock_allowance"
|
||||
],
|
||||
"properties": {
|
||||
"unlock_allowance": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"amount"
|
||||
],
|
||||
"properties": {
|
||||
"amount": {
|
||||
"$ref": "#/definitions/Coin"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"description": "Attempt to use part of the locked allowance",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"use_locked_allowance"
|
||||
],
|
||||
"properties": {
|
||||
"use_locked_allowance": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"recipients"
|
||||
],
|
||||
"properties": {
|
||||
"recipients": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/TransferRecipient"
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"description": "Attempt to withdraw the specified amount of locked tokens into the grantee's account",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"withdraw_locked_allowance"
|
||||
],
|
||||
"properties": {
|
||||
"withdraw_locked_allowance": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"amount"
|
||||
],
|
||||
"properties": {
|
||||
"amount": {
|
||||
"$ref": "#/definitions/Coin"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"description": "Attempt to add a new account to the permitted set of grant granters",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"add_new_granter"
|
||||
],
|
||||
"properties": {
|
||||
"add_new_granter": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"granter"
|
||||
],
|
||||
"properties": {
|
||||
"granter": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"description": "Revoke the provided account from the permitted set of granters",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"revoke_granter"
|
||||
],
|
||||
"properties": {
|
||||
"revoke_granter": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"granter"
|
||||
],
|
||||
"properties": {
|
||||
"granter": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"description": "Attempt to remove expired grant from the storage and unlock (if any) locked tokens",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"remove_expired_grant"
|
||||
],
|
||||
"properties": {
|
||||
"remove_expired_grant": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"grantee"
|
||||
],
|
||||
"properties": {
|
||||
"grantee": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
],
|
||||
"definitions": {
|
||||
"Allowance": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"basic"
|
||||
],
|
||||
"properties": {
|
||||
"basic": {
|
||||
"$ref": "#/definitions/BasicAllowance"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"classic_periodic"
|
||||
],
|
||||
"properties": {
|
||||
"classic_periodic": {
|
||||
"$ref": "#/definitions/ClassicPeriodicAllowance"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"cumulative_periodic"
|
||||
],
|
||||
"properties": {
|
||||
"cumulative_periodic": {
|
||||
"$ref": "#/definitions/CumulativePeriodicAllowance"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"delayed"
|
||||
],
|
||||
"properties": {
|
||||
"delayed": {
|
||||
"$ref": "#/definitions/DelayedAllowance"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"BasicAllowance": {
|
||||
"description": "BasicAllowance is an allowance with a one-time grant of coins that optionally expires. The grantee can use up to SpendLimit to cover fees.",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"expiration_unix_timestamp": {
|
||||
"description": "expiration specifies an optional time when this allowance expires",
|
||||
"type": [
|
||||
"integer",
|
||||
"null"
|
||||
],
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"spend_limit": {
|
||||
"description": "spend_limit specifies the maximum amount of coins that can be spent by this allowance and will be updated as coins are spent. If it is empty, there is no spend limit and any amount of coins can be spent.",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Coin"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"ClassicPeriodicAllowance": {
|
||||
"description": "ClassicPeriodicAllowance extends BasicAllowance to allow for both a maximum cap, as well as a limit per time period.",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"basic",
|
||||
"period_duration_secs",
|
||||
"period_spend_limit"
|
||||
],
|
||||
"properties": {
|
||||
"basic": {
|
||||
"description": "basic specifies a struct of `BasicAllowance`",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/BasicAllowance"
|
||||
}
|
||||
]
|
||||
},
|
||||
"period_can_spend": {
|
||||
"description": "period_can_spend is the number of coins left to be spent before the period_reset time",
|
||||
"default": null,
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Coin"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"period_duration_secs": {
|
||||
"description": "period_duration_secs specifies the time duration in which period_spend_limit coins can be spent before that allowance is reset",
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"period_reset_unix_timestamp": {
|
||||
"description": "period_reset is the time at which this period resets and a new one begins, it is calculated from the start time of the first transaction after the last period ended",
|
||||
"default": 0,
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"period_spend_limit": {
|
||||
"description": "period_spend_limit specifies the maximum number of coins that can be spent in the period",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Coin"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"Coin": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"amount",
|
||||
"denom"
|
||||
],
|
||||
"properties": {
|
||||
"amount": {
|
||||
"$ref": "#/definitions/Uint128"
|
||||
},
|
||||
"denom": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"CumulativePeriodicAllowance": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"basic",
|
||||
"period_duration_secs",
|
||||
"period_grant"
|
||||
],
|
||||
"properties": {
|
||||
"accumulation_limit": {
|
||||
"description": "accumulation_limit is the maximum value the grants and accumulate to",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Coin"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"basic": {
|
||||
"description": "basic specifies a struct of `BasicAllowance`",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/BasicAllowance"
|
||||
}
|
||||
]
|
||||
},
|
||||
"last_grant_applied_unix_timestamp": {
|
||||
"description": "last_grant_applied is the time at which last transaction associated with this allowance has been sent and `spendable` value has been adjusted",
|
||||
"default": 0,
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"period_duration_secs": {
|
||||
"description": "period_duration_secs specifies the time duration in which spendable coins can be spent before that allowance is incremented",
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"period_grant": {
|
||||
"description": "period_grant specifies the maximum number of coins that is granted per period",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Coin"
|
||||
}
|
||||
]
|
||||
},
|
||||
"spendable": {
|
||||
"description": "spendable is the number of coins left to be spent before additional grant is applied",
|
||||
"default": null,
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Coin"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"DelayedAllowance": {
|
||||
"description": "Create a grant to allow somebody to withdraw from the pool only after the specified time. For example, we could create a grant for mixnet rewarding/testing/etc However, if the required work has not been completed, the grant could be revoked before it's withdrawn",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"available_at_unix_timestamp",
|
||||
"basic"
|
||||
],
|
||||
"properties": {
|
||||
"available_at_unix_timestamp": {
|
||||
"description": "available_at specifies when this allowance is going to become usable",
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"basic": {
|
||||
"description": "basic specifies a struct of `BasicAllowance`",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/BasicAllowance"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"TransferRecipient": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"amount",
|
||||
"recipient"
|
||||
],
|
||||
"properties": {
|
||||
"amount": {
|
||||
"$ref": "#/definitions/Coin"
|
||||
},
|
||||
"recipient": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"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"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,262 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "InstantiateMsg",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"grants",
|
||||
"pool_denomination"
|
||||
],
|
||||
"properties": {
|
||||
"grants": {
|
||||
"description": "Initial map of grants to be created at instantiation",
|
||||
"type": "object",
|
||||
"additionalProperties": false
|
||||
},
|
||||
"pool_denomination": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"definitions": {
|
||||
"Allowance": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"basic"
|
||||
],
|
||||
"properties": {
|
||||
"basic": {
|
||||
"$ref": "#/definitions/BasicAllowance"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"classic_periodic"
|
||||
],
|
||||
"properties": {
|
||||
"classic_periodic": {
|
||||
"$ref": "#/definitions/ClassicPeriodicAllowance"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"cumulative_periodic"
|
||||
],
|
||||
"properties": {
|
||||
"cumulative_periodic": {
|
||||
"$ref": "#/definitions/CumulativePeriodicAllowance"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"delayed"
|
||||
],
|
||||
"properties": {
|
||||
"delayed": {
|
||||
"$ref": "#/definitions/DelayedAllowance"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"BasicAllowance": {
|
||||
"description": "BasicAllowance is an allowance with a one-time grant of coins that optionally expires. The grantee can use up to SpendLimit to cover fees.",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"expiration_unix_timestamp": {
|
||||
"description": "expiration specifies an optional time when this allowance expires",
|
||||
"type": [
|
||||
"integer",
|
||||
"null"
|
||||
],
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"spend_limit": {
|
||||
"description": "spend_limit specifies the maximum amount of coins that can be spent by this allowance and will be updated as coins are spent. If it is empty, there is no spend limit and any amount of coins can be spent.",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Coin"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"ClassicPeriodicAllowance": {
|
||||
"description": "ClassicPeriodicAllowance extends BasicAllowance to allow for both a maximum cap, as well as a limit per time period.",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"basic",
|
||||
"period_duration_secs",
|
||||
"period_spend_limit"
|
||||
],
|
||||
"properties": {
|
||||
"basic": {
|
||||
"description": "basic specifies a struct of `BasicAllowance`",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/BasicAllowance"
|
||||
}
|
||||
]
|
||||
},
|
||||
"period_can_spend": {
|
||||
"description": "period_can_spend is the number of coins left to be spent before the period_reset time",
|
||||
"default": null,
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Coin"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"period_duration_secs": {
|
||||
"description": "period_duration_secs specifies the time duration in which period_spend_limit coins can be spent before that allowance is reset",
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"period_reset_unix_timestamp": {
|
||||
"description": "period_reset is the time at which this period resets and a new one begins, it is calculated from the start time of the first transaction after the last period ended",
|
||||
"default": 0,
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"period_spend_limit": {
|
||||
"description": "period_spend_limit specifies the maximum number of coins that can be spent in the period",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Coin"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"Coin": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"amount",
|
||||
"denom"
|
||||
],
|
||||
"properties": {
|
||||
"amount": {
|
||||
"$ref": "#/definitions/Uint128"
|
||||
},
|
||||
"denom": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"CumulativePeriodicAllowance": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"basic",
|
||||
"period_duration_secs",
|
||||
"period_grant"
|
||||
],
|
||||
"properties": {
|
||||
"accumulation_limit": {
|
||||
"description": "accumulation_limit is the maximum value the grants and accumulate to",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Coin"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"basic": {
|
||||
"description": "basic specifies a struct of `BasicAllowance`",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/BasicAllowance"
|
||||
}
|
||||
]
|
||||
},
|
||||
"last_grant_applied_unix_timestamp": {
|
||||
"description": "last_grant_applied is the time at which last transaction associated with this allowance has been sent and `spendable` value has been adjusted",
|
||||
"default": 0,
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"period_duration_secs": {
|
||||
"description": "period_duration_secs specifies the time duration in which spendable coins can be spent before that allowance is incremented",
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"period_grant": {
|
||||
"description": "period_grant specifies the maximum number of coins that is granted per period",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Coin"
|
||||
}
|
||||
]
|
||||
},
|
||||
"spendable": {
|
||||
"description": "spendable is the number of coins left to be spent before additional grant is applied",
|
||||
"default": null,
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Coin"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"DelayedAllowance": {
|
||||
"description": "Create a grant to allow somebody to withdraw from the pool only after the specified time. For example, we could create a grant for mixnet rewarding/testing/etc However, if the required work has not been completed, the grant could be revoked before it's withdrawn",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"available_at_unix_timestamp",
|
||||
"basic"
|
||||
],
|
||||
"properties": {
|
||||
"available_at_unix_timestamp": {
|
||||
"description": "available_at specifies when this allowance is going to become usable",
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"basic": {
|
||||
"description": "basic specifies a struct of `BasicAllowance`",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/BasicAllowance"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"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"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "MigrateMsg",
|
||||
"type": "object",
|
||||
"additionalProperties": false
|
||||
}
|
||||
@@ -0,0 +1,201 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "QueryMsg",
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"admin"
|
||||
],
|
||||
"properties": {
|
||||
"admin": {
|
||||
"type": "object",
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"get_available_tokens"
|
||||
],
|
||||
"properties": {
|
||||
"get_available_tokens": {
|
||||
"type": "object",
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"get_total_locked_tokens"
|
||||
],
|
||||
"properties": {
|
||||
"get_total_locked_tokens": {
|
||||
"type": "object",
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"get_locked_tokens"
|
||||
],
|
||||
"properties": {
|
||||
"get_locked_tokens": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"grantee"
|
||||
],
|
||||
"properties": {
|
||||
"grantee": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"get_grant"
|
||||
],
|
||||
"properties": {
|
||||
"get_grant": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"grantee"
|
||||
],
|
||||
"properties": {
|
||||
"grantee": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"get_granter"
|
||||
],
|
||||
"properties": {
|
||||
"get_granter": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"granter"
|
||||
],
|
||||
"properties": {
|
||||
"granter": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"get_locked_tokens_paged"
|
||||
],
|
||||
"properties": {
|
||||
"get_locked_tokens_paged": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"limit": {
|
||||
"description": "Controls the maximum number of entries returned by the query. Note that too large values will be overwritten by a saner default.",
|
||||
"type": [
|
||||
"integer",
|
||||
"null"
|
||||
],
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"start_after": {
|
||||
"description": "Pagination control for the values returned by the query. Note that the provided value itself will **not** be used for the response.",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"get_granters_paged"
|
||||
],
|
||||
"properties": {
|
||||
"get_granters_paged": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"limit": {
|
||||
"description": "Controls the maximum number of entries returned by the query. Note that too large values will be overwritten by a saner default.",
|
||||
"type": [
|
||||
"integer",
|
||||
"null"
|
||||
],
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"start_after": {
|
||||
"description": "Pagination control for the values returned by the query. Note that the provided value itself will **not** be used for the response.",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"get_grants_paged"
|
||||
],
|
||||
"properties": {
|
||||
"get_grants_paged": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"limit": {
|
||||
"description": "Controls the maximum number of entries returned by the query. Note that too large values will be overwritten by a saner default.",
|
||||
"type": [
|
||||
"integer",
|
||||
"null"
|
||||
],
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"start_after": {
|
||||
"description": "Pagination control for the values returned by the query. Note that the provided value itself will **not** be used for the response.",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "AdminResponse",
|
||||
"description": "Returned from Admin.query_admin()",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"admin": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "AvailableTokensResponse",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"available"
|
||||
],
|
||||
"properties": {
|
||||
"available": {
|
||||
"$ref": "#/definitions/Coin"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"definitions": {
|
||||
"Coin": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"amount",
|
||||
"denom"
|
||||
],
|
||||
"properties": {
|
||||
"amount": {
|
||||
"$ref": "#/definitions/Uint128"
|
||||
},
|
||||
"denom": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"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"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,312 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "GrantResponse",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"grantee"
|
||||
],
|
||||
"properties": {
|
||||
"grant": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/GrantInformation"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"grantee": {
|
||||
"$ref": "#/definitions/Addr"
|
||||
}
|
||||
},
|
||||
"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"
|
||||
},
|
||||
"Allowance": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"basic"
|
||||
],
|
||||
"properties": {
|
||||
"basic": {
|
||||
"$ref": "#/definitions/BasicAllowance"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"classic_periodic"
|
||||
],
|
||||
"properties": {
|
||||
"classic_periodic": {
|
||||
"$ref": "#/definitions/ClassicPeriodicAllowance"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"cumulative_periodic"
|
||||
],
|
||||
"properties": {
|
||||
"cumulative_periodic": {
|
||||
"$ref": "#/definitions/CumulativePeriodicAllowance"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"delayed"
|
||||
],
|
||||
"properties": {
|
||||
"delayed": {
|
||||
"$ref": "#/definitions/DelayedAllowance"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"BasicAllowance": {
|
||||
"description": "BasicAllowance is an allowance with a one-time grant of coins that optionally expires. The grantee can use up to SpendLimit to cover fees.",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"expiration_unix_timestamp": {
|
||||
"description": "expiration specifies an optional time when this allowance expires",
|
||||
"type": [
|
||||
"integer",
|
||||
"null"
|
||||
],
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"spend_limit": {
|
||||
"description": "spend_limit specifies the maximum amount of coins that can be spent by this allowance and will be updated as coins are spent. If it is empty, there is no spend limit and any amount of coins can be spent.",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Coin"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"ClassicPeriodicAllowance": {
|
||||
"description": "ClassicPeriodicAllowance extends BasicAllowance to allow for both a maximum cap, as well as a limit per time period.",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"basic",
|
||||
"period_duration_secs",
|
||||
"period_spend_limit"
|
||||
],
|
||||
"properties": {
|
||||
"basic": {
|
||||
"description": "basic specifies a struct of `BasicAllowance`",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/BasicAllowance"
|
||||
}
|
||||
]
|
||||
},
|
||||
"period_can_spend": {
|
||||
"description": "period_can_spend is the number of coins left to be spent before the period_reset time",
|
||||
"default": null,
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Coin"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"period_duration_secs": {
|
||||
"description": "period_duration_secs specifies the time duration in which period_spend_limit coins can be spent before that allowance is reset",
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"period_reset_unix_timestamp": {
|
||||
"description": "period_reset is the time at which this period resets and a new one begins, it is calculated from the start time of the first transaction after the last period ended",
|
||||
"default": 0,
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"period_spend_limit": {
|
||||
"description": "period_spend_limit specifies the maximum number of coins that can be spent in the period",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Coin"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"Coin": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"amount",
|
||||
"denom"
|
||||
],
|
||||
"properties": {
|
||||
"amount": {
|
||||
"$ref": "#/definitions/Uint128"
|
||||
},
|
||||
"denom": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"CumulativePeriodicAllowance": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"basic",
|
||||
"period_duration_secs",
|
||||
"period_grant"
|
||||
],
|
||||
"properties": {
|
||||
"accumulation_limit": {
|
||||
"description": "accumulation_limit is the maximum value the grants and accumulate to",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Coin"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"basic": {
|
||||
"description": "basic specifies a struct of `BasicAllowance`",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/BasicAllowance"
|
||||
}
|
||||
]
|
||||
},
|
||||
"last_grant_applied_unix_timestamp": {
|
||||
"description": "last_grant_applied is the time at which last transaction associated with this allowance has been sent and `spendable` value has been adjusted",
|
||||
"default": 0,
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"period_duration_secs": {
|
||||
"description": "period_duration_secs specifies the time duration in which spendable coins can be spent before that allowance is incremented",
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"period_grant": {
|
||||
"description": "period_grant specifies the maximum number of coins that is granted per period",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Coin"
|
||||
}
|
||||
]
|
||||
},
|
||||
"spendable": {
|
||||
"description": "spendable is the number of coins left to be spent before additional grant is applied",
|
||||
"default": null,
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Coin"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"DelayedAllowance": {
|
||||
"description": "Create a grant to allow somebody to withdraw from the pool only after the specified time. For example, we could create a grant for mixnet rewarding/testing/etc However, if the required work has not been completed, the grant could be revoked before it's withdrawn",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"available_at_unix_timestamp",
|
||||
"basic"
|
||||
],
|
||||
"properties": {
|
||||
"available_at_unix_timestamp": {
|
||||
"description": "available_at specifies when this allowance is going to become usable",
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"basic": {
|
||||
"description": "basic specifies a struct of `BasicAllowance`",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/BasicAllowance"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"Grant": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"allowance",
|
||||
"granted_at_height",
|
||||
"grantee",
|
||||
"granter"
|
||||
],
|
||||
"properties": {
|
||||
"allowance": {
|
||||
"$ref": "#/definitions/Allowance"
|
||||
},
|
||||
"granted_at_height": {
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"grantee": {
|
||||
"$ref": "#/definitions/Addr"
|
||||
},
|
||||
"granter": {
|
||||
"$ref": "#/definitions/Addr"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"GrantInformation": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"expired",
|
||||
"grant"
|
||||
],
|
||||
"properties": {
|
||||
"expired": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"grant": {
|
||||
"$ref": "#/definitions/Grant"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"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"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "GranterResponse",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"granter"
|
||||
],
|
||||
"properties": {
|
||||
"granter": {
|
||||
"$ref": "#/definitions/Addr"
|
||||
},
|
||||
"information": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/GranterInformation"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"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"
|
||||
},
|
||||
"GranterInformation": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"created_at_height",
|
||||
"created_by"
|
||||
],
|
||||
"properties": {
|
||||
"created_at_height": {
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"created_by": {
|
||||
"$ref": "#/definitions/Addr"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "GrantersPagedResponse",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"granters"
|
||||
],
|
||||
"properties": {
|
||||
"granters": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/GranterDetails"
|
||||
}
|
||||
},
|
||||
"start_next_after": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
}
|
||||
},
|
||||
"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"
|
||||
},
|
||||
"GranterDetails": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"granter",
|
||||
"information"
|
||||
],
|
||||
"properties": {
|
||||
"granter": {
|
||||
"$ref": "#/definitions/Addr"
|
||||
},
|
||||
"information": {
|
||||
"$ref": "#/definitions/GranterInformation"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"GranterInformation": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"created_at_height",
|
||||
"created_by"
|
||||
],
|
||||
"properties": {
|
||||
"created_at_height": {
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"created_by": {
|
||||
"$ref": "#/definitions/Addr"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,311 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "GrantsPagedResponse",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"grants"
|
||||
],
|
||||
"properties": {
|
||||
"grants": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/GrantInformation"
|
||||
}
|
||||
},
|
||||
"start_next_after": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
}
|
||||
},
|
||||
"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"
|
||||
},
|
||||
"Allowance": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"basic"
|
||||
],
|
||||
"properties": {
|
||||
"basic": {
|
||||
"$ref": "#/definitions/BasicAllowance"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"classic_periodic"
|
||||
],
|
||||
"properties": {
|
||||
"classic_periodic": {
|
||||
"$ref": "#/definitions/ClassicPeriodicAllowance"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"cumulative_periodic"
|
||||
],
|
||||
"properties": {
|
||||
"cumulative_periodic": {
|
||||
"$ref": "#/definitions/CumulativePeriodicAllowance"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"delayed"
|
||||
],
|
||||
"properties": {
|
||||
"delayed": {
|
||||
"$ref": "#/definitions/DelayedAllowance"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"BasicAllowance": {
|
||||
"description": "BasicAllowance is an allowance with a one-time grant of coins that optionally expires. The grantee can use up to SpendLimit to cover fees.",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"expiration_unix_timestamp": {
|
||||
"description": "expiration specifies an optional time when this allowance expires",
|
||||
"type": [
|
||||
"integer",
|
||||
"null"
|
||||
],
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"spend_limit": {
|
||||
"description": "spend_limit specifies the maximum amount of coins that can be spent by this allowance and will be updated as coins are spent. If it is empty, there is no spend limit and any amount of coins can be spent.",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Coin"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"ClassicPeriodicAllowance": {
|
||||
"description": "ClassicPeriodicAllowance extends BasicAllowance to allow for both a maximum cap, as well as a limit per time period.",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"basic",
|
||||
"period_duration_secs",
|
||||
"period_spend_limit"
|
||||
],
|
||||
"properties": {
|
||||
"basic": {
|
||||
"description": "basic specifies a struct of `BasicAllowance`",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/BasicAllowance"
|
||||
}
|
||||
]
|
||||
},
|
||||
"period_can_spend": {
|
||||
"description": "period_can_spend is the number of coins left to be spent before the period_reset time",
|
||||
"default": null,
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Coin"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"period_duration_secs": {
|
||||
"description": "period_duration_secs specifies the time duration in which period_spend_limit coins can be spent before that allowance is reset",
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"period_reset_unix_timestamp": {
|
||||
"description": "period_reset is the time at which this period resets and a new one begins, it is calculated from the start time of the first transaction after the last period ended",
|
||||
"default": 0,
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"period_spend_limit": {
|
||||
"description": "period_spend_limit specifies the maximum number of coins that can be spent in the period",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Coin"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"Coin": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"amount",
|
||||
"denom"
|
||||
],
|
||||
"properties": {
|
||||
"amount": {
|
||||
"$ref": "#/definitions/Uint128"
|
||||
},
|
||||
"denom": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"CumulativePeriodicAllowance": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"basic",
|
||||
"period_duration_secs",
|
||||
"period_grant"
|
||||
],
|
||||
"properties": {
|
||||
"accumulation_limit": {
|
||||
"description": "accumulation_limit is the maximum value the grants and accumulate to",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Coin"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"basic": {
|
||||
"description": "basic specifies a struct of `BasicAllowance`",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/BasicAllowance"
|
||||
}
|
||||
]
|
||||
},
|
||||
"last_grant_applied_unix_timestamp": {
|
||||
"description": "last_grant_applied is the time at which last transaction associated with this allowance has been sent and `spendable` value has been adjusted",
|
||||
"default": 0,
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"period_duration_secs": {
|
||||
"description": "period_duration_secs specifies the time duration in which spendable coins can be spent before that allowance is incremented",
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"period_grant": {
|
||||
"description": "period_grant specifies the maximum number of coins that is granted per period",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Coin"
|
||||
}
|
||||
]
|
||||
},
|
||||
"spendable": {
|
||||
"description": "spendable is the number of coins left to be spent before additional grant is applied",
|
||||
"default": null,
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Coin"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"DelayedAllowance": {
|
||||
"description": "Create a grant to allow somebody to withdraw from the pool only after the specified time. For example, we could create a grant for mixnet rewarding/testing/etc However, if the required work has not been completed, the grant could be revoked before it's withdrawn",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"available_at_unix_timestamp",
|
||||
"basic"
|
||||
],
|
||||
"properties": {
|
||||
"available_at_unix_timestamp": {
|
||||
"description": "available_at specifies when this allowance is going to become usable",
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"basic": {
|
||||
"description": "basic specifies a struct of `BasicAllowance`",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/BasicAllowance"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"Grant": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"allowance",
|
||||
"granted_at_height",
|
||||
"grantee",
|
||||
"granter"
|
||||
],
|
||||
"properties": {
|
||||
"allowance": {
|
||||
"$ref": "#/definitions/Allowance"
|
||||
},
|
||||
"granted_at_height": {
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"grantee": {
|
||||
"$ref": "#/definitions/Addr"
|
||||
},
|
||||
"granter": {
|
||||
"$ref": "#/definitions/Addr"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"GrantInformation": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"expired",
|
||||
"grant"
|
||||
],
|
||||
"properties": {
|
||||
"expired": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"grant": {
|
||||
"$ref": "#/definitions/Grant"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"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"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "LockedTokensResponse",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"grantee"
|
||||
],
|
||||
"properties": {
|
||||
"grantee": {
|
||||
"$ref": "#/definitions/Addr"
|
||||
},
|
||||
"locked": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Coin"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"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"
|
||||
},
|
||||
"Coin": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"amount",
|
||||
"denom"
|
||||
],
|
||||
"properties": {
|
||||
"amount": {
|
||||
"$ref": "#/definitions/Uint128"
|
||||
},
|
||||
"denom": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"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"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "LockedTokensPagedResponse",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"locked"
|
||||
],
|
||||
"properties": {
|
||||
"locked": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/LockedTokens"
|
||||
}
|
||||
},
|
||||
"start_next_after": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
}
|
||||
},
|
||||
"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"
|
||||
},
|
||||
"Coin": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"amount",
|
||||
"denom"
|
||||
],
|
||||
"properties": {
|
||||
"amount": {
|
||||
"$ref": "#/definitions/Uint128"
|
||||
},
|
||||
"denom": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"LockedTokens": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"grantee",
|
||||
"locked"
|
||||
],
|
||||
"properties": {
|
||||
"grantee": {
|
||||
"$ref": "#/definitions/Addr"
|
||||
},
|
||||
"locked": {
|
||||
"$ref": "#/definitions/Coin"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"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"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "TotalLockedTokensResponse",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"locked"
|
||||
],
|
||||
"properties": {
|
||||
"locked": {
|
||||
"$ref": "#/definitions/Coin"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"definitions": {
|
||||
"Coin": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"amount",
|
||||
"denom"
|
||||
],
|
||||
"properties": {
|
||||
"amount": {
|
||||
"$ref": "#/definitions/Uint128"
|
||||
},
|
||||
"denom": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"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"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use cosmwasm_schema::write_api;
|
||||
use nym_pool_contract_common::{ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg};
|
||||
|
||||
fn main() {
|
||||
write_api! {
|
||||
instantiate: InstantiateMsg,
|
||||
query: QueryMsg,
|
||||
execute: ExecuteMsg,
|
||||
migrate: MigrateMsg,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,327 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::queries::{
|
||||
query_admin, query_available_tokens, query_grant, query_granter, query_granters_paged,
|
||||
query_grants_paged, query_locked_tokens, query_locked_tokens_paged, query_total_locked_tokens,
|
||||
};
|
||||
use crate::storage::NYM_POOL_STORAGE;
|
||||
use crate::transactions::{
|
||||
try_add_new_granter, try_grant_allowance, try_lock_allowance, try_remove_expired,
|
||||
try_revoke_grant, try_revoke_granter, try_unlock_allowance, try_update_contract_admin,
|
||||
try_use_allowance, try_use_locked_allowance, try_withdraw_allowance,
|
||||
try_withdraw_locked_allowance,
|
||||
};
|
||||
use cosmwasm_std::{
|
||||
entry_point, to_json_binary, Binary, Deps, DepsMut, Env, MessageInfo, Response,
|
||||
};
|
||||
use nym_contracts_common::set_build_information;
|
||||
use nym_pool_contract_common::{
|
||||
ExecuteMsg, InstantiateMsg, MigrateMsg, NymPoolContractError, QueryMsg,
|
||||
};
|
||||
|
||||
const CONTRACT_NAME: &str = "crate:nym-pool-contract";
|
||||
const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
|
||||
#[entry_point]
|
||||
pub fn instantiate(
|
||||
deps: DepsMut,
|
||||
env: Env,
|
||||
info: MessageInfo,
|
||||
msg: InstantiateMsg,
|
||||
) -> Result<Response, NymPoolContractError> {
|
||||
cw2::set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?;
|
||||
set_build_information!(deps.storage)?;
|
||||
|
||||
NYM_POOL_STORAGE.initialise(deps, env, info.sender, &msg.pool_denomination, msg.grants)?;
|
||||
|
||||
Ok(Response::default())
|
||||
}
|
||||
|
||||
#[entry_point]
|
||||
pub fn execute(
|
||||
deps: DepsMut,
|
||||
env: Env,
|
||||
info: MessageInfo,
|
||||
msg: ExecuteMsg,
|
||||
) -> Result<Response, NymPoolContractError> {
|
||||
match msg {
|
||||
ExecuteMsg::UpdateAdmin {
|
||||
admin,
|
||||
update_granter_set,
|
||||
} => try_update_contract_admin(deps, env, info, admin, update_granter_set),
|
||||
ExecuteMsg::GrantAllowance { grantee, allowance } => {
|
||||
try_grant_allowance(deps, env, info, grantee, *allowance)
|
||||
}
|
||||
ExecuteMsg::RevokeAllowance { grantee } => try_revoke_grant(deps, env, info, grantee),
|
||||
ExecuteMsg::UseAllowance { recipients } => try_use_allowance(deps, env, info, recipients),
|
||||
ExecuteMsg::WithdrawAllowance { amount } => try_withdraw_allowance(deps, env, info, amount),
|
||||
ExecuteMsg::LockAllowance { amount } => try_lock_allowance(deps, env, info, amount),
|
||||
ExecuteMsg::UnlockAllowance { amount } => try_unlock_allowance(deps, env, info, amount),
|
||||
ExecuteMsg::UseLockedAllowance { recipients } => {
|
||||
try_use_locked_allowance(deps, env, info, recipients)
|
||||
}
|
||||
ExecuteMsg::WithdrawLockedAllowance { amount } => {
|
||||
try_withdraw_locked_allowance(deps, env, info, amount)
|
||||
}
|
||||
ExecuteMsg::AddNewGranter { granter } => try_add_new_granter(deps, env, info, granter),
|
||||
ExecuteMsg::RevokeGranter { granter } => try_revoke_granter(deps, env, info, granter),
|
||||
ExecuteMsg::RemoveExpiredGrant { grantee } => try_remove_expired(deps, env, info, grantee),
|
||||
}
|
||||
}
|
||||
|
||||
#[entry_point]
|
||||
pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> Result<Binary, NymPoolContractError> {
|
||||
match msg {
|
||||
QueryMsg::Admin {} => Ok(to_json_binary(&query_admin(deps)?)?),
|
||||
QueryMsg::GetAvailableTokens {} => Ok(to_json_binary(&query_available_tokens(deps, env)?)?),
|
||||
QueryMsg::GetTotalLockedTokens {} => Ok(to_json_binary(&query_total_locked_tokens(deps)?)?),
|
||||
QueryMsg::GetLockedTokens { grantee } => {
|
||||
Ok(to_json_binary(&query_locked_tokens(deps, grantee)?)?)
|
||||
}
|
||||
QueryMsg::GetLockedTokensPaged { limit, start_after } => Ok(to_json_binary(
|
||||
&query_locked_tokens_paged(deps, limit, start_after)?,
|
||||
)?),
|
||||
QueryMsg::GetGrant { grantee } => Ok(to_json_binary(&query_grant(deps, env, grantee)?)?),
|
||||
QueryMsg::GetGranter { granter } => Ok(to_json_binary(&query_granter(deps, granter)?)?),
|
||||
QueryMsg::GetGrantersPaged { limit, start_after } => Ok(to_json_binary(
|
||||
&query_granters_paged(deps, limit, start_after)?,
|
||||
)?),
|
||||
QueryMsg::GetGrantsPaged { limit, start_after } => Ok(to_json_binary(
|
||||
&query_grants_paged(deps, env, limit, start_after)?,
|
||||
)?),
|
||||
}
|
||||
}
|
||||
|
||||
#[entry_point]
|
||||
pub fn migrate(
|
||||
deps: DepsMut,
|
||||
_env: Env,
|
||||
_msg: MigrateMsg,
|
||||
) -> Result<Response, NymPoolContractError> {
|
||||
set_build_information!(deps.storage)?;
|
||||
cw2::ensure_from_older_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?;
|
||||
|
||||
Ok(Default::default())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[cfg(test)]
|
||||
mod contract_instantiaton {
|
||||
use super::*;
|
||||
use crate::storage::NYM_POOL_STORAGE;
|
||||
use crate::testing::TEST_DENOM;
|
||||
use cosmwasm_std::testing::{message_info, mock_dependencies, mock_env};
|
||||
|
||||
#[test]
|
||||
fn sets_contract_admin_to_the_message_sender() -> anyhow::Result<()> {
|
||||
let mut deps = mock_dependencies();
|
||||
let env = mock_env();
|
||||
let init_msg = InstantiateMsg {
|
||||
pool_denomination: TEST_DENOM.to_string(),
|
||||
grants: Default::default(),
|
||||
};
|
||||
|
||||
let some_sender = deps.api.addr_make("some_sender");
|
||||
instantiate(
|
||||
deps.as_mut(),
|
||||
env,
|
||||
message_info(&some_sender, &[]),
|
||||
init_msg,
|
||||
)?;
|
||||
|
||||
NYM_POOL_STORAGE
|
||||
.contract_admin
|
||||
.assert_admin(deps.as_ref(), &some_sender)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sets_the_pool_denomination() -> anyhow::Result<()> {
|
||||
let mut deps = mock_dependencies();
|
||||
let env = mock_env();
|
||||
let init_msg = InstantiateMsg {
|
||||
pool_denomination: "some_denom".to_string(),
|
||||
grants: Default::default(),
|
||||
};
|
||||
|
||||
let some_sender = deps.api.addr_make("some_sender");
|
||||
instantiate(
|
||||
deps.as_mut(),
|
||||
env,
|
||||
message_info(&some_sender, &[]),
|
||||
init_msg,
|
||||
)?;
|
||||
|
||||
assert_eq!(
|
||||
NYM_POOL_STORAGE
|
||||
.pool_denomination
|
||||
.load(deps.as_ref().storage)?,
|
||||
"some_denom"
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn adds_sender_to_set_of_initial_granters() -> anyhow::Result<()> {
|
||||
let mut deps = mock_dependencies();
|
||||
let env = mock_env();
|
||||
let init_msg = InstantiateMsg {
|
||||
pool_denomination: TEST_DENOM.to_string(),
|
||||
grants: Default::default(),
|
||||
};
|
||||
|
||||
let some_sender = deps.api.addr_make("some_sender");
|
||||
instantiate(
|
||||
deps.as_mut(),
|
||||
env,
|
||||
message_info(&some_sender, &[]),
|
||||
init_msg,
|
||||
)?;
|
||||
|
||||
let granter = query_granter(deps.as_ref(), some_sender.to_string())?;
|
||||
assert!(granter.information.is_some());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod setting_initial_grants {
|
||||
use super::*;
|
||||
use crate::testing::deps_with_balance;
|
||||
use cosmwasm_std::{coin, Order, Storage};
|
||||
use nym_pool_contract_common::{Allowance, BasicAllowance, Grant, GranteeAddress};
|
||||
use std::collections::HashMap;
|
||||
|
||||
fn all_grants(storage: &dyn Storage) -> HashMap<GranteeAddress, Grant> {
|
||||
NYM_POOL_STORAGE
|
||||
.grants
|
||||
.range(storage, None, None, Order::Ascending)
|
||||
.collect::<Result<HashMap<_, _>, _>>()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn with_empty_map() -> anyhow::Result<()> {
|
||||
let mut deps = mock_dependencies();
|
||||
let env = mock_env();
|
||||
let grants = HashMap::new();
|
||||
let init_msg = InstantiateMsg {
|
||||
pool_denomination: TEST_DENOM.to_string(),
|
||||
grants,
|
||||
};
|
||||
|
||||
let some_sender = deps.api.addr_make("some_sender");
|
||||
instantiate(
|
||||
deps.as_mut(),
|
||||
env,
|
||||
message_info(&some_sender, &[]),
|
||||
init_msg,
|
||||
)?;
|
||||
|
||||
assert!(all_grants(&deps.storage).is_empty());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn with_insufficient_tokens() -> anyhow::Result<()> {
|
||||
// limited grant
|
||||
let mut deps = mock_dependencies();
|
||||
let env = mock_env();
|
||||
let mut grants = HashMap::new();
|
||||
grants.insert(
|
||||
deps.api.addr_make("grantee1").to_string(),
|
||||
Allowance::Basic(BasicAllowance {
|
||||
spend_limit: Some(coin(100, TEST_DENOM)),
|
||||
expiration_unix_timestamp: None,
|
||||
}),
|
||||
);
|
||||
let init_msg = InstantiateMsg {
|
||||
pool_denomination: TEST_DENOM.to_string(),
|
||||
grants,
|
||||
};
|
||||
|
||||
let some_sender = deps.api.addr_make("some_sender");
|
||||
let res = instantiate(
|
||||
deps.as_mut(),
|
||||
env,
|
||||
message_info(&some_sender, &[]),
|
||||
init_msg,
|
||||
);
|
||||
assert!(res.is_err());
|
||||
|
||||
// unlimited grant
|
||||
let mut deps = mock_dependencies();
|
||||
let env = mock_env();
|
||||
let mut grants = HashMap::new();
|
||||
grants.insert(
|
||||
deps.api.addr_make("grantee1").to_string(),
|
||||
Allowance::Basic(BasicAllowance::unlimited()),
|
||||
);
|
||||
let init_msg = InstantiateMsg {
|
||||
pool_denomination: TEST_DENOM.to_string(),
|
||||
grants,
|
||||
};
|
||||
|
||||
let some_sender = deps.api.addr_make("some_sender");
|
||||
let res = instantiate(
|
||||
deps.as_mut(),
|
||||
env,
|
||||
message_info(&some_sender, &[]),
|
||||
init_msg,
|
||||
);
|
||||
assert!(res.is_err());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn with_valid_request() -> anyhow::Result<()> {
|
||||
let env = mock_env();
|
||||
let mut deps = deps_with_balance(&env);
|
||||
let mut grants = HashMap::new();
|
||||
grants.insert(
|
||||
deps.api.addr_make("grantee1").to_string(),
|
||||
Allowance::Basic(BasicAllowance {
|
||||
spend_limit: Some(coin(100, TEST_DENOM)),
|
||||
expiration_unix_timestamp: None,
|
||||
}),
|
||||
);
|
||||
grants.insert(
|
||||
deps.api.addr_make("grantee2").to_string(),
|
||||
Allowance::Basic(BasicAllowance {
|
||||
spend_limit: Some(coin(200, TEST_DENOM)),
|
||||
expiration_unix_timestamp: None,
|
||||
}),
|
||||
);
|
||||
grants.insert(
|
||||
deps.api.addr_make("grantee3").to_string(),
|
||||
Allowance::Basic(BasicAllowance {
|
||||
spend_limit: Some(coin(300, TEST_DENOM)),
|
||||
expiration_unix_timestamp: None,
|
||||
}),
|
||||
);
|
||||
let init_msg = InstantiateMsg {
|
||||
pool_denomination: TEST_DENOM.to_string(),
|
||||
grants,
|
||||
};
|
||||
|
||||
let some_sender = deps.api.addr_make("some_sender");
|
||||
instantiate(
|
||||
deps.as_mut(),
|
||||
env,
|
||||
message_info(&some_sender, &[coin(600, TEST_DENOM)]),
|
||||
init_msg,
|
||||
)?;
|
||||
|
||||
assert_eq!(all_grants(&deps.storage).len(), 3);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::storage::NYM_POOL_STORAGE;
|
||||
use cosmwasm_std::{Coin, Storage};
|
||||
use nym_pool_contract_common::NymPoolContractError;
|
||||
|
||||
pub fn validate_usage_coin(storage: &dyn Storage, coin: &Coin) -> Result<(), NymPoolContractError> {
|
||||
let denom = NYM_POOL_STORAGE.pool_denomination.load(storage)?;
|
||||
|
||||
if coin.amount.is_zero() {
|
||||
return Err(NymPoolContractError::EmptyUsageRequest);
|
||||
}
|
||||
|
||||
if coin.denom != denom {
|
||||
return Err(NymPoolContractError::InvalidDenom {
|
||||
expected: denom,
|
||||
got: coin.denom.to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::storage::NymPoolStorage;
|
||||
use crate::testing::TestSetup;
|
||||
use cosmwasm_std::coin;
|
||||
|
||||
#[test]
|
||||
fn validating_coin_usage() -> anyhow::Result<()> {
|
||||
let test = TestSetup::init();
|
||||
let storage = NymPoolStorage::new();
|
||||
let denom = storage.pool_denomination.load(test.storage())?;
|
||||
|
||||
// amount has to be non-zero
|
||||
assert_eq!(
|
||||
validate_usage_coin(test.storage(), &coin(0, &denom)).unwrap_err(),
|
||||
NymPoolContractError::EmptyUsageRequest
|
||||
);
|
||||
|
||||
// denom has to match the value set in the storage
|
||||
assert_eq!(
|
||||
validate_usage_coin(test.storage(), &coin(1000, "bad-denom")).unwrap_err(),
|
||||
NymPoolContractError::InvalidDenom {
|
||||
expected: denom.to_string(),
|
||||
got: "bad-denom".to_string(),
|
||||
}
|
||||
);
|
||||
|
||||
assert!(validate_usage_coin(test.storage(), &coin(1000, denom)).is_ok());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#![warn(clippy::expect_used)]
|
||||
#![warn(clippy::unwrap_used)]
|
||||
#![warn(clippy::todo)]
|
||||
#![warn(clippy::dbg_macro)]
|
||||
|
||||
pub mod contract;
|
||||
pub mod queued_migrations;
|
||||
pub mod storage;
|
||||
|
||||
mod helpers;
|
||||
mod queries;
|
||||
#[cfg(test)]
|
||||
pub mod testing;
|
||||
mod transactions;
|
||||
@@ -0,0 +1,642 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::storage::{retrieval_limits, NYM_POOL_STORAGE};
|
||||
use cosmwasm_std::{Coin, Deps, Env, Order, StdResult};
|
||||
use cw_controllers::AdminResponse;
|
||||
use cw_storage_plus::Bound;
|
||||
use nym_pool_contract_common::{
|
||||
AvailableTokensResponse, GrantInformation, GrantResponse, GranterDetails, GranterResponse,
|
||||
GrantersPagedResponse, GrantsPagedResponse, LockedTokens, LockedTokensPagedResponse,
|
||||
LockedTokensResponse, NymPoolContractError, TotalLockedTokensResponse,
|
||||
};
|
||||
|
||||
pub fn query_admin(deps: Deps) -> Result<AdminResponse, NymPoolContractError> {
|
||||
NYM_POOL_STORAGE
|
||||
.contract_admin
|
||||
.query_admin(deps)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
pub fn query_available_tokens(
|
||||
deps: Deps,
|
||||
env: Env,
|
||||
) -> Result<AvailableTokensResponse, NymPoolContractError> {
|
||||
Ok(AvailableTokensResponse {
|
||||
available: NYM_POOL_STORAGE.available_tokens(deps, &env)?,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn query_total_locked_tokens(
|
||||
deps: Deps,
|
||||
) -> Result<TotalLockedTokensResponse, NymPoolContractError> {
|
||||
let denom = NYM_POOL_STORAGE.pool_denomination.load(deps.storage)?;
|
||||
let amount = NYM_POOL_STORAGE.locked.total_locked.load(deps.storage)?;
|
||||
Ok(TotalLockedTokensResponse {
|
||||
locked: Coin::new(amount, denom),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn query_locked_tokens(
|
||||
deps: Deps,
|
||||
grantee: String,
|
||||
) -> Result<LockedTokensResponse, NymPoolContractError> {
|
||||
let grantee = deps.api.addr_validate(&grantee)?;
|
||||
let denom = NYM_POOL_STORAGE.pool_denomination.load(deps.storage)?;
|
||||
let amount = NYM_POOL_STORAGE
|
||||
.locked
|
||||
.maybe_grantee_locked(deps.storage, &grantee)?;
|
||||
|
||||
Ok(LockedTokensResponse {
|
||||
locked: amount.map(|amount| Coin::new(amount, denom)),
|
||||
grantee,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn query_locked_tokens_paged(
|
||||
deps: Deps,
|
||||
limit: Option<u32>,
|
||||
start_after: Option<String>,
|
||||
) -> Result<LockedTokensPagedResponse, NymPoolContractError> {
|
||||
let limit = limit
|
||||
.unwrap_or(retrieval_limits::LOCKED_TOKENS_DEFAULT_LIMIT)
|
||||
.min(retrieval_limits::LOCKED_TOKENS_MAX_LIMIT) as usize;
|
||||
let grantee = start_after
|
||||
.map(|grantee| deps.api.addr_validate(&grantee))
|
||||
.transpose()?;
|
||||
let denom = NYM_POOL_STORAGE.pool_denomination.load(deps.storage)?;
|
||||
|
||||
let start = grantee.map(Bound::exclusive);
|
||||
|
||||
let locked = NYM_POOL_STORAGE
|
||||
.locked
|
||||
.grantees
|
||||
.range(deps.storage, start, None, Order::Ascending)
|
||||
.take(limit)
|
||||
.map(|res| {
|
||||
res.map(|(grantee, amount)| LockedTokens {
|
||||
grantee,
|
||||
locked: Coin::new(amount, &denom),
|
||||
})
|
||||
})
|
||||
.collect::<StdResult<Vec<_>>>()?;
|
||||
|
||||
let start_next_after = locked.last().map(|locked| locked.grantee.to_string());
|
||||
|
||||
Ok(LockedTokensPagedResponse {
|
||||
locked,
|
||||
start_next_after,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn query_grant(
|
||||
deps: Deps,
|
||||
env: Env,
|
||||
grantee: String,
|
||||
) -> Result<GrantResponse, NymPoolContractError> {
|
||||
let grantee = deps.api.addr_validate(&grantee)?;
|
||||
let grant = NYM_POOL_STORAGE.try_load_grant(deps, &grantee)?;
|
||||
|
||||
Ok(GrantResponse {
|
||||
grant: grant.map(|grant| GrantInformation {
|
||||
expired: grant.allowance.expired(&env),
|
||||
grant,
|
||||
}),
|
||||
grantee,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn query_granter(deps: Deps, granter: String) -> Result<GranterResponse, NymPoolContractError> {
|
||||
let granter = deps.api.addr_validate(&granter)?;
|
||||
|
||||
Ok(GranterResponse {
|
||||
information: NYM_POOL_STORAGE.try_load_granter(deps, &granter)?,
|
||||
granter,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn query_grants_paged(
|
||||
deps: Deps,
|
||||
env: Env,
|
||||
limit: Option<u32>,
|
||||
start_after: Option<String>,
|
||||
) -> Result<GrantsPagedResponse, NymPoolContractError> {
|
||||
let limit = limit
|
||||
.unwrap_or(retrieval_limits::GRANTERS_DEFAULT_LIMIT)
|
||||
.min(retrieval_limits::GRANTERS_MAX_LIMIT) as usize;
|
||||
let grantee = start_after
|
||||
.map(|grantee| deps.api.addr_validate(&grantee))
|
||||
.transpose()?;
|
||||
|
||||
let start = grantee.map(Bound::exclusive);
|
||||
|
||||
let grants = NYM_POOL_STORAGE
|
||||
.grants
|
||||
.range(deps.storage, start, None, Order::Ascending)
|
||||
.take(limit)
|
||||
.map(|res| {
|
||||
res.map(|(_, grant)| GrantInformation {
|
||||
expired: grant.allowance.expired(&env),
|
||||
grant,
|
||||
})
|
||||
})
|
||||
.collect::<StdResult<Vec<_>>>()?;
|
||||
|
||||
let start_next_after = grants.last().map(|info| info.grant.grantee.to_string());
|
||||
|
||||
Ok(GrantsPagedResponse {
|
||||
grants,
|
||||
start_next_after,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn query_granters_paged(
|
||||
deps: Deps,
|
||||
limit: Option<u32>,
|
||||
start_after: Option<String>,
|
||||
) -> Result<GrantersPagedResponse, NymPoolContractError> {
|
||||
let limit = limit
|
||||
.unwrap_or(retrieval_limits::GRANTERS_DEFAULT_LIMIT)
|
||||
.min(retrieval_limits::GRANTERS_MAX_LIMIT) as usize;
|
||||
let granter = start_after
|
||||
.map(|granter| deps.api.addr_validate(&granter))
|
||||
.transpose()?;
|
||||
let start = granter.map(Bound::exclusive);
|
||||
|
||||
let granters = NYM_POOL_STORAGE
|
||||
.granters
|
||||
.range(deps.storage, start, None, Order::Ascending)
|
||||
.take(limit)
|
||||
.map(|res| res.map(|(granter, info)| GranterDetails::from((granter, info))))
|
||||
.collect::<StdResult<Vec<_>>>()?;
|
||||
|
||||
let start_next_after = granters.last().map(|details| details.granter.to_string());
|
||||
|
||||
Ok(GrantersPagedResponse {
|
||||
granters,
|
||||
start_next_after,
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::contract::instantiate;
|
||||
use crate::testing::{TestSetup, TEST_DENOM};
|
||||
use cosmwasm_std::testing::{message_info, mock_dependencies_with_balance, mock_env};
|
||||
use cosmwasm_std::{coin, Uint128};
|
||||
use nym_pool_contract_common::{Allowance, BasicAllowance, GranterInformation, InstantiateMsg};
|
||||
|
||||
#[cfg(test)]
|
||||
mod admin_query {
|
||||
use super::*;
|
||||
use crate::testing::TestSetup;
|
||||
use nym_pool_contract_common::ExecuteMsg;
|
||||
|
||||
#[test]
|
||||
fn returns_current_admin() -> anyhow::Result<()> {
|
||||
let mut test = TestSetup::init();
|
||||
|
||||
let initial_admin = test.admin_unchecked();
|
||||
|
||||
// initial
|
||||
let res = query_admin(test.deps())?;
|
||||
assert_eq!(res.admin, Some(initial_admin.to_string()));
|
||||
|
||||
let new_admin = test.generate_account();
|
||||
|
||||
// sanity check
|
||||
assert_ne!(initial_admin, new_admin);
|
||||
|
||||
// after update
|
||||
test.execute_msg(
|
||||
initial_admin.clone(),
|
||||
&ExecuteMsg::UpdateAdmin {
|
||||
admin: new_admin.to_string(),
|
||||
update_granter_set: None,
|
||||
},
|
||||
)?;
|
||||
|
||||
let updated_admin = query_admin(test.deps())?;
|
||||
assert_eq!(updated_admin.admin, Some(new_admin.to_string()));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn available_tokens_query() {
|
||||
// no need to test the inner functionalities as this is dealt with in the storage tests
|
||||
// (i.e. logic to do with calculating diff against locked tokens, etc.)
|
||||
let env = mock_env();
|
||||
let mut deps = mock_dependencies_with_balance(&[coin(100, TEST_DENOM)]);
|
||||
|
||||
let init_msg = InstantiateMsg {
|
||||
pool_denomination: TEST_DENOM.to_string(),
|
||||
grants: Default::default(),
|
||||
};
|
||||
|
||||
let some_sender = deps.api.addr_make("some_sender");
|
||||
instantiate(
|
||||
deps.as_mut(),
|
||||
env.clone(),
|
||||
message_info(&some_sender, &[]),
|
||||
init_msg,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
query_available_tokens(deps.as_ref(), env)
|
||||
.unwrap()
|
||||
.available,
|
||||
coin(100, TEST_DENOM)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn total_locked_tokens_query() {
|
||||
let mut test = TestSetup::init();
|
||||
|
||||
let locked = query_total_locked_tokens(test.deps()).unwrap().locked;
|
||||
assert!(locked.amount.is_zero());
|
||||
assert_eq!(locked.denom, test.denom());
|
||||
|
||||
let grantee = test.add_dummy_grant().grantee;
|
||||
test.lock_allowance(grantee, Uint128::new(1234));
|
||||
|
||||
let locked = query_total_locked_tokens(test.deps()).unwrap().locked;
|
||||
assert_eq!(locked.amount, Uint128::new(1234));
|
||||
assert_eq!(locked.denom, test.denom());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn locked_tokens_query() {
|
||||
let mut test = TestSetup::init();
|
||||
|
||||
let grantee1 = test.add_dummy_grant().grantee;
|
||||
test.lock_allowance(grantee1.as_str(), Uint128::new(1234));
|
||||
|
||||
let grantee2 = test.add_dummy_grant().grantee;
|
||||
let not_grantee = test.generate_account();
|
||||
|
||||
let res = query_locked_tokens(test.deps(), grantee1.to_string()).unwrap();
|
||||
assert_eq!(res.grantee, grantee1);
|
||||
assert_eq!(res.locked, Some(coin(1234, TEST_DENOM)));
|
||||
|
||||
let res = query_locked_tokens(test.deps(), grantee2.to_string()).unwrap();
|
||||
assert_eq!(res.grantee, grantee2);
|
||||
assert!(res.locked.is_none());
|
||||
|
||||
let res = query_locked_tokens(test.deps(), not_grantee.to_string()).unwrap();
|
||||
assert_eq!(res.grantee, not_grantee);
|
||||
assert!(res.locked.is_none());
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod locked_tokens_paged_query {
|
||||
use super::*;
|
||||
|
||||
fn lock_sorted(test: &mut TestSetup, count: usize) -> Vec<LockedTokens> {
|
||||
let mut grantees = Vec::new();
|
||||
|
||||
for _ in 0..count {
|
||||
let grantee = test.add_dummy_grant().grantee;
|
||||
test.lock_allowance(grantee.as_str(), Uint128::new(100));
|
||||
grantees.push(LockedTokens {
|
||||
grantee,
|
||||
locked: coin(100, test.denom()),
|
||||
});
|
||||
}
|
||||
|
||||
grantees.sort_by_key(|g| g.grantee.clone());
|
||||
grantees
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn obeys_limits() {
|
||||
let mut test = TestSetup::init();
|
||||
let _locked = lock_sorted(&mut test, 1000);
|
||||
|
||||
let limit = 42;
|
||||
let page1 = query_locked_tokens_paged(test.deps(), Some(limit), None).unwrap();
|
||||
assert_eq!(page1.locked.len(), limit as usize);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn has_default_limit() {
|
||||
let mut test = TestSetup::init();
|
||||
let _locked = lock_sorted(&mut test, 1000);
|
||||
|
||||
// query without explicitly setting a limit
|
||||
let page1 = query_locked_tokens_paged(test.deps(), None, None).unwrap();
|
||||
assert_eq!(
|
||||
page1.locked.len() as u32,
|
||||
retrieval_limits::LOCKED_TOKENS_DEFAULT_LIMIT
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn has_max_limit() {
|
||||
let mut test = TestSetup::init();
|
||||
let _locked = lock_sorted(&mut test, 1000);
|
||||
|
||||
// query with a crazily high limit in an attempt to use too many resources
|
||||
let crazy_limit = 1000;
|
||||
let page1 = query_locked_tokens_paged(test.deps(), Some(crazy_limit), None).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
page1.locked.len() as u32,
|
||||
retrieval_limits::LOCKED_TOKENS_MAX_LIMIT
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pagination_works() {
|
||||
let mut test = TestSetup::init();
|
||||
let locked = lock_sorted(&mut test, 1000);
|
||||
|
||||
// first page should return 2 results...
|
||||
let page1 = query_locked_tokens_paged(test.deps(), Some(2), None).unwrap();
|
||||
assert_eq!(page1.locked, locked[..2].to_vec());
|
||||
|
||||
// if we start after 5th entry, the returned following page should have 6th and onwards
|
||||
let second = locked[1].clone();
|
||||
|
||||
let page2 =
|
||||
query_locked_tokens_paged(test.deps(), Some(3), Some(second.grantee.to_string()))
|
||||
.unwrap();
|
||||
assert_eq!(page2.locked, locked[2..5].to_vec());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn grant_query() {
|
||||
let mut test = TestSetup::init();
|
||||
let env = test.env();
|
||||
|
||||
// bad address
|
||||
let bad_address = "not-valid-bech32";
|
||||
assert!(query_grant(test.deps(), env.clone(), bad_address.to_string()).is_err());
|
||||
|
||||
// exists
|
||||
let grant = test.add_dummy_grant();
|
||||
let grantee = grant.grantee.clone();
|
||||
|
||||
assert_eq!(
|
||||
query_grant(test.deps(), env.clone(), grantee.to_string()).unwrap(),
|
||||
GrantResponse {
|
||||
grantee,
|
||||
grant: Some(GrantInformation {
|
||||
grant,
|
||||
expired: false,
|
||||
}),
|
||||
}
|
||||
);
|
||||
|
||||
// exists expired
|
||||
let grantee = test.generate_account();
|
||||
let exp = env.block.time.seconds() + 1;
|
||||
let allowance = Allowance::Basic(BasicAllowance {
|
||||
spend_limit: None,
|
||||
expiration_unix_timestamp: Some(exp),
|
||||
});
|
||||
let admin = test.admin_unchecked();
|
||||
NYM_POOL_STORAGE
|
||||
.insert_new_grant(test.deps_mut(), &env, &admin, &grantee, allowance)
|
||||
.unwrap();
|
||||
let grant = NYM_POOL_STORAGE.load_grant(test.deps(), &grantee).unwrap();
|
||||
|
||||
test.next_block();
|
||||
let env = test.env();
|
||||
|
||||
assert_eq!(
|
||||
query_grant(test.deps(), env.clone(), grantee.to_string()).unwrap(),
|
||||
GrantResponse {
|
||||
grantee,
|
||||
grant: Some(GrantInformation {
|
||||
grant,
|
||||
expired: true,
|
||||
}),
|
||||
}
|
||||
);
|
||||
|
||||
// doesn't exist
|
||||
let doesnt_exist = test.generate_account();
|
||||
assert_eq!(
|
||||
query_grant(test.deps(), env.clone(), doesnt_exist.to_string()).unwrap(),
|
||||
GrantResponse {
|
||||
grantee: doesnt_exist,
|
||||
grant: None,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn granter_query() {
|
||||
let mut test = TestSetup::init();
|
||||
let admin = test.admin_unchecked();
|
||||
let env = test.env();
|
||||
|
||||
// bad address
|
||||
let bad_address = "not-valid-bech32";
|
||||
assert!(query_granter(test.deps(), bad_address.to_string()).is_err());
|
||||
|
||||
// exists
|
||||
let granter = test.generate_account();
|
||||
test.add_granter(&granter);
|
||||
|
||||
assert_eq!(
|
||||
query_granter(test.deps(), granter.to_string()).unwrap(),
|
||||
GranterResponse {
|
||||
granter,
|
||||
information: Some(GranterInformation {
|
||||
created_by: admin.clone(),
|
||||
created_at_height: env.block.height,
|
||||
}),
|
||||
}
|
||||
);
|
||||
|
||||
// (admin is also a granter)
|
||||
assert_eq!(
|
||||
query_granter(test.deps(), admin.to_string()).unwrap(),
|
||||
GranterResponse {
|
||||
information: Some(GranterInformation {
|
||||
created_by: admin.clone(),
|
||||
created_at_height: env.block.height,
|
||||
}),
|
||||
granter: admin,
|
||||
}
|
||||
);
|
||||
|
||||
// doesn't exist
|
||||
let not_granter = test.generate_account();
|
||||
assert_eq!(
|
||||
query_granter(test.deps(), not_granter.to_string()).unwrap(),
|
||||
GranterResponse {
|
||||
granter: not_granter,
|
||||
information: None,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod granters_paged_query {
|
||||
use super::*;
|
||||
|
||||
fn granters_sorted(test: &mut TestSetup, count: usize) -> Vec<GranterDetails> {
|
||||
let mut granters = Vec::new();
|
||||
|
||||
for _ in 0..count {
|
||||
let granter = test.add_dummy_grant().grantee;
|
||||
test.add_granter(&granter);
|
||||
granters.push(GranterDetails {
|
||||
granter,
|
||||
information: GranterInformation {
|
||||
created_by: test.admin_unchecked(),
|
||||
created_at_height: test.env().block.height,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
granters.sort_by_key(|g| g.granter.clone());
|
||||
granters
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn obeys_limits() {
|
||||
let mut test = TestSetup::init();
|
||||
let _granters = granters_sorted(&mut test, 1000);
|
||||
|
||||
let limit = 42;
|
||||
let page1 = query_granters_paged(test.deps(), Some(limit), None).unwrap();
|
||||
assert_eq!(page1.granters.len(), limit as usize);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn has_default_limit() {
|
||||
let mut test = TestSetup::init();
|
||||
let _granters = granters_sorted(&mut test, 1000);
|
||||
|
||||
// query without explicitly setting a limit
|
||||
let page1 = query_granters_paged(test.deps(), None, None).unwrap();
|
||||
assert_eq!(
|
||||
page1.granters.len() as u32,
|
||||
retrieval_limits::GRANTERS_DEFAULT_LIMIT
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn has_max_limit() {
|
||||
let mut test = TestSetup::init();
|
||||
let _granters = granters_sorted(&mut test, 1000);
|
||||
|
||||
// query with a crazily high limit in an attempt to use too many resources
|
||||
let crazy_limit = 1000;
|
||||
let page1 = query_granters_paged(test.deps(), Some(crazy_limit), None).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
page1.granters.len() as u32,
|
||||
retrieval_limits::GRANTERS_MAX_LIMIT
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pagination_works() {
|
||||
let mut test = TestSetup::init();
|
||||
let locked = granters_sorted(&mut test, 1000);
|
||||
|
||||
// first page should return 2 results...
|
||||
let page1 = query_granters_paged(test.deps(), Some(2), None).unwrap();
|
||||
assert_eq!(page1.granters, locked[..2].to_vec());
|
||||
|
||||
// if we start after 5th entry, the returned following page should have 6th and onwards
|
||||
let second = locked[1].clone();
|
||||
|
||||
let page2 =
|
||||
query_granters_paged(test.deps(), Some(3), Some(second.granter.to_string()))
|
||||
.unwrap();
|
||||
assert_eq!(page2.granters, locked[2..5].to_vec());
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod grants_paged_query {
|
||||
use super::*;
|
||||
|
||||
fn grants_sorted(test: &mut TestSetup, count: usize) -> Vec<GrantInformation> {
|
||||
let mut grantees = Vec::new();
|
||||
|
||||
for _ in 0..count {
|
||||
let grant = test.add_dummy_grant();
|
||||
grantees.push(GrantInformation {
|
||||
grant,
|
||||
expired: false,
|
||||
});
|
||||
}
|
||||
|
||||
grantees.sort_by_key(|g| g.grant.grantee.clone());
|
||||
grantees
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn obeys_limits() {
|
||||
let mut test = TestSetup::init();
|
||||
let _grantees = grants_sorted(&mut test, 1000);
|
||||
|
||||
let limit = 42;
|
||||
let page1 = query_grants_paged(test.deps(), test.env(), Some(limit), None).unwrap();
|
||||
assert_eq!(page1.grants.len(), limit as usize);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn has_default_limit() {
|
||||
let mut test = TestSetup::init();
|
||||
let _grantees = grants_sorted(&mut test, 1000);
|
||||
|
||||
// query without explicitly setting a limit
|
||||
let page1 = query_grants_paged(test.deps(), test.env(), None, None).unwrap();
|
||||
assert_eq!(
|
||||
page1.grants.len() as u32,
|
||||
retrieval_limits::GRANTS_DEFAULT_LIMIT
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn has_max_limit() {
|
||||
let mut test = TestSetup::init();
|
||||
let _grantees = grants_sorted(&mut test, 1000);
|
||||
|
||||
// query with a crazily high limit in an attempt to use too many resources
|
||||
let crazy_limit = 1000;
|
||||
let page1 =
|
||||
query_grants_paged(test.deps(), test.env(), Some(crazy_limit), None).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
page1.grants.len() as u32,
|
||||
retrieval_limits::GRANTS_MAX_LIMIT
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pagination_works() {
|
||||
let mut test = TestSetup::init();
|
||||
let grants = grants_sorted(&mut test, 1000);
|
||||
|
||||
// first page should return 2 results...
|
||||
let page1 = query_grants_paged(test.deps(), test.env(), Some(2), None).unwrap();
|
||||
assert_eq!(page1.grants, grants[..2].to_vec());
|
||||
|
||||
// if we start after 5th entry, the returned following page should have 6th and onwards
|
||||
let second = grants[1].clone();
|
||||
|
||||
let page2 = query_grants_paged(
|
||||
test.deps(),
|
||||
test.env(),
|
||||
Some(3),
|
||||
Some(second.grant.grantee.to_string()),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(page2.grants, grants[2..5].to_vec());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,311 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::contract;
|
||||
use crate::contract::{execute, instantiate, migrate, query};
|
||||
use crate::storage::NYM_POOL_STORAGE;
|
||||
use crate::testing::storage::{ContractStorageWrapper, StorageWrapper};
|
||||
use cosmwasm_std::testing::{message_info, mock_env, MockApi, MockQuerier, MockStorage};
|
||||
use cosmwasm_std::{
|
||||
coin, coins, Addr, Coin, ContractInfo, Deps, DepsMut, Empty, Env, MemoryStorage, MessageInfo,
|
||||
Order, OwnedDeps, Response, StdResult, Storage, Uint128,
|
||||
};
|
||||
use cw_multi_test::{
|
||||
next_block, App, AppBuilder, AppResponse, BankKeeper, Contract, ContractWrapper, Executor,
|
||||
};
|
||||
use nym_pool_contract_common::{
|
||||
Allowance, BasicAllowance, ExecuteMsg, Grant, InstantiateMsg, NymPoolContractError, QueryMsg,
|
||||
};
|
||||
use rand::{RngCore, SeedableRng};
|
||||
use rand_chacha::ChaCha20Rng;
|
||||
use serde::de::DeserializeOwned;
|
||||
use std::collections::HashMap;
|
||||
|
||||
mod storage;
|
||||
|
||||
pub fn test_rng() -> ChaCha20Rng {
|
||||
let dummy_seed = [42u8; 32];
|
||||
ChaCha20Rng::from_seed(dummy_seed)
|
||||
}
|
||||
|
||||
pub fn deps_with_balance(env: &Env) -> OwnedDeps<MemoryStorage, MockApi, MockQuerier<Empty>> {
|
||||
OwnedDeps {
|
||||
storage: MockStorage::default(),
|
||||
api: MockApi::default(),
|
||||
querier: MockQuerier::<Empty>::new(&[(
|
||||
env.contract.address.as_str(),
|
||||
coins(100000000000, TEST_DENOM).as_slice(),
|
||||
)]),
|
||||
custom_query_type: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub const TEST_DENOM: &str = "unym";
|
||||
|
||||
pub struct TestSetup {
|
||||
pub app: App<BankKeeper, MockApi, StorageWrapper>,
|
||||
pub rng: ChaCha20Rng,
|
||||
pub contract_address: Addr,
|
||||
pub master_address: Addr,
|
||||
pub(crate) storage: ContractStorageWrapper,
|
||||
}
|
||||
|
||||
pub fn contract() -> Box<dyn Contract<Empty>> {
|
||||
let contract = ContractWrapper::new(execute, instantiate, query).with_migrate(migrate);
|
||||
Box::new(contract)
|
||||
}
|
||||
|
||||
impl TestSetup {
|
||||
pub fn init() -> TestSetup {
|
||||
let storage = StorageWrapper::new();
|
||||
|
||||
let api = MockApi::default().with_prefix("n");
|
||||
let master_address = api.addr_make("master-owner");
|
||||
|
||||
let mut app = AppBuilder::new()
|
||||
.with_api(api)
|
||||
.with_storage(storage.clone())
|
||||
.build(|router, _api, storage| {
|
||||
router
|
||||
.bank
|
||||
.init_balance(
|
||||
storage,
|
||||
&master_address,
|
||||
coins(1000000000000000, TEST_DENOM),
|
||||
)
|
||||
.unwrap()
|
||||
});
|
||||
let code_id = app.store_code(contract());
|
||||
let contract_address = app
|
||||
.instantiate_contract(
|
||||
code_id,
|
||||
master_address.clone(),
|
||||
&InstantiateMsg {
|
||||
pool_denomination: TEST_DENOM.to_string(),
|
||||
grants: Default::default(),
|
||||
},
|
||||
&[],
|
||||
"nym-pool-contract",
|
||||
Some(master_address.to_string()),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// send some tokens to the contract
|
||||
app.send_tokens(
|
||||
master_address.clone(),
|
||||
contract_address.clone(),
|
||||
&[coin(100000000, TEST_DENOM)],
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
TestSetup {
|
||||
app,
|
||||
rng: test_rng(),
|
||||
storage: storage.contract_storage_wrapper(&contract_address),
|
||||
contract_address,
|
||||
master_address,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_contract_balance(&mut self, balance: Coin) {
|
||||
let contract_address = &self.contract_address;
|
||||
self.app
|
||||
.router()
|
||||
.bank
|
||||
.init_balance(
|
||||
&mut self.storage.inner_storage(),
|
||||
contract_address,
|
||||
vec![balance],
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
pub fn deps(&self) -> Deps<'_> {
|
||||
Deps {
|
||||
storage: &self.storage,
|
||||
api: self.app.api(),
|
||||
querier: self.app.wrap(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn deps_mut(&mut self) -> DepsMut<'_> {
|
||||
DepsMut {
|
||||
storage: &mut self.storage,
|
||||
api: self.app.api(),
|
||||
querier: self.app.wrap(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn deps_mut_env(&mut self) -> (DepsMut<'_>, Env) {
|
||||
let env = self.env().clone();
|
||||
(self.deps_mut(), env)
|
||||
}
|
||||
|
||||
pub fn storage(&self) -> &dyn Storage {
|
||||
&self.storage
|
||||
}
|
||||
|
||||
pub fn storage_mut(&mut self) -> &mut dyn Storage {
|
||||
&mut self.storage
|
||||
}
|
||||
|
||||
pub fn env(&self) -> Env {
|
||||
Env {
|
||||
block: self.app.block_info(),
|
||||
contract: ContractInfo {
|
||||
address: self.contract_address.clone(),
|
||||
},
|
||||
..mock_env()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn next_block(&mut self) {
|
||||
self.app.update_block(next_block)
|
||||
}
|
||||
|
||||
pub fn execute_raw(
|
||||
&mut self,
|
||||
sender: Addr,
|
||||
message: ExecuteMsg,
|
||||
) -> Result<Response, NymPoolContractError> {
|
||||
self.execute_raw_with_balance(sender, &[], message)
|
||||
}
|
||||
|
||||
pub fn execute_raw_with_balance(
|
||||
&mut self,
|
||||
sender: Addr,
|
||||
coins: &[Coin],
|
||||
message: ExecuteMsg,
|
||||
) -> Result<Response, NymPoolContractError> {
|
||||
let env = self.env();
|
||||
let info = message_info(&sender, coins);
|
||||
contract::execute(self.deps_mut(), env, info, message)
|
||||
}
|
||||
|
||||
pub fn execute_msg(
|
||||
&mut self,
|
||||
sender: Addr,
|
||||
message: &ExecuteMsg,
|
||||
) -> anyhow::Result<AppResponse> {
|
||||
self.execute_msg_with_balance(sender, &[], message)
|
||||
}
|
||||
|
||||
pub fn execute_msg_with_balance(
|
||||
&mut self,
|
||||
sender: Addr,
|
||||
coins: &[Coin],
|
||||
message: &ExecuteMsg,
|
||||
) -> anyhow::Result<AppResponse> {
|
||||
self.app
|
||||
.execute_contract(sender, self.contract_address.clone(), message, coins)
|
||||
}
|
||||
|
||||
pub fn query<T: DeserializeOwned>(&self, message: &QueryMsg) -> StdResult<T> {
|
||||
self.app
|
||||
.wrap()
|
||||
.query_wasm_smart(self.contract_address.as_str(), message)
|
||||
}
|
||||
|
||||
pub fn generate_account(&mut self) -> Addr {
|
||||
self.app
|
||||
.api()
|
||||
.addr_make(&format!("foomp{}", self.rng.next_u64()))
|
||||
}
|
||||
|
||||
pub fn admin_unchecked(&self) -> Addr {
|
||||
NYM_POOL_STORAGE
|
||||
.contract_admin
|
||||
.get(self.deps())
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
pub fn change_admin(&mut self, new_admin: &Addr) {
|
||||
self.execute_msg(
|
||||
self.admin_unchecked(),
|
||||
&ExecuteMsg::UpdateAdmin {
|
||||
admin: new_admin.to_string(),
|
||||
update_granter_set: Some(true),
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
pub fn admin_msg(&self) -> MessageInfo {
|
||||
message_info(&self.admin_unchecked(), &[])
|
||||
}
|
||||
|
||||
pub fn denom(&self) -> String {
|
||||
NYM_POOL_STORAGE
|
||||
.pool_denomination
|
||||
.load(self.storage())
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
pub fn coin(&self, amount: u128) -> Coin {
|
||||
coin(amount, self.denom())
|
||||
}
|
||||
|
||||
pub fn coins(&self, amount: u128) -> Vec<Coin> {
|
||||
coins(amount, self.denom())
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
pub fn add_dummy_grant(&mut self) -> Grant {
|
||||
let grantee = self.generate_account();
|
||||
self.add_dummy_grant_for(&grantee)
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
pub fn add_dummy_grant_for(&mut self, grantee: impl Into<String>) -> Grant {
|
||||
let grantee = Addr::unchecked(grantee);
|
||||
let granter = self.admin_unchecked();
|
||||
let env = self.env();
|
||||
NYM_POOL_STORAGE
|
||||
.insert_new_grant(
|
||||
self.deps_mut(),
|
||||
&env,
|
||||
&granter,
|
||||
&grantee,
|
||||
Allowance::Basic(BasicAllowance::unlimited()),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
NYM_POOL_STORAGE.load_grant(self.deps(), &grantee).unwrap()
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
pub fn lock_allowance(&mut self, grantee: impl Into<String>, amount: impl Into<Uint128>) {
|
||||
let denom = NYM_POOL_STORAGE
|
||||
.pool_denomination
|
||||
.load(self.deps().storage)
|
||||
.unwrap();
|
||||
|
||||
self.execute_msg(
|
||||
Addr::unchecked(grantee),
|
||||
&ExecuteMsg::LockAllowance {
|
||||
amount: coin(amount.into().u128(), denom),
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
pub fn full_locked_map(&self) -> HashMap<Addr, Uint128> {
|
||||
NYM_POOL_STORAGE
|
||||
.locked
|
||||
.grantees
|
||||
.range(self.deps().storage, None, None, Order::Ascending)
|
||||
.collect::<Result<HashMap<_, _>, _>>()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
pub fn add_granter(&mut self, granter: &Addr) {
|
||||
let env = self.env();
|
||||
let admin = self.admin_unchecked();
|
||||
NYM_POOL_STORAGE
|
||||
.add_new_granter(self.deps_mut(), &env, &admin, granter)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use cosmwasm_std::storage_keys::to_length_prefixed_nested;
|
||||
use cosmwasm_std::testing::MockStorage;
|
||||
use cosmwasm_std::{Addr, MemoryStorage, Order, Record, Storage};
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct StorageWrapper(Rc<RefCell<MemoryStorage>>);
|
||||
|
||||
impl StorageWrapper {
|
||||
pub(super) fn contract_storage_wrapper(&self, contract: &Addr) -> ContractStorageWrapper {
|
||||
ContractStorageWrapper {
|
||||
address: contract.clone(),
|
||||
inner: self.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn new() -> Self {
|
||||
StorageWrapper(Rc::new(RefCell::new(MockStorage::new())))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct ContractStorageWrapper {
|
||||
address: Addr,
|
||||
inner: StorageWrapper,
|
||||
}
|
||||
|
||||
impl ContractStorageWrapper {
|
||||
pub fn inner_storage(&self) -> StorageWrapper {
|
||||
self.inner.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl Storage for StorageWrapper {
|
||||
fn get(&self, key: &[u8]) -> Option<Vec<u8>> {
|
||||
self.0.borrow().get(key)
|
||||
}
|
||||
|
||||
fn range<'a>(
|
||||
&'a self,
|
||||
start: Option<&[u8]>,
|
||||
end: Option<&[u8]>,
|
||||
order: Order,
|
||||
) -> Box<dyn Iterator<Item = Record> + 'a> {
|
||||
// hehe, that's nasty
|
||||
let vals = self.0.borrow().range(start, end, order).collect::<Vec<_>>();
|
||||
Box::new(vals.into_iter())
|
||||
}
|
||||
|
||||
fn set(&mut self, key: &[u8], value: &[u8]) {
|
||||
self.0.borrow_mut().set(key, value);
|
||||
}
|
||||
|
||||
fn remove(&mut self, key: &[u8]) {
|
||||
self.0.borrow_mut().remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
impl ContractStorageWrapper {
|
||||
fn contract_namespace(&self) -> Vec<u8> {
|
||||
let mut name = b"contract_data/".to_vec();
|
||||
name.extend_from_slice(self.address.as_bytes());
|
||||
name
|
||||
}
|
||||
|
||||
fn prefix(&self) -> Vec<u8> {
|
||||
to_length_prefixed_nested(&[b"wasm", &self.contract_namespace()])
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn trim(namespace: &[u8], key: &[u8]) -> Vec<u8> {
|
||||
key[namespace.len()..].to_vec()
|
||||
}
|
||||
|
||||
/// Returns a new vec of same length and last byte incremented by one
|
||||
/// If last bytes are 255, we handle overflow up the chain.
|
||||
/// If all bytes are 255, this returns wrong data - but that is never possible as a namespace
|
||||
fn namespace_upper_bound(input: &[u8]) -> Vec<u8> {
|
||||
let mut copy = input.to_vec();
|
||||
// zero out all trailing 255, increment first that is not such
|
||||
for i in (0..input.len()).rev() {
|
||||
if copy[i] == 255 {
|
||||
copy[i] = 0;
|
||||
} else {
|
||||
copy[i] += 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
copy
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn concat(namespace: &[u8], key: &[u8]) -> Vec<u8> {
|
||||
let mut k = namespace.to_vec();
|
||||
k.extend_from_slice(key);
|
||||
k
|
||||
}
|
||||
|
||||
fn get_with_prefix(storage: &dyn Storage, namespace: &[u8], key: &[u8]) -> Option<Vec<u8>> {
|
||||
storage.get(&Self::concat(namespace, key))
|
||||
}
|
||||
|
||||
fn set_with_prefix(storage: &mut dyn Storage, namespace: &[u8], key: &[u8], value: &[u8]) {
|
||||
storage.set(&Self::concat(namespace, key), value);
|
||||
}
|
||||
|
||||
fn remove_with_prefix(storage: &mut dyn Storage, namespace: &[u8], key: &[u8]) {
|
||||
storage.remove(&Self::concat(namespace, key));
|
||||
}
|
||||
|
||||
fn range_with_prefix<'a>(
|
||||
storage: &'a dyn Storage,
|
||||
namespace: &[u8],
|
||||
start: Option<&[u8]>,
|
||||
end: Option<&[u8]>,
|
||||
order: Order,
|
||||
) -> Box<dyn Iterator<Item = Record> + 'a> {
|
||||
// prepare start, end with prefix
|
||||
let start = match start {
|
||||
Some(s) => Self::concat(namespace, s),
|
||||
None => namespace.to_vec(),
|
||||
};
|
||||
let end = match end {
|
||||
Some(e) => Self::concat(namespace, e),
|
||||
// end is updating last byte by one
|
||||
None => Self::namespace_upper_bound(namespace),
|
||||
};
|
||||
|
||||
// get iterator from storage
|
||||
let base_iterator = storage.range(Some(&start), Some(&end), order);
|
||||
|
||||
// make a copy for the closure to handle lifetimes safely
|
||||
let prefix = namespace.to_vec();
|
||||
let mapped = base_iterator.map(move |(k, v)| (Self::trim(&prefix, &k), v));
|
||||
Box::new(mapped)
|
||||
}
|
||||
}
|
||||
|
||||
impl Storage for ContractStorageWrapper {
|
||||
fn get(&self, key: &[u8]) -> Option<Vec<u8>> {
|
||||
let prefix = self.prefix();
|
||||
Self::get_with_prefix(&self.inner, &prefix, key)
|
||||
}
|
||||
|
||||
fn range<'a>(
|
||||
&'a self,
|
||||
start: Option<&[u8]>,
|
||||
end: Option<&[u8]>,
|
||||
order: Order,
|
||||
) -> Box<dyn Iterator<Item = Record> + 'a> {
|
||||
let prefix = self.prefix();
|
||||
Self::range_with_prefix(&self.inner, &prefix, start, end, order)
|
||||
}
|
||||
|
||||
fn set(&mut self, key: &[u8], value: &[u8]) {
|
||||
let prefix = self.prefix();
|
||||
Self::set_with_prefix(&mut self.inner, &prefix, key, value);
|
||||
}
|
||||
|
||||
fn remove(&mut self, key: &[u8]) {
|
||||
let prefix = self.prefix();
|
||||
Self::remove_with_prefix(&mut self.inner, &prefix, key);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user