Skip to main content
Version: Next

Assets Module

Overview

The Assets module within PRYZM manages refractable assets, their maturity levels, and the exchange rates between each base asset and its refracted form.

Concepts

Refractable Asset

Refractable assets are assets refracted by PRYZM. They can be registered or disabled via governance proposals. These assets encompass the following details:

  • Id: A unique identifier provided by the user, used in the p/y token denom.
  • Token Denom: Denomination of the refractable token, e.g., cLuna.
  • Host Chain Id: The id for the host chain where the asset is staked. This is left blank for external assets.
  • Disabled: If an asset is disabled via governance, this property is set. Subsequently, no new maturity for this asset will be introduced, and the tokens of this asset will no longer be refracted in PRYZM. However, the refracted tokens can still be merged and redeemed even if the asset is disabled.
  • Maturity Parameters: Parameters that control the generation of maturities for the asset.
  • Fee Ratios: Fee ratios used in various modules to collect protocol fees.

Below is the structure of a refractable asset:

// The properties of a supported asset
message RefractableAsset {
// A unique user-provided identifier. Is used in the p/y token denom
string id = 1;

// The denomination of the token on Pryzm. This may be an icstaking cToken or an IBC transferred token denom for external assets.
string token_denom = 2;

// The id for the host chain on which the asset is staked. This is empty if the asset is external.
string host_chain_id = 3;

// Disabled assets cannot be refracted, but can still be redeemed.
bool disabled = 4;

MaturityParams maturity_params = 5 [(gogoproto.nullable) = false];

// The amount of fee for each operation on the asset.
FeeRatios fee_ratios = 6 [(gogoproto.nullable) = false];
}

// The parameters based on which new maturities are introduced
message MaturityParams {
// The number of maturities per year: can be 0, 1, 2, 4, 12
// note: levels_per_year should be zero, if and only if years is 0 (which means no automatic maturity creation)
int32 levels_per_year = 1 [(amino.dont_omitempty) = true];

// The number of years in advance that maturities are made available for
// note: years should be zero, if and only if levels_per_year is 0 (which means no automatic maturity creation)
int32 years = 2 [(amino.dont_omitempty) = true];
}
// Fee ratio per each operation
message FeeRatios {
string yield = 1 [
(gogoproto.nullable) = true,
(cosmos_proto.scalar) = "cosmos.Dec",
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec"
];
string refractor_refract = 2 [
(gogoproto.nullable) = true,
(cosmos_proto.scalar) = "cosmos.Dec",
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec"
];
string refractor_merge = 3 [
(gogoproto.nullable) = true,
(cosmos_proto.scalar) = "cosmos.Dec",
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec"
];
string refractor_redeem = 4 [
(gogoproto.nullable) = true,
(cosmos_proto.scalar) = "cosmos.Dec",
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec"
];
string y_staking_claim_reward = 5 [
(gogoproto.nullable) = true,
(cosmos_proto.scalar) = "cosmos.Dec",
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec"
];
}

Maturity Level

Maturity levels are automatically generated maturities, allowing users to refract their assets. They're governed by two parameters: the number of maturities per year and the number of years in advance that maturities are available. The former can be discrete values of 1, 2, 4, or 12 - representing annually, semi-annually, quarterly, or monthly maturities. The latter can be any number from 1 to 10.

These values are set per asset and can be updated through governance proposals. If a proposal to change these parameters is passed, the protocol will instantly verify the new maturities that should be listed against the outstanding maturities. Outstanding maturities are never deleted; they can be used as long as they're not expired and the asset remains refractable. As time progresses, new maturity levels are added based on the updated parameters.

Maturity levels are stored using the following structure:

message MaturityLevel {
bool active = 1;
string asset_id = 2;
string symbol = 3;
google.protobuf.Timestamp introduction_time = 4 [
(gogoproto.stdtime) = true,
(gogoproto.nullable) = false
];
google.protobuf.Timestamp expiration_time = 5 [
(gogoproto.stdtime) = true,
(gogoproto.nullable) = false
];
}

The symbol of the maturity level is the expiration time of the maturity, formatted as 02Jan2006.

Transitions

Begin Block

At the start of each block, the assets module updates the list of available maturity levels. Expired maturity levels ( e.g., Dec22 expires at the beginning of Jan 1st, 2023) are deactivated. New maturities are also calculated for the visible timeframe (based on maturity level parameters) and added to the list of active maturities. Here's a pseudocode of the computation:

monthsPerLevel := int(12 / asset.MaturityParameters.LevelsPerYear)

