From e923227156c0e4fc13834cbf3a25033b7417721e Mon Sep 17 00:00:00 2001 From: zlayine Date: Fri, 18 Aug 2023 13:18:10 +0300 Subject: [PATCH] improve transactions --- resources/js/components/SignTransaction.vue | 27 +++- resources/js/components/Slideover.vue | 7 +- resources/js/components/beam/BeamsList.vue | 4 +- resources/js/components/pages/Collections.vue | 3 +- resources/js/components/pages/FuelTanks.vue | 3 +- resources/js/components/pages/Tokens.vue | 4 +- .../js/components/pages/Transactions.vue | 3 + resources/js/components/pages/Wallets.vue | 3 +- .../common/DetailsTransactionSlideover.vue | 115 ++++++++------ resources/js/factory/transaction.ts | 5 +- resources/js/graphql/query/GetTransaction.ts | 19 ++- resources/js/store/index.ts | 105 +------------ resources/js/store/transaction.ts | 147 ++++++++++++++++++ resources/js/types/types.interface.ts | 4 + 14 files changed, 277 insertions(+), 172 deletions(-) create mode 100644 resources/js/store/transaction.ts diff --git a/resources/js/components/SignTransaction.vue b/resources/js/components/SignTransaction.vue index 75bb26d..748fad0 100644 --- a/resources/js/components/SignTransaction.vue +++ b/resources/js/components/SignTransaction.vue @@ -5,9 +5,10 @@ - Select an account to sign + Select an account to sign the transaction
+
Transaction fee: {{ feeCost }}
{ - useAppStore().getAccounts(); - showAccountsModal.value = true; + try { + if (!appStore.provider) { + snackbar.error({ title: 'Please connect your wallet to sign' }); + return; + } + appStore.getAccounts(); + await transactionStore.init(); + feeCost.value = await transactionStore.getTransactionCost(props.transaction); + showAccountsModal.value = true; + } catch (e) { + console.log(e) + snackbar.error({ title: 'Failed to sign transaction' }); + } }; const closeModal = () => { @@ -58,9 +75,9 @@ const closeModal = () => { const selectAccount = async (account) => { try { isLoading.value = true; - useAppStore().setAccount(account); + appStore.setAccount(account); showAccountsModal.value = false; - const res = await useAppStore().signTransaction(props.transaction); + const res = await transactionStore.signTransaction(props.transaction); if (res) { emit('success'); } diff --git a/resources/js/components/Slideover.vue b/resources/js/components/Slideover.vue index 80fe0c3..4519fe1 100644 --- a/resources/js/components/Slideover.vue +++ b/resources/js/components/Slideover.vue @@ -22,6 +22,10 @@ @close="emit('close')" @update="($event) => emit('update', $event)" /> +
@@ -33,6 +37,7 @@ diff --git a/resources/js/factory/transaction.ts b/resources/js/factory/transaction.ts index d2418b1..92a2336 100644 --- a/resources/js/factory/transaction.ts +++ b/resources/js/factory/transaction.ts @@ -25,9 +25,6 @@ export class DTOTransactionFactory { public static forTransaction(transactionData: any): any { const transaction = transactionData.data.GetTransaction; - return { - items: [DTOTransactionFactory.buildTransaction(transaction)], - cursor: null, - }; + return DTOTransactionFactory.buildTransaction(transaction); } } diff --git a/resources/js/graphql/query/GetTransaction.ts b/resources/js/graphql/query/GetTransaction.ts index 18eb545..22a86bd 100644 --- a/resources/js/graphql/query/GetTransaction.ts +++ b/resources/js/graphql/query/GetTransaction.ts @@ -1,20 +1,29 @@ export default `query GetTransaction($id: BigInt!) { GetTransaction(id: $id) { id - method + wallet { + account { + publicKey + } + } transactionHash + method result state transactionId + encodedData + signedAtBlock events { - edges { + edges { node { - params { + moduleId + eventId + params { type value - } - } + } } + } } } }`; diff --git a/resources/js/store/index.ts b/resources/js/store/index.ts index 2a98761..00f1cce 100644 --- a/resources/js/store/index.ts +++ b/resources/js/store/index.ts @@ -6,72 +6,19 @@ import snackbar from '~/util/snackbar'; import { AuthApi } from '~/api/auth'; import { CollectionApi } from '~/api/collection'; import { WalletConnectModalSign } from '@walletconnect/modal-sign-html'; -import { formatData, snackbarErrors, wcOptions } from '~/util'; +import { wcOptions } from '~/util'; import { wcRequiredNamespaces } from '~/util'; import { getSdkError } from '@walletconnect/utils'; import { PolkadotjsWallet, Wallet } from '@talismn/connect-wallets'; import { ApiPromise, WsProvider } from '@polkadot/api'; import { SignerPayloadJSON } from '@polkadot/types/types'; import { AccountInfoWithTripleRefCount } from '@polkadot/types/interfaces'; -import { TransactionApi } from '~/api/transaction'; -const RPC_URLS = { - canary: 'wss://rpc.matrix.canary.enjin.io', - polkadot: 'wss://rpc.efinity.io', -}; const parseConfigURL = (url: string): URL => { return new URL(url); }; -const updateTransaction = async ({ - id, - transactionHash, - signingAccount, - signedAtBlock, -}: { - id: string; - transactionHash: string; - signingAccount: string; - signedAtBlock: number; -}) => { - try { - const res = await TransactionApi.updateTransaction( - formatData({ - id: id, - transactionHash: transactionHash, - state: 'BROADCAST', - signingAccount: signingAccount, - signedAtBlock: signedAtBlock, - }) - ); - - const updated = res.data?.UpdateTransaction; - - if (updated) { - snackbar.success({ - title: 'Transaction signed', - text: `The transaction was signed successfully`, - }); - - return true; - } - - snackbar.error({ - title: 'Sign Transaction', - text: 'Signing transaction failed', - }); - - return false; - } catch (e) { - if (snackbarErrors(e)) return; - snackbar.error({ - title: 'Sign Transaction', - text: 'Signing transaction failed', - }); - } -}; - export const useAppStore = defineStore('app', { state: (): AppState => ({ url: undefined, @@ -340,55 +287,7 @@ export const useAppStore = defineStore('app', { this.provider = 'polkadot.js'; } }, - async signTransaction(transaction: any) { - const provider = new WsProvider(RPC_URLS[this.config.network]); - const api = await ApiPromise.create({ provider }); - const [genesisHash, currentBlock, runtime, account] = await Promise.all([ - api.rpc.chain.getBlockHash(0), - api.rpc.chain.getBlock(), - api.rpc.state.getRuntimeVersion(), - // @ts-ignore - api.query.system.account(this.account.address), - ]); - - // This is the call that comes from the platform transactions 'encodedCall' - const call = transaction.encodedData; - const era = '00'; // 00 is for immortal transactions - const genesis = genesisHash.toHex(); // The genesis block - const blockHash = genesisHash.toHex(); // For immortal transactions the blockhash needs to be the genesis - - const payloadToSign: SignerPayloadJSON = { - specVersion: runtime.specVersion.toString(), - transactionVersion: runtime.transactionVersion.toString(), - address: this.account.address, - blockHash: blockHash, - blockNumber: '0x00', - era: '0x' + era, - genesisHash: genesis, - method: call, - nonce: account.nonce.toHex(), - signedExtensions: api.registry.signedExtensions, - tip: '0x00', - version: 4, - }; - - const { signature } = await this.account.signer.signPayload(payloadToSign); - - const extrinsic = api.registry.createType( - 'Extrinsic', - { method: payloadToSign.method }, - { version: payloadToSign.version } - ); - extrinsic.addSignature(this.account.address, signature, payloadToSign); - - const transactionHash = await api.rpc.author.submitExtrinsic(extrinsic.toHex()); - return await updateTransaction({ - id: transaction.id, - transactionHash: transactionHash.toHex(), - signingAccount: this.account.address, - signedAtBlock: currentBlock.block.header.number.toNumber(), - }); - }, + async disconnectWallet() { try { if (this.provider === 'wc') { diff --git a/resources/js/store/transaction.ts b/resources/js/store/transaction.ts new file mode 100644 index 0000000..904dfcf --- /dev/null +++ b/resources/js/store/transaction.ts @@ -0,0 +1,147 @@ +import { ApiPromise, WsProvider } from '@polkadot/api'; +import { defineStore } from 'pinia'; +import { TransactionState } from '~/types/types.interface'; +import { useAppStore } from '.'; +import { TransactionApi } from '~/api/transaction'; +import { formatData, snackbarErrors } from '~/util'; +import snackbar from '~/util/snackbar'; +import { SignerPayloadJSON } from '@polkadot/types/types'; + +const RPC_URLS = { + canary: 'wss://rpc.matrix.canary.enjin.io', + polkadot: 'wss://rpc.efinity.io', +}; + +export const useTransactionStore = defineStore('transaction', { + state: (): TransactionState => ({ + api: null, + }), + actions: { + async init() { + const provider = new WsProvider(RPC_URLS[useAppStore().config.network]); + const api = await ApiPromise.create({ provider }); + console.log(api) + }, + async getExtrinsicData(transaction: any, address: string) { + console.log('here'); + console.log(address); + const [genesisHash, currentBlock, runtime, account] = await Promise.all([ + this.api.rpc.chain.getBlockHash(0), + this.api.rpc.chain.getBlock(), + this.api.rpc.state.getRuntimeVersion(), + // @ts-ignore + this.api.query.system.account(address), + ]); + + // This is the call that comes from the platform transactions 'encodedCall' + const call = transaction.encodedData; + const era = '00'; // 00 is for immortal transactions + const genesis = genesisHash.toHex(); // The genesis block + const blockHash = genesisHash.toHex(); // For immortal transactions the blockhash needs to be the genesis + + const payloadToSign: SignerPayloadJSON = { + specVersion: runtime.specVersion.toString(), + transactionVersion: runtime.transactionVersion.toString(), + address: address, + blockHash: blockHash, + blockNumber: '0x00', + era: '0x' + era, + genesisHash: genesis, + method: call, + nonce: account.nonce.toHex(), + signedExtensions: this.api.registry.signedExtensions, + tip: '0x00', + version: 4, + }; + + const extrinsic = this.api.registry.createType( + 'Extrinsic', + { method: payloadToSign.method }, + { version: payloadToSign.version } + ); + + return { + extrinsic, + payloadToSign, + currentBlock, + }; + }, + async getTransactionCost(transaction: any) { + const { extrinsic } = await this.getExtrinsicData(transaction, useAppStore().accounts[0].address); + console.log('here 1'); + + const paymentInfo = await this.api.tx[extrinsic.method.section] + [extrinsic.method.method](...extrinsic.method.args) + .paymentInfo(useAppStore().accounts[0].address); + + console.log('here 2'); + return paymentInfo.partialFee.toHuman(); + }, + async signTransaction(transaction: any) { + const { extrinsic, payloadToSign, currentBlock } = await this.getExtrinsicData( + transaction, + useAppStore().account.address + ); + + const { signature } = await useAppStore().account.signer.signPayload(payloadToSign); + + extrinsic.addSignature(useAppStore().account.address, signature, payloadToSign); + + const transactionHash = await this.api.rpc.author.submitExtrinsic(extrinsic.toHex()); + + return await this.updateTransaction({ + id: transaction.id, + transactionHash: transactionHash.toHex(), + signingAccount: useAppStore().account.address, + signedAtBlock: currentBlock.block.header.number.toNumber(), + }); + }, + async updateTransaction({ + id, + transactionHash, + signingAccount, + signedAtBlock, + }: { + id: string; + transactionHash: string; + signingAccount: string; + signedAtBlock: number; + }) { + try { + const res = await TransactionApi.updateTransaction( + formatData({ + id: id, + transactionHash: transactionHash, + state: 'BROADCAST', + signingAccount: signingAccount, + signedAtBlock: signedAtBlock, + }) + ); + + const updated = res.data?.UpdateTransaction; + + if (updated) { + snackbar.success({ + title: 'Transaction signed', + text: `The transaction was signed successfully`, + }); + + return true; + } + + snackbar.error({ + title: 'Sign Transaction', + text: 'Signing transaction failed', + }); + + return false; + } catch (e) { + if (snackbarErrors(e)) return; + snackbar.error({ + title: 'Sign Transaction', + text: 'Signing transaction failed', + }); + } + }, + }, +}); diff --git a/resources/js/types/types.interface.ts b/resources/js/types/types.interface.ts index e05cc80..38ea4a8 100644 --- a/resources/js/types/types.interface.ts +++ b/resources/js/types/types.interface.ts @@ -25,6 +25,10 @@ export interface AppState { accounts: any; } +export interface TransactionState { + api: any; +} + export interface NotificationsState { notifications: NotificationInfo[]; }