diff --git a/package.json b/package.json index 6021a9fd..dd1e8c21 100644 --- a/package.json +++ b/package.json @@ -13,9 +13,13 @@ "test:coverage": "jest --coverage" }, "dependencies": { - "@greymass/eosio": "^0.6.8", "@quasar/extras": "^1.13.4", - "@telosnetwork/ual-cleos": "^1.13.1", + "@wharfkit/session": "^1.2.8", + "@wharfkit/transact-plugin-resource-provider": "^1.1.1", + "@wharfkit/wallet-plugin-anchor": "^1.3.4", + "@wharfkit/wallet-plugin-cleos": "^1.1.1", + "@wharfkit/wallet-plugin-privatekey": "^1.1.0", + "@wharfkit/web-renderer": "^1.2.4", "axios": "^0.21.1", "core-js": "^3.6.5", "csvtojson": "^2.0.10", @@ -26,8 +30,6 @@ "ol": "^6.14.1", "pinia": "^2.1.6", "quasar": "^2.6.2", - "ual-anchor": "1.3.0", - "universal-authenticator-library": "^0.3.0", "vue": "^3.3.0", "vue-json-viewer": "^3.0.4", "vue-router": "^4.0.0", diff --git a/quasar.conf.js b/quasar.conf.js index 9c5eea74..a22292dd 100644 --- a/quasar.conf.js +++ b/quasar.conf.js @@ -30,7 +30,7 @@ module.exports = configure(function (ctx) { // app boot file (/src/boot) // --> boot files are part of "main.js" // https://quasar.dev/quasar-cli/boot-files - boot: ['config', 'fathom', 'api', 'ual', 'fuel'], + boot: ['config', 'fathom', 'api', 'wharf'], // https://quasar.dev/quasar-cli/quasar-conf-js#Property%3A-css css: ['app.sass'], diff --git a/src/api/eosio_core.ts b/src/api/eosio_core.ts index c8af814a..196b7aba 100644 --- a/src/api/eosio_core.ts +++ b/src/api/eosio_core.ts @@ -9,7 +9,7 @@ import { Serializer, PublicKey, Name, -} from '@greymass/eosio'; +} from '@wharfkit/session'; import { GetTableRowsParams } from 'src/types'; import { Chain } from 'src/types/Chain'; import { getChain } from 'src/config/ConfigManager'; diff --git a/src/api/fuel.ts b/src/api/fuel.ts deleted file mode 100644 index 71de5b60..00000000 --- a/src/api/fuel.ts +++ /dev/null @@ -1,573 +0,0 @@ -/* eslint-disable quotes */ -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ -// Most of this code was taken and addapted from https://gist.github.com/aaroncox/d74a73b3d9fbc20836c32ea9deda5d70 -import { - SignTransactionConfig, - SignTransactionResponse, - User, -} from 'universal-authenticator-library'; -import { - AnyTransaction, - APIClient, - Name, - NameType, - PackedTransaction, - PermissionLevel, - Serializer, - Signature, - SignedTransaction, - Transaction, -} from '@greymass/eosio'; -import { getChain } from 'src/config/ConfigManager'; -import { Dialog } from 'quasar'; -import { formatCurrency } from "src/utils/string-utils"; - -// The maximum fee per transaction this script is willing to accept -const maxFee = 0.05; - -// expire time in millisec -const expireSeconds = 3600; - -const chain = getChain(); -const client = new APIClient({ - url: chain.getHyperionEndpoint(), -}); - -const fuelrpc = chain.getFuelRPCEndpoint(); -const resourceProviderEndpoint = `${fuelrpc?.protocol}://${fuelrpc?.host}:${fuelrpc?.port}/v1/resource_provider/request_transaction`; - -// Auxiliar interfaces -interface ResourceProviderResponse { - code: number; - data: ResponseData; -} - -interface CostsType { - ram: string; - cpu: string; - net: string; -} - -interface ResponseData { - request: [string, SignedTransaction]; - signatures: Signature[]; - version: number; - costs?: CostsType; -} - -interface SignedTransactionResponse extends SignTransactionResponse { - transaction: { signatures: Signature[] }; -} - -// Wrapper for the user to intersect the signTransaction call -export class FuelUserWrapper extends User { - constructor(private user: User) { - super(); - } - - async signTransaction( - originalTransaction: AnyTransaction, - originalconfig?: SignTransactionConfig, - ): Promise { - try { - // if fuel is not supported, just let the normal implementation perform - if (!fuelrpc) { - return this.user.signTransaction(originalTransaction, originalconfig); - } - - // Retrieve transaction headers - const info = await client.v1.chain.get_info(); - const header = info.getTransactionHeader(expireSeconds); - - // collect all contract abis - const abi_promises = originalTransaction.actions.map(a => - client.v1.chain.get_abi(a.account), - ); - const responses = await Promise.all(abi_promises); - const abis = responses.map(x => x.abi); - const abis_and_names = originalTransaction.actions.map((x, i) => ({ - contract: x.account, - abi: abis[i], - })); - - // create complete well formed transaction - const transaction = Transaction.from( - { - ...header, - actions: originalTransaction.actions, - }, - abis_and_names, - ); - - // Pack the transaction for transport - const packedTransaction = PackedTransaction.from({ - signatures: [], - packed_context_free_data: '', - packed_trx: Serializer.encode({ object: transaction }), - }); - - const signer = PermissionLevel.from({ - actor: (await this.user.getAccountName()) as NameType, - permission: this.requestPermission, - }); - - // Submit the transaction to the resource provider endpoint - const cosigned = await fetch(resourceProviderEndpoint, { - body: JSON.stringify({ - signer, - packedTransaction, - }), - method: 'POST', - }); - - // Interpret the resulting JSON - const rpResponse = - (await cosigned.json()) as unknown as ResourceProviderResponse; - - switch (rpResponse.code) { - case 402: { - // Resource Provider provided signature in exchange for a fee - // is ok to treat them with the same logic of code = 200? - // Yes acording to this: https://gist.github.com/aaroncox/d74a73b3d9fbc20836c32ea9deda5d70#file-fuel-core-presign-js-L128-L159 - // Aron rightly suggests that we should show and confirm the fee costs for this service: - // https://github.com/telosnetwork/open-block-explorer/pull/477#discussion_r1053417964 - } - case 200: { - // Resource Provider provided signature for free - - const { data } = rpResponse; - const [, returnedTransaction] = data.request; - const modifiedTransaction: SignedTransaction = returnedTransaction; - - // Ensure the modifed transaction is what the application expects - // These validation methods will throw an exception if invalid data exists - const fees: string | null = validateTransaction( - signer, - modifiedTransaction, - transaction, - data.costs, - ); - - // validate with the user whether to use the service at all - try { - await confirmWithUser(this.user, fees); - } catch (e) { - // The user refuseed to use the service - break; - } - - modifiedTransaction.signatures = [...data.signatures]; - // Sign the modified transaction - const locallySigned: SignedTransactionResponse = - (await this.user.signTransaction( - modifiedTransaction, - Object.assign({ broadcast: false }, originalconfig), - )) as SignedTransactionResponse; - - // When using CleosAuthenticator the transaction returns empty - if (!locallySigned.transaction.signatures) { - return Promise.reject( - 'The transaction was not broadcasted because no signatures were obtained', - ); - } - - // Merge signatures from the user and the cosigned responsetab - modifiedTransaction.signatures = [ - ...locallySigned.transaction.signatures, - ...data.signatures, - ]; - - // Broadcast the signed transaction to the blockchain - const pushResponse = await client.v1.chain.push_transaction( - modifiedTransaction, - ); - - // we compose the final response - const finalResponse: SignTransactionResponse = { - wasBroadcast: true, - transactionId: pushResponse.transaction_id, - status: pushResponse.processed.receipt.status, - transaction: modifiedTransaction, - }; - - return Promise.resolve(finalResponse); - } - case 400: { - // Resource Provider refused to sign the transaction, aborting - break; - } - default: - throw ( - 'Code ' + - (+rpResponse.code).toString() + - ' not expected from resource provider endpoint: ' + - resourceProviderEndpoint - ); - } - - // If we got here it means the resource provider will not participate in this transaction - return this.user.signTransaction(originalTransaction, originalconfig); - } catch (e) { - throw e; - } - } - - // since this is a wrapper is also wraps the posible requestPermission hidden property - get requestPermission() { - return ( - (this.user as unknown as { requestPermission: string }) - .requestPermission || 'active' - ); - } - - // These functions are just proxies - signArbitrary = async ( - publicKey: string, - data: string, - helpText: string, - ): Promise => this.user.signArbitrary(publicKey, data, helpText); - verifyKeyOwnership = async (challenge: string): Promise => - this.user.verifyKeyOwnership(challenge); - getAccountName = async (): Promise => this.user.getAccountName(); - getChainId = async (): Promise => this.user.getChainId(); - getKeys = async (): Promise => this.user.getKeys(); -} - -// Auxiliar functions to validate with the user the use of the service -interface Preference { - remember?: boolean; - approve?: boolean; -} -export default class GreymassFuelService { - static preferences: { [account: string]: Preference } = {}; - static globals: Record string> = null; - static save() { - localStorage.setItem( - 'fuel_preferences', - JSON.stringify(GreymassFuelService.preferences), - ); - } - static drop() { - localStorage.removeItem('fuel_preferences'); - } - static load() { - try { - GreymassFuelService.preferences = GreymassFuelService.preferences || {}; - const str = localStorage.getItem('fuel_preferences'); - if (str) { - GreymassFuelService.preferences = JSON.parse(str); - } - } catch (e) { - console.error('ERROR: ', e); - } - } - - static setPreferences(account: string, p: Preference) { - GreymassFuelService.preferences[account] = { - ...GreymassFuelService.preferences[account], - ...p, - }; - if (GreymassFuelService.preferences[account].remember) { - GreymassFuelService.save(); - } else { - GreymassFuelService.drop(); - } - } - static setGlobals(g: Record string>) { - GreymassFuelService.globals = g; - } -} - -async function confirmWithUser(user: User, fees: string | null) { - const username = await user.getAccountName(); - let mymodel: string[] = []; - mymodel = []; - - return new Promise((resolve, reject) => { - // Try and see if the user already answer (remembered) - if ( - GreymassFuelService.preferences[username] && - GreymassFuelService.preferences[username].remember - ) { - // ok, the user did. What's the answer? - if (GreymassFuelService.preferences[username].approve) { - resolve(); - } else { - reject(); - } - return; - } - - const handler = function (approve: boolean) { - GreymassFuelService.setPreferences(username, { approve }); - if (approve) { - resolve(); - } else { - reject(); - } - }; - - // this are the normal texts for random wallet. - const cancel: string | boolean = 'Reject'; - const ok = 'Confirm'; - let message = - "Your account doesn't have sufficient resources (CPU, NET, or RAM) to pay for your next transaction. " + - 'Don\'t worry! Telos has partnered with Greymass to proceed with your transaction using "Greymass Fuel", allowing you to continue for free.

' + - 'We recommend powering up your account with at least 0.5 TLOS in CPU and NET each and purchasing RAM, as this service is not supported on all dAPPs in our ecosystem. Please click here to proceed and power up your account'; - - // If the wallet is Greymass Anchor is not possible to avoid Fuel service (it is incorporated) - try { - if (typeof fees === 'string') { - message = - "Your account doesn't have sufficient resources (CPU, NET, or RAM) to pay for your next transaction and it can not be processed without fees. " + - 'Telos has partnered with Greymass to proceed with your transaction using "Greymass Fuel", reducing cost significantly.

' + - 'Please confirm fees below to proceed.

' + - `
${fees}

` + - 'We recommend powering up your account with at least 0.5 TLOS in CPU and NET each and purchasing RAM, as this service is not supported on all dAPPs in our ecosystem. Please click here to proceed and power up your account'; - } - } catch (e) {} - - Dialog.create({ - title: 'Resource Warning!', - message, - html: true, - cancel, - ok, - persistent: true, - options: { - type: 'checkbox', - model: mymodel, - isValid: (model: string | string[]) => { - GreymassFuelService.setPreferences(username, { - remember: model.length === 1, - }); - return true; - }, - items: [ - { label: 'Remember my decision', value: 'remember', color: 'primary' }, - ], - }, - }) - // all answers should save the preferences - .onOk(() => handler(true)) - .onCancel(() => handler(false)); - }); -} - -// Auxiliar functions to validate modified transaction returned by the resourse provider - -// Validate the transaction -function validateTransaction( - signer: PermissionLevel, - modifiedTransaction: Transaction, - transaction: Transaction, - costs: CostsType | null = null, -): string | null { - // Ensure the first action is the `greymassnoop:noop` - validateNoop(modifiedTransaction); - - // Ensure the actions within the transaction match what was provided - return validateActions(signer, modifiedTransaction, transaction, costs); -} - -// Validate the actions of the modified transaction vs the original transaction -function validateActions( - signer: PermissionLevel, - modifiedTransaction: Transaction, - transaction: Transaction, - costs: CostsType | null, -): string | null { - // Determine how many actions we expect to have been added to the transaction based on the costs - const expectedNewActions = determineExpectedActionsLength(costs); - - // Ensure the proper number of actions was returned - validateActionsLength(expectedNewActions, modifiedTransaction, transaction); - - // Ensure the appended actions were expected - return validateActionsContent( - signer, - expectedNewActions, - modifiedTransaction, - transaction, - ); -} - -// Validate the number of actions is the number expected -function determineExpectedActionsLength(costs: CostsType | null) { - // By default, 1 new action is appended (noop) - let expectedNewActions = 1; - - // If there are costs associated with this transaction, 1 new actions is added (the fee) - if (costs) { - expectedNewActions += 1; - - // RAM cost is in format '0.000 TLOS' - const ramCostAsNumber = +(costs.ram.replace(/[^0-9.]/g, '')); - - // If there is a RAM cost associated with this transaction, 1 new action is added (the ram purchase) - if (ramCostAsNumber !== 0) { - expectedNewActions += 1; - } - } - - return expectedNewActions; -} - -// Validate the contents of each action -function validateActionsContent( - signer: PermissionLevel, - expectedNewActions: number, - modifiedTransaction: Transaction, - transaction: Transaction, -): string | null { - // Make sure the originally requested actions are still intact and unmodified - validateActionsOriginalContent( - expectedNewActions, - modifiedTransaction, - transaction, - ); - - // If a fee has been added, ensure the fee is set properly - if (expectedNewActions > 1) { - let totalFee: null | number = null; - totalFee = validateActionsFeeContent(signer, modifiedTransaction); - // If a ram purchase has been added, ensure the purchase was set properly - if (expectedNewActions > 2) { - validateActionsRamContent(signer, modifiedTransaction); - } - return formatCurrency(totalFee, 4, 'TLOS', true); - } else { - return null; - } -} - -interface AuxTransactionData { - [key: string]: string; -} - -function descerialize(data: unknown): AuxTransactionData { - return data as AuxTransactionData; -} - -// Ensure the transaction fee transfer is valid -function validateActionsFeeContent( - signer: PermissionLevel, - modifiedTransaction: Transaction, -): number { - const feeAction = modifiedTransaction.actions[1]; - const data = descerialize(feeAction.data); - const amount = parseFloat(data.quantity?.split(' ')[0]); - if (amount > maxFee) { - throw new Error(`Fee of ${amount} exceeds the maximum fee of ${maxFee}.`); - } - if ( - feeAction.account.toString() !== 'eosio.token' || - feeAction.name.toString() !== 'transfer' || - data.to.toString() !== 'fuel.gm' - ) { - throw new Error('Fee action was deemed invalid.'); - } - return amount; -} - -// Ensure the RAM purchasing action is valid -function validateActionsRamContent( - signer: PermissionLevel, - modifiedTransaction: Transaction, -): number { - const ramAction = modifiedTransaction.actions[2]; - const data = descerialize(ramAction.data); - const amount = parseFloat(data.quant?.split(' ')[0]); - - if ( - ramAction.account.toString() !== 'eosio' || - !['buyram', 'buyrambytes'].includes(String(ramAction.name)) || - data.payer.toString() !== 'greymassfuel' || - data.receiver.toString() !== signer.actor.toString() - ) { - throw new Error('RAM action was deemed invalid.'); - } - return amount; -} - -// Make sure the actions returned in the API response match what was submitted -function validateActionsOriginalContent( - expectedNewActions: number, - modifiedTransaction: Transaction, - transaction: Transaction, -) { - for (const [i] of modifiedTransaction.actions.entries()) { - // Skip the expected new actions - if (i < expectedNewActions) { - continue; - } - // Compare each action to the originally generated actions - const original = transaction.actions[i - expectedNewActions]; - const action = modifiedTransaction.actions[i]; - const matchesAccount = - action.account.toString() === original.account.toString(); - const matchesAction = action.name.toString() === original.name.toString(); - const matchesLength = - action.authorization.length === original.authorization.length; - const matchesActor = - action.authorization[0].actor.toString() === - original.authorization[0].actor.toString(); - const matchesPermission = - action.authorization[0].permission.toString() === - original.authorization[0].permission.toString(); - const matchesData = action.data.toString() === original.data.toString(); - if ( - !action || - !matchesAccount || - !matchesAction || - !matchesLength || - !matchesActor || - !matchesPermission || - !matchesData - ) { - const { account, name } = original; - throw new Error( - `Transaction returned by API has non-matching action at index ${i} (${account.toString()}:${name.toString()})`, - ); - } - } -} - -// Ensure no unexpected actions were appended in the response -function validateActionsLength( - expectedNewActions: number, - modifiedTransaction: Transaction, - transaction: Transaction, -) { - if ( - modifiedTransaction.actions.length !== - transaction.actions.length + expectedNewActions - ) { - throw new Error('transaction returned contains additional actions.'); - } -} - -const expectedCosignerContract = Name.from('greymassnoop'); -const expectedCosignerAction = Name.from('noop'); -const expectedCosignerAccountName = Name.from('greymassfuel'); -const expectedCosignerAccountPermission = Name.from('cosign'); - -// Make sure the first action is the greymassnoop:noop and properly defined -function validateNoop(modifiedTransaction: Transaction) { - const [firstAction] = modifiedTransaction.actions; - const [firstAuthorization] = firstAction.authorization; - if ( - firstAction.account.toString() !== expectedCosignerContract.toString() || - firstAction.name.toString() !== expectedCosignerAction.toString() || - firstAuthorization.actor.toString() !== - expectedCosignerAccountName.toString() || - firstAuthorization.permission.toString() !== - expectedCosignerAccountPermission.toString() || - (JSON.stringify(firstAction.data) !== '""' && - JSON.stringify(firstAction.data) !== '{}') - ) { - throw new Error( - `First action within transaction response is not valid noop (${expectedCosignerContract.toString()}:${expectedCosignerAction.toString()} signed by ${expectedCosignerAccountName.toString()}:${expectedCosignerAccountPermission.toString()}).`, - ); - } -} diff --git a/src/boot/fuel.ts b/src/boot/fuel.ts deleted file mode 100644 index d07093d9..00000000 --- a/src/boot/fuel.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { boot } from 'quasar/wrappers'; -import GreymassFuelService from 'src/api/fuel'; - -declare module '@vue/runtime-core' { - interface ComponentCustomProperties { - $fuel: GreymassFuelService; - } -} - -export default boot(({ app }) => { - app.config.globalProperties.$fuel = GreymassFuelService; - GreymassFuelService.setGlobals(app.config.globalProperties); - GreymassFuelService.load(); -}); diff --git a/src/boot/ual.ts b/src/boot/ual.ts deleted file mode 100644 index 3c83f6f4..00000000 --- a/src/boot/ual.ts +++ /dev/null @@ -1,174 +0,0 @@ -import { boot } from 'quasar/wrappers'; -import { - Authenticator, - RpcEndpoint, - UAL, - User, -} from 'universal-authenticator-library'; -import { Anchor } from 'ual-anchor'; -import { getChain } from 'src/config/ConfigManager'; -import { CleosAuthenticator } from '@telosnetwork/ual-cleos'; -import { Dialog, Notify, copyToClipboard } from 'quasar'; -import { isValidAccount } from 'src/utils/string-utils'; - -declare module '@vue/runtime-core' { - interface ComponentCustomProperties { - $ual: UAL; - $user: User; - } -} - -async function loginHandler() { - let accountName = 'eosio'; - let permission = 'active'; - if (localStorage.getItem('autoLogin_' + getChain().getChainId()) === 'cleos') { - accountName = localStorage.getItem('account_' + getChain().getChainId()); - } else { - await new Promise((resolve) => { - Dialog.create({ - color: 'primary', - title: 'Connect to cleos', - message: 'Account name (.,a-z,1-5)', - prompt: { - model: '', - type: 'text', - isValid: val => isValidAccount(val), - }, - cancel: true, - persistent: true, - }) - .onOk((data: string) => { - accountName = data !== '' ? data : 'eosio'; - }) - .onCancel(() => { - throw 'Cancelled!'; - }) - .onDismiss(() => { - resolve(true); - }); - }); - await new Promise((resolve) => { - Dialog.create({ - color: 'primary', - title: 'Connect to cleos', - message: 'Account permission', - options: { - type: 'radio', - model: [], - items: [ - { label: 'Active', value: 'active' }, - { label: 'Owner', value: 'owner' }, - ], - }, - cancel: true, - persistent: true, - }) - .onOk((data: string) => { - permission = data; - }) - .onCancel(() => { - throw 'Cancelled!'; - }) - .onDismiss(() => { - resolve(true); - }); - }); - } - return { - accountName, - permission, - }; -} - -async function signHandler(rpc: RpcEndpoint, trx: string) { - const trxJSON: string = JSON.stringify( - Object.assign( - { - delay_sec: 0, - max_cpu_usage_ms: 0, - }, - trx, - ), - null, - 4, - ); - await new Promise((resolve) => { - Dialog.create({ - color: 'primary', - message: `
cleos -u ${rpc.protocol}://${rpc.host}:${rpc.port} push transaction '${trxJSON}'
`, - html: true, - cancel: true, - fullWidth: true, - ok: { - label: 'Copy', - }, - }) - .onOk(() => { - copyToClipboard( - `cleos -u ${rpc.protocol}://${rpc.host}:${rpc.port} push transaction '${trxJSON}'`, - ) - .then((): void => { - Notify.create({ - color: 'green-4', - textColor: 'white', - message: 'Copied to clipboard', - timeout: 1000, - }); - }) - .catch(() => { - Notify.create({ - color: 'red-8', - textColor: 'white', - message: 'Could not copy', - timeout: 1000, - }); - }); - }) - .onCancel(() => { - throw 'Cancelled!'; - }) - .onDismiss(() => { - resolve(true); - }); - }); -} - -async function signHandlerForMainChain(trx: string) { - return signHandler(getChain().getRPCEndpoint(), trx); -} - -function getMainChain() { - return { - chainId: getChain().getChainId(), - rpcEndpoints: [getChain().getRPCEndpoint()], - }; -} - -const authenticators: Authenticator[] = []; - -export const getAuthenticators = () => { - // we initialize the authenticators inside this function on demand - if (authenticators.length === 0) { - // UAL is not looking at the chain when checking the sessionStorage for an already logged in account - // A quick fix is to add the chain in appName until we move forward with WharfKit - const mainChain = getMainChain(); - authenticators.push(new Anchor([mainChain], { appName: `${process.env.APP_NAME}_${mainChain.chainId}` })), - authenticators.push(new CleosAuthenticator([mainChain], { - appName: `${process.env.APP_NAME}_${mainChain.chainId}`, - loginHandler, - signHandler: signHandlerForMainChain, - })); - } - return authenticators; -}; - -export const resetUalState = () => { - authenticators.length = 0; -}; - -export default boot(({ app }) => { - const authenticators = getAuthenticators(); - const ual = new UAL([getMainChain()], 'ual', authenticators); - - app.config.globalProperties.$ual = ual; -}); diff --git a/src/boot/wharf.ts b/src/boot/wharf.ts new file mode 100644 index 00000000..a70e613d --- /dev/null +++ b/src/boot/wharf.ts @@ -0,0 +1,45 @@ +import { Session, SessionKit } from '@wharfkit/session'; +import { TransactPluginResourceProvider } from '@wharfkit/transact-plugin-resource-provider'; +import { WalletPluginAnchor } from '@wharfkit/wallet-plugin-anchor'; +import { WalletPluginCleos } from '@wharfkit/wallet-plugin-cleos'; +import { WalletPluginPrivateKey } from '@wharfkit/wallet-plugin-privatekey'; +import WebRenderer from '@wharfkit/web-renderer'; +import { boot } from 'quasar/wrappers'; +import { getChain } from 'src/config/ConfigManager'; +import { Chain } from 'src/types/Chain'; + +const chain: Chain = getChain(); + +declare module 'vue' { + interface ComponentCustomProperties { + $kit: SessionKit; + $user: Session; + } +} + +export const ui = new WebRenderer(); + +export const kit = new SessionKit({ + appName: process.env.APP_NAME, + chains: [ + { + id: chain.getChainId(), + url: String(chain.getRPCEndpoint()), + }, + ], + ui, + walletPlugins: [ + new WalletPluginAnchor(), + new WalletPluginCleos(), + new WalletPluginPrivateKey('5Jtoxgny5tT7NiNFp1MLogviuPJ9NniWjnU4wKzaX4t7pL4kJ8s'), + ], +}, +{ + transactPlugins: [ + new TransactPluginResourceProvider(), + ], +}); + +export default boot(({ app }) => { + app.config.globalProperties.$kit = kit; +}); diff --git a/src/components/AccountCard.vue b/src/components/AccountCard.vue index 0e7d28d4..d5bb35dd 100644 --- a/src/components/AccountCard.vue +++ b/src/components/AccountCard.vue @@ -11,7 +11,7 @@ import { getChain } from 'src/config/ConfigManager'; import { api } from 'src/api'; import { useRouter } from 'vue-router'; import { TableIndexType } from 'src/types/Api'; -import { API, UInt64 } from '@greymass/eosio'; +import { API, UInt64 } from '@wharfkit/session'; import { formatCurrency } from 'src/utils/string-utils'; import ConfigManager from 'src/config/ConfigManager'; import { isSystemAccount } from 'src/utils/systemAccount'; diff --git a/src/components/BlockCard.vue b/src/components/BlockCard.vue index efbeab91..406298b7 100644 --- a/src/components/BlockCard.vue +++ b/src/components/BlockCard.vue @@ -15,6 +15,10 @@ import { formatDate } from 'src/utils/string-utils'; export default defineComponent({ name: 'BlockCard', props: { + block_num: { + type: String, + required: true, + }, block: { type: Object as PropType, required: false, @@ -22,8 +26,10 @@ export default defineComponent({ }, }, setup(props) { + const loading = ref(true); const router = useRouter(); const q = useQuasar(); + const blockNum = computed(() => props.block_num); const Block = computed(() => props.block); const blockInfo = ref<{ key: string; value: string }[]>([]); async function nextBlock() { @@ -96,6 +102,7 @@ export default defineComponent({ }, { key: 'Actions', value: actionCount.toString() }, ]; + loading.value = false; } } watch(Block, () => { @@ -105,13 +112,14 @@ export default defineComponent({ setBlockData(); }); return { - block_num: computed(() => Block.value?.block_num || 0), nextBlock, previousBlock, numberWithCommas, formatDate, copy, blockInfo, + blockNum, + loading, }; }, }); @@ -154,7 +162,7 @@ export default defineComponent({
-
{{numberWithCommas(block_num)}}
+
{{numberWithCommas(parseInt(block_num))}}
- -
SUMMARY
+ + +
+ +
-
- +
diff --git a/src/components/KeyAccountsCard.vue b/src/components/KeyAccountsCard.vue index 62a18122..7d203355 100644 --- a/src/components/KeyAccountsCard.vue +++ b/src/components/KeyAccountsCard.vue @@ -3,7 +3,7 @@ import { useQuasar } from 'quasar'; import { defineComponent, PropType, computed, ref } from 'vue'; import { copyToClipboard } from 'quasar'; import { getChain } from 'src/config/ConfigManager'; -import { Name, PublicKey } from '@greymass/eosio'; +import { Name, PublicKey } from '@wharfkit/session'; export default defineComponent({ name: 'KeyAccountsCard', diff --git a/src/components/KeyToggle.vue b/src/components/KeyToggle.vue index 4516492d..48944151 100644 --- a/src/components/KeyToggle.vue +++ b/src/components/KeyToggle.vue @@ -2,7 +2,7 @@ import { useQuasar } from 'quasar'; import { computed, defineComponent, ref } from 'vue'; import { copyToClipboard } from 'quasar'; -import { PublicKey, Weight } from '@greymass/eosio'; +import { PublicKey, Weight } from '@wharfkit/session'; export default defineComponent({ name: 'KeyToggle', diff --git a/src/components/KeysPanel.vue b/src/components/KeysPanel.vue index 98aed1f9..462a22a1 100644 --- a/src/components/KeysPanel.vue +++ b/src/components/KeysPanel.vue @@ -4,7 +4,7 @@ import PermissionCard from 'components/PermissionCard.vue'; import { computed, defineComponent, onMounted, ref, watch } from 'vue'; import { api } from 'src/api'; import { useQuasar } from 'quasar'; -import { API } from '@greymass/eosio'; +import { API } from '@wharfkit/session'; export default defineComponent({ name: 'KeysPanel', diff --git a/src/components/LoginHandler.vue b/src/components/LoginHandler.vue index d572f959..606faa6d 100644 --- a/src/components/LoginHandler.vue +++ b/src/components/LoginHandler.vue @@ -1,42 +1,56 @@