currentLevelMonth := int(blockUtcTime.Month())
// finds next maturity level after blockTime
currentLevelMonth = currentLevelMonth + monthsPerLevel - (currentLevelMonth % monthsPerLevel)
currentMaturity := time.Date(blockUtcTime.Year(), time.Month(currentLevelMonth),
1, 0, 0, 0, 0, time.UTC) // first day of maturity level month

endOfVisibleRange := blockUtcTime.AddDate(int(asset.MaturityParameters.Years), 0, 0)

var maturityLevels []types.MaturityLevel
for !currentMaturity.After(endOfVisibleRange) {
maturityLevels.add(currentMaturity)
currentMaturity = currentMaturity.AddDate(0, monthsPerLevel, 0)
}

The code snippet above calculates maturity levels based on the predefined number of maturities per year and the duration that these maturities are visible in advance. It determines the number of months per maturity level, finds the next maturity level after the current block timestamp, and continues to produce maturity levels until the visible range's end is reached.

Messages

MsgRegisterAsset

This message registers a new refractable asset on the chain. It can be executed by the governance module address (using a governance proposal) or one of the admin addresses defined in the module's parameters.

message MsgRegisterAsset {
string creator = 1;
RefractableAsset asset = 2 [(gogoproto.nullable) = false];
}

MsgDisableAsset

This message toggles the asset's disabled property to true, effectively disabling refraction and further introduction of maturity levels. It can be executed by the governance module address(using a governance proposal) or one of the admin addresses defined in the module's parameters.

message MsgDisableAsset {
string creator = 1;
string asset_id = 2;
}

MsgIntroduceMaturityLevel

This message allows custom creation of maturity levels. Note that this message can be executed both through governance and with admin authority.

message MsgIntroduceMaturityLevel {
option (cosmos.msg.v1.signer) = "creator";
option (amino.name) = "pryzm/assets/v1/IntroduceMaturityLevel";

string creator = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
string asset_id = 2;
string symbol = 3;
}

message MsgIntroduceMaturityLevelResponse {
MaturityLevel maturity_level = 1 [(gogoproto.nullable) = false];
}

MsgUpdateMaturityParams

This message modifies an asset's maturity level parameters. Note that this message can be executed both through governance and with admin authority.

message MsgUpdateMaturityParams {
string authority = 1;
string asset_id = 2;
MaturityParams params = 3 [(gogoproto.nullable) = false];
}

MsgUpdateFeeRatios

This message adjusts an asset's fee ratios. Note that this message can be executed both through governance and with admin authority.

message MsgUpdateFeeRatios {
string authority = 1;
string asset_id = 2;
FeeRatios fee_ratios = 3 [(gogoproto.nullable) = false];
}

MsgUpdateParams

This message updates the module parameters through governance.

message MsgUpdateParams {
option (cosmos.msg.v1.signer) = "authority";

string authority = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
Params params = 2 [(gogoproto.nullable) = false];
}

message MsgUpdateParamsResponse {}

Queries

Params

A query to extract the current module parameters.

// QueryParamsRequest is request type for the Query/Params RPC method.
message QueryParamsRequest {}

// QueryParamsResponse is response type for the Query/Params RPC method.
message QueryParamsResponse {
// params holds all the parameters of this module.
Params params = 1 [(gogoproto.nullable) = false];
}

The corresponding CLI command is:

pryzmd query assets params

Refractable Asset

A query to retrieve a refractable asset.

message QueryGetRefractableAssetRequest {
string asset_id = 1;
}

message QueryGetRefractableAssetResponse {
RefractableAsset asset = 1 [(gogoproto.nullable) = false];
}

The corresponding CLI command is:

pryzmd query assets show-refractable-asset [id]

Refractable Assets

This section provides queries to fetch lists of refractable assets and maturity levels. The enabled property in the first query is optional. If left empty, it returns all assets. If set to "true" or "false", it fetches enabled or disabled assets respectively.

message QueryAllRefractableAssetRequest {
string enabled = 1;
cosmos.base.query.v1beta1.PageRequest pagination = 2;
}

message QueryAllRefractableAssetResponse {
repeated RefractableAsset assets = 1 [(gogoproto.nullable) = false];
cosmos.base.query.v1beta1.PageResponse pagination = 2;
}

The corresponding CLI command is:

pryzmd query assets list-refractable-asset --enabled true

Note: The CLI command supports pagination flags.

Maturity Level

This query fetches a specific maturity level.

message QueryGetMaturityLevelRequest {
bool active = 1;
string asset_id = 2;
string symbol = 3;
}

message QueryGetMaturityLevelResponse {
MaturityLevel maturity_level = 1 [(gogoproto.nullable) = false];
}

