diff --git a/app/params/config.go b/app/params/config.go index 4479099ee5..5221e348d1 100644 --- a/app/params/config.go +++ b/app/params/config.go @@ -39,17 +39,17 @@ func SetupDenomsAndPrefixes() { func RegisterDenoms() { // register pylon - err := sdk.RegisterDenom(PylonsHumanCoinUnit, sdk.OneDec()) - if err != nil { - panic(err) - } - err = sdk.RegisterDenom(PylonsBaseCoinUnit, sdk.NewDecWithPrec(1, PylonsExponent)) - if err != nil { - panic(err) - } + // err := sdk.RegisterDenom(PylonsHumanCoinUnit, sdk.OneDec()) + // if err != nil { + // panic(err) + // } + // err = sdk.RegisterDenom(PylonsBaseCoinUnit, sdk.NewDecWithPrec(1, PylonsExponent)) + // if err != nil { + // panic(err) + // } // register bedrock - err = sdk.RegisterDenom(StakingHumanCoinUnit, sdk.OneDec()) + err := sdk.RegisterDenom(StakingHumanCoinUnit, sdk.OneDec()) if err != nil { panic(err) } diff --git a/config.yml b/config.yml index 22f6824a25..46c78f56f8 100644 --- a/config.yml +++ b/config.yml @@ -1,11 +1,24 @@ +genesis: + chain_id: "pylons" + app_state: + staking: + params: + bond_denom: "ubedrock" + crisis: + constant_fee: + denom: ubedrock + gov: + deposit_params: + min_deposit: + denom: ubedrock accounts: - name: alice - coins: ["20000000000upylon", "200000000stake"] + coins: ["20000000000upylon", "200000000ubedrock"] - name: bob - coins: ["10000000000upylon", "100000000stake"] + coins: ["10000000000upylon", "100000000ubedrock"] validator: name: alice - staked: "100000000stake" + staked: "100000000ubedrock" client: vuex: path: "vue/src/store" @@ -13,5 +26,5 @@ client: path: "docs/static/openapi.yml" faucet: name: bob - coins: [ "10000000000upylon", "100000000stake" ] + coins: [ "10000000000upylon", "100000000ubedrock" ] diff --git a/docs/spec/01_concepts.md b/docs/spec/01_concepts.md index 7c69c41a2e..f027b41e60 100644 --- a/docs/spec/01_concepts.md +++ b/docs/spec/01_concepts.md @@ -3,4 +3,72 @@ order: 1 --> # Concepts -TODO \ No newline at end of file +_Disclaimer: This is work in progress. Mechanisms are susceptible to change._ + +An NFT acts like a deed of ownership to a digital item. The Pylons permissionless network for creating and trading virtual assets allows users to freely collect and display their digital assets. + +## NFTs on Pylons + +An "Item" is the pylons representation of an NFT. + +Simple NFT representations are described in the [EIP-721: Non-Fungible Token Standard](https://eips.ethereum.org/EIPS/eip-721) where every NFT is identified by a unique uint256 ID inside the ERC-721 smart contract. + +The `pylons` module moves beyond EIP-721 and uses the `Item` type with a variety of fields to describe arbitrary assets like images or in-game items. + +Items provide flexible fields such as "doubles", "longs" and "strings" to allow creators to provide deeply customized on-chain digital assets. +A `json` example of an Item is shown below. This item represents a character in a fantasy role-playing game called LOUD: + +```json +{ + "owner": "pylo1dfk3zqq7ysmzd34wfrsqqfhsnqdekh8lwatqre", + "cookbookID": "cookbookLOUD", + "ID": "11111111", + "doubles": [ + {"XP": "1351.000000000000000000"} + ], + "longs": [ + {"level": "11"}, + {"giantKills": "0"}, + {"special": "0"}, + {"specialDragonKill": "0"}, + {"undeadDragonKill": "0"} + ], + "strings": [ + {"entityType": "character"} + ], + "mutableStrings": [], + "tradeable": true, + "lastUpdate": "81", + "transferFee": [], + "tradePercentage": "0.100000000000000000" +} +``` + +## Item Lifecycle + +Items are created through executing Recipes. Recipes can be thought of as "blueprints" or "mini-programs" that can mint or modify items (NFTs). A collection of recipes is called a Cookbook. To bring this analogy full circle, executing a recipe is equivalent to "cooking" a recipe with the final +dish being the new or modified items. + +An item is created with this workflow: +- Bob creates a Cookbook +- Bob creates a Recipe from their Cookbook that mints Items +- Alice *executes* the Recipe +- After the Execution finalizes, the minted Item is in Alice's wallet + +Now that Alice owns an Item, they can send it to other users, use it in digital experiences, or even use it as an input to another recipe. +## Fees + +Pylons uses a fee model that differs from most Cosmos SDK-based blockchains. There are no base gas fees on transactions. Instead, Pylons uses custom fee logic implementations with fees only for certain transactions. This fee model enables certain transactions to occur for free to provide a 0-barrier-to-entry experience for users. + +Fees always exist on the following transactions: +- Send Items +- Trades +- Update Account +- Update Item String + +Fees are optional on the recipe execution transaction. + +## Accounts + +Since there are no fees on the Pylons chain, accounts need to be created for users with no coins in their balances. Pylons exposes a custom free +transaction for account creation to bypass this restriction and allow free experiences for uses. See the *Messages* section for more details on transactions. \ No newline at end of file diff --git a/docs/spec/02_state.md b/docs/spec/02_state.md index fabeda6cfe..a4ac6c1ea4 100644 --- a/docs/spec/02_state.md +++ b/docs/spec/02_state.md @@ -1,6 +1,146 @@ # State -TODO \ No newline at end of file + +The `pylons` module tracks the state of these primary objects: + +- Cookbooks +- Recipes +- Executions, pending and completed +- Items +- Trades +- PylonsAccounts + +## Cookbooks + +Cookbooks objects are containers for recipes. A cookbook could be a collection of recipes that make up a game experience or be a portfolio of recipes an artist uses to mint their NFTs from. + +The definition of a cookbook can be found in [`cookbook.proto`](LINK). + +```go +type Cookbook struct { + Creator string `protobuf:"bytes,1,opt,name=creator,proto3" json:"creator,omitempty"` + ID string `protobuf:"bytes,2,opt,name=ID,proto3" json:"ID,omitempty"` + NodeVersion string `protobuf:"bytes,3,opt,name=nodeVersion,proto3" json:"nodeVersion,omitempty"` + Name string `protobuf:"bytes,4,opt,name=name,proto3" json:"name,omitempty"` + Description string `protobuf:"bytes,5,opt,name=description,proto3" json:"description,omitempty"` + Developer string `protobuf:"bytes,6,opt,name=developer,proto3" json:"developer,omitempty"` + Version string `protobuf:"bytes,7,opt,name=version,proto3" json:"version,omitempty"` + SupportEmail string `protobuf:"bytes,8,opt,name=supportEmail,proto3" json:"supportEmail,omitempty"` + CostPerBlock types.Coin `protobuf:"bytes,9,opt,name=costPerBlock,proto3" json:"costPerBlock"` + Enabled bool `protobuf:"varint,10,opt,name=enabled,proto3" json:"enabled,omitempty"` +} +``` + +## Recipes + +Recipe objects are blueprints for digital experiences involving coins and NFT items. They can deterministically mint an NFT as users are familiar with from +other blockchains experiences like Ethereum, or specify mini-programs to probabilistically result in a variety of outcomes. The recipe structure contains +fields specifying the rules and logic of a recipe. + +The definition of a recipe can be found in [`recipe.proto`](LINK). + +```go +type Recipe struct { + CookbookID string `protobuf:"bytes,1,opt,name=cookbookID,proto3" json:"cookbookID,omitempty"` + ID string `protobuf:"bytes,2,opt,name=ID,proto3" json:"ID,omitempty"` + NodeVersion string `protobuf:"bytes,3,opt,name=nodeVersion,proto3" json:"nodeVersion,omitempty"` + Name string `protobuf:"bytes,4,opt,name=name,proto3" json:"name,omitempty"` + Description string `protobuf:"bytes,5,opt,name=description,proto3" json:"description,omitempty"` + Version string `protobuf:"bytes,6,opt,name=version,proto3" json:"version,omitempty"` + CoinInputs []CoinInput `protobuf:"bytes,7,rep,name=coinInputs,proto3" json:"coinInputs"` + ItemInputs []ItemInput `protobuf:"bytes,8,rep,name=itemInputs,proto3" json:"itemInputs"` + Entries EntriesList `protobuf:"bytes,9,opt,name=entries,proto3" json:"entries"` + Outputs []WeightedOutputs `protobuf:"bytes,10,rep,name=outputs,proto3" json:"outputs"` + BlockInterval int64 `protobuf:"varint,11,opt,name=blockInterval,proto3" json:"blockInterval,omitempty"` + Enabled bool `protobuf:"varint,12,opt,name=enabled,proto3" json:"enabled,omitempty"` + ExtraInfo string `protobuf:"bytes,13,opt,name=extraInfo,proto3" json:"extraInfo,omitempty"` +} +``` + +## Executions + +Execution objects are instances created when a user actually runs a recipe. The data structure contains information about the specific coins, items, +recipe and outputs involved in the execution. + +The definition of a recipe can be found in [`execution.proto`](LINK). + +```go +type Execution struct { + Creator string `protobuf:"bytes,1,opt,name=creator,proto3" json:"creator,omitempty"` + ID string `protobuf:"bytes,2,opt,name=ID,proto3" json:"ID,omitempty"` + RecipeID string `protobuf:"bytes,3,opt,name=recipeID,proto3" json:"recipeID,omitempty"` + CookbookID string `protobuf:"bytes,4,opt,name=cookbookID,proto3" json:"cookbookID,omitempty"` + RecipeVersion string `protobuf:"bytes,5,opt,name=recipeVersion,proto3" json:"recipeVersion,omitempty"` + NodeVersion string `protobuf:"bytes,6,opt,name=nodeVersion,proto3" json:"nodeVersion,omitempty"` + BlockHeight int64 `protobuf:"varint,7,opt,name=blockHeight,proto3" json:"blockHeight,omitempty"` + ItemInputs []ItemRecord `protobuf:"bytes,8,rep,name=itemInputs,proto3" json:"itemInputs"` + CoinInputs github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,9,rep,name=coinInputs,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"coinInputs"` + CoinOutputs github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,10,rep,name=coinOutputs,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"coinOutputs"` + ItemOutputIDs []string `protobuf:"bytes,11,rep,name=itemOutputIDs,proto3" json:"itemOutputIDs,omitempty"` + ItemModifyOutputIDs []string `protobuf:"bytes,12,rep,name=itemModifyOutputIDs,proto3" json:"itemModifyOutputIDs,omitempty"` +} +``` + +## Items + +Item objects provide the core asset identity file for the `pylons` module. . + +The definition of a recipe can be found in [`item.proto`](LINK). + + +````go +type Item struct { + Owner string `protobuf:"bytes,1,opt,name=owner,proto3" json:"owner,omitempty"` + CookbookID string `protobuf:"bytes,2,opt,name=cookbookID,proto3" json:"cookbookID,omitempty"` + ID string `protobuf:"bytes,3,opt,name=ID,proto3" json:"ID,omitempty"` + NodeVersion string `protobuf:"bytes,4,opt,name=nodeVersion,proto3" json:"nodeVersion,omitempty"` + Doubles []DoubleKeyValue `protobuf:"bytes,5,rep,name=doubles,proto3" json:"doubles"` + Longs []LongKeyValue `protobuf:"bytes,6,rep,name=longs,proto3" json:"longs"` + Strings []StringKeyValue `protobuf:"bytes,7,rep,name=strings,proto3" json:"strings"` + MutableStrings []StringKeyValue `protobuf:"bytes,8,rep,name=mutableStrings,proto3" json:"mutableStrings"` + Tradeable bool `protobuf:"varint,9,opt,name=tradeable,proto3" json:"tradeable,omitempty"` + LastUpdate int64 `protobuf:"varint,10,opt,name=lastUpdate,proto3" json:"lastUpdate,omitempty"` + TransferFee []types.Coin `protobuf:"bytes,11,rep,name=transferFee,proto3" json:"transferFee"` + // The percentage of a trade sale retained by the cookbook owner. In the range (0.0, 1.0). + TradePercentage github_com_cosmos_cosmos_sdk_types.Dec `protobuf:"bytes,12,opt,name=tradePercentage,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Dec" json:"tradePercentage"` +} +```` + +## Trades + +Trades objects are . + +The definition of a recipe can be found in [`trade.proto`](LINK). + + +```go +type Trade struct { + Creator string `protobuf:"bytes,1,opt,name=creator,proto3" json:"creator,omitempty"` + ID uint64 `protobuf:"varint,2,opt,name=ID,proto3" json:"ID,omitempty"` + CoinInputs []CoinInput `protobuf:"bytes,3,rep,name=coinInputs,proto3" json:"coinInputs"` + ItemInputs []ItemInput `protobuf:"bytes,4,rep,name=itemInputs,proto3" json:"itemInputs"` + CoinOutputs github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,5,rep,name=coinOutputs,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"coinOutputs"` + ItemOutputs []ItemRef `protobuf:"bytes,6,rep,name=itemOutputs,proto3" json:"itemOutputs"` + ExtraInfo string `protobuf:"bytes,7,opt,name=extraInfo,proto3" json:"extraInfo,omitempty"` + Receiver string `protobuf:"bytes,8,opt,name=receiver,proto3" json:"receiver,omitempty"` + TradedItemInputs []ItemRef `protobuf:"bytes,9,rep,name=tradedItemInputs,proto3" json:"tradedItemInputs"` +} +``` + +## PylonsAccounts + +The PylonsAccounts objects define a two-way map between a Cosmos SDK address and a username. + + . +The definition of a recipe can be found in [`accounts.proto`](LINK). + + +```go +type UserMap struct { + AccountAddr string `protobuf:"bytes,1,opt,name=accountAddr,proto3" json:"accountAddr,omitempty"` + Username string `protobuf:"bytes,2,opt,name=username,proto3" json:"username,omitempty"` +} +``` \ No newline at end of file diff --git a/docs/spec/03_messages.md b/docs/spec/03_messages.md index f2e6a58e56..89fd873e85 100644 --- a/docs/spec/03_messages.md +++ b/docs/spec/03_messages.md @@ -1,6 +1,454 @@ # Messages -TODO \ No newline at end of file + +This section describes the processing of the `pylons` messages and the corresponding updates to the state. + +## Accounts + +Accounts in `pylons` are a two-way map between a Cosmos SDK address and a username. + +Account creation in Pylons differs from standard Cosmos SDK chains where an account is created automatically and only when an address receives tokens. + +Pylons allows for free experiences for accounts that have 0 balance. A manual transaction for account creation is included. + +Usernames are enforced to be globally unique and MUST satisfy the following regular expression rule: + +```text +^[A-Za-z0-9]+(?:[ _-][A-Za-z0-9]+)*$ +``` + +### MsgCreateAccount + +```protobuf +message MsgCreateAccount { + string creator = 1; + string username = 2; +} +``` + +The message handling should fail if: +- an account has already been created with the creator address +- the username is already taken by another account + +### MsgUpdateAccount + + +```protobuf +message MsgUpdateAccount { + string creator = 1; + string username = 2; +} +``` + +The message handling should fail if: +- the username is already taken by another account + +## Cookbooks + +Cookbooks are the "container" object that `Recipe` objects are scoped to. +They specify a collection of recipes with some common relationship or ecosystem. + +### MsgCreateCookbook + +The `ID` string field MUST satisfy the following regular expression rule: + +```text +^[a-zA-Z_][a-zA-Z_0-9]*$ +``` + +The `name` string field MUST be a minimum of `MinNameFieldLength` characters. + +The `description` string field MUST be a minimum of `MinDescriptionFieldLength ` characters. + +The `version` string field MUST be a valid Semantic Version string. + +The `supportEmail` string field MUST be a valid email address satisfying the following regular expression rule: + +```text +^([a-zA-Z0-9_\-.]+)@([a-zA-Z0-9_\-.]+)\.([a-zA-Z0-9]{2,})$ +``` + +```protobuf +message MsgCreateCookbook { + string creator = 1; + string ID = 2; + string name = 3; + string description = 4; + string developer = 5; + string version = 6; + string supportEmail = 7; + cosmos.base.v1beta1.Coin costPerBlock = 8 [(gogoproto.nullable) = false]; + bool enabled = 9; +} +``` + +The message handling should fail if: +- the value of ID is already taken by another cookbook + +### `MsgUpdateCookbook` + +When updating a `Cookbook` the following fields may be modified: + +- `name` +- `description` +- `developer` +- `version` +- `supportEmail` +- `costPerBlock` +- `enabled` + +following the established regular expression rule restrictions. + +The `version` field MUST be greater than the current version when updating (ex. v0.1.1 > v0.1.0). + +```protobuf +message MsgUpdateCookbook { + string creator = 1; + string ID = 2; + string name = 3; + string description = 4; + string developer = 5; + string version = 6; + string supportEmail = 7; + cosmos.base.v1beta1.Coin costPerBlock = 8 [(gogoproto.nullable) = false]; + bool enabled = 9; +} +``` + +The message handling should fail if: +- the cookbook specified by ID is not owned by the message creator address or does not exist +- the version field is incorrectly updated + +`Cookbook`s may also be transferred. The `recipient` of the transfer will become the new `Cookbook` owner +and will therefore control the creation of new `Recipe`s and collect future fees from executions and transfers. + +### `MsgTransferCookbook` + +The `receiver` string field MUST be a valid Cosmos SDK address with the "pylo" prefix. + +```protobuf +message MsgTransferCookbook { + string creator = 1; + string ID = 2; + string recipient = 3; +} +``` + +The message handling should fail if: +- the cookbook specified by ID is not owned by the message creator address or does not exist + +## Recipes + +`Recipe` objects are blueprints for `Execution` objects, specifying inputs (coins and `Item`s) and outputs (coins and `Item`) as well as logic and probabilities for execution. + +### `MsgCreateRecipe` + +The `cookbookID` string field MUST: + +- Point to an existing `Cookbook` that is owned by `Recipe.creator`. +- Satisfy the following regular expression rule: + +```text +^[a-zA-Z_][a-zA-Z_0-9]*$ +``` + +The `ID` string field MUST satisfy the following regex: + +```text +^[a-zA-Z_][a-zA-Z_0-9]*$ +``` + +The `name` string field MUST be a minimum of `MinNameFieldLength` characters. + +The `description` string field MUST be a minimum of `MinDescriptionFieldLength ` characters. + +The `version` string field MUST be a valid Semantic Version string. + +The `blockInterval` field MUST be a non-negative integer. + +```protobuf +message MsgCreateRecipe { + string creator = 1; + string cookbookID = 2; + string ID = 3; + string name = 4; + string description = 5; + string version = 6; + repeated CoinInput coinInputs = 7 [(gogoproto.nullable) = false]; + repeated ItemInput itemInputs = 8 [(gogoproto.nullable) = false]; + EntriesList entries = 9 [(gogoproto.nullable) = false]; + repeated WeightedOutputs outputs = 10 [(gogoproto.nullable) = false]; + int64 blockInterval = 11; + bool enabled = 12; + string extraInfo = 13; +} +``` + +The message handling should fail if: +- the cookbook specified by cookbookID is not owned by the message creator address +- the value of ID is already taken by another recipe + +### `MsgUpdateRecipe` + +When a `Recipe` is updated, the following fields can be modified: + +- `name` +- `description` +- `developer` +- `version` +- `coinInputs` +- `itemInputs` +- `entries` +- `outputs` +- `enabled` +- `extraInfo` + +Updates must follow the established regex restrictions. + +The `version` field MUST be greater than the current version. For example, v0.1.1 > v0.1.0. + +```protobuf +message MsgUpdateRecipe { + string creator = 1; + string cookbookID = 2; + string ID = 3; + string name = 4; + string description = 5; + string version = 6; + repeated CoinInput coinInputs = 7 [(gogoproto.nullable) = false]; + repeated ItemInput itemInputs = 8 [(gogoproto.nullable) = false]; + EntriesList entries = 9 [(gogoproto.nullable) = false]; + repeated WeightedOutputs outputs = 10 [(gogoproto.nullable) = false]; + int64 blockInterval = 11; + bool enabled = 12; + string extraInfo = 13; +} +``` + +The message handling should fail if: +- the cookbook specified by cookbookID is not owned by the message creator address +- the recipe specified by ID is not owned by the message creator address +- the version field is incorrectly updated + +## Executions + +`Execution` objects represent an instance of running the "program" that is specified by a `Recipe`. Execution is essentially a function +taking a specified input (coins and `Item`s) and returning a concrete output (coins and `Item`s) based on its internal logic. + +When an `Execution` is created, it is added to the pending list of executions in a Store. Only the validity of the inputs is checked on submission. Execution and creation of the outputs is deferred until the `EndBlocker` of block #`executionSubmissionHeight + recipe.blockInterval`. + +### `MsgExecuteRecipe` + +The `cookbookID` string field MUST satisfy the following regular expression rule: + +```text +^[a-zA-Z_][a-zA-Z_0-9]*$ +``` + +The `recipeID` string field MUST satisfy the following regular expression rule: + +```text +^[a-zA-Z_][a-zA-Z_0-9]*$ +``` + +The `Recipe` and `Cookbook` specified by `recipeID` and `cookbookID` MUST have the same owner address. + +```protobuf +message MsgExecuteRecipe { + string creator = 1; + string cookbookID = 2; + string recipeID = 3; + uint64 coinInputsIndex = 4; + repeated string itemIDs = 5 [(gogoproto.nullable) = false]; +} +``` + +The message handling should fail if: +- the cookbook specified by cookbookID does not exist or is disabled +- the recipe specified by recipeID does not exist or is disabled +- the account of the creator message address does not have sufficient coins to cover the recipe coinInputs +- the items specified by itemIDs do not exist or are not owned by the message creator address +- the itemIDs provided by the message creator address do not [satisfy](https://github.com/Pylons-tech/pylons/blob/e0cc654fed2be191b7d10735a6ef1705cb1996d6/x/pylons/keeper/msg_server_execute_recipe.go#L80) the message itemInputs + +### `MsgCompleteExecutionEarly` + +An `Execution` can be completed before block #`executionSubmissionHeight + recipe.blockInterval` by submitting the Tx corresponding to`MsgCompleteExecutionEarly` and paying a fee +of: + +``` +completeEarlyFee = cookbook.costPerBlock * ((executionSubmissionHeight + recipe.blockInterval) - currentBlockHeightHeight ) +``` + +```protobuf +message MsgCompleteExecutionEarly { + string creator = 1; + string ID = 2; +} +``` + +The message handling should fail if: +- the execution specified by ID does not exist or was not created by the message creator address +- the account of the creator message address does not have sufficient coins to cover `completeEarlyFee` + +## Items + +`Item`s can be created only by executing a `Recipe` and cannot be created directly with a `Msg`. Items can, however, have their mutable strings updated or be transferred between accounts. + +### `MsgSetItemString` + +When modifying an `Item`'s strings, only the fields in its `mutableStrings` field can be mutated. + +The "mutableStrings" are stored as a map with a `field` and a corresponding `value`. + +The `cookbookID` string field MUST satisfy the following regular expression rule: + +```text +^[a-zA-Z_][a-zA-Z_0-9]*$ +``` + +It MUST also point to an existing `Cookbook` that is owned by `Recipe.creator`. + +The `ID` string field MUST be a valid 8 Byte base58 encoded unsigned integer ([encoding logic](https://github.com/Pylons-tech/pylons/blob/a5f1d165c41a3ab120f2997dd465b2685644b331/x/pylons/types/item.go#L14)). + +```protobuf +message MsgSetItemString { + string creator = 1; + string cookbookID = 2; + string ID = 4; + string field = 5; + string value = 6; +} +``` + +The message handling should fail if: +- the item specified by ID is not owned by the message creator address or does not exist + +### `MsgSendItems` + +Items can be sent from one account to another using the following `Msg` if the `creator` has enough balance to cover +all of the `item.transferFee` fields of each specified `Item`. + +The `reciever` string field MUST be a valid Cosmos SDK address with the "pylo" prefix. + +```protobuf +message MsgSendItems { + string creator = 1; + string receiver = 2; + repeated ItemRef items = 3 [(gogoproto.nullable) = false]; +} +``` + +The message handling should fail if: +- an item in the items field does not exist or is not owned by the message creator +- an item in the items field is not tradeable +- the account of the creator message address does not have sufficient coins to cover the item transferFees + +## Trades + +`Trade`s are posted to the blockchain when created. They can then be queried and "fulfilled" in another Tx. + + +### `MsgCreateTrade` + +Each `cookbookID` string field of the `itemOutputs` field MUST satisfy the following regular expression rule: + +```text +^[a-zA-Z_][a-zA-Z_0-9]*$ +``` + +Each `ID` string field of the `itemOutputs` field MUST be a valid 8 Byte base58 encoded unsigned integer ([encoding logic](https://github.com/Pylons-tech/pylons/blob/a5f1d165c41a3ab120f2997dd465b2685644b331/x/pylons/types/item.go#L14)). + +```protobuf +message MsgCreateTrade { + string creator = 1; + repeated CoinInput coinInputs = 2 [(gogoproto.nullable) = false]; + repeated ItemInput itemInputs = 3 [(gogoproto.nullable) = false]; + repeated cosmos.base.v1beta1.Coin coinOutputs = 4 [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; + repeated ItemRef itemOutputs = 5 [(gogoproto.nullable) = false]; + string extraInfo = 6; +} +``` + +The message handling should fail if: +- an item in the itemOutputs field does not exist or is not owned by the message creator +- an item in the itemOutputs field is not tradeable +- an `sdk.Coins` list in the coinInputs field cannot [cover](https://github.com/Pylons-tech/pylons/blob/e0cc654fed2be191b7d10735a6ef1705cb1996d6/x/pylons/keeper/msg_server_trade.go#L36) the fees of the itemOutputs items +- the account of the creator message address does not have sufficient coins to cover the coinOutputs + +### `MsgFulfillTrade` + +The `ID` string field MUST satisfy the following regular expression rule: + +```text +^[a-zA-Z_][a-zA-Z_0-9]*$ +``` + +Each `cookbookID` string field of the `items` field MUST satisfy the following regular expression rule: + +```text +^[a-zA-Z_][a-zA-Z_0-9]*$ +``` + +Each `ID` string field of the `items` field MUST be a valid 8-byte base58-encoded unsigned integer ([encoding logic](https://github.com/Pylons-tech/pylons/blob/a5f1d165c41a3ab120f2997dd465b2685644b331/x/pylons/types/item.go#L14)). + + +```protobuf +message MsgFulfillTrade { + string creator = 1; + uint64 ID = 2; + uint64 coinInputsIndex = 3; + repeated ItemRef items = 4 [(gogoproto.nullable) = false]; +} +``` + +The message handling should fail if: +- the trade specified by ID does not exist +- the coinInputsIndex value is larger than the number of coinInputs to choose from in the trade +- an item from the items field is not owned by the message creator or does not exist +- an item from the items field is not tradeable +- the items provided by the message creator address do not [satisfy](https://github.com/Pylons-tech/pylons/blob/e0cc654fed2be191b7d10735a6ef1705cb1996d6/x/pylons/keeper/msg_server_fulfill_trade.go#L81) the message itemInputs +- the `sdk.Coins` list in the coinOutputs field cannot [cover](https://github.com/Pylons-tech/pylons/blob/e0cc654fed2be191b7d10735a6ef1705cb1996d6/x/pylons/keeper/msg_server_fulfill_trade.go#L94) the fees of all items in the items field +- the selected coinInputs `sdk.Coins` list cannot [cover](https://github.com/Pylons-tech/pylons/blob/e0cc654fed2be191b7d10735a6ef1705cb1996d6/x/pylons/keeper/msg_server_fulfill_trade.go#L116) the fees of all items in the trade itemOutputs + +### `MsgCancelTrade` + +The `ID` string field MUST satisfy the following regular expression rule: + +```text +^[a-zA-Z_][a-zA-Z_0-9]*$ +``` + +```protobuf +message MsgCancelTrade { + string creator = 1; + uint64 ID = 2; +} +``` + +The message handling should fail if: +- the trade specified by ID does not exist or was not created by the message creator + +## Purchases + +### `MsgGoogleInAppPurchaseGetCoins` + +```protobuf +message MsgGoogleInAppPurchaseGetCoins { + string creator = 1; + string productID = 2; + string purchaseToken = 3; + string receiptDataBase64 = 4; + string signature = 5; +} +``` + +The message handling should fail if: +- a Google IAP Order specified by the purchaseToken does not exist +- the Google IAP signature is invalid + + \ No newline at end of file diff --git a/docs/spec/04_events.md b/docs/spec/04_events.md index ec0b96a4e5..6babe15581 100644 --- a/docs/spec/04_events.md +++ b/docs/spec/04_events.md @@ -1,6 +1,207 @@ # Events -TODO \ No newline at end of file + +The `pylons` module emits Cosmos SDK `TypedEvent`s in the form of proto messages to provide state updates for applications like block explorers. + +The `pylons` module emits the following events: + +## EventCreateAccount + +Emitted when a PylonsAccount is successfully created. +```protobuf +message EventCreateAccount { + string address = 1; + string username = 2; +} +``` + +## EventUpdateAccount + +Emitted when a PylonsAccount is successfully updated. +```protobuf +message EventUpdateAccount { + string address = 1; + string username = 2; +} +``` + +## EventCreateCookbook + +Emitted when a `Cookbook` is successfully created. +```protobuf +message EventCreateCookbook { + string creator = 1; + string ID = 2; +} +``` + +## EventUpdateCookbook + +Emitted when a `Cookbook` is successfully updated. Message contains the `Cookbook` data before updating for archiving purposes. +```protobuf +message EventUpdateCookbook { + Cookbook originalCookbook = 1 [(gogoproto.nullable) = false]; +} +``` + +## EventTransferCookbook + +Emitted when a `Cookbook` is successfully transferred between two addresses. +```protobuf +message EventTransferCookbook { + string sender = 1; + string receiver = 2; + string ID = 3; +} +``` + +## EventCreateRecipe + +Emitted when a `Recipe` is successfully created. +```protobuf +message EventCreateRecipe { + string creator = 1; + string CookbookID = 2; + string ID = 3; +} +``` + +## EventUpdateRecipe + +Emitted when a `Recipe` is successfully updated. Message contains the `Recipe` data before updating for archiving purposes. +```protobuf +message EventUpdateRecipe { + Recipe originalRecipe = 1 [(gogoproto.nullable) = false]; +} + +``` + +## EventCreateExecution + +Emitted when an `Execution` is successfully submitted. +```protobuf +message EventCreateExecution { + string creator = 1; + string ID = 2; +} +``` + +## EventCompleteExecution + +Emitted when an `Execution` is successfully completed. Contains extra information since execution outcome is not predictable until completion. +```protobuf +message EventCompleteExecution { + string creator = 1; + string ID = 2; + repeated cosmos.base.v1beta1.Coin burnCoins = 3 [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; + repeated cosmos.base.v1beta1.Coin payCoins = 4 [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; + repeated cosmos.base.v1beta1.Coin transferCoins = 5 [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; + repeated cosmos.base.v1beta1.Coin feeCoins = 6 [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; + repeated cosmos.base.v1beta1.Coin coinOutputs = 7 [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; + repeated Item mintItems = 8 [(gogoproto.nullable) = false]; + repeated Item modifyItems = 9 [(gogoproto.nullable) = false]; +} +``` + +## EventDropExecution + +Emitted when an `Execution` is dropped. This indicates that the `Execution` could not be completed and was abandoned. +```protobuf +message EventDropExecution { + string creator = 1; + string ID = 2; +} +``` + +## EventCompleteExecutionEarly + +Emitted when an `Execution` is pushed to be executed immediately using the MsgCompleteExecutionEarly Tx. The `Execution` still must be finalized, so it can either become "dropped" or "completed". +```protobuf +message EventCompleteExecutionEarly { + string creator = 1; + string ID = 2; +} +``` + +## EventSendItems + +Emitted when a `SendItems` Tx is successfully completed. +```protobuf +message EventSendItems { + string sender = 1; + string receiver = 2; + repeated ItemRef items = 3 [(gogoproto.nullable) = false]; +} +``` + +## EventSetItemString + +Emitted when MutableStrings fields are updated on an `Item`. Message contains the original MutableStrings fields for archival purposes. +```protobuf +message EventSetItemString { + string creator = 1; + string CookbookID = 2; + string ID = 3; + repeated StringKeyValue originalMutableStrings = 4 [(gogoproto.nullable) = false]; +} +``` + +## EventCreateTrade + +Emitted when a `Trade` is successfully created. +```protobuf +message EventCreateTrade { + string creator = 1; + uint64 ID = 2; +``` + +## EventCancelTrade + +Emitted when a `Trade` is canceled +```protobuf +message EventCancelTrade { + string creator = 1; + uint64 ID = 2; +} +``` + +## EventFulfillTrade + +Emitted when a `Trade` is completed. +```protobuf +message EventFulfillTrade { + uint64 ID = 1; + string creator = 2; + string fulfiller = 3; + repeated ItemRef itemInputs = 4 [(gogoproto.nullable) = false]; + repeated cosmos.base.v1beta1.Coin coinInputs = 5 [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; + repeated ItemRef itemOutputs = 6 [(gogoproto.nullable) = false]; + repeated cosmos.base.v1beta1.Coin coinOutputs = 7 [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; +} +``` + +## EventGooglePurchase + +Emitted when a Google IAP Purchase is completed. +```protobuf +message EventGooglePurchase { + string creator = 1; + string productID = 2; + string purchaseToken = 3; + string receiptDataBase64 = 4; + string signature = 5; +} +``` + +## EventStripePurchase + +Emitted when a Stripe purchase is completed. +```protobuf +message EventStripePurchase { + string creator = 1; + string ID = 2; +} +``` \ No newline at end of file diff --git a/docs/spec/05_parameters.md b/docs/spec/05_parameters.md new file mode 100644 index 0000000000..b8d145b7d0 --- /dev/null +++ b/docs/spec/05_parameters.md @@ -0,0 +1,100 @@ + + +# Parameters + +The pylons module contains the following parameters: + +| Key | Type | Example | +| ------------------------------------- | ------------- | -------------------------------- | +| MinNameFieldLength | uint64 | 8 | +| MinDescriptionFieldLength | uint64 | 20 | +| CoinIssuers | []CoinIssuer |(see below) | +| RecipeFeePercentage |sdk.Dec | 10.0 | +| ItemTransferFeePercentage | sdk.Dec | 20.0 | +| UpdateItemStringFee | sdk.Coin | {"denom": "upylon", "amount", 10} | +| UpdateUsernameFee | sdk.Coin | {"denom": "upylon", "amount", 10} | +| MinTransferFee | sdk.Int | 20 | +| MaxTransferFee | sdk.Int | 20 | + + +## MinNameFieldLength + +Minimum string length for the "name" field in `Recipe` and `Cookbook` types on chain. + +## MinDescriptionFieldLength + +Minimum string length for the "description" field in `Recipe` and `Cookbook` types on chain. + +## CoinIssuers + +Structure to represent a trusted Entity with coin issuing credentials on the pylons chain. For example, +Pylons Inc is a trusted coin issuer for issuing the "upylons" denom token. The structure also contains +fields for `Packages` which represent the Issuer's Google IAP credentials for on-chain Google IAP functionality. + +Example: + +```go + DefaultCoinIssuers = []CoinIssuer{ + { + CoinDenom: "upylon", + Packages: []GoogleInAppPurchasePackage{ + {PackageName: "com.pylons.loud", ProductID: "pylons_1000", Amount: sdk.NewInt(1000)}, + {PackageName: "com.pylons.loud", ProductID: "pylons_55000", Amount: sdk.NewInt(55000)}, + }, + GoogleInAppPurchasePubKey: "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwZsjhk6eN5Pve9pP3uqz2MwBFixvmCRtQJoDQLTEJo3zTd9VMZcXoerQX8cnDPclZWmMZWkO+BWcN1ikYdGHvU2gC7yBLi+TEkhsEkixMlbqOGRdmNptJJhqxuVmXK+drWTb6W0IgQ9g8CuCjZUiMTc0UjHb5mPOE/IhcuTZ0wCHdoqc5FS2spdQqrohvSEP7gR4ZgGzYNI1U+YZHskIEm2qC4ZtSaX9J/fDkAmmJFV2hzeDMcljCxY9+ZM1mdzIpZKwM7O6UdWRpwD1QJ7yXND8AQ9M46p16F0VQuZbbMKCs90NIcKkx6jDDGbVmJrFnUT1Oq1uYxNYtiZjTp+JowIDAQAB", + EntityName: "Pylons_Inc", + }, + { + CoinDenom: "uatom", + EntityName: "Cosmos_Hub", + }, + { + CoinDenom: "ubedrock", + EntityName: "Pylons_Chain", + }, + } +``` + +## RecipeFeePercentage + +Percentage of `Recipe` coinInputs that is taken as a fee for execution. + +``` +CookbookOwnerAmount = (1 - RecipeFeePercentage) * coinInputs +``` + +``` +FeeAmount = RecipeFeePercentage * coinInputs +``` + +## ItemTransferFeePercentage + +Percentage of coinInputs or transferFees for `Item` objects that is taken as a fee for trades or item-sending, respectively. + +``` +CookbookOwnerAmount = (1 - ItemTransferFeePercentage) * coinInputs +``` + +``` +FeeAmount = ItemTransferFeePercentage * coinInputs +``` + +## UpdateItemStringFee + +Fee for updating a string in the `MutableStrings` field of an `Item` using the `SetItemString` Tx. + +## UpdateUsernameFee + +Fee for updating the username of an account. + +## MinTransferFee + +Minimum transfer fee for sending `Item`s. + +## MaxTransferFee + +Minimum transfer fee for sending `Item`s. + + diff --git a/docs/spec/06_client.md b/docs/spec/06_client.md new file mode 100644 index 0000000000..33555b2715 --- /dev/null +++ b/docs/spec/06_client.md @@ -0,0 +1,1192 @@ + + +# Client + + +## CLI + +A user can query and interact with the `pylons` module using the CLI. + +### Query + +The `query` commands allow users to query `pylons` state. + +```bash +pylonsd query pylons --help +``` + +#### get-address-by-username + +```bash + pylonsd query pylons get-address-by-username [username] [flags] +``` + +#### get-username-by-address + +```bash + pylonsd query pylons get-username-by-address [address] [flags] +``` + +#### get-cookbook + +```bash +pylonsd query pylons get-cookbook [id] [flags] +``` + +#### get-execution + +```bash + pylonsd query pylons get-execution [id] [flags] +``` + +#### get-google-iap-order + +```bash + pylonsd query pylons get-google-iap-order [purchase-token] [flags] +``` + +#### get-item + +```bash + pylonsd query pylons get-item [cookbook-id] [id] [flags] +``` + +#### get-recipe + +```bash + pylonsd query pylons get-recipe [cookbook-id] [id] [flags] +``` + +#### get-trade + +```bash + pylonsd query pylons get-trade [id] [flags] +``` + +#### list-cookbooks + +```bash + pylonsd query pylons list-cookbooks [addr] [flags] +``` + +#### list-executions-by-item + +```bash + pylonsd query pylons list-executions-by-item [cookbook-id] [id] [flags] +``` + +#### list-executions-by-recipe + +```bash + pylonsd query pylons list-executions-by-item [cookbook-id] [id] [flags] +``` + +#### list-item-by-owner + +```bash + pylonsd query pylons list-item-by-owner [owner] [flags] +``` + +#### list-recipes-by-cookbook + +```bash + pylonsd query pylons list-recipes-by-cookbook [id] [flags] +``` + +#### list-trades + +```bash + pylonsd query pylons list-trades [creator] [flags] +``` + +### Transactions + +The `tx` commands allow users to interact with the `pylons` module. + +#### create-account + +```bash + pylonsd tx pylons create-account [username] [flags] +``` + +#### update-account + +```bash + pylonsd tx pylons update-account [username] [flags] +``` + + +#### create-cookbook + +```bash + pylonsd tx pylons create-cookbook [id] [name] [description] [developer] [version] [support-email] [cost-per-block] [enabled] [flags] +``` + +#### transfer-cookbook + +```bash + pylonsd tx pylons transfer-cookbook [cookbookID] [recipient] [flags] +``` + +#### update-cookbook + +```bash + pylonsd tx pylons update-cookbook [id] [name] [description] [developer] [version] [support-email] [cost-per-block] [enabled] [flags] +``` + +#### create-recipe + +```bash + pylonsd tx pylons create-recipe [cookbook-id] [id] [name] [description] [version] [coin-inputs] [item-inputs] [entries] [outputs] [block-interval] [enabled] [extra-info] [flags] +``` + +#### update-recipe + +```bash + pylonsd tx pylons update-recipe [cookbook-id] [id] [name] [description] [version] [coinInputs] [itemInputs] [entries] [outputs] [blockInterval] [enabled] [extraInfo] [flags] +``` + +#### execute-recipe + +```bash + pylonsd tx pylons execute-recipe [cookbook-id] [recipe-id] [item-ids] [flags] +``` + +#### set-item-string + +```bash + pylonsd tx pylons set-item-string [cookbook-id] [id] [field] [value] [flags] +``` + +#### send-items + +```bash + pylonsd tx pylons send-items [receiver] [items] [flags] +``` + +#### create-trade + +```bash + pylonsd tx pylons create-trade [coinInputs] [itemInputs] [coinOutputs] [itemOutputs] [extraInfo] [flags] +``` + +#### cancel-trade + +```bash + pylonsd tx pylons cancel-trade [id] [flags] +``` + +#### fulfill-trade + +```bash + pylonsd tx pylons fulfill-trade [id] [items] [flags] +``` + +#### google-iap-get-pylons + +```bash + pylonsd tx pylons google-iap-get-pylons [productID] [purchaseToken] [recieptDataBase64] [signature] [flags] +``` + +## gRPC + +A user can query the `pylons` module using gRPC endpoints. + +#### get-address-by-username + +Endpoint: +``` +Pylonstech.pylons.pylons.Query/AddressByUsername +``` + +#### get-username-by-address + +Endpoint: +``` +Pylonstech.pylons.pylons.Query/UsernameByAddress +``` + +#### get-cookbook + +Endpoint: +``` +Pylonstech.pylons.pylons.Query/Cookbook +``` + +#### list-cookbooks + +Endpoint: +``` +Pylonstech.pylons.pylons.Query/ListCookbooksByCreator +``` + +#### get-recipe + +Endpoint: +``` +Pylonstech.pylons.pylons.Query/Recipe +``` + +#### list-recipes-by-cookbook + +Endpoint: +``` +Pylonstech.pylons.pylons.Query/ListRecipesByCookbook +``` + +#### get-execution + +Endpoint: +``` +Pylonstech.pylons.pylons.Query/Execution +``` + +#### list-executions-by-item + +Endpoint: +``` +Pylonstech.pylons.pylons.Query/ListExecutionsByItem +``` + +#### list-executions-by-recipe + +Endpoint: +``` +Pylonstech.pylons.pylons.Query/ListExecutionsByRecipe +``` + +#### get-item + +Endpoint: +``` +Pylonstech.pylons.pylons.Query/Item +``` + +#### list-items-by-owner + +Endpoint: +``` +Pylonstech.pylons.pylons.Query/ListItemByOwner +``` + +#### get-trade + +Endpoint: +``` +Pylonstech.pylons.pylons.Query/Trade +``` + +#### get-google-iap-order + +Endpoint: +``` +Pylonstech.pylons.pylons.Query/GoogleInAppPurchaseOrder +``` + +## REST + +A user can query the `pylons` module using REST endpoints. The URL shown below uses "HOST" as the base. If running a node locally using starport for testing, +"HOST" would be http://0.0.0.0:1317. + +#### get-address-by-username + +Request: + +```bash +curl -X GET "HOST/pylons/account/username/{username}" -H "accept: application/json" +``` + +Example response: + +```json +{ + "address": { + "value": "string" + } +} +``` + +#### get-username-by-address + +Request: + +```bash +curl -X GET "HOST/pylons/account/address/{address}" -H "accept: application/json" +``` + +Example response: + +```json +{ + "username": { + "value": "string" + } +} +``` + +#### get-cookbook + +Request: + + +```bash +curl -X GET "HOST/pylons/cookbook/{ID}" -H "accept: application/json" +``` + +Example response: + +```json +{ + "Cookbook": { + "creator": "string", + "ID": "string", + "nodeVersion": "string", + "name": "string", + "description": "string", + "developer": "string", + "version": "string", + "supportEmail": "string", + "costPerBlock": { + "denom": "string", + "amount": "string" + }, + "enabled": true + } +} +``` + +#### list-cookbooks + +Request: + + +This query is shown with pagination options. + +```bash +curl -X GET "HOST/pylons/cookbooks/{address}?pagination.countTotal=true&pagination.reverse=true" -H "accept: application/json" +``` + +Example response: + +```json +{ + "Cookbooks": [ + { + "creator": "string", + "ID": "string", + "nodeVersion": "string", + "name": "string", + "description": "string", + "developer": "string", + "version": "string", + "supportEmail": "string", + "costPerBlock": { + "denom": "string", + "amount": "string" + }, + "enabled": true + } + ], + "pagination": { + "nextKey": "string", + "total": "string" + } +} +``` + +#### get-recipe + +Request: + + +```bash +curl -X GET "HOST/pylons/recipe/{cookbookID}/{ID}" -H "accept: application/json" +``` + +Example response: + +```json +{ + "Recipe": { + "cookbookID": "string", + "ID": "string", + "nodeVersion": "string", + "name": "string", + "description": "string", + "version": "string", + "coinInputs": [ + { + "coins": [ + { + "denom": "string", + "amount": "string" + } + ] + } + ], + "itemInputs": [ + { + "ID": "string", + "doubles": [ + { + "key": "string", + "minValue": "string", + "maxValue": "string" + } + ], + "longs": [ + { + "key": "string", + "minValue": "string", + "maxValue": "string" + } + ], + "strings": [ + { + "key": "string", + "value": "string" + } + ], + "conditions": { + "doubles": [ + { + "key": "string", + "minValue": "string", + "maxValue": "string" + } + ], + "longs": [ + { + "key": "string", + "minValue": "string", + "maxValue": "string" + } + ], + "strings": [ + { + "key": "string", + "value": "string" + } + ] + } + } + ], + "entries": { + "coinOutputs": [ + { + "ID": "string", + "coin": { + "denom": "string", + "amount": "string" + }, + "program": "string" + } + ], + "itemOutputs": [ + { + "ID": "string", + "doubles": [ + { + "key": "string", + "rate": "string", + "weightRanges": [ + { + "lower": "string", + "upper": "string", + "weight": "string" + } + ], + "program": "string" + } + ], + "longs": [ + { + "key": "string", + "rate": "string", + "weightRanges": [ + { + "lower": "string", + "upper": "string", + "weight": "string" + } + ], + "program": "string" + } + ], + "strings": [ + { + "key": "string", + "rate": "string", + "value": "string", + "program": "string" + } + ], + "mutableStrings": [ + { + "Key": "string", + "Value": "string" + } + ], + "transferFee": [ + { + "denom": "string", + "amount": "string" + } + ], + "tradePercentage": "string", + "quantity": "string", + "amountMinted": "string", + "tradeable": true + } + ], + "itemModifyOutputs": [ + { + "ID": "string", + "itemInputRef": "string", + "doubles": [ + { + "key": "string", + "rate": "string", + "weightRanges": [ + { + "lower": "string", + "upper": "string", + "weight": "string" + } + ], + "program": "string" + } + ], + "longs": [ + { + "key": "string", + "rate": "string", + "weightRanges": [ + { + "lower": "string", + "upper": "string", + "weight": "string" + } + ], + "program": "string" + } + ], + "strings": [ + { + "key": "string", + "rate": "string", + "value": "string", + "program": "string" + } + ], + "mutableStrings": [ + { + "Key": "string", + "Value": "string" + } + ], + "transferFee": [ + { + "denom": "string", + "amount": "string" + } + ], + "tradePercentage": "string", + "quantity": "string", + "amountMinted": "string", + "tradeable": true + } + ] + }, + "outputs": [ + { + "entryIDs": [ + "string" + ], + "weight": "string" + } + ], + "blockInterval": "string", + "enabled": true, + "extraInfo": "string" + } +} +``` + +#### list-recipes-by-cookbook + +Request: + +This query is shown with pagination options. + +```bash +curl -X GET "HOST/pylons/recipes/{address}?pagination.countTotal=false&pagination.reverse=true" -H "accept: application/json" +``` + +Example response: + +```json +{ + "Recipes": [ + { + "cookbookID": "string", + "ID": "string", + "nodeVersion": "string", + "name": "string", + "description": "string", + "version": "string", + "coinInputs": [ + { + "coins": [ + { + "denom": "string", + "amount": "string" + } + ] + } + ], + "itemInputs": [ + { + "ID": "string", + "doubles": [ + { + "key": "string", + "minValue": "string", + "maxValue": "string" + } + ], + "longs": [ + { + "key": "string", + "minValue": "string", + "maxValue": "string" + } + ], + "strings": [ + { + "key": "string", + "value": "string" + } + ], + "conditions": { + "doubles": [ + { + "key": "string", + "minValue": "string", + "maxValue": "string" + } + ], + "longs": [ + { + "key": "string", + "minValue": "string", + "maxValue": "string" + } + ], + "strings": [ + { + "key": "string", + "value": "string" + } + ] + } + } + ], + "entries": { + "coinOutputs": [ + { + "ID": "string", + "coin": { + "denom": "string", + "amount": "string" + }, + "program": "string" + } + ], + "itemOutputs": [ + { + "ID": "string", + "doubles": [ + { + "key": "string", + "rate": "string", + "weightRanges": [ + { + "lower": "string", + "upper": "string", + "weight": "string" + } + ], + "program": "string" + } + ], + "longs": [ + { + "key": "string", + "rate": "string", + "weightRanges": [ + { + "lower": "string", + "upper": "string", + "weight": "string" + } + ], + "program": "string" + } + ], + "strings": [ + { + "key": "string", + "rate": "string", + "value": "string", + "program": "string" + } + ], + "mutableStrings": [ + { + "Key": "string", + "Value": "string" + } + ], + "transferFee": [ + { + "denom": "string", + "amount": "string" + } + ], + "tradePercentage": "string", + "quantity": "string", + "amountMinted": "string", + "tradeable": true + } + ], + "itemModifyOutputs": [ + { + "ID": "string", + "itemInputRef": "string", + "doubles": [ + { + "key": "string", + "rate": "string", + "weightRanges": [ + { + "lower": "string", + "upper": "string", + "weight": "string" + } + ], + "program": "string" + } + ], + "longs": [ + { + "key": "string", + "rate": "string", + "weightRanges": [ + { + "lower": "string", + "upper": "string", + "weight": "string" + } + ], + "program": "string" + } + ], + "strings": [ + { + "key": "string", + "rate": "string", + "value": "string", + "program": "string" + } + ], + "mutableStrings": [ + { + "Key": "string", + "Value": "string" + } + ], + "transferFee": [ + { + "denom": "string", + "amount": "string" + } + ], + "tradePercentage": "string", + "quantity": "string", + "amountMinted": "string", + "tradeable": true + } + ] + }, + "outputs": [ + { + "entryIDs": [ + "string" + ], + "weight": "string" + } + ], + "blockInterval": "string", + "enabled": true, + "extraInfo": "string" + } + ], + "pagination": { + "nextKey": "string", + "total": "string" + } +} +``` + +#### get-execution + +Request: + +```bash +curl -X GET "HOST/pylons/execution/{ID}" -H "accept: application/json" +``` + +Example Response: + +```json +{ + "Execution": { + "creator": "string", + "ID": "string", + "recipeID": "string", + "cookbookID": "string", + "recipeVersion": "string", + "nodeVersion": "string", + "blockHeight": "string", + "itemInputs": [ + { + "ID": "string", + "doubles": [ + { + "Key": "string", + "Value": "string" + } + ], + "longs": [ + { + "Key": "string", + "Value": "string" + } + ], + "strings": [ + { + "Key": "string", + "Value": "string" + } + ] + } + ], + "coinInputs": [ + { + "denom": "string", + "amount": "string" + } + ], + "coinOutputs": [ + { + "denom": "string", + "amount": "string" + } + ], + "itemOutputIDs": [ + "string" + ], + "itemModifyOutputIDs": [ + "string" + ] + }, + "Completed": true +} +``` +#### list-executions-by-item + +Request: + +This query is shown with pagination options. + + +```bash +curl -X GET "HOST/pylons/executions/item/{cookbookID}/{ID}?pagination.countTotal=false&pagination.reverse=true" -H "accept: application/json" +``` + +Example Response: + +```json +{ + "CompletedExecutions": [], + "PendingExecutions": [], + "pagination": { + "next_key": null, + "total": "0" + } +} +``` +#### list-executions-by-recipe + +Request: + +This query is shown with pagination options. + + +```bash +curl -X GET "HOST/pylons/executions/recipe/a/a?pagination.countTotal=true&pagination.reverse=false" -H "accept: application/json" + +``` + +Example Response: + +```json +{ + "CompletedExecutions": [], + "PendingExecutions": [], + "pagination": { + "next_key": null, + "total": "0" + } +} +``` + +#### get-item + +Request: + +```bash +curl -X GET "HOST/pylons/item/{cookbookID}/{ID}" -H "accept: application/json" + +``` + +Example Response: + +```json +{ + "Item": { + "owner": "string", + "cookbookID": "string", + "ID": "string", + "nodeVersion": "string", + "doubles": [ + { + "Key": "string", + "Value": "string" + } + ], + "longs": [ + { + "Key": "string", + "Value": "string" + } + ], + "strings": [ + { + "Key": "string", + "Value": "string" + } + ], + "mutableStrings": [ + { + "Key": "string", + "Value": "string" + } + ], + "tradeable": true, + "lastUpdate": "string", + "transferFee": [ + { + "denom": "string", + "amount": "string" + } + ], + "tradePercentage": "string" + } +} +``` + +#### list-items-by-owner + +This query is shown with pagination options. + +Request: + +```bash +curl -X GET "HOST/pylons/items/{address}?pagination.countTotal=true&pagination.reverse=true" -H "accept: application/json" +``` + +Example Response: + +```json +{ + "Items": [ + { + "owner": "string", + "cookbookID": "string", + "ID": "string", + "nodeVersion": "string", + "doubles": [ + { + "Key": "string", + "Value": "string" + } + ], + "longs": [ + { + "Key": "string", + "Value": "string" + } + ], + "strings": [ + { + "Key": "string", + "Value": "string" + } + ], + "mutableStrings": [ + { + "Key": "string", + "Value": "string" + } + ], + "tradeable": true, + "lastUpdate": "string", + "transferFee": [ + { + "denom": "string", + "amount": "string" + } + ], + "tradePercentage": "string" + } + ], + "pagination": { + "nextKey": "string", + "total": "string" + } +} + +``` + +### get-trade + +Request: + +```bash +curl -X GET "HOST/pylons/trade/{ID}" -H "accept: application/json" +``` + +Example Response: + +```json +{ + "Trade": { + "creator": "string", + "ID": "string", + "coinInputs": [ + { + "coins": [ + { + "denom": "string", + "amount": "string" + } + ] + } + ], + "itemInputs": [ + { + "ID": "string", + "doubles": [ + { + "key": "string", + "minValue": "string", + "maxValue": "string" + } + ], + "longs": [ + { + "key": "string", + "minValue": "string", + "maxValue": "string" + } + ], + "strings": [ + { + "key": "string", + "value": "string" + } + ], + "conditions": { + "doubles": [ + { + "key": "string", + "minValue": "string", + "maxValue": "string" + } + ], + "longs": [ + { + "key": "string", + "minValue": "string", + "maxValue": "string" + } + ], + "strings": [ + { + "key": "string", + "value": "string" + } + ] + } + } + ], + "coinOutputs": [ + { + "denom": "string", + "amount": "string" + } + ], + "itemOutputs": [ + { + "cookbookID": "string", + "itemID": "string" + } + ], + "extraInfo": "string", + "receiver": "string", + "tradedItemInputs": [ + { + "cookbookID": "string", + "itemID": "string" + } + ] + } +} +``` + +#### get-google-iap-order + +Request: + +```bash +curl -X GET "HOST/pylons/iap/{purchaseToken}" -H "accept: application/json" +``` + +Example Response: + +```json +{ + "Order": { + "creator": "string", + "productID": "string", + "purchaseToken": "string", + "receiptDataBase64": "string", + "signature": "string" + } +} +``` + diff --git a/docs/spec/07_future_improvements.md b/docs/spec/07_future_improvements.md new file mode 100644 index 0000000000..dc2f9a9224 --- /dev/null +++ b/docs/spec/07_future_improvements.md @@ -0,0 +1,9 @@ + + +# Future Improvements + +The current documentation only describes the minimum viable product for the `pylons` module. Future improvements may include: +- Stripe purchases similar to Google IAP orders +- Separation of functionalities into modules (Executions module, trades module) instead of a monolithic `pylons` module diff --git a/docs/spec/README.md b/docs/spec/README.md index f9e89220cf..98ffc67b8c 100644 --- a/docs/spec/README.md +++ b/docs/spec/README.md @@ -7,13 +7,21 @@ parent: # `pylons` -## Contents +Pylons is the flagship NFT chain in the Cosmos ecosystem. Built on the Cosmos SDK, Pylons is a fast and interoperable system for brands and creators to build engaging products with meaningful NFT experiences. + +Pylons is the fastest, easiest way to launch a massive NFT product with minimal transaction fees. ## Abstract -TODO + +This document specifies the `pylons` module for the Pylons NFT blockchain. + +## Contents 1. **[Concepts](01_concepts.md)** 2. **[State](02_state.md)** 3. **[Messages](03_messages.md)** 4. **[Events](04_events.md)** +5. **[Parameters](05_parameters.md)** +6. **[Client](06_client.md)** +7. **[Future Improvements](07_future_improvements.md)** \ No newline at end of file