diff --git a/README.md b/README.md index 6c373b0..27c7667 100644 --- a/README.md +++ b/README.md @@ -8,11 +8,11 @@ Proton CLI [![License](https://img.shields.io/npm/l/@proton/cli.svg)](https://github.com/ProtonProtocol/proton-cli/blob/master/package.json) - -- [Installation](#installation) -- [Install NodeJS](#install-nodejs) -- [Usage](#usage) -- [Commands](#commands) +* [@proton/cli](#protoncli) +* [Installation](#installation) +* [Install NodeJS](#install-nodejs) +* [Usage](#usage) +* [Commands](#commands) # Installation @@ -62,77 +62,74 @@ nvm use 16 # Usage - ```sh-session $ npm install -g @proton/cli $ proton COMMAND running command... $ proton (--version) -@proton/cli/0.1.95 darwin-arm64 node-v22.14.0 +@proton/cli/0.1.95 darwin-arm64 node-v25.2.1 $ proton --help [COMMAND] USAGE $ proton COMMAND ... ``` - # Commands - -- [`proton account ACCOUNT`](#proton-account-account) -- [`proton account:create ACCOUNT`](#proton-accountcreate-account) -- [`proton action CONTRACT [ACTION] [DATA] [AUTHORIZATION]`](#proton-action-contract-action-data-authorization) -- [`proton block:get BLOCKNUMBER`](#proton-blockget-blocknumber) -- [`proton boilerplate [FOLDER]`](#proton-boilerplate-folder) -- [`proton chain:get`](#proton-chainget) -- [`proton chain:info`](#proton-chaininfo) -- [`proton chain:list`](#proton-chainlist) -- [`proton chain:set [CHAIN]`](#proton-chainset-chain) -- [`proton contract:abi ACCOUNT`](#proton-contractabi-account) -- [`proton contract:clear ACCOUNT`](#proton-contractclear-account) -- [`proton contract:enableinline ACCOUNT`](#proton-contractenableinline-account) -- [`proton contract:set ACCOUNT SOURCE`](#proton-contractset-account-source) -- [`proton encode:name ACCOUNT`](#proton-encodename-account) -- [`proton encode:symbol SYMBOL PRECISION`](#proton-encodesymbol-symbol-precision) -- [`proton endpoint`](#proton-endpoint) -- [`proton endpoint:default [ENDPOINT]`](#proton-endpointdefault-endpoint) -- [`proton endpoint:get`](#proton-endpointget) -- [`proton endpoint:set [ENDPOINT]`](#proton-endpointset-endpoint) -- [`proton faucet`](#proton-faucet) -- [`proton faucet:claim SYMBOL AUTHORIZATION`](#proton-faucetclaim-symbol-authorization) -- [`proton generate:action`](#proton-generateaction) -- [`proton generate:contract CONTRACTNAME`](#proton-generatecontract-contractname) -- [`proton generate:inlineaction ACTIONNAME`](#proton-generateinlineaction-actionname) -- [`proton generate:table TABLENAME`](#proton-generatetable-tablename) -- [`proton help [COMMAND]`](#proton-help-command) -- [`proton key:add [PRIVATEKEY]`](#proton-keyadd-privatekey) -- [`proton key:generate`](#proton-keygenerate) -- [`proton key:get PUBLICKEY`](#proton-keyget-publickey) -- [`proton key:list`](#proton-keylist) -- [`proton key:lock`](#proton-keylock) -- [`proton key:remove [PRIVATEKEY]`](#proton-keyremove-privatekey) -- [`proton key:reset`](#proton-keyreset) -- [`proton key:unlock [PASSWORD]`](#proton-keyunlock-password) -- [`proton msig:approve PROPOSER PROPOSAL AUTH`](#proton-msigapprove-proposer-proposal-auth) -- [`proton msig:cancel PROPOSALNAME AUTH`](#proton-msigcancel-proposalname-auth) -- [`proton msig:exec PROPOSER PROPOSAL AUTH`](#proton-msigexec-proposer-proposal-auth) -- [`proton msig:propose PROPOSALNAME ACTIONS AUTH`](#proton-msigpropose-proposalname-actions-auth) -- [`proton network`](#proton-network) -- [`proton permission ACCOUNT`](#proton-permission-account) -- [`proton permission:link ACCOUNT PERMISSION CONTRACT [ACTION]`](#proton-permissionlink-account-permission-contract-action) -- [`proton permission:unlink ACCOUNT CONTRACT [ACTION]`](#proton-permissionunlink-account-contract-action) -- [`proton psr URI`](#proton-psr-uri) -- [`proton ram`](#proton-ram) -- [`proton ram:buy BUYER RECEIVER BYTES`](#proton-rambuy-buyer-receiver-bytes) -- [`proton rpc:accountsbyauthorizers AUTHORIZATIONS [KEYS]`](#proton-rpcaccountsbyauthorizers-authorizations-keys) -- [`proton scan ACCOUNT`](#proton-scan-account) -- [`proton table CONTRACT [TABLE] [SCOPE]`](#proton-table-contract-table-scope) -- [`proton transaction JSON`](#proton-transaction-json) -- [`proton transaction:get ID`](#proton-transactionget-id) -- [`proton transaction:push TRANSACTION`](#proton-transactionpush-transaction) -- [`proton version`](#proton-version) +* [`proton account ACCOUNT`](#proton-account-account) +* [`proton account:create ACCOUNT`](#proton-accountcreate-account) +* [`proton action CONTRACT [ACTION] [DATA] [AUTHORIZATION]`](#proton-action-contract-action-data-authorization) +* [`proton block:get BLOCKNUMBER`](#proton-blockget-blocknumber) +* [`proton boilerplate [FOLDER]`](#proton-boilerplate-folder) +* [`proton chain:get`](#proton-chainget) +* [`proton chain:info`](#proton-chaininfo) +* [`proton chain:list`](#proton-chainlist) +* [`proton chain:set [CHAIN]`](#proton-chainset-chain) +* [`proton contract:abi ACCOUNT`](#proton-contractabi-account) +* [`proton contract:clear ACCOUNT`](#proton-contractclear-account) +* [`proton contract:enableinline ACCOUNT`](#proton-contractenableinline-account) +* [`proton contract:set ACCOUNT SOURCE`](#proton-contractset-account-source) +* [`proton encode:name ACCOUNT`](#proton-encodename-account) +* [`proton encode:symbol SYMBOL PRECISION`](#proton-encodesymbol-symbol-precision) +* [`proton endpoint`](#proton-endpoint) +* [`proton endpoint:default [ENDPOINT]`](#proton-endpointdefault-endpoint) +* [`proton endpoint:get`](#proton-endpointget) +* [`proton endpoint:set [ENDPOINT]`](#proton-endpointset-endpoint) +* [`proton faucet`](#proton-faucet) +* [`proton faucet:claim SYMBOL AUTHORIZATION`](#proton-faucetclaim-symbol-authorization) +* [`proton generate:action`](#proton-generateaction) +* [`proton generate:contract CONTRACTNAME`](#proton-generatecontract-contractname) +* [`proton generate:inlineaction ACTIONNAME`](#proton-generateinlineaction-actionname) +* [`proton generate:table TABLENAME`](#proton-generatetable-tablename) +* [`proton help [COMMAND]`](#proton-help-command) +* [`proton key:add [PRIVATEKEY]`](#proton-keyadd-privatekey) +* [`proton key:generate`](#proton-keygenerate) +* [`proton key:get PUBLICKEY`](#proton-keyget-publickey) +* [`proton key:list`](#proton-keylist) +* [`proton key:lock`](#proton-keylock) +* [`proton key:remove [PRIVATEKEY]`](#proton-keyremove-privatekey) +* [`proton key:reset`](#proton-keyreset) +* [`proton key:unlock [PASSWORD]`](#proton-keyunlock-password) +* [`proton msig:approve PROPOSER PROPOSAL AUTH`](#proton-msigapprove-proposer-proposal-auth) +* [`proton msig:cancel PROPOSALNAME AUTH`](#proton-msigcancel-proposalname-auth) +* [`proton msig:exec PROPOSER PROPOSAL AUTH`](#proton-msigexec-proposer-proposal-auth) +* [`proton msig:propose PROPOSALNAME ACTIONS AUTH`](#proton-msigpropose-proposalname-actions-auth) +* [`proton network`](#proton-network) +* [`proton permission ACCOUNT`](#proton-permission-account) +* [`proton permission:link ACCOUNT PERMISSION CONTRACT [ACTION]`](#proton-permissionlink-account-permission-contract-action) +* [`proton permission:unlink ACCOUNT CONTRACT [ACTION]`](#proton-permissionunlink-account-contract-action) +* [`proton psr URI`](#proton-psr-uri) +* [`proton ram`](#proton-ram) +* [`proton ram:buy BUYER RECEIVER BYTES`](#proton-rambuy-buyer-receiver-bytes) +* [`proton rpc:accountsbyauthorizers AUTHORIZATIONS [KEYS]`](#proton-rpcaccountsbyauthorizers-authorizations-keys) +* [`proton scan ACCOUNT`](#proton-scan-account) +* [`proton table CONTRACT [TABLE] [SCOPE]`](#proton-table-contract-table-scope) +* [`proton transaction JSON`](#proton-transaction-json) +* [`proton transaction:get ID`](#proton-transactionget-id) +* [`proton transaction:push TRANSACTION`](#proton-transactionpush-transaction) +* [`proton version`](#proton-version) ## `proton account ACCOUNT` @@ -403,7 +400,7 @@ USAGE $ proton endpoint:default [ENDPOINT] ARGUMENTS - ENDPOINT Specific endpoint + ENDPOINT Restore default endpoints DESCRIPTION Restore default enpoint @@ -850,7 +847,7 @@ _See code: [lib/commands/ram/index.js](https://github.com/ProtonProtocol/proton- ## `proton ram:buy BUYER RECEIVER BYTES` -Claim faucet +Buy RAM for an account ``` USAGE @@ -859,13 +856,18 @@ USAGE ARGUMENTS BUYER Account paying for RAM RECEIVER Account receiving RAM - BYTES Bytes of RAM to purchase + BYTES Number of bytes of RAM to purchase FLAGS - -p, --authorization= Use a specific authorization other than buyer@active + -p, --authorization= Authorization to use (e.g., account@active). Defaults to buyer@active DESCRIPTION - Claim faucet + Buy RAM for an account + +EXAMPLES + $ proton ram:buy myaccount myaccount 10000 + + $ proton ram:buy payer receiver 50000 -p payer@active ``` _See code: [lib/commands/ram/buy.js](https://github.com/ProtonProtocol/proton-cli/blob/v0.1.95/lib/commands/ram/buy.js)_ @@ -980,5 +982,4 @@ DESCRIPTION ``` _See code: [lib/commands/version.js](https://github.com/ProtonProtocol/proton-cli/blob/v0.1.95/lib/commands/version.js)_ - diff --git a/package-lock.json b/package-lock.json index 769bd03..10442d0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -116,6 +116,7 @@ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.18.5.tgz", "integrity": "sha512-MGY8vg3DxMnctw0LdvSEojOsumc70g0t18gNyUdAZqB1Rpd1Bqo/svHGvt+UJ6JcGX+DIekGFDxxIWofBxLCnQ==", "dev": true, + "peer": true, "dependencies": { "@ampproject/remapping": "^2.1.0", "@babel/code-frame": "^7.16.7", @@ -1582,6 +1583,7 @@ "resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.6.0.tgz", "integrity": "sha512-7RKRKuA4xTjMhY+eG3jthb3hlZCsOwg3rztWh75Xc+ShDWOfDDATWbeZpAHBNRpm4Tv9WgBMOy1zEJYXG6NJ7Q==", "dev": true, + "peer": true, "dependencies": { "@octokit/auth-token": "^2.4.4", "@octokit/graphql": "^4.5.8", @@ -1869,11 +1871,6 @@ "ripemd-ts": "0.0.2" } }, - "node_modules/@proton/js/node_modules/@bloks/constants": { - "version": "28.8.3", - "resolved": "https://registry.npmjs.org/@bloks/constants/-/constants-28.8.3.tgz", - "integrity": "sha512-9pTMxfij1Vwfhncl7NLvDUxPFAKzfVL2F8HVjHKj+mzBvKSCY+VOg2CM3Ob7TTo5PJD5MiH0VnHAYQLoLAp1Vw==" - }, "node_modules/@proton/js/node_modules/bn.js": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", @@ -2413,6 +2410,7 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.33.0.tgz", "integrity": "sha512-ZohdsbXadjGBSK0/r+d87X0SBmKzOq4/S5nzK6SBgJspFo9/CUDJ7hjayuze+JK7CZQLDMroqytp7pOcFKTxZA==", "dev": true, + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "4.33.0", "@typescript-eslint/types": "4.33.0", @@ -2560,6 +2558,7 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", "dev": true, + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -4405,6 +4404,7 @@ "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.32.0.tgz", "integrity": "sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==", "dev": true, + "peer": true, "dependencies": { "@babel/code-frame": "7.12.11", "@eslint/eslintrc": "^0.4.3", @@ -10868,6 +10868,7 @@ "version": "4.7.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.3.tgz", "integrity": "sha512-WOkT3XYvrpXx4vMMqlD+8R8R37fZkjyLGlxavMc4iB8lrl8L0DeTcHbYgw/v0N/z9wAFsgBhcsF0ruoySS22mA==", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -11221,6 +11222,7 @@ "version": "8.8.0", "resolved": "https://registry.npmjs.org/ws/-/ws-8.8.0.tgz", "integrity": "sha512-JDAgSYQ1ksuwqfChJusw1LSJ8BizJ2e/vVu5Lxjq3YvNJNlROv1ui4i+c/kUUrPheBvQl4c5UbERhTwKa6QBJQ==", + "peer": true, "engines": { "node": ">=10.0.0" }, @@ -11799,6 +11801,7 @@ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.18.5.tgz", "integrity": "sha512-MGY8vg3DxMnctw0LdvSEojOsumc70g0t18gNyUdAZqB1Rpd1Bqo/svHGvt+UJ6JcGX+DIekGFDxxIWofBxLCnQ==", "dev": true, + "peer": true, "requires": { "@ampproject/remapping": "^2.1.0", "@babel/code-frame": "^7.16.7", @@ -13010,6 +13013,7 @@ "resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.6.0.tgz", "integrity": "sha512-7RKRKuA4xTjMhY+eG3jthb3hlZCsOwg3rztWh75Xc+ShDWOfDDATWbeZpAHBNRpm4Tv9WgBMOy1zEJYXG6NJ7Q==", "dev": true, + "peer": true, "requires": { "@octokit/auth-token": "^2.4.4", "@octokit/graphql": "^4.5.8", @@ -13284,11 +13288,6 @@ "ripemd-ts": "0.0.2" }, "dependencies": { - "@bloks/constants": { - "version": "28.8.3", - "resolved": "https://registry.npmjs.org/@bloks/constants/-/constants-28.8.3.tgz", - "integrity": "sha512-9pTMxfij1Vwfhncl7NLvDUxPFAKzfVL2F8HVjHKj+mzBvKSCY+VOg2CM3Ob7TTo5PJD5MiH0VnHAYQLoLAp1Vw==" - }, "bn.js": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", @@ -13770,6 +13769,7 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.33.0.tgz", "integrity": "sha512-ZohdsbXadjGBSK0/r+d87X0SBmKzOq4/S5nzK6SBgJspFo9/CUDJ7hjayuze+JK7CZQLDMroqytp7pOcFKTxZA==", "dev": true, + "peer": true, "requires": { "@typescript-eslint/scope-manager": "4.33.0", "@typescript-eslint/types": "4.33.0", @@ -13861,7 +13861,8 @@ "version": "7.4.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", - "dev": true + "dev": true, + "peer": true }, "acorn-jsx": { "version": "5.3.2", @@ -15281,6 +15282,7 @@ "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.32.0.tgz", "integrity": "sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==", "dev": true, + "peer": true, "requires": { "@babel/code-frame": "7.12.11", "@eslint/eslintrc": "^0.4.3", @@ -20142,7 +20144,8 @@ "typescript": { "version": "4.7.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.3.tgz", - "integrity": "sha512-WOkT3XYvrpXx4vMMqlD+8R8R37fZkjyLGlxavMc4iB8lrl8L0DeTcHbYgw/v0N/z9wAFsgBhcsF0ruoySS22mA==" + "integrity": "sha512-WOkT3XYvrpXx4vMMqlD+8R8R37fZkjyLGlxavMc4iB8lrl8L0DeTcHbYgw/v0N/z9wAFsgBhcsF0ruoySS22mA==", + "peer": true }, "uint8array-tools": { "version": "0.0.7", @@ -20446,6 +20449,7 @@ "version": "8.8.0", "resolved": "https://registry.npmjs.org/ws/-/ws-8.8.0.tgz", "integrity": "sha512-JDAgSYQ1ksuwqfChJusw1LSJ8BizJ2e/vVu5Lxjq3YvNJNlROv1ui4i+c/kUUrPheBvQl4c5UbERhTwKa6QBJQ==", + "peer": true, "requires": {} }, "xml2js": { diff --git a/src/apis/uri/parseUri.ts b/src/apis/uri/parseUri.ts index 318bf5b..d3e3c9c 100644 --- a/src/apis/uri/parseUri.ts +++ b/src/apis/uri/parseUri.ts @@ -250,10 +250,19 @@ export async function signRequest( // if (returnPath && !(await isIosAppOnMac())) { // Linking.openURL(returnPath) // } - } catch (err) { + } catch (err: any) { console.error(err) - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - callbackURIWithError(callbackUrl, 'Request cancelled from WebAuth.com Wallet') + + // Provide more helpful error messages + let errorMessage = err?.message || 'Transaction signing failed' + + // Check for common issues + if (errorMessage.includes('No private key') || errorMessage.includes('unknown key')) { + errorMessage = 'No signing key available. WebAuthn keys (PUB_WA_*) cannot sign from CLI. Please add a K1 private key: proton key:add' + } + + if (callbackUrl) { + callbackURIWithError(callbackUrl, errorMessage) + } } } diff --git a/src/commands/contract/set.ts b/src/commands/contract/set.ts index 14078ee..fcd72d7 100644 --- a/src/commands/contract/set.ts +++ b/src/commands/contract/set.ts @@ -249,6 +249,7 @@ export default class SetContract extends Command { // 3. Set code if (!flags.abiOnly) { try { + CliUx.ux.log(yellow(`Deploying WASM to ${args.account}...`)) const res = await network.transact({ actions: [{ account: 'eosio', @@ -266,16 +267,27 @@ export default class SetContract extends Command { }], }) - CliUx.ux.log(green(`WASM Successfully ${deployText}:`)) - CliUx.ux.url(`View TX`, `${getExplorer()}/tx/${(res as any).transaction_id}?tab=traces`) - } catch (e) { - parseDetailsError(e) + const txId = (res as any).transaction_id + CliUx.ux.log(green(`✓ WASM Successfully ${deployText}`)) + CliUx.ux.log(` Account: ${args.account}`) + CliUx.ux.log(` Size: ${(wasm.length / 1024).toFixed(2)} KB`) + CliUx.ux.log(` TX: ${txId}`) + CliUx.ux.url(` View on Explorer`, `${getExplorer()}/tx/${txId}?tab=traces`) + } catch (e: any) { + // Check if this is just a "contract is unchanged" scenario + const errorMsg = e?.message || '' + if (errorMsg.includes('contract is already running this version of code')) { + CliUx.ux.log(yellow(`WASM unchanged - contract already has this code`)) + } else { + parseDetailsError(e) + } } } // 4. Set ABI if (!flags.wasmOnly) { try { + CliUx.ux.log(yellow(`Deploying ABI to ${args.account}...`)) const res = await network.transact({ actions: [{ account: 'eosio', @@ -290,10 +302,19 @@ export default class SetContract extends Command { }], }], }) - CliUx.ux.log(green(`ABI Successfully ${deployText}:`)) - CliUx.ux.url(`View TX`, `${getExplorer()}/tx/${(res as any).transaction_id}?tab=traces`) - } catch (e) { - parseDetailsError(e) + + const txId = (res as any).transaction_id + CliUx.ux.log(green(`✓ ABI Successfully ${deployText}`)) + CliUx.ux.log(` Account: ${args.account}`) + CliUx.ux.log(` TX: ${txId}`) + CliUx.ux.url(` View on Explorer`, `${getExplorer()}/tx/${txId}?tab=traces`) + } catch (e: any) { + const errorMsg = e?.message || '' + if (errorMsg.includes('contract is already running this version of code')) { + CliUx.ux.log(yellow(`ABI unchanged - contract already has this ABI`)) + } else { + parseDetailsError(e) + } } } diff --git a/src/commands/ram/buy.ts b/src/commands/ram/buy.ts index 1814a3a..d44db8e 100644 --- a/src/commands/ram/buy.ts +++ b/src/commands/ram/buy.ts @@ -2,43 +2,56 @@ import { Command, flags } from '@oclif/command' import { CliUx } from '@oclif/core' import { green } from 'colors' import { network } from '../../storage/networks' +import { parseDetailsError } from '../../utils/detailsError' -export default class ClaimFaucet extends Command { - static description = 'Claim faucet' +export default class RamBuy extends Command { + static description = 'Buy RAM for an account' + + static examples = [ + '$ proton ram:buy myaccount myaccount 10000', + '$ proton ram:buy payer receiver 50000 -p payer@active', + ] static args = [ { name: 'buyer', required: true, description: 'Account paying for RAM' }, { name: 'receiver', required: true, description: 'Account receiving RAM' }, - { name: 'bytes', required: true, description: 'Bytes of RAM to purchase' }, + { name: 'bytes', required: true, description: 'Number of bytes of RAM to purchase' }, ] static flags = { - authorization: flags.string({ char: 'p', description: 'Use a specific authorization other than buyer@active' }), + authorization: flags.string({ + char: 'p', + description: 'Authorization to use (e.g., account@active). Defaults to buyer@active', + }), } async run() { - const { args, flags } = this.parse(ClaimFaucet) + const { args, flags } = this.parse(RamBuy) const [actor, permission] = flags.authorization ? flags.authorization.split('@') : [args.buyer] - await network.transact({ - actions: [{ - account: 'eosio', - name: 'buyrambytes', - data: { - payer: actor, - receiver: args.receiver, - bytes: args.bytes, - }, - authorization: [{ - actor, - permission: permission || 'active' + try { + const res = await network.transact({ + actions: [{ + account: 'eosio', + name: 'buyrambytes', + data: { + payer: actor, + receiver: args.receiver, + bytes: Number(args.bytes), + }, + authorization: [{ + actor, + permission: permission || 'active' + }] }] - }] - }) + }) - CliUx.ux.log(`${green('Success:')} RAM Purchased`) + CliUx.ux.log(`${green('Success:')} Purchased ${args.bytes} bytes of RAM for ${args.receiver}`) + } catch (e) { + parseDetailsError(e) + } } } diff --git a/src/utils/detailsError.ts b/src/utils/detailsError.ts index cc7f514..29a8871 100644 --- a/src/utils/detailsError.ts +++ b/src/utils/detailsError.ts @@ -1,10 +1,42 @@ import { CliUx } from "@oclif/core" -import { red } from "colors" +import { red, yellow } from "colors" + +// Map common blockchain errors to user-friendly messages +const ERROR_HINTS: Record = { + 'insufficient ram': 'Try buying more RAM: proton ram:buy -p @active', + 'cpu usage exceeded': 'Your account has exceeded its CPU allocation. Wait for it to reset or stake more CPU.', + 'net usage exceeded': 'Your account has exceeded its NET allocation. Wait for it to reset or stake more NET.', + 'unknown key': 'The signing key is not available. Make sure you have added the correct private key: proton key:add', + 'transaction expired': 'The transaction took too long. Check your network connection and try again.', + 'insufficient balance': 'Your account does not have enough tokens for this transaction.', + 'missing authority': 'You do not have permission to perform this action. Check the authorization.', + 'duplicate transaction': 'This exact transaction was already executed. If intentional, wait a moment and retry.', + 'action blacklisted': 'This action is not allowed on this contract.', + 'account does not exist': 'The specified account does not exist on this chain.', +} + +function getHint(errorMessage: string): string | null { + const lowerError = errorMessage.toLowerCase() + for (const [key, hint] of Object.entries(ERROR_HINTS)) { + if (lowerError.includes(key)) { + return hint + } + } + return null +} export const parseDetailsError = (e: Error | any) => { const error = e && e.details && e.details.length && e.details[0] && e.details[0].message - if (error || typeof e === 'object') { - CliUx.ux.log('\n' + red(error || e.message)) + const message = error || e?.message || '' + + if (message || typeof e === 'object') { + CliUx.ux.log('\n' + red('Error: ' + message)) + + // Check for helpful hints + const hint = getHint(message) + if (hint) { + CliUx.ux.log(yellow('Hint: ' + hint)) + } } else { CliUx.ux.styledJSON(e) }