The corresponding CLI command is:

pryzmd query assets show-maturity-level [active] [asset-id] [symbol]

Maturity Levels

This query fetches a list of maturity levels. The asset_id and asset_enabled parameters are optional.

message QueryAllMaturityLevelRequest {
bool active = 1;
string asset_id = 2;
string asset_enabled = 3;
cosmos.base.query.v1beta1.PageRequest pagination = 4;
}

message QueryAllMaturityLevelResponse {
repeated MaturityLevel maturity_level = 1 [(gogoproto.nullable) = false];
cosmos.base.query.v1beta1.PageResponse pagination = 2;
}

The corresponding CLI command is:

pryzmd query assets list-maturity-level active --enabled true --asset xyz

Note: The CLI command supports pagination flags.

Events

EventAddMaturityLevel

This event triggers when a new maturity level is listed. It contains the full structure of a maturity level (including asset base denom and maturity symbol).

message EventAddMaturityLevel {
MaturityLevel maturityLevel = 1;
}

EventDeactivateMaturityLevel

This event triggers when an existing maturity level expires. It contains the full structure of a maturity level.

message EventDeactivateMaturityLevel {
MaturityLevel maturityLevel = 1;
}

EventSetParams

This event triggers when parameters of the module are updated.

message EventSetParams {
Params params = 1 [(gogoproto.nullable) = false];
}

EventSetRefractableAsset

This event triggers when a new refractable asset is added to the store, or an existing one is updated.

message EventSetRefractableAsset {
RefractableAsset refractable_asset = 1 [(gogoproto.nullable) = false];
}

EventSetMaturityLevel

This event triggers when a maturity level is added to the store.

message EventSetMaturityLevel {
MaturityLevel maturity_level = 1 [(gogoproto.nullable) = false];
}

Internal Events

Exchange-Rate events: The Assets module dispatches internal events for any update in the exchange rates of each refractable asset. Listeners interested in such changes should implement the following interface:

type ExchangeRateListener interface {
ExchangeRateUpdated(ctx sdk.Context, assetID string, newExchangeRate sdk.Dec) error
}

Add listeners using the Assets keeper method in app.go :

app.AssetsKeeper.AddExchangeRateListener(app.RefractorKeeper.ExchangeRateListener())

Maturity-Level events: These events dispatch when a new maturity level is activated or deactivated.

// MaturityLevelListener is used for publishing events related to maturity level
type MaturityLevelListener interface {
MaturityLevelAdded(ctx sdk.Context, maturityLevel MaturityLevel) error
MaturityLevelDeactivated(ctx sdk.Context, maturityLevel MaturityLevel) error
}

Add listeners in app.go:

app.AssetsKeeper.AddMaturityLevelListener(app.YStakingKeeper.MaturityLevelListener())

State

Refractable assets

Assets are stored using their id and the following store key:

(
[]byte("v1/refractable-asset/") |
[]byte(assetId)
) -> ProtoBuf(RefractableAsset)

For unique denoms, a map from tokenDenom to assetId is used.

(
[]byte("v1/refractable-asset-by-token-denom/") |
[]byte(tokenDenom)
) -> assetId)

Maturity-level

The assets module maintains a list of all added maturity levels, indexed by activeness for easy traversal of active levels.

(
[]byte("v1/maturity-level/") |
[]byte(activeByte) | // 1 for inactive, 0 for active
[]byte(length-prefixed-assetId) |
[]byte(maturityLevel) // expiration in format of 20060102
) -> maturityLevel)

Params

The module parameters are stored as follows.

(
[]byte("v1/params/")
) -> parameters)

Parameters

The parameters of the assets module are stored in the following structure:

message Params {
FeeRatios default_fee_ratios = 1 [(gogoproto.nullable) = false];
repeated string admins = 2;
}

DefaultFeeRatios

This parameter specifies the default values for fee ratios, utilized when the ratio for an asset isn't set.

Admins

This parameter specifies the list of admin addresses, authorized to register assets, disable assets, and introduce maturities without gov proposal.

Genesis

The genesis state of this module contains:

  1. Params: Parameters for the assets module.
  2. Assets: A list of RefractableAsset objects, representing refractable assets.
  3. MaturityLevelList: A list of MaturityLevel objects, representing maturity levels for asset refracturing.
// GenesisState defines the assets module's genesis state.
message GenesisState {
Params params = 1 [(gogoproto.nullable) = false];
repeated RefractableAsset assets = 2 [(gogoproto.nullable) = false];
repeated MaturityLevel maturity_level_list = 4 [(gogoproto.nullable) = false];
}