diff --git a/Q-Apps.md b/Q-Apps.md new file mode 100644 index 00000000..18c57682 --- /dev/null +++ b/Q-Apps.md @@ -0,0 +1,401 @@ +# Qortal Project - Q-Apps Documentation + +## Introduction + +Q-Apps are static web apps written in javascript, HTML, CSS, and other static assets. The key difference between a Q-App and a fully static site is its ability to interact with both the logged-in user and on-chain data. This is achieved using the API described in this document. + + + +## Making a request + +Qortal core will automatically inject a `qortalRequest()` javascript function (a Promise) to all websites/apps. This can be used to fetch or publish data to or from the Qortal blockchain. This functionality supports async/await, as well as try/catch error handling. + +``` +async function myfunction() { + try { + let res = await qortalRequest({ + action: "GET_ACCOUNT_DATA", + address: "QZLJV7wbaFyxaoZQsjm6rb9MWMiDzWsqM2" + }); + console.log(JSON.stringify(res)); // Log the response to the console + + } catch(e) { + console.log("Error: " + JSON.stringify(e)); + } +} +myfunction(); +``` + +## Timeouts + +By default, all requests will timeout after 10 seconds, and will throw an error - `The request timed out`. If you need a longer timeout - e.g. when fetching large QDN resources that may take a long time to be retried, you can use `qortalRequestWithTimeout(request, timeout)` as an alternative to `qortalRequest(request)`. + +``` +async function myfunction() { + try { + let timeout = 60000; // 60 seconds + let res = await qortalRequestWithTimeout({ + action: "FETCH_QDN_RESOURCE", + name: "QortalDemo", + service: "THUMBNAIL", + identifier: "qortal_avatar" + }, timeout); + + // Do something with the avatar here + + } catch(e) { + console.log("Error: " + JSON.stringify(e)); + } +} +myfunction(); +``` + +## Supported methods + +Here is a list of currently supported methods: +- GET_ACCOUNT_DATA +- GET_ACCOUNT_NAMES +- GET_NAME_DATA +- SEARCH_QDN_RESOURCES +- GET_QDN_RESOURCE_STATUS +- FETCH_QDN_RESOURCE +- PUBLISH_QDN_RESOURCE +- GET_WALLET_BALANCE +- GET_BALANCE +- SEND_COIN +- SEARCH_CHAT_MESSAGES +- SEND_CHAT_MESSAGE +- LIST_GROUPS +- JOIN_GROUP +- DEPLOY_AT +- GET_AT +- GET_AT_DATA + +More functionality will be added in the future. + +## Example Requests + +Here is some example requests for each of the above: + +### Get account data +``` +let res = await qortalRequest({ + action: "GET_ACCOUNT_DATA", + address: "QZLJV7wbaFyxaoZQsjm6rb9MWMiDzWsqM2" + }); +``` + +### Get names owned by account +``` +let res = await qortalRequest({ + action: "GET_ACCOUNT_NAMES", + address: "QZLJV7wbaFyxaoZQsjm6rb9MWMiDzWsqM2" + }); +``` + +### Get name data +``` +let res = await qortalRequest({ + action: "GET_NAME_DATA", + name: "QortalDemo" +}); +``` + + +### Search QDN resources +``` +let res = await qortalRequest({ + action: "SEARCH_QDN_RESOURCES", + service: "THUMBNAIL", + identifier: "qortal_avatar", // Optional + default: true, // Optional + nameListFilter: "FollowedNames", // Optional + includeStatus: false, + includeMetadata: false, + limit: 100, + offset: 0, + reverse: true +}); +``` + +### Fetch QDN single file resource +Data is returned in the base64 format +``` +let res = await qortalRequest({ + action: "FETCH_QDN_RESOURCE", + name: "QortalDemo", + service: "THUMBNAIL", + identifier: "qortal_avatar", // Optional. If omitted, the default resource is returned, or you can alternatively use the keyword "default" + rebuild: false +}); +``` + +### Fetch file from multi file QDN resource +Data is returned in the base64 format +``` +let res = await qortalRequest({ + action: "FETCH_QDN_RESOURCE", + name: "QortalDemo", + service: "WEBSITE", + identifier: "default", // Optional. If omitted, the default resource is returned, or you can alternatively request that using the keyword "default", as shown here + filepath: "index.html", // Required only for resources containing more than one file + rebuild: false +}); +``` + +### Get QDN resource status +``` +let res = await qortalRequest({ + action: "GET_QDN_RESOURCE_STATUS", + name: "QortalDemo", + service: "THUMBNAIL", + identifier: "qortal_avatar" // Optional +}); +``` + +### Publish QDN resource +_Requires user approval_ +``` +await qortalRequest({ + action: "PUBLISH_QDN_RESOURCE", + name: "Demo", // Publisher must own the registered name - use GET_ACCOUNT_NAMES for a list + service: "WEBSITE", + data64: "base64_encoded_data", + title: "Title", + description: "Description", + category: "TECHNOLOGY", + tags: ["tag1", "tag2", "tag3", "tag4", "tag5"] +}); +``` + +### Get wallet balance (QORT) +_Requires user approval_ +``` +await qortalRequest({ + action: "GET_WALLET_BALANCE", + coin: "QORT" +}); +``` + +### Get wallet balance (foreign coin) +_Requires user approval_ +``` +await qortalRequest({ + action: "GET_WALLET_BALANCE", + coin: "LTC" +}); +``` + +### Get address or asset balance +``` +let res = await qortalRequest({ + action: "GET_BALANCE", + address: "QZLJV7wbaFyxaoZQsjm6rb9MWMiDzWsqM2" +}); +``` +``` +let res = await qortalRequest({ + action: "GET_BALANCE", + assetId: 1, + address: "QZLJV7wbaFyxaoZQsjm6rb9MWMiDzWsqM2" +}); +``` + +### Send coin to address +_Requires user approval_ +``` +await qortalRequest({ + action: "SEND_COIN", + coin: "QORT", + destinationAddress: "QZLJV7wbaFyxaoZQsjm6rb9MWMiDzWsqM2", + amount: 100000000, // 1 QORT + fee: 10000 // 0.0001 QORT +}); +``` + +### Send coin to address +_Requires user approval_ +``` +await qortalRequest({ + action: "SEND_COIN", + coin: "LTC", + destinationAddress: "LSdTvMHRm8sScqwCi6x9wzYQae8JeZhx6y", + amount: 100000000, // 1 LTC + fee: 20 // 0.00000020 LTC per byte +}); +``` + +### Search or list chat messages +``` +let res = await qortalRequest({ + action: "SEARCH_CHAT_MESSAGES", + before: 999999999999999, + after: 0, + txGroupId: 0, // Optional (must specify either txGroupId or two involving addresses) + // involving: ["QZLJV7wbaFyxaoZQsjm6rb9MWMiDzWsqM2", "QSefrppsDCsZebcwrqiM1gNbWq7YMDXtG2"], // Optional (must specify either txGroupId or two involving addresses) + // reference: "reference", // Optional + // chatReference: "chatreference", // Optional + // hasChatReference: true, // Optional + limit: 100, + offset: 0, + reverse: true +}); +``` + +### Send a group chat message +_Requires user approval_ +``` +await qortalRequest({ + action: "SEND_CHAT_MESSAGE", + groupId: 0, + message: "Test" +}); +``` + +### Send a private chat message +_Requires user approval_ +``` +await qortalRequest({ + action: "SEND_CHAT_MESSAGE", + destinationAddress: "QZLJV7wbaFyxaoZQsjm6rb9MWMiDzWsqM2", + message: "Test" +}); +``` + +### List groups +``` +let res = await qortalRequest({ + action: "LIST_GROUPS", + limit: 100, + offset: 0, + reverse: true +}); +``` + +### Join a group +_Requires user approval_ +``` +await qortalRequest({ + action: "JOIN_GROUP", + groupId: 100 +}); +``` + + +### Deploy an AT +_Requires user approval_ +``` +let res = await qortalRequest({ + action: "DEPLOY_AT", + creationBytes: "12345", + name: "test name", + description: "test description", + type: "test type", + tags: "test tags", + amount: 100000000, // 1 QORT + assetId: 0, + fee: 20000 // 0.0002 QORT +}); +``` + +### Get AT info +``` +let res = await qortalRequest({ + action: "GET_AT", + atAddress: "ASRUsCjk6fa5bujv3oWYmWaVqNtvxydpPH" +}); +``` + +### Get AT data bytes (base58 encoded) +``` +let res = await qortalRequest({ + action: "GET_AT_DATA", + atAddress: "ASRUsCjk6fa5bujv3oWYmWaVqNtvxydpPH" +}); +``` + +### List ATs by functionality +``` +let res = await qortalRequest({ + action: "LIST_ATS", + codeHash58: "4KdJETRAdymE7dodDmJbf5d9L1bp4g5Nxky8m47TBkvA", + isExecutable: true, + limit: 100, + offset: 0, + reverse: true +}); +``` + + +## Sample App + +Here is a sample application to display the logged-in user's avatar: +``` + +
+ + + +