@solana-mobile/dapp-store-cli 0.13.1 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/CliSetup.js +7 -1
- package/lib/CliUtils.js +1 -1
- package/lib/__tests__/CliSetupTest.js +3 -3
- package/lib/commands/create/index.js +1 -1
- package/lib/generated/config_obj.json +1 -1
- package/lib/generated/config_schema.json +1 -1
- package/lib/package.json +2 -2
- package/lib/prebuild_schema/publishing_source.yaml +1 -0
- package/lib/prebuild_schema/schemagen.js +5 -0
- package/lib/upload/TurboStorageDriver.js +27 -5
- package/package.json +2 -2
- package/src/CliSetup.ts +10 -0
- package/src/CliUtils.ts +1 -1
- package/src/__tests__/CliSetupTest.ts +7 -4
- package/src/commands/create/index.ts +1 -1
- package/src/prebuild_schema/publishing_source.yaml +1 -0
- package/src/prebuild_schema/schemagen.js +4 -0
- package/src/upload/TurboStorageDriver.ts +36 -7
package/lib/CliSetup.js
CHANGED
|
@@ -149,7 +149,8 @@ function resolveBuildToolsPath(buildToolsPath) {
|
|
|
149
149
|
*/ function latestReleaseMessage() {
|
|
150
150
|
var messages = [
|
|
151
151
|
"- Banner Graphic image of size 1200x600px is now manadatory for publishing updates.",
|
|
152
|
-
"- Feature Graphic image of size 1200x1200px is required to be featured in Editor's choice carousel. (optional)"
|
|
152
|
+
"- Feature Graphic image of size 1200x1200px is required to be featured in Editor's choice carousel. (optional)",
|
|
153
|
+
"- Release metadata now publishes publisher.support_email when provided; otherwise we reuse publisher.email for end-user support."
|
|
153
154
|
].join('\n\n');
|
|
154
155
|
showMessage("Publishing Tools Version ".concat(Constants.CLI_VERSION), messages, "warning");
|
|
155
156
|
}
|
|
@@ -223,6 +224,11 @@ export var initCliCmd = mainCli.command("init").description("First-time initiali
|
|
|
223
224
|
})();
|
|
224
225
|
});
|
|
225
226
|
export var createCliCmd = mainCli.command("create").description("Create a `app`, or `release`");
|
|
227
|
+
createCliCmd.addHelpText("after", [
|
|
228
|
+
"",
|
|
229
|
+
"Release metadata notes:",
|
|
230
|
+
" We include publisher.support_email when provided; if omitted we fall back to publisher.email."
|
|
231
|
+
].join("\n"));
|
|
226
232
|
export var createAppCliCmd = createCliCmd.command("app").description("Create a app").requiredOption("-k, --keypair <path-to-keypair-file>", "Path to keypair file").option("-u, --url <url>", "RPC URL", Constants.DEFAULT_RPC_DEVNET).option("-d, --dry-run", "Flag for dry run. Doesn't mint an NFT").option("-s, --storage-config <storage-config>", "Provide alternative storage configuration details").option("-p, --priority-fee-lamports <priority-fee-lamports>", "Priority Fee lamports").action(function(param) {
|
|
227
233
|
var keypair = param.keypair, url = param.url, dryRun = param.dryRun, storageConfig = param.storageConfig, priorityFeeLamports = param.priorityFeeLamports;
|
|
228
234
|
return _async_to_generator(function() {
|
package/lib/CliUtils.js
CHANGED
|
@@ -156,7 +156,7 @@ export var Constants = function Constants() {
|
|
|
156
156
|
"use strict";
|
|
157
157
|
_class_call_check(this, Constants);
|
|
158
158
|
};
|
|
159
|
-
_define_property(Constants, "CLI_VERSION", "0.
|
|
159
|
+
_define_property(Constants, "CLI_VERSION", "0.15.0");
|
|
160
160
|
_define_property(Constants, "CONFIG_FILE_NAME", "config.yaml");
|
|
161
161
|
_define_property(Constants, "DEFAULT_RPC_DEVNET", "https://api.devnet.solana.com");
|
|
162
162
|
_define_property(Constants, "DEFAULT_PRIORITY_FEE", 500000);
|
|
@@ -17,10 +17,10 @@ describe("Cli Setup & Execution", function() {
|
|
|
17
17
|
return 250;
|
|
18
18
|
},
|
|
19
19
|
writeOut: function writeOut(str) {
|
|
20
|
-
otherOutput
|
|
20
|
+
otherOutput += str;
|
|
21
21
|
},
|
|
22
22
|
writeErr: function writeErr(str) {
|
|
23
|
-
errorOutput
|
|
23
|
+
errorOutput += str;
|
|
24
24
|
}
|
|
25
25
|
});
|
|
26
26
|
});
|
|
@@ -111,7 +111,7 @@ describe("Cli Setup & Execution", function() {
|
|
|
111
111
|
var generalHelp = "Usage: dapp-store [options] [command]\n\nCLI to assist with publishing to the Saga Dapp Store\n\nOptions:\n -V, --version output the version number\n -h, --help display help for command\n\nCommands:\n init First-time initialization of tooling configuration\n create Create a `app`, or `release`\n validate [options] Validates details prior to publishing\n publish Submit a publishing request (`submit`, `update`, `remove`, or `support`) to the Solana Mobile dApp publisher portal\n help [command] display help for command\n";
|
|
112
112
|
var initHelp = "Usage: dapp-store init [options]\n\nFirst-time initialization of tooling configuration\n\nOptions:\n -h, --help display help for command\n";
|
|
113
113
|
var keyPairArgHelp = "error: required option '-k, --keypair <path-to-keypair-file>' not specified";
|
|
114
|
-
var createHelp = "Usage: dapp-store create [options] [command]\n\nCreate a `app`, or `release`\n\nOptions:\n -h, --help display help for command\n\nCommands:\n app [options] Create a app\n release [options] Create a release\n help [command] display help for command\n";
|
|
114
|
+
var createHelp = "Usage: dapp-store create [options] [command]\n\nCreate a `app`, or `release`\n\nOptions:\n -h, --help display help for command\n\nCommands:\n app [options] Create a app\n release [options] Create a release\n help [command] display help for command\n\nRelease metadata notes:\n We include publisher.support_email when provided; if omitted we fall back to publisher.email.\n";
|
|
115
115
|
var createAppHelp = 'Usage: dapp-store create app [options]\n\nCreate a app\n\nOptions:\n -k, --keypair <path-to-keypair-file> Path to keypair file\n -u, --url <url> RPC URL (default: "https://api.devnet.solana.com")\n -d, --dry-run Flag for dry run. Doesn\'t mint an NFT\n -s, --storage-config <storage-config> Provide alternative storage configuration details\n -p, --priority-fee-lamports <priority-fee-lamports> Priority Fee lamports\n -h, --help display help for command\n';
|
|
116
116
|
var createReleaseHelp = 'Usage: dapp-store create release [options]\n\nCreate a release\n\nOptions:\n -k, --keypair <path-to-keypair-file> Path to keypair file\n -a, --app-mint-address <app-mint-address> The mint address of the app NFT\n -u, --url <url> RPC URL (default: "https://api.devnet.solana.com")\n -d, --dry-run Flag for dry run. Doesn\'t mint an NFT\n -b, --build-tools-path <build-tools-path> Path to Android build tools which contains AAPT2\n -s, --storage-config <storage-config> Provide alternative storage configuration details\n -p, --priority-fee-lamports <priority-fee-lamports> Priority Fee lamports\n -h, --help display help for command\n';
|
|
117
117
|
});
|
|
@@ -10,7 +10,7 @@ export * from "./CreateCliRelease.js"; /*
|
|
|
10
10
|
// Publisher
|
|
11
11
|
// Public key attached to a publisher must also verify applications and releases
|
|
12
12
|
// Most information here can be be edited after the fact
|
|
13
|
-
// Only required fields are name, address, publisher website, and contact
|
|
13
|
+
// Only required fields are name, address, publisher website, and contact email
|
|
14
14
|
// Optional fields are: description, image_url (need dimensions!)
|
|
15
15
|
// App
|
|
16
16
|
// Publisher creator key required
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"publisher":{"name":"<<[REQUIRED] YOUR_PUBLISHER_NAME>>","website":"<<[REQUIRED] URL_OF_PUBLISHER_WEBSITE>>","email":"<<[REQUIRED] EMAIL_ADDRESS_TO_CONTACT_PUBLISHER>>"},"app":{"name":"<<[REQUIRED] APP_NAME>>","address":"","android_package":"<<[REQUIRED] ANDROID_PACKAGE_NAME>>","urls":{"license_url":"<<[REQUIRED] URL For App's T&C. Don't put placeholder urls.>>","copyright_url":"<<[REQUIRED] URL For App's Copyright. Don't put placeholder urls.>>","privacy_policy_url":"<<[REQUIRED] URL For App's Privacy Policy. Don't put placeholder urls.>>","website":"<<[REQUIRED] URL_OF_APP_WEBSITE>>"},"media":[{"purpose":"icon","uri":"<<[REQUIRED] RELATIVE_PATH_TO_APP_ICON>>"}]},"release":{"address":"","media":[{"purpose":"icon","uri":"<<[REQUIRED] RELATIVE_PATH_TO_RELEASE_ICON>>"},{"purpose":"banner","uri":"<<[REQUIRED] RELATIVE_PATH_TO_BANNER>>"},{"purpose":"featureGraphic","uri":"<<[Optional] RELATIVE_PATH_TO_FEATURE_GRAPHIC>>"},{"purpose":"screenshot","uri":"<<[REQUIRED] RELATIVE_PATH_TO_SCREENSHOT1>>"},{"purpose":"screenshot","uri":"<<[REQUIRED] RELATIVE_PATH_TO_SCREENSHOT2>>"},{"purpose":"screenshot","uri":"<<[REQUIRED] RELATIVE_PATH_TO_SCREENSHOT3>>"},{"purpose":"screenshot","uri":"<<[REQUIRED] RELATIVE_PATH_TO_SCREENSHOT4>>"},{"purpose":"video","uri":"<<[Optional] RELATIVE_PATH_TO_VIDEO1>>"}],"files":[{"purpose":"install","uri":"<<[REQUIRED] RELATIVE_PATH_TO_APK>>"}],"catalog":{"en-US":{"name":"<<[REQUIRED] APP_NAME>>","short_description":"<<[REQUIRED] SHORT_APP_DESCRIPTION>>","long_description":"<<[REQUIRED] LONG_APP_DESCRIPTION>>","new_in_version":"<<[REQUIRED] WHATS_NEW_IN_THIS_VERSION>>","saga_features":"<<[Optional.] ANY_FEATURES_ONLY_AVAILBLE_WHEN_RUNNING_ON_SAGA>>"}},"android_details":{"locales":["en-US","<Add more supported locales>"]}},"solana_mobile_dapp_publisher_portal":{"google_store_package":"<<[Optional] ANDROID_PACKAGE_NAME_OF_GOOGLE_PLAY_STORE_VERSION>>","testing_instructions":"<<[REQUIRED] TESTING_INSTRUCTIONS. Please provide any test account details if applicable>>","alpha_testers":[{"address":"<<Optional. genesis token wallet address>>","comment":"<<Optional. For internal use only>>"},{"address":"<<Optional. genesis token wallet address>>","comment":"<<Optional. For internal use only>>"}]}}
|
|
1
|
+
{"publisher":{"name":"<<[REQUIRED] YOUR_PUBLISHER_NAME>>","website":"<<[REQUIRED] URL_OF_PUBLISHER_WEBSITE>>","email":"<<[REQUIRED] EMAIL_ADDRESS_TO_CONTACT_PUBLISHER>>","support_email":"<<[Optional] SUPPORT_EMAIL_ADDRESS_FOR_END_USERS>>"},"app":{"name":"<<[REQUIRED] APP_NAME>>","address":"","android_package":"<<[REQUIRED] ANDROID_PACKAGE_NAME>>","urls":{"license_url":"<<[REQUIRED] URL For App's T&C. Don't put placeholder urls.>>","copyright_url":"<<[REQUIRED] URL For App's Copyright. Don't put placeholder urls.>>","privacy_policy_url":"<<[REQUIRED] URL For App's Privacy Policy. Don't put placeholder urls.>>","website":"<<[REQUIRED] URL_OF_APP_WEBSITE>>"},"media":[{"purpose":"icon","uri":"<<[REQUIRED] RELATIVE_PATH_TO_APP_ICON>>"}]},"release":{"address":"","media":[{"purpose":"icon","uri":"<<[REQUIRED] RELATIVE_PATH_TO_RELEASE_ICON>>"},{"purpose":"banner","uri":"<<[REQUIRED] RELATIVE_PATH_TO_BANNER>>"},{"purpose":"featureGraphic","uri":"<<[Optional] RELATIVE_PATH_TO_FEATURE_GRAPHIC>>"},{"purpose":"screenshot","uri":"<<[REQUIRED] RELATIVE_PATH_TO_SCREENSHOT1>>"},{"purpose":"screenshot","uri":"<<[REQUIRED] RELATIVE_PATH_TO_SCREENSHOT2>>"},{"purpose":"screenshot","uri":"<<[REQUIRED] RELATIVE_PATH_TO_SCREENSHOT3>>"},{"purpose":"screenshot","uri":"<<[REQUIRED] RELATIVE_PATH_TO_SCREENSHOT4>>"},{"purpose":"video","uri":"<<[Optional] RELATIVE_PATH_TO_VIDEO1>>"}],"files":[{"purpose":"install","uri":"<<[REQUIRED] RELATIVE_PATH_TO_APK>>"}],"catalog":{"en-US":{"name":"<<[REQUIRED] APP_NAME>>","short_description":"<<[REQUIRED] SHORT_APP_DESCRIPTION>>","long_description":"<<[REQUIRED] LONG_APP_DESCRIPTION>>","new_in_version":"<<[REQUIRED] WHATS_NEW_IN_THIS_VERSION>>","saga_features":"<<[Optional.] ANY_FEATURES_ONLY_AVAILBLE_WHEN_RUNNING_ON_SAGA>>"}},"android_details":{"locales":["en-US","<Add more supported locales>"]}},"solana_mobile_dapp_publisher_portal":{"google_store_package":"<<[Optional] ANDROID_PACKAGE_NAME_OF_GOOGLE_PLAY_STORE_VERSION>>","testing_instructions":"<<[REQUIRED] TESTING_INSTRUCTIONS. Please provide any test account details if applicable>>","alpha_testers":[{"address":"<<Optional. genesis token wallet address>>","comment":"<<Optional. For internal use only>>"},{"address":"<<Optional. genesis token wallet address>>","comment":"<<Optional. For internal use only>>"}]}}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"type":"object","properties":{"publisher":{"type":"object","properties":{"name":{"type":"string"},"website":{"type":"string"},"email":{"type":"string"}}},"app":{"type":"object","properties":{"name":{"type":"string"},"address":{"type":"string"},"android_package":{"type":"string"},"urls":{"type":"object","properties":{"license_url":{"type":"string"},"copyright_url":{"type":"string"},"privacy_policy_url":{"type":"string"},"website":{"type":"string"}}},"media":{"type":"array","items":{"type":"object","properties":{"purpose":{"type":"string"},"uri":{"type":"string"}}}}}},"release":{"type":"object","properties":{"address":{"type":"string"},"media":{"type":"array","items":{"type":"object","properties":{"purpose":{"type":"string"},"uri":{"type":"string"}},"required":["purpose","uri"]}},"files":{"type":"array","items":{"type":"object","properties":{"purpose":{"type":"string"},"uri":{"type":"string"}}}},"catalog":{"type":"object","properties":{"en-US":{"type":"object","properties":{"name":{"type":"string"},"short_description":{"type":"string"},"long_description":{"type":"string"},"new_in_version":{"type":"string"},"saga_features":{"type":"string"}},"required":["short_description"]}}},"android_details":{"type":"object","properties":{"locales":{"type":"array","items":{"type":"string"}}}}}},"solana_mobile_dapp_publisher_portal":{"type":"object","properties":{"google_store_package":{"type":"string"},"testing_instructions":{"type":"string"},"alpha_testers":{"type":"array","items":{"type":"object","properties":{"address":{"type":"string"},"comment":{"type":"string"}},"required":["address","comment"]}}}}}}
|
|
1
|
+
{"type":"object","properties":{"publisher":{"type":"object","properties":{"name":{"type":"string"},"website":{"type":"string"},"email":{"type":"string"},"support_email":{"type":"string"}},"required":["name","website","email"]},"app":{"type":"object","properties":{"name":{"type":"string"},"address":{"type":"string"},"android_package":{"type":"string"},"urls":{"type":"object","properties":{"license_url":{"type":"string"},"copyright_url":{"type":"string"},"privacy_policy_url":{"type":"string"},"website":{"type":"string"}}},"media":{"type":"array","items":{"type":"object","properties":{"purpose":{"type":"string"},"uri":{"type":"string"}}}}}},"release":{"type":"object","properties":{"address":{"type":"string"},"media":{"type":"array","items":{"type":"object","properties":{"purpose":{"type":"string"},"uri":{"type":"string"}},"required":["purpose","uri"]}},"files":{"type":"array","items":{"type":"object","properties":{"purpose":{"type":"string"},"uri":{"type":"string"}}}},"catalog":{"type":"object","properties":{"en-US":{"type":"object","properties":{"name":{"type":"string"},"short_description":{"type":"string"},"long_description":{"type":"string"},"new_in_version":{"type":"string"},"saga_features":{"type":"string"}},"required":["short_description"]}}},"android_details":{"type":"object","properties":{"locales":{"type":"array","items":{"type":"string"}}}}}},"solana_mobile_dapp_publisher_portal":{"type":"object","properties":{"google_store_package":{"type":"string"},"testing_instructions":{"type":"string"},"alpha_testers":{"type":"array","items":{"type":"object","properties":{"address":{"type":"string"},"comment":{"type":"string"}},"required":["address","comment"]}}}}}}
|
package/lib/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@solana-mobile/dapp-store-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.15.0",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
"@ardrive/turbo-sdk": "^1.31.1",
|
|
55
55
|
"@aws-sdk/client-s3": "^3.321.1",
|
|
56
56
|
"@metaplex-foundation/js-plugin-aws": "^0.20.0",
|
|
57
|
-
"@solana-mobile/dapp-store-publishing-tools": "workspace:0.
|
|
57
|
+
"@solana-mobile/dapp-store-publishing-tools": "workspace:0.15.0",
|
|
58
58
|
"@solana/web3.js": "1.92.1",
|
|
59
59
|
"@types/semver": "^7.3.13",
|
|
60
60
|
"ajv": "^8.11.0",
|
|
@@ -2,6 +2,7 @@ publisher:
|
|
|
2
2
|
name: <<[REQUIRED] YOUR_PUBLISHER_NAME>>
|
|
3
3
|
website: <<[REQUIRED] URL_OF_PUBLISHER_WEBSITE>>
|
|
4
4
|
email: <<[REQUIRED] EMAIL_ADDRESS_TO_CONTACT_PUBLISHER>>
|
|
5
|
+
support_email: <<[Optional] SUPPORT_EMAIL_ADDRESS_FOR_END_USERS>>
|
|
5
6
|
app:
|
|
6
7
|
name: <<[REQUIRED] APP_NAME>>
|
|
7
8
|
address: ""
|
|
@@ -10,6 +10,11 @@ try {
|
|
|
10
10
|
schema["properties"]["release"]["properties"]["catalog"]["properties"]["en-US"].required = [
|
|
11
11
|
"short_description"
|
|
12
12
|
];
|
|
13
|
+
schema["properties"]["publisher"].required = [
|
|
14
|
+
"name",
|
|
15
|
+
"website",
|
|
16
|
+
"email"
|
|
17
|
+
];
|
|
13
18
|
// Generator adds some keys/values we don't need & mess up validation
|
|
14
19
|
delete schema.$schema;
|
|
15
20
|
delete schema.title;
|
|
@@ -248,11 +248,16 @@ import { TurboFactory, lamportToTokenAmount } from "@ardrive/turbo-sdk";
|
|
|
248
248
|
import bs58 from "bs58";
|
|
249
249
|
import debugModule from "debug";
|
|
250
250
|
var debug = debugModule("cli:turbo-storage");
|
|
251
|
+
var SOL_IN_LAMPORTS = 1000000000;
|
|
252
|
+
var MIN_TOP_UP_LAMPORTS = 1000000;
|
|
253
|
+
var MIN_TOP_UP_SOL = MIN_TOP_UP_LAMPORTS / SOL_IN_LAMPORTS;
|
|
251
254
|
var CONSTANTS = {
|
|
252
255
|
FREE_UPLOAD_LIMIT: 97280,
|
|
253
256
|
UPLOAD_DELAY_MS: 2000,
|
|
254
257
|
MAX_RETRIES: 5,
|
|
255
|
-
SOL_IN_LAMPORTS:
|
|
258
|
+
SOL_IN_LAMPORTS: SOL_IN_LAMPORTS,
|
|
259
|
+
MIN_TOP_UP_SOL: MIN_TOP_UP_SOL,
|
|
260
|
+
MIN_TOP_UP_LAMPORTS: MIN_TOP_UP_LAMPORTS,
|
|
256
261
|
BACKOFF: {
|
|
257
262
|
BASE_MS: 500,
|
|
258
263
|
MAX_MS: 8000
|
|
@@ -427,6 +432,12 @@ export var TurboStorageDriver = /*#__PURE__*/ function() {
|
|
|
427
432
|
switch(_state.label){
|
|
428
433
|
case 0:
|
|
429
434
|
_this = this;
|
|
435
|
+
if (wincAmount === 0n) {
|
|
436
|
+
debug("No Winston Credits requested; skipping top-up.");
|
|
437
|
+
return [
|
|
438
|
+
2
|
|
439
|
+
];
|
|
440
|
+
}
|
|
430
441
|
_state.label = 1;
|
|
431
442
|
case 1:
|
|
432
443
|
_state.trys.push([
|
|
@@ -439,7 +450,7 @@ export var TurboStorageDriver = /*#__PURE__*/ function() {
|
|
|
439
450
|
4,
|
|
440
451
|
this.withRetry(function() {
|
|
441
452
|
return _async_to_generator(function() {
|
|
442
|
-
var _this_turbo_getWincForToken, _this_turbo, _this_turbo_topUpWithTokens, _this_turbo1, exchangeRate, wincPerSol,
|
|
453
|
+
var _this_turbo_getWincForToken, _this_turbo, _this_turbo_topUpWithTokens, _this_turbo1, exchangeRate, wincPerSol, solInLamports, numerator, lamportsCalculated, minLamports, lamportsToUse, solAmount;
|
|
443
454
|
return _ts_generator(this, function(_state) {
|
|
444
455
|
switch(_state.label){
|
|
445
456
|
case 0:
|
|
@@ -455,12 +466,23 @@ export var TurboStorageDriver = /*#__PURE__*/ function() {
|
|
|
455
466
|
throw new Error("Unable to get Winston Credits exchange rate");
|
|
456
467
|
}
|
|
457
468
|
wincPerSol = BigInt(String(exchangeRate.winc));
|
|
458
|
-
|
|
459
|
-
|
|
469
|
+
if (wincPerSol <= 0n) {
|
|
470
|
+
throw new Error("Invalid Winston Credits exchange rate");
|
|
471
|
+
}
|
|
472
|
+
solInLamports = BigInt(CONSTANTS.SOL_IN_LAMPORTS);
|
|
473
|
+
numerator = wincAmount * solInLamports;
|
|
474
|
+
lamportsCalculated = (numerator + (wincPerSol - 1n)) / wincPerSol;
|
|
475
|
+
minLamports = BigInt(CONSTANTS.MIN_TOP_UP_LAMPORTS);
|
|
476
|
+
lamportsToUse = lamportsCalculated < minLamports ? minLamports : lamportsCalculated;
|
|
477
|
+
if (lamportsToUse > lamportsCalculated) {
|
|
478
|
+
debug("Applying minimum top-up of ".concat(CONSTANTS.MIN_TOP_UP_SOL, " SOL (").concat(CONSTANTS.MIN_TOP_UP_LAMPORTS, " lamports)"));
|
|
479
|
+
}
|
|
480
|
+
solAmount = Number(lamportsToUse) / CONSTANTS.SOL_IN_LAMPORTS;
|
|
481
|
+
debug("Buying at least ".concat(wincAmount, " Winston Credits (~").concat(solAmount.toFixed(9), " SOL / ").concat(lamportsToUse, " lamports)"));
|
|
460
482
|
return [
|
|
461
483
|
4,
|
|
462
484
|
(_this_turbo_topUpWithTokens = (_this_turbo1 = this.turbo).topUpWithTokens) === null || _this_turbo_topUpWithTokens === void 0 ? void 0 : _this_turbo_topUpWithTokens.call(_this_turbo1, {
|
|
463
|
-
tokenAmount: String(lamportToTokenAmount(
|
|
485
|
+
tokenAmount: String(lamportToTokenAmount(lamportsToUse.toString()))
|
|
464
486
|
})
|
|
465
487
|
];
|
|
466
488
|
case 2:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@solana-mobile/dapp-store-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.15.0",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
"@ardrive/turbo-sdk": "^1.31.1",
|
|
48
48
|
"@aws-sdk/client-s3": "^3.321.1",
|
|
49
49
|
"@metaplex-foundation/js-plugin-aws": "^0.20.0",
|
|
50
|
-
"@solana-mobile/dapp-store-publishing-tools": "0.
|
|
50
|
+
"@solana-mobile/dapp-store-publishing-tools": "0.15.0",
|
|
51
51
|
"@solana/web3.js": "1.92.1",
|
|
52
52
|
"@types/semver": "^7.3.13",
|
|
53
53
|
"ajv": "^8.11.0",
|
package/src/CliSetup.ts
CHANGED
|
@@ -52,6 +52,7 @@ function latestReleaseMessage() {
|
|
|
52
52
|
const messages = [
|
|
53
53
|
`- Banner Graphic image of size 1200x600px is now manadatory for publishing updates.`,
|
|
54
54
|
`- Feature Graphic image of size 1200x1200px is required to be featured in Editor's choice carousel. (optional)`,
|
|
55
|
+
`- Release metadata now publishes publisher.support_email when provided; otherwise we reuse publisher.email for end-user support.`,
|
|
55
56
|
].join('\n\n')
|
|
56
57
|
showMessage(
|
|
57
58
|
`Publishing Tools Version ${ Constants.CLI_VERSION }`,
|
|
@@ -91,6 +92,15 @@ export const createCliCmd = mainCli
|
|
|
91
92
|
.command("create")
|
|
92
93
|
.description("Create a `app`, or `release`")
|
|
93
94
|
|
|
95
|
+
createCliCmd.addHelpText(
|
|
96
|
+
"after",
|
|
97
|
+
[
|
|
98
|
+
"",
|
|
99
|
+
"Release metadata notes:",
|
|
100
|
+
" We include publisher.support_email when provided; if omitted we fall back to publisher.email.",
|
|
101
|
+
].join("\n")
|
|
102
|
+
);
|
|
103
|
+
|
|
94
104
|
export const createAppCliCmd = createCliCmd
|
|
95
105
|
.command("app")
|
|
96
106
|
.description("Create a app")
|
package/src/CliUtils.ts
CHANGED
|
@@ -17,7 +17,7 @@ import { awsStorage } from "@metaplex-foundation/js-plugin-aws";
|
|
|
17
17
|
import { S3StorageManager } from "./config/index.js";
|
|
18
18
|
|
|
19
19
|
export class Constants {
|
|
20
|
-
static CLI_VERSION = "0.
|
|
20
|
+
static CLI_VERSION = "0.15.0";
|
|
21
21
|
static CONFIG_FILE_NAME = "config.yaml";
|
|
22
22
|
static DEFAULT_RPC_DEVNET = "https://api.devnet.solana.com";
|
|
23
23
|
static DEFAULT_PRIORITY_FEE = 500000;
|
|
@@ -24,11 +24,11 @@ describe("Cli Setup & Execution", () => {
|
|
|
24
24
|
getErrHelpWidth(): number { return 250;},
|
|
25
25
|
|
|
26
26
|
writeOut(str: string) {
|
|
27
|
-
otherOutput
|
|
27
|
+
otherOutput += str;
|
|
28
28
|
},
|
|
29
29
|
|
|
30
30
|
writeErr(str: string) {
|
|
31
|
-
errorOutput
|
|
31
|
+
errorOutput += str;
|
|
32
32
|
}
|
|
33
33
|
});
|
|
34
34
|
});
|
|
@@ -136,7 +136,7 @@ Options:
|
|
|
136
136
|
|
|
137
137
|
const keyPairArgHelp = "error: required option '-k, --keypair <path-to-keypair-file>' not specified"
|
|
138
138
|
|
|
139
|
-
|
|
139
|
+
const createHelp = `Usage: dapp-store create [options] [command]
|
|
140
140
|
|
|
141
141
|
Create a \`app\`, or \`release\`
|
|
142
142
|
|
|
@@ -147,6 +147,9 @@ Commands:
|
|
|
147
147
|
app [options] Create a app
|
|
148
148
|
release [options] Create a release
|
|
149
149
|
help [command] display help for command
|
|
150
|
+
|
|
151
|
+
Release metadata notes:
|
|
152
|
+
We include publisher.support_email when provided; if omitted we fall back to publisher.email.
|
|
150
153
|
`;
|
|
151
154
|
|
|
152
155
|
const createAppHelp = `Usage: dapp-store create app [options]
|
|
@@ -177,4 +180,4 @@ Options:
|
|
|
177
180
|
-h, --help display help for command
|
|
178
181
|
`;
|
|
179
182
|
|
|
180
|
-
});
|
|
183
|
+
});
|
|
@@ -15,7 +15,7 @@ export * from "./CreateCliRelease.js";
|
|
|
15
15
|
// Publisher
|
|
16
16
|
// Public key attached to a publisher must also verify applications and releases
|
|
17
17
|
// Most information here can be be edited after the fact
|
|
18
|
-
// Only required fields are name, address, publisher website, and contact
|
|
18
|
+
// Only required fields are name, address, publisher website, and contact email
|
|
19
19
|
// Optional fields are: description, image_url (need dimensions!)
|
|
20
20
|
|
|
21
21
|
// App
|
|
@@ -2,6 +2,7 @@ publisher:
|
|
|
2
2
|
name: <<[REQUIRED] YOUR_PUBLISHER_NAME>>
|
|
3
3
|
website: <<[REQUIRED] URL_OF_PUBLISHER_WEBSITE>>
|
|
4
4
|
email: <<[REQUIRED] EMAIL_ADDRESS_TO_CONTACT_PUBLISHER>>
|
|
5
|
+
support_email: <<[Optional] SUPPORT_EMAIL_ADDRESS_FOR_END_USERS>>
|
|
5
6
|
app:
|
|
6
7
|
name: <<[REQUIRED] APP_NAME>>
|
|
7
8
|
address: ""
|
|
@@ -16,6 +16,10 @@ try {
|
|
|
16
16
|
["properties"]
|
|
17
17
|
["en-US"].required = ["short_description"];
|
|
18
18
|
|
|
19
|
+
schema["properties"]
|
|
20
|
+
["publisher"]
|
|
21
|
+
.required = ["name", "website", "email"];
|
|
22
|
+
|
|
19
23
|
// Generator adds some keys/values we don't need & mess up validation
|
|
20
24
|
delete schema.$schema;
|
|
21
25
|
delete schema.title;
|
|
@@ -22,11 +22,17 @@ interface TurboClient {
|
|
|
22
22
|
}): Promise<{ id: string }>;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
const SOL_IN_LAMPORTS = 1_000_000_000;
|
|
26
|
+
const MIN_TOP_UP_LAMPORTS = 1_000_000;
|
|
27
|
+
const MIN_TOP_UP_SOL = MIN_TOP_UP_LAMPORTS / SOL_IN_LAMPORTS;
|
|
28
|
+
|
|
25
29
|
const CONSTANTS = {
|
|
26
30
|
FREE_UPLOAD_LIMIT: 97_280, // 95 KiB
|
|
27
31
|
UPLOAD_DELAY_MS: 2000,
|
|
28
32
|
MAX_RETRIES: 5,
|
|
29
|
-
SOL_IN_LAMPORTS
|
|
33
|
+
SOL_IN_LAMPORTS,
|
|
34
|
+
MIN_TOP_UP_SOL,
|
|
35
|
+
MIN_TOP_UP_LAMPORTS,
|
|
30
36
|
BACKOFF: {
|
|
31
37
|
BASE_MS: 500,
|
|
32
38
|
MAX_MS: 8000,
|
|
@@ -131,6 +137,11 @@ export class TurboStorageDriver {
|
|
|
131
137
|
}
|
|
132
138
|
|
|
133
139
|
private async topUpCredits(wincAmount: bigint): Promise<void> {
|
|
140
|
+
if (wincAmount === 0n) {
|
|
141
|
+
debug("No Winston Credits requested; skipping top-up.");
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
|
|
134
145
|
try {
|
|
135
146
|
await this.withRetry(async () => {
|
|
136
147
|
const exchangeRate = await this.turbo.getWincForToken?.({
|
|
@@ -142,17 +153,35 @@ export class TurboStorageDriver {
|
|
|
142
153
|
}
|
|
143
154
|
|
|
144
155
|
const wincPerSol = BigInt(String(exchangeRate.winc));
|
|
145
|
-
|
|
146
|
-
|
|
156
|
+
if (wincPerSol <= 0n) {
|
|
157
|
+
throw new Error("Invalid Winston Credits exchange rate");
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const solInLamports = BigInt(CONSTANTS.SOL_IN_LAMPORTS);
|
|
161
|
+
const numerator = wincAmount * solInLamports;
|
|
162
|
+
const lamportsCalculated =
|
|
163
|
+
(numerator + (wincPerSol - 1n)) / wincPerSol;
|
|
164
|
+
|
|
165
|
+
const minLamports = BigInt(CONSTANTS.MIN_TOP_UP_LAMPORTS);
|
|
166
|
+
const lamportsToUse =
|
|
167
|
+
lamportsCalculated < minLamports ? minLamports : lamportsCalculated;
|
|
168
|
+
|
|
169
|
+
if (lamportsToUse > lamportsCalculated) {
|
|
170
|
+
debug(
|
|
171
|
+
`Applying minimum top-up of ${CONSTANTS.MIN_TOP_UP_SOL} SOL (${CONSTANTS.MIN_TOP_UP_LAMPORTS} lamports)`
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
const solAmount =
|
|
175
|
+
Number(lamportsToUse) / CONSTANTS.SOL_IN_LAMPORTS;
|
|
147
176
|
|
|
148
177
|
debug(
|
|
149
|
-
`Buying ${wincAmount} Winston Credits
|
|
150
|
-
Number(lamportsNeeded) / 1e9
|
|
151
|
-
} SOL`
|
|
178
|
+
`Buying at least ${wincAmount} Winston Credits (~${solAmount.toFixed(9)} SOL / ${lamportsToUse} lamports)`
|
|
152
179
|
);
|
|
153
180
|
|
|
154
181
|
await this.turbo.topUpWithTokens?.({
|
|
155
|
-
tokenAmount: String(
|
|
182
|
+
tokenAmount: String(
|
|
183
|
+
lamportToTokenAmount(lamportsToUse.toString())
|
|
184
|
+
),
|
|
156
185
|
});
|
|
157
186
|
|
|
158
187
|
debug(`Top-up initiated for ${wincAmount} Winston Credits`);
|