From 8150f21123efa360a7cb558c9a24fa91a0781600 Mon Sep 17 00:00:00 2001 From: magestrio <9oleg8@gmail.com> Date: Tue, 26 Nov 2024 09:48:44 +0200 Subject: [PATCH 1/3] implement zkdb transaction. Part 1 --- packages/api/src/graphql/client.ts | 2 +- .../serverless/src/apollo/app/transaction.ts | 14 -- .../src/apollo/app/types/transaction.ts | 15 ++ packages/zkdb/src/index.ts | 2 + .../authentication-transaction.ts | 6 + .../src/sdk/authentication/authentication.ts | 76 ++++++--- .../zkdb/src/sdk/authentication/sign-in.ts | 59 +++++++ packages/zkdb/src/sdk/global/environment.ts | 26 +++ .../zkdb/src/sdk/global/zkdatabase-client.ts | 30 ++-- .../zkdb/src/sdk/signer/browser-signer.ts | 13 +- packages/zkdb/src/sdk/signer/nodejs-signer.ts | 42 +++-- .../src/sdk/transaction/zkdb-transaction.ts | 150 ++++++++++++++++++ packages/zkdb/src/sdk/wallet/auro-wallet.ts | 16 +- packages/zkdb/src/types/transaction.ts | 16 ++ 14 files changed, 385 insertions(+), 82 deletions(-) create mode 100644 packages/zkdb/src/sdk/authentication/authentication-transaction.ts create mode 100644 packages/zkdb/src/sdk/authentication/sign-in.ts create mode 100644 packages/zkdb/src/sdk/global/environment.ts create mode 100644 packages/zkdb/src/sdk/transaction/zkdb-transaction.ts diff --git a/packages/api/src/graphql/client.ts b/packages/api/src/graphql/client.ts index 0e7d9306..14e9dd92 100644 --- a/packages/api/src/graphql/client.ts +++ b/packages/api/src/graphql/client.ts @@ -6,7 +6,7 @@ import { } from "@apollo/client"; import { setContext } from "@apollo/client/link/context/index.js"; import { removeTypenameFromVariables } from "@apollo/client/link/remove-typename/index.js"; -import { Context } from "@authentication"; +import { Context } from "../authentication"; import { collection } from "./collection"; import { collectionIndex } from "./collection-index"; import { database } from "./database"; diff --git a/packages/serverless/src/apollo/app/transaction.ts b/packages/serverless/src/apollo/app/transaction.ts index bede9161..b1ceda99 100644 --- a/packages/serverless/src/apollo/app/transaction.ts +++ b/packages/serverless/src/apollo/app/transaction.ts @@ -14,20 +14,6 @@ scalar JSON type Query type Mutation -enum TransactionType { - deploy - rollup -} - -type DbTransaction { - databaseName: String! - transactionType: TransactionType! - zkAppPublicKey: String! - status: TransactionStatus! - tx: String! - id: String! -} - extend type Query { getTransaction(databaseName: String!, transactionType: TransactionType!): DbTransaction! } diff --git a/packages/serverless/src/apollo/app/types/transaction.ts b/packages/serverless/src/apollo/app/types/transaction.ts index ef1f724b..e0331871 100644 --- a/packages/serverless/src/apollo/app/types/transaction.ts +++ b/packages/serverless/src/apollo/app/types/transaction.ts @@ -9,4 +9,19 @@ enum TransactionStatus { success unknown } + +enum TransactionType { + deploy + rollup +} + +type DbTransaction { + databaseName: String! + transactionType: TransactionType! + zkAppPublicKey: String! + status: TransactionStatus! + tx: String! + id: String! +} + `; diff --git a/packages/zkdb/src/index.ts b/packages/zkdb/src/index.ts index e5fc3c74..608258af 100644 --- a/packages/zkdb/src/index.ts +++ b/packages/zkdb/src/index.ts @@ -1,2 +1,4 @@ export * from './sdk'; export * from './types'; + + diff --git a/packages/zkdb/src/sdk/authentication/authentication-transaction.ts b/packages/zkdb/src/sdk/authentication/authentication-transaction.ts new file mode 100644 index 00000000..dafdf58d --- /dev/null +++ b/packages/zkdb/src/sdk/authentication/authentication-transaction.ts @@ -0,0 +1,6 @@ +import { isBrowser } from '@utils'; +import { PrivateKey } from 'o1js'; + +export interface AuthenticationTransaction { + signAndSend(privateKey?: PrivateKey): Promise; +} diff --git a/packages/zkdb/src/sdk/authentication/authentication.ts b/packages/zkdb/src/sdk/authentication/authentication.ts index f43014c5..7434a999 100644 --- a/packages/zkdb/src/sdk/authentication/authentication.ts +++ b/packages/zkdb/src/sdk/authentication/authentication.ts @@ -1,6 +1,10 @@ /* eslint-disable no-unused-vars */ import { IApiClient, TSignInInfo } from '@zkdb/api'; -import { Signer } from '../signer'; +import { AuroWalletSigner, NodeSigner } from '../signer'; +import { PrivateKey } from 'o1js'; +import { SignedData } from '../../types/signing'; +import { Environment } from '../global/environment'; +import { isBrowser } from '../../utils/environment'; export const ZKDB_KEY_ACCESS_TOKEN = 'accessToken'; @@ -15,19 +19,19 @@ export interface ISecureStorage { } export class Authenticator { - #signer: Signer | undefined; - #storage: ISecureStorage; private apiClient: IApiClient; + private environment: Environment; + constructor( - signer: Signer, apiClient: IApiClient, + environment: Environment, storage: ISecureStorage = new Map() ) { - this.#signer = signer; this.apiClient = apiClient; + this.environment = environment; this.#storage = storage; } @@ -39,24 +43,27 @@ export class Authenticator { return Math.floor(Date.now() / 1000); } - public get signer(): Signer { - if (this.#signer) { - return this.#signer; - } - throw new Error('Signer is not initialized'); - } - - public connect(signer: Signer) { - this.#signer = signer; - } - isLoggedIn(): boolean { return typeof this.#storage.get(ZKDB_KEY_ACCESS_TOKEN) === 'string'; } - public async signIn() { + public async signIn(privateKey?: PrivateKey) { const ecdsa = (await this.user.ecdsa(undefined)).unwrap(); - const proof = await this.signer.signMessage(ecdsa); + + let proof: SignedData; + + if (privateKey) { + const { networkId } = this.environment.getEnv(); + proof = await NodeSigner.getInstance(networkId).signMessage( + ecdsa, + privateKey + ); + } else if (isBrowser()) { + proof = await AuroWalletSigner.getInstance().signMessage(ecdsa); + } else { + throw Error('Missed private key'); + } + const userData = (await this.user.signIn({ proof })).unwrap(); this.#storage.set(ZKDB_KEY_ACCESS_TOKEN, userData.accessToken); @@ -72,13 +79,32 @@ export class Authenticator { return userData; } - public async signUp(userName: string, email: string) { - const proof = await this.signer.signMessage( - JSON.stringify({ - userName, - email, - }) - ); + public async signUp( + userName: string, + email: string, + privateKey?: PrivateKey + ) { + let proof: SignedData; + + if (privateKey) { + const { networkId } = this.environment.getEnv(); + proof = await NodeSigner.getInstance(networkId).signMessage( + JSON.stringify({ + userName, + email, + }), + privateKey + ); + } else if (isBrowser()) { + proof = await AuroWalletSigner.getInstance().signMessage( + JSON.stringify({ + userName, + email, + }) + ); + } else { + throw Error('Missed private key'); + } return ( await this.user.signUp({ diff --git a/packages/zkdb/src/sdk/authentication/sign-in.ts b/packages/zkdb/src/sdk/authentication/sign-in.ts new file mode 100644 index 00000000..9b027cc2 --- /dev/null +++ b/packages/zkdb/src/sdk/authentication/sign-in.ts @@ -0,0 +1,59 @@ +import { IApiClient } from '@zkdb/api'; +import { PrivateKey } from 'o1js'; +import { Environment } from '../global/environment'; +import { AuthenticationTransaction } from './authentication-transaction'; +import { isBrowser } from '@utils'; +import { AuroWalletSigner, NodeSigner } from '../signer'; +import { SignedData } from '@types'; +import { ZKDB_KEY_ACCESS_TOKEN, ZKDB_KEY_USER_INFO } from './authentication'; + +export class SignInTransaction implements AuthenticationTransaction { + private apiClient: IApiClient; + private environment: Environment; + #storage: Storage; + private ecdsa: string; + + constructor( + apiClient: IApiClient, + storage: Storage, + environment: Environment, + ecdsa: string + ) { + this.apiClient = apiClient; + this.#storage = storage; + this.ecdsa = ecdsa; + this.environment = environment; + } + + async signAndSend(privateKey?: PrivateKey) { + let proof: SignedData; + + if (privateKey) { + const { networkId } = this.environment.getEnv(); + proof = await NodeSigner.getInstance(networkId).signMessage( + this.ecdsa, + privateKey + ); + } else if (isBrowser()) { + proof = await AuroWalletSigner.getInstance().signMessage(this.ecdsa); + } else { + throw Error('Missed private key'); + } + + const userData = (await this.user.signIn({ proof })).unwrap(); + this.#storage.set(ZKDB_KEY_ACCESS_TOKEN, userData.accessToken); + + this.#storage.set( + ZKDB_KEY_USER_INFO, + JSON.stringify({ + userName: userData.userName, + email: userData.email, + publicKey: userData.publicKey, + }) + ); + } + + private get user() { + return this.apiClient.user; + } +} diff --git a/packages/zkdb/src/sdk/global/environment.ts b/packages/zkdb/src/sdk/global/environment.ts new file mode 100644 index 00000000..e9a38ac2 --- /dev/null +++ b/packages/zkdb/src/sdk/global/environment.ts @@ -0,0 +1,26 @@ +import { NetworkId } from "o1js" + +export type TEnvironment = { + networkId: NetworkId +} + +export class Environment { + private static instance: Environment; + #environment: TEnvironment; + + public static getInstance() { + if (!Environment.instance) { + Environment.instance = new Environment(); + } + + return Environment.instance + } + + setEnv(environment: TEnvironment) { + this.#environment = environment; + } + + getEnv() { + return this.#environment; + } +} \ No newline at end of file diff --git a/packages/zkdb/src/sdk/global/zkdatabase-client.ts b/packages/zkdb/src/sdk/global/zkdatabase-client.ts index 65cb20c9..d0ab3cc5 100644 --- a/packages/zkdb/src/sdk/global/zkdatabase-client.ts +++ b/packages/zkdb/src/sdk/global/zkdatabase-client.ts @@ -4,34 +4,38 @@ import { ZKDatabase, GlobalContext } from '../interfaces'; import { ZKDatabaseImpl, GlobalContextImpl } from '../impl'; import { Signer } from '../signer'; +import { NetworkId } from 'o1js'; +import { Environment } from './environment'; export class ZKDatabaseClient { public apiClient: IApiClient; - public authenticator: Authenticator; + private environment: Environment; - private constructor(apiClient: IApiClient, authenticator: Authenticator) { + private constructor( + apiClient: IApiClient, + authenticator: Authenticator, + environment: Environment + ) { this.apiClient = apiClient; this.authenticator = authenticator; + this.environment = environment; } public static newInstance( url: string, - signer: Signer, + networkId: NetworkId, storage: ISecureStorage ) { const apiClient = ApiClient.newInstance(url); - const authenticator = new Authenticator(signer, apiClient, storage); - apiClient.api.setContext(() => authenticator.getAccessToken()); - return new ZKDatabaseClient(apiClient, authenticator); - } + const environment = Environment.getInstance(); + environment.setEnv({ + networkId, + }); + const authenticator = new Authenticator(apiClient, environment, storage); - public getSigner(): Signer { - return this.authenticator.signer; - } - - public setSigner(signer: Signer) { - this.authenticator.connect(signer); + apiClient.api.setContext(() => authenticator.getAccessToken()); + return new ZKDatabaseClient(apiClient, authenticator, environment); } database(name: string): ZKDatabase { diff --git a/packages/zkdb/src/sdk/signer/browser-signer.ts b/packages/zkdb/src/sdk/signer/browser-signer.ts index ea8a61a9..72c6e0d8 100644 --- a/packages/zkdb/src/sdk/signer/browser-signer.ts +++ b/packages/zkdb/src/sdk/signer/browser-signer.ts @@ -5,10 +5,19 @@ import { SignedData } from '@types'; import { AuroWallet } from '../wallet/auro-wallet'; import { TransactionParams } from '../../types/transaction-params'; -export class AuroWalletSigner implements Signer { +export class AuroWalletSigner { + private static INSTANCE: AuroWalletSigner; + + public static getInstance() { + if (!AuroWalletSigner.INSTANCE) { + AuroWalletSigner.INSTANCE = new AuroWalletSigner(); + } + + return AuroWalletSigner.INSTANCE; + } async signAndSendTransaction(transaction: string, params: TransactionParams): Promise { - throw new Error('Method not implemented.'); + return AuroWallet.signAndSendTransaction(transaction, params) } async signTransaction( transaction: MinaTransaction, diff --git a/packages/zkdb/src/sdk/signer/nodejs-signer.ts b/packages/zkdb/src/sdk/signer/nodejs-signer.ts index 2a8e8c95..f91ae13a 100644 --- a/packages/zkdb/src/sdk/signer/nodejs-signer.ts +++ b/packages/zkdb/src/sdk/signer/nodejs-signer.ts @@ -1,20 +1,24 @@ import { SignedData } from '@types'; -import { MinaNetwork, sendTransaction } from '@zkdb/smart-contract'; +import { sendTransaction } from '@zkdb/smart-contract'; import Client from 'mina-signer'; import { fetchAccount, NetworkId, PrivateKey } from 'o1js'; import { TransactionParams } from '../../types/transaction-params'; import { MinaTransaction } from '../types/o1js'; -import { Signer } from './interface/signer'; -export class NodeSigner implements Signer { - private privateKey: PrivateKey; +export class NodeSigner { + private static instance: NodeSigner; private client: Client; private endpoint: string; - private network: NetworkId; - constructor(privateKey: PrivateKey, network: NetworkId) { - this.privateKey = privateKey; - this.network = network; + public static getInstance(networkId: NetworkId) { + if (!NodeSigner.instance) { + NodeSigner.instance = new NodeSigner(networkId); + } + + return NodeSigner.instance; + } + + constructor(network: NetworkId) { if (network === 'testnet') { this.client = new Client({ network: 'testnet' }); this.endpoint = 'https://api.minascan.io/node/devnet/v1/graphql'; @@ -28,11 +32,12 @@ export class NodeSigner implements Signer { async signAndSendTransaction( transaction: string, - params: TransactionParams + params: TransactionParams, + privateKey: PrivateKey ): Promise { - const userPublicKey = this.privateKey.toPublicKey(); + const publicKey = privateKey.toPublicKey(); - const { account, error } = await fetchAccount({ publicKey: userPublicKey }); + const { account, error } = await fetchAccount({ publicKey }); if (error) { throw Error(error.statusText); @@ -46,7 +51,7 @@ export class NodeSigner implements Signer { const signBody = { zkappCommand: JSON.parse(transaction), feePayer: { - feePayer: userPublicKey.toBase58(), + feePayer: publicKey.toBase58(), fee: params.fee, nonce: nextNonce, memo: params.memo ?? '', @@ -55,7 +60,7 @@ export class NodeSigner implements Signer { const signedLegacy = this.client.signTransaction( signBody, - this.privateKey.toBase58() + privateKey.toBase58() ); const result = await sendTransaction( @@ -71,13 +76,16 @@ export class NodeSigner implements Signer { async signTransaction( transaction: MinaTransaction, - otherKeys: PrivateKey[] + privateKey: PrivateKey ): Promise { - transaction.sign(otherKeys.concat(this.privateKey)); + transaction.sign([privateKey]); return transaction; } - async signMessage(message: string): Promise { - return this.client.signMessage(message, this.privateKey.toBase58()); + async signMessage( + message: string, + privateKey: PrivateKey + ): Promise { + return this.client.signMessage(message, privateKey.toBase58()); } } diff --git a/packages/zkdb/src/sdk/transaction/zkdb-transaction.ts b/packages/zkdb/src/sdk/transaction/zkdb-transaction.ts new file mode 100644 index 00000000..a0b95fd0 --- /dev/null +++ b/packages/zkdb/src/sdk/transaction/zkdb-transaction.ts @@ -0,0 +1,150 @@ +import { IApiClient } from '@zkdb/api'; +import { + TDbTransaction, + transactionOrder, + TWaitTransactionStatus, +} from '../../types/transaction'; +import { isBrowser } from '@utils'; +import { PrivateKey } from 'o1js'; +import { AuroWalletSigner, NodeSigner } from '../signer'; +import { TransactionParams } from '../../types/transaction-params'; +import { Environment } from '../global/environment'; + +export class ZkDbTransaction { + private transaction: TDbTransaction; + private apiClient: IApiClient; + private environment: Environment; + private txHash: string; + + constructor( + transaction: TDbTransaction, + apiClient: IApiClient, + environment: Environment + ) { + this.transaction = transaction; + this.apiClient = apiClient; + this.environment = environment; + } + + public async update() { + if ( + this.transaction.status === 'success' || + this.transaction.status === 'failed' + ) { + throw Error( + `Update is unnecessary. Transaction is already ${this.transaction.status}` + ); + } + + const result = await this.apiClient.transaction.getTransaction({ + databaseName: this.transaction.databaseName, + transactionType: this.transaction.transactionType, + }); + + this.transaction = result.unwrap(); + } + + public async wait(status: TWaitTransactionStatus) { + if (status === this.transaction.status) { + return; + } + + if ( + transactionOrder.get(status)! > + transactionOrder.get(this.transaction.status)! + ) { + return new Promise((resolve, reject) => { + const checkStatus = async () => { + try { + await this.update(); + + if (this.transaction.status === status) { + resolve(); + } else { + setTimeout(checkStatus, 3000); + } + } catch (error) { + reject(error); + } + }; + + checkStatus(); + }); + } else { + throw Error('Wrong status order'); + } + } + + public async signAndSend( + privateKey?: PrivateKey, + transactionParams: TransactionParams = { + fee: 0.1, + memo: '', + } + ): Promise { + if (this.transaction.status !== 'ready') { + throw Error('To sign transaction its status must be ready'); + } + + if (privateKey) { + const { networkId } = this.environment.getEnv(); + + this.txHash = await NodeSigner.getInstance( + networkId + ).signAndSendTransaction( + this.transaction.tx, + transactionParams, + privateKey + ); + } else if (isBrowser()) { + this.txHash = await AuroWalletSigner.getInstance().signAndSendTransaction( + this.transaction.tx, + transactionParams + ); + } else { + throw Error('You should pass the private key to sign the transaction'); + } + + if (!this.txHash) { + throw Error('Failed to acquire transaction hash'); + } + + const result = await this.apiClient.transaction.confirmTransaction({ + databaseName: this.transaction.databaseName, + confirmTransactionId: this.transaction.id, + txHash: this.txHash, + }); + + const isConfirmed = result.unwrap(); + + if (!isConfirmed) { + throw Error(`Failed to confirm transaction`); + } + + return this.txHash; + } + + public async retryConfirm(): Promise { + if (this.transaction.status === 'ready' && this.txHash) { + const result = await this.apiClient.transaction.confirmTransaction({ + databaseName: this.transaction.databaseName, + confirmTransactionId: this.transaction.id, + txHash: this.txHash, + }); + + const isConfirmed = result.unwrap(); + + return isConfirmed; + } else { + return false; + } + } + + public getStatus() { + return this.transaction.status; + } + + public getTxHash() { + return this.txHash; + } +} diff --git a/packages/zkdb/src/sdk/wallet/auro-wallet.ts b/packages/zkdb/src/sdk/wallet/auro-wallet.ts index 1bcd3cb8..be278e71 100644 --- a/packages/zkdb/src/sdk/wallet/auro-wallet.ts +++ b/packages/zkdb/src/sdk/wallet/auro-wallet.ts @@ -1,14 +1,10 @@ import { Mina } from 'o1js'; import { SignedData } from '../../types'; -import { isBrowser } from '@utils'; +import { isBrowser } from '../../utils'; +import { TransactionParams } from '../../types/transaction-params'; type Transaction = Awaited>; -export type TransactionMetadata = { - fee: number; - memo: string; -}; - export class AuroWallet { private constructor() {} @@ -23,8 +19,8 @@ export class AuroWallet { } static async signAndSendTransaction( - transaction: Transaction, - transactionMetadata: TransactionMetadata = { + transaction: string, + transactionMetadata: TransactionParams = { fee: 0.1, memo: '', } @@ -32,7 +28,7 @@ export class AuroWallet { this.ensureWalletSupport(); const { hash } = await (window as any).mina.sendTransaction({ - transaction: transaction.toJSON(), + transaction: transaction, feePayer: { fee: transactionMetadata.fee, memo: transactionMetadata.memo, @@ -44,7 +40,7 @@ export class AuroWallet { static async signTransaction( transaction: Transaction, - transactionMetadata: TransactionMetadata = { + transactionMetadata: TransactionParams = { fee: 0.1, memo: '', } diff --git a/packages/zkdb/src/types/transaction.ts b/packages/zkdb/src/types/transaction.ts index 91708d56..ee6cdadc 100644 --- a/packages/zkdb/src/types/transaction.ts +++ b/packages/zkdb/src/types/transaction.ts @@ -1,4 +1,5 @@ export type TTransactionType = 'deploy' | 'rollup'; + export type TTransactionStatus = | 'start' | 'ready' @@ -6,6 +7,12 @@ export type TTransactionStatus = | 'failed' | 'success' | 'unknown'; + +export type TWaitTransactionStatus = Exclude< + TTransactionStatus, + 'start' | 'unknown' | 'failed' +>; + export type TDbTransaction = { id: string; databaseName: string; @@ -14,3 +21,12 @@ export type TDbTransaction = { tx: string; zkAppPublicKey: string; }; + +export const transactionOrder: Map = new Map([ + ['start', 0], + ['ready', 1], + ['pending', 2], + ['success', 3], + ['failed', -1], + ['failed', -2], +]); From ee72fa1733426f5be790791f7dafba6b82b98962 Mon Sep 17 00:00:00 2001 From: magestrio <9oleg8@gmail.com> Date: Tue, 26 Nov 2024 16:00:03 +0200 Subject: [PATCH 2/3] ZkDbTransaction Part 2 --- packages/api/src/graphql/database.ts | 15 +++- packages/api/src/graphql/rollup.ts | 15 +++- packages/api/src/graphql/transaction.ts | 25 +++++++ packages/api/src/graphql/types/transaction.ts | 6 ++ .../serverless/src/apollo/app/database.ts | 18 ++++- packages/serverless/src/apollo/app/rollup.ts | 2 +- .../serverless/src/apollo/app/transaction.ts | 23 +++++-- .../src/apollo/app/types/transaction.ts | 5 +- .../src/domain/use-case/database.ts | 14 +++- .../serverless/src/domain/use-case/rollup.ts | 9 +++ .../src/domain/use-case/transaction.ts | 68 ++++++++++++++++++- .../src/database/common/transactions.ts | 1 + .../authentication-transaction.ts | 6 -- .../zkdb/src/sdk/authentication/sign-in.ts | 59 ---------------- packages/zkdb/src/sdk/global/environment.ts | 26 ------- packages/zkdb/src/sdk/impl/database.ts | 32 +++++---- packages/zkdb/src/sdk/interfaces/database.ts | 8 +-- packages/zkdb/src/sdk/signer/nodejs-signer.ts | 42 +++++------- .../src/sdk/transaction/zkdb-transaction.ts | 47 +++++-------- packages/zkdb/src/types/transaction.ts | 1 + 20 files changed, 235 insertions(+), 187 deletions(-) delete mode 100644 packages/zkdb/src/sdk/authentication/authentication-transaction.ts delete mode 100644 packages/zkdb/src/sdk/authentication/sign-in.ts delete mode 100644 packages/zkdb/src/sdk/global/environment.ts diff --git a/packages/api/src/graphql/database.ts b/packages/api/src/graphql/database.ts index 5f7d4fb8..72645635 100644 --- a/packages/api/src/graphql/database.ts +++ b/packages/api/src/graphql/database.ts @@ -11,6 +11,7 @@ import { TPagination, TPaginationResponse, } from "./types"; +import { TDbTransaction } from "./types/transaction.js"; const DATABASE_CHANGE_OWNER = gql` mutation DbChangeOwner($databaseName: String!, $newOwner: String!) { @@ -20,7 +21,15 @@ const DATABASE_CHANGE_OWNER = gql` const DATABASE_CREATE = gql` mutation DbCreate($databaseName: String!, $merkleHeight: Int!) { - dbCreate(databaseName: $databaseName, merkleHeight: $merkleHeight) + dbCreate(databaseName: $databaseName, merkleHeight: $merkleHeight) { + databaseName + transactionType + status + id + tx + zkAppPublicKey + error + } } `; @@ -102,9 +111,9 @@ export const database = (client: TApolloClient) => ({ { dbChangeOwner: boolean } >(client, DATABASE_CHANGE_OWNER, (data) => data.dbChangeOwner), create: createMutateFunction< - boolean, + TDbTransaction, { databaseName: string; merkleHeight: number }, - { dbCreate: boolean } + { dbCreate: TDbTransaction } >(client, DATABASE_CREATE, (data) => data.dbCreate), setting: createQueryFunction< TDatabaseSettings, diff --git a/packages/api/src/graphql/rollup.ts b/packages/api/src/graphql/rollup.ts index aa47025b..0b6801c2 100644 --- a/packages/api/src/graphql/rollup.ts +++ b/packages/api/src/graphql/rollup.ts @@ -4,10 +4,19 @@ import { TCreateRollUpRequest, TGetRollUpHistoryResponse, } from "./types/rollup.js"; +import { TDbTransaction } from "./types/transaction.js"; const ROLLUP_CREATE = gql` mutation CreateRollUp($databaseName: String!) { - createRollUp(databaseName: $databaseName) + createRollUp(databaseName: $databaseName) { + databaseName + transactionType + status + id + tx + zkAppPublicKey + error + } } `; @@ -31,9 +40,9 @@ const ROLLUP_HISTORY_GET = gql` `; export const rollup = (client: TApolloClient) => ({ createRollUp: createMutateFunction< - boolean, + TDbTransaction, TCreateRollUpRequest, - { createRollUp: boolean } + { createRollUp: TDbTransaction } >(client, ROLLUP_CREATE, (data) => data.createRollUp), getRollUpHistory: createMutateFunction< TGetRollUpHistoryResponse, diff --git a/packages/api/src/graphql/transaction.ts b/packages/api/src/graphql/transaction.ts index 08071dd6..b404cf1d 100644 --- a/packages/api/src/graphql/transaction.ts +++ b/packages/api/src/graphql/transaction.ts @@ -6,6 +6,7 @@ import { } from "./common.js"; import { TDbTransaction, + TTransactionByIdRequest, TTransactionConfirmRequest, TTransactionRequest, } from "./types/transaction.js"; @@ -25,6 +26,25 @@ const TRANSACTION_GET = gql` id tx zkAppPublicKey + error + } + } +`; + +const TRANSACTION_BY_ID_GET = gql` + query GetTransactionById( + $id: String! + ) { + getTransactionById( + id: $id + ) { + databaseName + transactionType + status + id + tx + zkAppPublicKey + error } } `; @@ -49,6 +69,11 @@ export const transaction = (client: TApolloClient) => ({ TTransactionRequest, { getTransaction: TDbTransaction } >(client, TRANSACTION_GET, (data) => data.getTransaction), + getTransactionById: createQueryFunction< + TDbTransaction, + TTransactionByIdRequest, + { getTransactionById: TDbTransaction } + >(client, TRANSACTION_BY_ID_GET, (data) => data.getTransactionById), confirmTransaction: createMutateFunction< boolean, TTransactionConfirmRequest, diff --git a/packages/api/src/graphql/types/transaction.ts b/packages/api/src/graphql/types/transaction.ts index faa305aa..d1fd0f57 100644 --- a/packages/api/src/graphql/types/transaction.ts +++ b/packages/api/src/graphql/types/transaction.ts @@ -3,6 +3,11 @@ export type TTransactionRequest = { transactionType: "deploy" | "rollup"; }; +export type TTransactionByIdRequest = { + id: string; +}; + + export type TTransactionConfirmRequest = { databaseName: string; confirmTransactionId: string; @@ -23,4 +28,5 @@ export type TDbTransaction = { transactionType: TTransactionType; tx: string; zkAppPublicKey: string; + error: string; }; diff --git a/packages/serverless/src/apollo/app/database.ts b/packages/serverless/src/apollo/app/database.ts index 1b6c0b0f..e87315f1 100644 --- a/packages/serverless/src/apollo/app/database.ts +++ b/packages/serverless/src/apollo/app/database.ts @@ -1,4 +1,9 @@ -import { DB, ModelDatabase, ModelDbSetting } from '@zkdb/storage'; +import { + DB, + ModelDatabase, + ModelDbSetting, + withTransaction, +} from '@zkdb/storage'; import GraphQLJSON from 'graphql-type-json'; import Joi from 'joi'; import { @@ -107,7 +112,7 @@ extend type Query { } extend type Mutation { - dbCreate(databaseName: String!, merkleHeight: Int!): Boolean + dbCreate(databaseName: String!, merkleHeight: Int!): DbTransaction dbChangeOwner(databaseName: String!, newOwner: String!): Boolean dbDeployedUpdate(databaseName: String!, appPublicKey: String!): Boolean #dbDrop(databaseName: String!): Boolean @@ -175,7 +180,14 @@ const dbDeployedUpdate = authorizeWrapper( const dbCreate = authorizeWrapper( DatabaseCreateRequest, async (_root: unknown, args: TDatabaseCreateRequest, ctx) => - createDatabase(args.databaseName, args.merkleHeight, ctx.userName) + withTransaction((session) => + createDatabase( + args.databaseName, + args.merkleHeight, + ctx.userName, + session + ) + ) ); const dbChangeOwner = authorizeWrapper( diff --git a/packages/serverless/src/apollo/app/rollup.ts b/packages/serverless/src/apollo/app/rollup.ts index 83350a36..d14a949a 100644 --- a/packages/serverless/src/apollo/app/rollup.ts +++ b/packages/serverless/src/apollo/app/rollup.ts @@ -38,7 +38,7 @@ type RollUpHistory { extend type Mutation { getRollUpHistory(databaseName: String!): RollUpHistory! - createRollUp(databaseName: String!): Boolean + createRollUp(databaseName: String!): DbTransaction } `; diff --git a/packages/serverless/src/apollo/app/transaction.ts b/packages/serverless/src/apollo/app/transaction.ts index b1ceda99..95917509 100644 --- a/packages/serverless/src/apollo/app/transaction.ts +++ b/packages/serverless/src/apollo/app/transaction.ts @@ -6,8 +6,10 @@ import { enqueueTransaction as enqueueTransactionDomain, getTransactionForSigning, confirmTransaction as confirmTransactionDomain, + getTransactionById as getTransactionByIdDomain } from '../../domain/use-case/transaction.js'; import GraphQLJSON from 'graphql-type-json'; +import { ModelDbTransaction, withTransaction } from '@zkdb/storage'; export const typeDefsTransaction = `#graphql scalar JSON @@ -16,6 +18,7 @@ type Mutation extend type Query { getTransaction(databaseName: String!, transactionType: TransactionType!): DbTransaction! + getTransactionById(id: String!): DbTransaction } extend type Mutation { @@ -28,6 +31,10 @@ export type TTransactionRequest = TDatabaseRequest & { transactionType: 'deploy' | 'rollup'; }; +export type TTransactionByIdRequest = { + id: string; +}; + export type TTransactionIdRequest = TDatabaseRequest & { id: string; }; @@ -48,7 +55,6 @@ const getTransaction = authorizeWrapper( args.transactionType ); - transaction.status return { databaseName: transaction.databaseName, transactionType: transaction.transactionType, @@ -60,17 +66,20 @@ const getTransaction = authorizeWrapper( } ); +const getTransactionById = authorizeWrapper( + Joi.object({ + id: Joi.string().required(), + }), + async (_root: unknown, args: TTransactionByIdRequest, ctx) => withTransaction((session) => getTransactionByIdDomain(args.id, session)) +); + const enqueueDeployTransaction = authorizeWrapper( Joi.object({ databaseName, }), async (_root: unknown, args: TDatabaseRequest, ctx) => ( - await enqueueTransactionDomain( - args.databaseName, - ctx.userName, - 'deploy' - ) + await enqueueTransactionDomain(args.databaseName, ctx.userName, 'deploy') ).toString() ); @@ -93,6 +102,7 @@ type TTransactionResolver = { JSON: typeof GraphQLJSON; Query: { getTransaction: typeof getTransaction; + getTransactionById: typeof getTransactionById; }; Mutation: { enqueueDeployTransaction: typeof enqueueDeployTransaction; @@ -104,6 +114,7 @@ export const resolversTransaction: TTransactionResolver = { JSON: GraphQLJSON, Query: { getTransaction, + getTransactionById }, Mutation: { enqueueDeployTransaction, diff --git a/packages/serverless/src/apollo/app/types/transaction.ts b/packages/serverless/src/apollo/app/types/transaction.ts index e0331871..ee73c616 100644 --- a/packages/serverless/src/apollo/app/types/transaction.ts +++ b/packages/serverless/src/apollo/app/types/transaction.ts @@ -18,10 +18,11 @@ enum TransactionType { type DbTransaction { databaseName: String! transactionType: TransactionType! - zkAppPublicKey: String! + zkAppPublicKey: String status: TransactionStatus! - tx: String! + tx: String id: String! + error: String } `; diff --git a/packages/serverless/src/domain/use-case/database.ts b/packages/serverless/src/domain/use-case/database.ts index 247b3250..70c422ee 100644 --- a/packages/serverless/src/domain/use-case/database.ts +++ b/packages/serverless/src/domain/use-case/database.ts @@ -24,7 +24,8 @@ import { MinaNetwork } from '@zkdb/smart-contract'; export async function createDatabase( databaseName: string, merkleHeight: number, - actor: string + actor: string, + session?: ClientSession ) { const user = await new ModelUser().findOne({ userName: actor }); @@ -44,9 +45,16 @@ export async function createDatabase( databaseOwner: actor, }); - await enqueueTransaction(databaseName, actor, 'deploy'); + const id = await enqueueTransaction(databaseName, actor, 'deploy', session); - return true; + const modelTransaction = ModelDbTransaction.getInstance(); + + const tx = await modelTransaction.findById(id.toString(), { session }); + + return { + id: id.toString(), + ...tx, + }; } throw Error(`User ${actor} has not been found`); diff --git a/packages/serverless/src/domain/use-case/rollup.ts b/packages/serverless/src/domain/use-case/rollup.ts index e5be272d..bad3be49 100644 --- a/packages/serverless/src/domain/use-case/rollup.ts +++ b/packages/serverless/src/domain/use-case/rollup.ts @@ -72,6 +72,15 @@ export async function createRollUp( }, { session: compoundSession?.sessionService } ); + + const tx = await modelTransaction.findById(txId.toString(), { + session: compoundSession?.sessionService, + }); + + return { + id: txId.toString(), + ...tx, + }; } export async function getRollUpHistory( diff --git a/packages/serverless/src/domain/use-case/transaction.ts b/packages/serverless/src/domain/use-case/transaction.ts index 5e1a6b5f..bf451e8b 100644 --- a/packages/serverless/src/domain/use-case/transaction.ts +++ b/packages/serverless/src/domain/use-case/transaction.ts @@ -78,7 +78,7 @@ export async function enqueueTransaction( } else if (onchainTx.txStatus === 'failed') { await modelTransaction.updateById(pendingTx._id.toString(), { status: 'failed', - error: onchainTx.failures.join(" "), + error: onchainTx.failures.join(' '), }); // Proceed and create new transaction } @@ -182,6 +182,72 @@ export async function getLatestTransaction( return txs.length === 0 ? null : txs[0]; } + +export async function getTransactionById(id: string, session?: ClientSession) { + const modelTransaction = ModelDbTransaction.getInstance(); + const transaction = await modelTransaction.findById(id); + + if (!transaction) { + throw Error('Transaction has not been found'); + } + + let status = transaction.status; + + if (transaction.status === 'pending') { + if (transaction.txHash) { + const zkAppTransaction = + await MinaNetwork.getInstance().getZkAppTransactionByTxHash( + transaction.txHash + ); + + if (zkAppTransaction?.txStatus === 'failed') { + status = 'failed'; + await modelTransaction.updateById( + id.toString(), + { + status: 'failed', + error: zkAppTransaction.failures.join(' '), + }, + { session } + ); + } else if (zkAppTransaction?.txStatus === 'applied') { + status = 'success'; + await modelTransaction.updateById( + id.toString(), + { + status: 'success', + }, + { session } + ); + } + } else { + status = 'failed'; + await modelTransaction.updateById( + id.toString(), + { + status: 'failed', + error: 'Transaction hash is missed', + }, + { session } + ); + } + } + + if (status !== transaction.status) { + const newTransaction = await modelTransaction.findById(id); + + return { + id: (newTransaction as any)._id, + ...newTransaction, + }; + } + + return { + id: (transaction as any)._id, + ...transaction, + }; +} + export async function confirmTransaction( databaseName: string, actor: string, diff --git a/packages/storage/src/database/common/transactions.ts b/packages/storage/src/database/common/transactions.ts index 1dad587f..7517bc1a 100644 --- a/packages/storage/src/database/common/transactions.ts +++ b/packages/storage/src/database/common/transactions.ts @@ -89,6 +89,7 @@ export class ModelDbTransaction extends ModelBasic { { _id: new ObjectId(id) }, options ); + return tx; } diff --git a/packages/zkdb/src/sdk/authentication/authentication-transaction.ts b/packages/zkdb/src/sdk/authentication/authentication-transaction.ts deleted file mode 100644 index dafdf58d..00000000 --- a/packages/zkdb/src/sdk/authentication/authentication-transaction.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { isBrowser } from '@utils'; -import { PrivateKey } from 'o1js'; - -export interface AuthenticationTransaction { - signAndSend(privateKey?: PrivateKey): Promise; -} diff --git a/packages/zkdb/src/sdk/authentication/sign-in.ts b/packages/zkdb/src/sdk/authentication/sign-in.ts deleted file mode 100644 index 9b027cc2..00000000 --- a/packages/zkdb/src/sdk/authentication/sign-in.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { IApiClient } from '@zkdb/api'; -import { PrivateKey } from 'o1js'; -import { Environment } from '../global/environment'; -import { AuthenticationTransaction } from './authentication-transaction'; -import { isBrowser } from '@utils'; -import { AuroWalletSigner, NodeSigner } from '../signer'; -import { SignedData } from '@types'; -import { ZKDB_KEY_ACCESS_TOKEN, ZKDB_KEY_USER_INFO } from './authentication'; - -export class SignInTransaction implements AuthenticationTransaction { - private apiClient: IApiClient; - private environment: Environment; - #storage: Storage; - private ecdsa: string; - - constructor( - apiClient: IApiClient, - storage: Storage, - environment: Environment, - ecdsa: string - ) { - this.apiClient = apiClient; - this.#storage = storage; - this.ecdsa = ecdsa; - this.environment = environment; - } - - async signAndSend(privateKey?: PrivateKey) { - let proof: SignedData; - - if (privateKey) { - const { networkId } = this.environment.getEnv(); - proof = await NodeSigner.getInstance(networkId).signMessage( - this.ecdsa, - privateKey - ); - } else if (isBrowser()) { - proof = await AuroWalletSigner.getInstance().signMessage(this.ecdsa); - } else { - throw Error('Missed private key'); - } - - const userData = (await this.user.signIn({ proof })).unwrap(); - this.#storage.set(ZKDB_KEY_ACCESS_TOKEN, userData.accessToken); - - this.#storage.set( - ZKDB_KEY_USER_INFO, - JSON.stringify({ - userName: userData.userName, - email: userData.email, - publicKey: userData.publicKey, - }) - ); - } - - private get user() { - return this.apiClient.user; - } -} diff --git a/packages/zkdb/src/sdk/global/environment.ts b/packages/zkdb/src/sdk/global/environment.ts deleted file mode 100644 index e9a38ac2..00000000 --- a/packages/zkdb/src/sdk/global/environment.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { NetworkId } from "o1js" - -export type TEnvironment = { - networkId: NetworkId -} - -export class Environment { - private static instance: Environment; - #environment: TEnvironment; - - public static getInstance() { - if (!Environment.instance) { - Environment.instance = new Environment(); - } - - return Environment.instance - } - - setEnv(environment: TEnvironment) { - this.#environment = environment; - } - - getEnv() { - return this.#environment; - } -} \ No newline at end of file diff --git a/packages/zkdb/src/sdk/impl/database.ts b/packages/zkdb/src/sdk/impl/database.ts index 2f4516d9..7609179a 100644 --- a/packages/zkdb/src/sdk/impl/database.ts +++ b/packages/zkdb/src/sdk/impl/database.ts @@ -15,24 +15,30 @@ import { } from '../interfaces'; import { CollectionImpl } from './collection'; import { ZKGroupImpl } from './group'; +import { ZkDbTransaction } from '../transaction/zkdb-transaction'; +import { Signer } from '../signer'; // TODO: Implement transactions endpoints export class ZKDatabaseImpl implements ZKDatabase { private databaseName: string; private apiClient: IApiClient; + private signer: Signer; - constructor(databaseName: string, apiClient: IApiClient) { + constructor(databaseName: string, apiClient: IApiClient, signer: Signer) { this.databaseName = databaseName; this.apiClient = apiClient; + this.signer = signer; } - async create(config: ZKDatabaseConfig): Promise { + async create(config: ZKDatabaseConfig): Promise { const result = await this.apiClient.db.create({ databaseName: this.databaseName, merkleHeight: config.merkleHeight, }); - return result.unwrap(); + const dbTransaction = result.unwrap(); + + return new ZkDbTransaction(dbTransaction, this.apiClient, this.signer); } async exist(): Promise { @@ -104,28 +110,24 @@ export class ZKDatabaseImpl implements ZKDatabase { async getTransaction( transactionType: TTransactionType - ): Promise { + ): Promise { const result = await this.apiClient.transaction.getTransaction({ databaseName: this.databaseName, transactionType, }); - return result.unwrap(); - } + const dbTransaction = result.unwrap(); - async confirmTransaction(id: string, txHash: string): Promise { - const result = await this.apiClient.transaction.confirmTransaction({ - databaseName: this.databaseName, - confirmTransactionId: id, - txHash, - }); - return result.unwrap(); + return new ZkDbTransaction(dbTransaction, this.apiClient, this.signer); } - async createRollup(): Promise { + async createRollup(): Promise { const result = await this.apiClient.rollup.createRollUp({ databaseName: this.databaseName, }); - return result.unwrap(); + + const dbTransaction = result.unwrap(); + + return new ZkDbTransaction(dbTransaction, this.apiClient, this.signer); } async getRollUpHistory(): Promise { diff --git a/packages/zkdb/src/sdk/interfaces/database.ts b/packages/zkdb/src/sdk/interfaces/database.ts index e0064f1a..bb146c83 100644 --- a/packages/zkdb/src/sdk/interfaces/database.ts +++ b/packages/zkdb/src/sdk/interfaces/database.ts @@ -9,6 +9,7 @@ import { } from '../../types'; import { ZKCollection } from './collection'; import { ZKGroup } from './group'; +import { ZkDbTransaction } from '../transaction/zkdb-transaction'; export interface ZKDatabaseConfig { merkleHeight: number; @@ -16,7 +17,7 @@ export interface ZKDatabaseConfig { export interface ZKDatabase { // Database - create(config: ZKDatabaseConfig): Promise; + create(config: ZKDatabaseConfig): Promise; exist(): Promise; @@ -38,10 +39,9 @@ export interface ZKDatabase { getProof(): Promise; // Transaction - getTransaction(transactionType: TTransactionType): Promise; - confirmTransaction(id: string, txHash: string): Promise; + getTransaction(transactionType: TTransactionType): Promise; // Rollup - createRollup(): Promise; + createRollup(): Promise; getRollUpHistory(): Promise; } diff --git a/packages/zkdb/src/sdk/signer/nodejs-signer.ts b/packages/zkdb/src/sdk/signer/nodejs-signer.ts index f91ae13a..60254dac 100644 --- a/packages/zkdb/src/sdk/signer/nodejs-signer.ts +++ b/packages/zkdb/src/sdk/signer/nodejs-signer.ts @@ -4,21 +4,17 @@ import Client from 'mina-signer'; import { fetchAccount, NetworkId, PrivateKey } from 'o1js'; import { TransactionParams } from '../../types/transaction-params'; import { MinaTransaction } from '../types/o1js'; +import { Signer } from './interface/signer'; -export class NodeSigner { - private static instance: NodeSigner; +export class NodeSigner implements Signer { + private privateKey: PrivateKey; private client: Client; private endpoint: string; - public static getInstance(networkId: NetworkId) { - if (!NodeSigner.instance) { - NodeSigner.instance = new NodeSigner(networkId); - } - - return NodeSigner.instance; - } - - constructor(network: NetworkId) { + private network: NetworkId; + constructor(privateKey: PrivateKey, network: NetworkId) { + this.privateKey = privateKey; + this.network = network; if (network === 'testnet') { this.client = new Client({ network: 'testnet' }); this.endpoint = 'https://api.minascan.io/node/devnet/v1/graphql'; @@ -32,12 +28,11 @@ export class NodeSigner { async signAndSendTransaction( transaction: string, - params: TransactionParams, - privateKey: PrivateKey + params: TransactionParams ): Promise { - const publicKey = privateKey.toPublicKey(); + const userPublicKey = this.privateKey.toPublicKey(); - const { account, error } = await fetchAccount({ publicKey }); + const { account, error } = await fetchAccount({ publicKey: userPublicKey }); if (error) { throw Error(error.statusText); @@ -51,7 +46,7 @@ export class NodeSigner { const signBody = { zkappCommand: JSON.parse(transaction), feePayer: { - feePayer: publicKey.toBase58(), + feePayer: userPublicKey.toBase58(), fee: params.fee, nonce: nextNonce, memo: params.memo ?? '', @@ -60,7 +55,7 @@ export class NodeSigner { const signedLegacy = this.client.signTransaction( signBody, - privateKey.toBase58() + this.privateKey.toBase58() ); const result = await sendTransaction( @@ -76,16 +71,13 @@ export class NodeSigner { async signTransaction( transaction: MinaTransaction, - privateKey: PrivateKey + otherKeys: PrivateKey[] ): Promise { - transaction.sign([privateKey]); + transaction.sign(otherKeys.concat(this.privateKey)); return transaction; } - async signMessage( - message: string, - privateKey: PrivateKey - ): Promise { - return this.client.signMessage(message, privateKey.toBase58()); + async signMessage(message: string): Promise { + return this.client.signMessage(message, this.privateKey.toBase58()); } -} +} \ No newline at end of file diff --git a/packages/zkdb/src/sdk/transaction/zkdb-transaction.ts b/packages/zkdb/src/sdk/transaction/zkdb-transaction.ts index a0b95fd0..a93330a6 100644 --- a/packages/zkdb/src/sdk/transaction/zkdb-transaction.ts +++ b/packages/zkdb/src/sdk/transaction/zkdb-transaction.ts @@ -6,24 +6,25 @@ import { } from '../../types/transaction'; import { isBrowser } from '@utils'; import { PrivateKey } from 'o1js'; -import { AuroWalletSigner, NodeSigner } from '../signer'; +import { AuroWalletSigner, NodeSigner, Signer } from '../signer'; import { TransactionParams } from '../../types/transaction-params'; -import { Environment } from '../global/environment'; + +const DEFAULT_FEE = 100_000_000; export class ZkDbTransaction { private transaction: TDbTransaction; private apiClient: IApiClient; - private environment: Environment; + private signer: Signer; private txHash: string; constructor( transaction: TDbTransaction, apiClient: IApiClient, - environment: Environment + signer: Signer ) { this.transaction = transaction; this.apiClient = apiClient; - this.environment = environment; + this.signer = signer; } public async update() { @@ -36,9 +37,8 @@ export class ZkDbTransaction { ); } - const result = await this.apiClient.transaction.getTransaction({ - databaseName: this.transaction.databaseName, - transactionType: this.transaction.transactionType, + const result = await this.apiClient.transaction.getTransactionById({ + id: this.transaction.id, }); this.transaction = result.unwrap(); @@ -60,6 +60,8 @@ export class ZkDbTransaction { if (this.transaction.status === status) { resolve(); + } else if (this.transaction.status === 'failed') { + throw Error(this.transaction.error); } else { setTimeout(checkStatus, 3000); } @@ -76,9 +78,8 @@ export class ZkDbTransaction { } public async signAndSend( - privateKey?: PrivateKey, transactionParams: TransactionParams = { - fee: 0.1, + fee: DEFAULT_FEE, memo: '', } ): Promise { @@ -86,24 +87,10 @@ export class ZkDbTransaction { throw Error('To sign transaction its status must be ready'); } - if (privateKey) { - const { networkId } = this.environment.getEnv(); - - this.txHash = await NodeSigner.getInstance( - networkId - ).signAndSendTransaction( - this.transaction.tx, - transactionParams, - privateKey - ); - } else if (isBrowser()) { - this.txHash = await AuroWalletSigner.getInstance().signAndSendTransaction( - this.transaction.tx, - transactionParams - ); - } else { - throw Error('You should pass the private key to sign the transaction'); - } + this.txHash = await this.signer.signAndSendTransaction( + this.transaction.tx, + transactionParams + ); if (!this.txHash) { throw Error('Failed to acquire transaction hash'); @@ -140,11 +127,11 @@ export class ZkDbTransaction { } } - public getStatus() { + public get status() { return this.transaction.status; } - public getTxHash() { + public get transactionHash() { return this.txHash; } } diff --git a/packages/zkdb/src/types/transaction.ts b/packages/zkdb/src/types/transaction.ts index ee6cdadc..e67638ad 100644 --- a/packages/zkdb/src/types/transaction.ts +++ b/packages/zkdb/src/types/transaction.ts @@ -20,6 +20,7 @@ export type TDbTransaction = { transactionType: TTransactionType; tx: string; zkAppPublicKey: string; + error: string }; export const transactionOrder: Map = new Map([ From f7c1ee1931cb8f638374f72d9c15a906927b323c Mon Sep 17 00:00:00 2001 From: magestrio <9oleg8@gmail.com> Date: Tue, 26 Nov 2024 16:03:17 +0200 Subject: [PATCH 3/3] unknown instead of failed --- packages/zkdb/src/types/transaction.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/zkdb/src/types/transaction.ts b/packages/zkdb/src/types/transaction.ts index e67638ad..2dd577ce 100644 --- a/packages/zkdb/src/types/transaction.ts +++ b/packages/zkdb/src/types/transaction.ts @@ -29,5 +29,5 @@ export const transactionOrder: Map = new Map([ ['pending', 2], ['success', 3], ['failed', -1], - ['failed', -2], + ['unknown', -2], ]);