From 75ef4f5bb7bb07c4ea0478bee360beba12220f28 Mon Sep 17 00:00:00 2001 From: Max Korsunov Date: Wed, 18 Sep 2024 13:06:22 +0300 Subject: [PATCH 1/8] fix(ui, minifront): #1764: fix copy button styles (#1794) * fix(minifront): #1590: fix copy button styles * chore: changeset * fix(#1764): fix copy button styles --- .changeset/itchy-files-flow.md | 5 +++++ .../components/ui/copy-to-clipboard/copy-to-clipboard.tsx | 6 +++++- 2 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 .changeset/itchy-files-flow.md diff --git a/.changeset/itchy-files-flow.md b/.changeset/itchy-files-flow.md new file mode 100644 index 000000000..0af966d7b --- /dev/null +++ b/.changeset/itchy-files-flow.md @@ -0,0 +1,5 @@ +--- +'@repo/ui': patch +--- + +Fix copy button styles diff --git a/packages/ui/components/ui/copy-to-clipboard/copy-to-clipboard.tsx b/packages/ui/components/ui/copy-to-clipboard/copy-to-clipboard.tsx index 1563de9e6..640c0b71c 100644 --- a/packages/ui/components/ui/copy-to-clipboard/copy-to-clipboard.tsx +++ b/packages/ui/components/ui/copy-to-clipboard/copy-to-clipboard.tsx @@ -38,7 +38,11 @@ const CopyToClipboard = forwardRef( > {copied ? ( - {successLabel ?? Copied} + {typeof successLabel !== 'undefined' ? ( + successLabel + ) : ( + Copied + )} ) : ( From e343d22374e5a333e77c9949fc0781d5f9221055 Mon Sep 17 00:00:00 2001 From: Gabe Rodriguez Date: Thu, 19 Sep 2024 13:40:36 +0200 Subject: [PATCH 2/8] Only process relevant transactions (#1793) * Only process txs from relevant txs * changeset * bug fix + refactor identify txs * Review updates --- .changeset/eight-hounds-compare.md | 5 + .changeset/late-peas-tap.md | 5 + .changeset/twenty-tomatoes-travel.md | 5 + packages/query/src/block-processor.ts | 105 +----- .../query/src/helpers/identify-txs.test.ts | 331 ++++++++++++++++++ packages/query/src/helpers/identify-txs.ts | 137 ++++++++ packages/storage/src/indexed-db/config.ts | 2 +- 7 files changed, 499 insertions(+), 91 deletions(-) create mode 100644 .changeset/eight-hounds-compare.md create mode 100644 .changeset/late-peas-tap.md create mode 100644 .changeset/twenty-tomatoes-travel.md create mode 100644 packages/query/src/helpers/identify-txs.test.ts create mode 100644 packages/query/src/helpers/identify-txs.ts diff --git a/.changeset/eight-hounds-compare.md b/.changeset/eight-hounds-compare.md new file mode 100644 index 000000000..fea03cb91 --- /dev/null +++ b/.changeset/eight-hounds-compare.md @@ -0,0 +1,5 @@ +--- +'@penumbra-zone/storage': patch +--- + +Version DB to 47 diff --git a/.changeset/late-peas-tap.md b/.changeset/late-peas-tap.md new file mode 100644 index 000000000..0a5fdf51c --- /dev/null +++ b/.changeset/late-peas-tap.md @@ -0,0 +1,5 @@ +--- +'@penumbra-zone/query': patch +--- + +[bug fix] Only process relevant transactions for NFTs diff --git a/.changeset/twenty-tomatoes-travel.md b/.changeset/twenty-tomatoes-travel.md new file mode 100644 index 000000000..7e3bebff2 --- /dev/null +++ b/.changeset/twenty-tomatoes-travel.md @@ -0,0 +1,5 @@ +--- +'@penumbra-zone/query': patch +--- + +Extract, refactor, and test IdentifyTransactions diff --git a/packages/query/src/block-processor.ts b/packages/query/src/block-processor.ts index 356869ed1..74002b2eb 100644 --- a/packages/query/src/block-processor.ts +++ b/packages/query/src/block-processor.ts @@ -4,21 +4,13 @@ import { PositionState, PositionState_PositionStateEnum, } from '@penumbra-zone/protobuf/penumbra/core/component/dex/v1/dex_pb'; -import { - CommitmentSource, - Nullifier, -} from '@penumbra-zone/protobuf/penumbra/core/component/sct/v1/sct_pb'; +import { Nullifier } from '@penumbra-zone/protobuf/penumbra/core/component/sct/v1/sct_pb'; import { ValidatorInfoResponse } from '@penumbra-zone/protobuf/penumbra/core/component/stake/v1/stake_pb'; -import { - Action, - Transaction, -} from '@penumbra-zone/protobuf/penumbra/core/transaction/v1/transaction_pb'; -import { TransactionId } from '@penumbra-zone/protobuf/penumbra/core/txhash/v1/txhash_pb'; +import { Action } from '@penumbra-zone/protobuf/penumbra/core/transaction/v1/transaction_pb'; import { StateCommitment } from '@penumbra-zone/protobuf/penumbra/crypto/tct/v1/tct_pb'; import { SpendableNoteRecord, SwapRecord } from '@penumbra-zone/protobuf/penumbra/view/v1/view_pb'; import { auctionIdFromBech32 } from '@penumbra-zone/bech32m/pauctid'; import { bech32mIdentityKey } from '@penumbra-zone/bech32m/penumbravalid'; -import { sha256Hash } from '@penumbra-zone/crypto-web/sha256'; import { getAssetId } from '@penumbra-zone/getters/metadata'; import { getExchangeRateFromValidatorInfoResponse, @@ -47,6 +39,7 @@ import { getSpendableNoteRecordCommitment } from '@penumbra-zone/getters/spendab import { getSwapRecordCommitment } from '@penumbra-zone/getters/swap-record'; import { CompactBlock } from '@penumbra-zone/protobuf/penumbra/core/component/compact_block/v1/compact_block_pb'; import { shouldSkipTrialDecrypt } from './helpers/skip-trial-decrypt.js'; +import { identifyTransactions, RelevantTx } from './helpers/identify-txs.js'; declare global { // eslint-disable-next-line no-var -- expected globals @@ -81,10 +74,6 @@ interface ProcessBlockParams { skipTrialDecrypt?: boolean; } -const BLANK_TX_SOURCE = new CommitmentSource({ - source: { case: 'transaction', value: { id: new Uint8Array() } }, -}); - const POSITION_STATES: PositionState[] = [ new PositionState({ state: PositionState_PositionStateEnum.OPENED }), new PositionState({ state: PositionState_PositionStateEnum.CLOSED }), @@ -129,9 +118,7 @@ export class BlockProcessor implements BlockProcessorInterface { numOfAttempts: Infinity, maxDelay: 20_000, // 20 seconds retry: async (e, attemptNumber) => { - if (globalThis.__DEV__) { - console.debug('Sync failure', attemptNumber, e); - } + console.error(`Sync failure #${attemptNumber}: `, e); await this.viewServer.resetTreeToStored(); return !this.abortController.signal.aborted; }, @@ -318,12 +305,8 @@ export class BlockProcessor implements BlockProcessorInterface { // this is a network query const blockTx = await this.querier.app.txsByHeight(compactBlock.height); - // identify tx that involve a new record - // - compare nullifiers - // - compare state commitments - // - collect relevant tx for info generation later - // - if matched by commitment, collect record with recovered source - const { relevantTx, recordsWithSources } = await this.identifyTransactions( + // Filter down to transactions & note records in block relevant to user + const { relevantTxs, recoveredSourceRecords } = await identifyTransactions( spentNullifiers, recordsByCommitment, blockTx, @@ -331,15 +314,15 @@ export class BlockProcessor implements BlockProcessorInterface { // this simply stores the new records with 'rehydrated' sources to idb // TODO: this is the second time we save these records, after "saveScanResult" - await this.saveRecoveredCommitmentSources(recordsWithSources); + await this.saveRecoveredCommitmentSources(recoveredSourceRecords); - await this.processTransactions(blockTx); + await this.processTransactions(relevantTxs); // at this point txinfo can be generated and saved. this will resolve // pending broadcasts, and populate the transaction list. // - calls wasm for each relevant tx // - saves to idb - await this.saveTransactions(compactBlock.height, relevantTx); + await this.saveTransactions(compactBlock.height, relevantTxs); } /** @@ -450,64 +433,6 @@ export class BlockProcessor implements BlockProcessorInterface { } } - private async identifyTransactions( - spentNullifiers: Set, - commitmentRecordsByStateCommitment: Map, - blockTx: Transaction[], - ) { - const relevantTx = new Map(); - const recordsWithSources = new Array(); - for (const tx of blockTx) { - let txId: TransactionId | undefined; - - const txCommitments = (tx.body?.actions ?? []).flatMap(({ action }) => { - switch (action.case) { - case 'output': - return action.value.body?.notePayload?.noteCommitment; - case 'swap': - return action.value.body?.payload?.commitment; - case 'swapClaim': - return [action.value.body?.output1Commitment, action.value.body?.output2Commitment]; - default: - return; - } - }); - - const txNullifiers = (tx.body?.actions ?? []).map(({ action }) => { - switch (action.case) { - case 'spend': - case 'swapClaim': - return action.value.body?.nullifier; - default: - return; - } - }); - - for (const spentNullifier of spentNullifiers) { - if (txNullifiers.some(txNullifier => spentNullifier.equals(txNullifier))) { - txId = new TransactionId({ inner: await sha256Hash(tx.toBinary()) }); - relevantTx.set(txId, tx); - spentNullifiers.delete(spentNullifier); - } - } - - for (const [stateCommitment, spendableNoteRecord] of commitmentRecordsByStateCommitment) { - if (txCommitments.some(txCommitment => stateCommitment.equals(txCommitment))) { - txId ??= new TransactionId({ inner: await sha256Hash(tx.toBinary()) }); - relevantTx.set(txId, tx); - if (BLANK_TX_SOURCE.equals(spendableNoteRecord.source)) { - spendableNoteRecord.source = new CommitmentSource({ - source: { case: 'transaction', value: { id: txId.inner } }, - }); - recordsWithSources.push(spendableNoteRecord); - } - commitmentRecordsByStateCommitment.delete(stateCommitment); - } - } - } - return { relevantTx, recordsWithSources }; - } - // TODO: refactor. there is definitely a better way to do this. batch // endpoint issue https://github.com/penumbra-zone/penumbra/issues/4688 private async saveAndReturnMetadata(assetId: AssetId): Promise { @@ -591,9 +516,9 @@ export class BlockProcessor implements BlockProcessorInterface { * Identify various pieces of data from the transaction that we need to save, * such as metadata, liquidity positions, etc. */ - private async processTransactions(txs: Transaction[]) { - for (const tx of txs) { - for (const { action } of tx.body?.actions ?? []) { + private async processTransactions(txs: RelevantTx[]) { + for (const { data } of txs) { + for (const { action } of data.body?.actions ?? []) { await Promise.all([this.identifyAuctionNfts(action), this.identifyLpNftPositions(action)]); } } @@ -685,9 +610,9 @@ export class BlockProcessor implements BlockProcessorInterface { }); } - private async saveTransactions(height: bigint, relevantTx: Map) { - for (const [id, transaction] of relevantTx) { - await this.indexedDb.saveTransaction(id, height, transaction); + private async saveTransactions(height: bigint, relevantTx: RelevantTx[]) { + for (const { id, data } of relevantTx) { + await this.indexedDb.saveTransaction(id, height, data); } } diff --git a/packages/query/src/helpers/identify-txs.test.ts b/packages/query/src/helpers/identify-txs.test.ts new file mode 100644 index 000000000..943ede38b --- /dev/null +++ b/packages/query/src/helpers/identify-txs.test.ts @@ -0,0 +1,331 @@ +import { describe, expect, test } from 'vitest'; +import { + CommitmentSource, + Nullifier, +} from '@penumbra-zone/protobuf/penumbra/core/component/sct/v1/sct_pb'; +import { StateCommitment } from '@penumbra-zone/protobuf/penumbra/crypto/tct/v1/tct_pb'; +import { + Action, + Transaction, + TransactionBody, +} from '@penumbra-zone/protobuf/penumbra/core/transaction/v1/transaction_pb'; +import { + getCommitmentsFromActions, + getNullifiersFromActions, + identifyTransactions, +} from './identify-txs.js'; +import { + Output, + OutputBody, + Spend, + SpendBody, +} from '@penumbra-zone/protobuf/penumbra/core/component/shielded_pool/v1/shielded_pool_pb'; +import { + Swap, + SwapBody, + SwapClaim, + SwapClaimBody, +} from '@penumbra-zone/protobuf/penumbra/core/component/dex/v1/dex_pb'; +import { SpendableNoteRecord, SwapRecord } from '@penumbra-zone/protobuf/penumbra/view/v1/view_pb'; + +const BLANK_TX_SOURCE = new CommitmentSource({ + source: { case: 'transaction', value: { id: new Uint8Array() } }, +}); + +describe('getCommitmentsFromActions', () => { + test('returns empty array when tx.body.actions is undefined', () => { + const tx = new Transaction(); + const commitments = getCommitmentsFromActions(tx); + expect(commitments).toEqual([]); + }); + + test('returns noteCommitment from output actions', () => { + const noteCommitment = new StateCommitment({ inner: new Uint8Array([1, 2, 3]) }); + const outputAction = new Action({ + action: { + case: 'output', + value: new Output({ + body: new OutputBody({ + notePayload: { + noteCommitment, + }, + }), + }), + }, + }); + + const tx = new Transaction({ + body: new TransactionBody({ + actions: [outputAction], + }), + }); + + const commitments = getCommitmentsFromActions(tx); + expect(commitments).toEqual([noteCommitment]); + }); + + test('returns commitment from swap actions', () => { + const commitment = new StateCommitment({ inner: new Uint8Array([4, 5, 6]) }); + const swapAction = new Action({ + action: { + case: 'swap', + value: new Swap({ + body: new SwapBody({ + payload: { + commitment, + }, + }), + }), + }, + }); + + const tx = new Transaction({ + body: new TransactionBody({ + actions: [swapAction], + }), + }); + + const commitments = getCommitmentsFromActions(tx); + expect(commitments).toEqual([commitment]); + }); + + test('returns output commitments from swapClaim actions', () => { + const output1Commitment = new StateCommitment({ inner: new Uint8Array([7, 8, 9]) }); + const output2Commitment = new StateCommitment({ inner: new Uint8Array([10, 11, 12]) }); + + const swapClaimAction = new Action({ + action: { + case: 'swapClaim', + value: new SwapClaim({ + body: new SwapClaimBody({ + output1Commitment, + output2Commitment, + }), + }), + }, + }); + + const tx = new Transaction({ + body: new TransactionBody({ + actions: [swapClaimAction], + }), + }); + + const commitments = getCommitmentsFromActions(tx); + expect(commitments).toEqual([output1Commitment, output2Commitment]); + }); + + test('ignores actions without commitments', () => { + const unknownAction = new Action({ + action: { + case: 'validatorDefinition', + value: {}, + }, + }); + + const tx = new Transaction({ + body: new TransactionBody({ + actions: [unknownAction], + }), + }); + + const commitments = getCommitmentsFromActions(tx); + expect(commitments).toEqual([]); + }); +}); + +describe('getNullifiersFromActions', () => { + test('returns empty array when tx.body.actions is undefined', () => { + const tx = new Transaction(); + const nullifiers = getNullifiersFromActions(tx); + expect(nullifiers).toEqual([]); + }); + + test('returns nullifier from spend actions', () => { + const nullifier = new Nullifier({ inner: new Uint8Array([1, 2, 3]) }); + const spendAction = new Action({ + action: { + case: 'spend', + value: new Spend({ + body: new SpendBody({ + nullifier, + }), + }), + }, + }); + + const tx = new Transaction({ + body: new TransactionBody({ + actions: [spendAction], + }), + }); + + const nullifiers = getNullifiersFromActions(tx); + expect(nullifiers).toEqual([nullifier]); + }); + + test('returns nullifier from swapClaim actions', () => { + const nullifier = new Nullifier({ inner: new Uint8Array([4, 5, 6]) }); + const swapClaimAction = new Action({ + action: { + case: 'swapClaim', + value: new SwapClaim({ + body: new SwapClaimBody({ + nullifier, + }), + }), + }, + }); + + const tx = new Transaction({ + body: new TransactionBody({ + actions: [swapClaimAction], + }), + }); + + const nullifiers = getNullifiersFromActions(tx); + expect(nullifiers).toEqual([nullifier]); + }); + + test('ignores actions without nullifiers', () => { + const outputAction = new Action({ + action: { + case: 'output', + value: new Output(), + }, + }); + + const tx = new Transaction({ + body: new TransactionBody({ + actions: [outputAction], + }), + }); + + const nullifiers = getNullifiersFromActions(tx); + expect(nullifiers).toEqual([]); + }); +}); + +describe('identifyTransactions', () => { + test('returns empty arrays when no relevant transactions are found', async () => { + const tx = new Transaction(); + const blockTx = [tx]; + const spentNullifiers = new Set(); + const commitmentRecords = new Map(); + + const result = await identifyTransactions(spentNullifiers, commitmentRecords, blockTx); + + expect(result.relevantTxs).toEqual([]); + expect(result.recoveredSourceRecords).toEqual([]); + }); + + test('identifies relevant transactions and recovers sources', async () => { + // Transaction 1: Matching nullifier + const nullifier = new Nullifier({ inner: new Uint8Array([1, 2, 3]) }); + const tx1 = new Transaction({ + body: new TransactionBody({ + actions: [ + new Action({ + action: { + case: 'spend', + value: new Spend({ + body: new SpendBody({ + nullifier, + }), + }), + }, + }), + ], + }), + }); + + // Transaction 2: Matching commitment + const commitment = new StateCommitment({ inner: new Uint8Array([4, 5, 6]) }); + const tx2 = new Transaction({ + body: new TransactionBody({ + actions: [ + new Action({ + action: { + case: 'output', + value: new Output({ + body: new OutputBody({ + notePayload: { + noteCommitment: commitment, + }, + }), + }), + }, + }), + ], + }), + }); + + // Transaction 3: Irrelevant commitment + const tx3 = new Transaction({ + body: new TransactionBody({ + actions: [ + new Action({ + action: { + case: 'output', + value: new Output({ + body: new OutputBody({ + notePayload: { + noteCommitment: new StateCommitment({ inner: new Uint8Array([7, 8, 9]) }), + }, + }), + }), + }, + }), + ], + }), + }); + + // Transaction 4: Irrelevant nullifier + const tx4 = new Transaction({ + body: new TransactionBody({ + actions: [ + new Action({ + action: { + case: 'spend', + value: new Spend({ + body: new SpendBody({ + nullifier: new Nullifier({ inner: new Uint8Array([4, 5, 6]) }), + }), + }), + }, + }), + ], + }), + }); + + const spentNullifiers = new Set([nullifier]); + + const spendableNoteRecord = new SpendableNoteRecord({ + source: BLANK_TX_SOURCE, + }); + + const commitmentRecords = new Map([ + [commitment, spendableNoteRecord], // Expecting match + [new StateCommitment({ inner: new Uint8Array([1, 6, 9]) }), new SpendableNoteRecord()], // not expecting match + ]); + + const spentNullifiersBeforeSize = spentNullifiers.size; + const commitmentRecordsBeforeSize = commitmentRecords.size; + const result = await identifyTransactions(spentNullifiers, commitmentRecords, [ + tx1, // relevant + tx2, // relevant + tx3, // not + tx4, // not + ]); + + expect(result.relevantTxs.length).toBe(2); + expect(result.recoveredSourceRecords.length).toBe(1); + + // Source was recovered + expect(result.recoveredSourceRecords[0]!.source?.equals(BLANK_TX_SOURCE)).toEqual(false); + + // Expect inputs where not mutated + expect(spentNullifiersBeforeSize).toEqual(spentNullifiers.size); + expect(commitmentRecordsBeforeSize).toEqual(commitmentRecords.size); + }); +}); diff --git a/packages/query/src/helpers/identify-txs.ts b/packages/query/src/helpers/identify-txs.ts new file mode 100644 index 000000000..389c121f6 --- /dev/null +++ b/packages/query/src/helpers/identify-txs.ts @@ -0,0 +1,137 @@ +import { + CommitmentSource, + Nullifier, +} from '@penumbra-zone/protobuf/penumbra/core/component/sct/v1/sct_pb'; +import { StateCommitment } from '@penumbra-zone/protobuf/penumbra/crypto/tct/v1/tct_pb'; +import { SpendableNoteRecord, SwapRecord } from '@penumbra-zone/protobuf/penumbra/view/v1/view_pb'; +import { Transaction } from '@penumbra-zone/protobuf/penumbra/core/transaction/v1/transaction_pb'; +import { TransactionId } from '@penumbra-zone/protobuf/penumbra/core/txhash/v1/txhash_pb'; +import { sha256Hash } from '@penumbra-zone/crypto-web/sha256'; + +const BLANK_TX_SOURCE = new CommitmentSource({ + source: { case: 'transaction', value: { id: new Uint8Array() } }, +}); + +// Used as a type-check helper as .filter(Boolean) still results with undefined as a possible value +const isDefined = (value: T | null | undefined): value is NonNullable => + value !== null && value !== undefined; + +export const getCommitmentsFromActions = (tx: Transaction): StateCommitment[] => { + if (!tx.body?.actions) { + return []; + } + + return tx.body.actions + .flatMap(({ action }) => { + switch (action.case) { + case 'output': + return action.value.body?.notePayload?.noteCommitment; + case 'swap': + return action.value.body?.payload?.commitment; + case 'swapClaim': + return [action.value.body?.output1Commitment, action.value.body?.output2Commitment]; + default: + return; + } + }) + .filter(isDefined); +}; + +export const getNullifiersFromActions = (tx: Transaction): Nullifier[] => { + if (!tx.body?.actions) { + return []; + } + + return tx.body.actions + .flatMap(({ action }) => { + switch (action.case) { + case 'spend': + case 'swapClaim': + return action.value.body?.nullifier; + default: + return; + } + }) + .filter(isDefined); +}; + +export interface RelevantTx { + id: TransactionId; + data: Transaction; +} + +type RecoveredSourceRecords = (SpendableNoteRecord | SwapRecord)[]; + +const generateTxId = async (tx: Transaction): Promise => { + return new TransactionId({ inner: await sha256Hash(tx.toBinary()) }); +}; + +const searchRelevant = async ( + tx: Transaction, + spentNullifiers: Set, + commitmentRecords: Map, +): Promise< + { relevantTx: RelevantTx; recoveredSourceRecords: RecoveredSourceRecords } | undefined +> => { + let txId: TransactionId | undefined; // If set, that means this tx is relevant and should be returned to the caller + const recoveredSourceRecords: RecoveredSourceRecords = []; + + const txNullifiers = getNullifiersFromActions(tx); + for (const spentNullifier of spentNullifiers) { + if (txNullifiers.some(txNullifier => spentNullifier.equals(txNullifier))) { + txId ??= await generateTxId(tx); + } + } + + const txCommitments = getCommitmentsFromActions(tx); + for (const [stateCommitment, spendableNoteRecord] of commitmentRecords) { + if (txCommitments.some(txCommitment => stateCommitment.equals(txCommitment))) { + txId ??= await generateTxId(tx); + + // Blank sources can be recovered by associating them with the transaction + if (BLANK_TX_SOURCE.equals(spendableNoteRecord.source)) { + const recovered = spendableNoteRecord.clone(); + recovered.source = new CommitmentSource({ + source: { case: 'transaction', value: { id: txId.inner } }, + }); + recoveredSourceRecords.push(recovered); + } + } + } + + if (txId) { + return { + relevantTx: { id: txId, data: tx }, + recoveredSourceRecords, + }; + } + + return undefined; +}; + +// identify transactions that involve a new record by comparing nullifiers and state commitments +// also returns records with recovered sources +export const identifyTransactions = async ( + spentNullifiers: Set, + commitmentRecords: Map, + blockTx: Transaction[], +): Promise<{ + relevantTxs: RelevantTx[]; + recoveredSourceRecords: RecoveredSourceRecords; +}> => { + const relevantTxs: RelevantTx[] = []; + const recoveredSourceRecords: RecoveredSourceRecords = []; + + const searchPromises = blockTx.map(tx => searchRelevant(tx, spentNullifiers, commitmentRecords)); + const results = await Promise.all(searchPromises); + + for (const result of results) { + if (result?.relevantTx) { + relevantTxs.push(result.relevantTx); + } + if (result?.recoveredSourceRecords.length) { + recoveredSourceRecords.push(...result.recoveredSourceRecords); + } + } + return { relevantTxs, recoveredSourceRecords }; +}; diff --git a/packages/storage/src/indexed-db/config.ts b/packages/storage/src/indexed-db/config.ts index cbf900d0d..360c728b9 100644 --- a/packages/storage/src/indexed-db/config.ts +++ b/packages/storage/src/indexed-db/config.ts @@ -2,4 +2,4 @@ * The version number for the IndexedDB schema. This version number is used to manage * database upgrades and ensure that the correct schema version is applied. */ -export const IDB_VERSION = 46; +export const IDB_VERSION = 47; From e3d0ac3ed91cccecc4d4579cdfa9ffc89c7b2b1a Mon Sep 17 00:00:00 2001 From: Gabe Rodriguez Date: Thu, 19 Sep 2024 15:53:58 +0200 Subject: [PATCH 3/8] Fix changeset package name (#1797) --- .changeset/itchy-files-flow.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/itchy-files-flow.md b/.changeset/itchy-files-flow.md index 0af966d7b..442515147 100644 --- a/.changeset/itchy-files-flow.md +++ b/.changeset/itchy-files-flow.md @@ -1,5 +1,5 @@ --- -'@repo/ui': patch +'@penumbra-zone/ui': patch --- Fix copy button styles From f3500b8d0a13e95f8af1933c0f9434d077f534cd Mon Sep 17 00:00:00 2001 From: Gabe Rodriguez Date: Fri, 20 Sep 2024 13:54:11 +0200 Subject: [PATCH 4/8] Update Select component (#1799) --- .../components/ibc/ibc-in/ibc-in-request.tsx | 26 ++++---- .../components/ibc/ibc-out/chain-selector.tsx | 8 +-- packages/ui/components/ui/select/index.tsx | 1 - packages/ui/components/ui/select/select.tsx | 61 +++++++++++++------ packages/ui/package.json | 2 +- pnpm-lock.yaml | 16 ++--- 6 files changed, 66 insertions(+), 48 deletions(-) diff --git a/apps/minifront/src/components/ibc/ibc-in/ibc-in-request.tsx b/apps/minifront/src/components/ibc/ibc-in/ibc-in-request.tsx index c81829770..aef1ba0d0 100644 --- a/apps/minifront/src/components/ibc/ibc-in/ibc-in-request.tsx +++ b/apps/minifront/src/components/ibc/ibc-in/ibc-in-request.tsx @@ -47,23 +47,21 @@ export const IbcInRequest = () => { )}
diff --git a/apps/minifront/src/components/ibc/ibc-out/chain-selector.tsx b/apps/minifront/src/components/ibc/ibc-out/chain-selector.tsx index 34fd2463f..5b75ff393 100644 --- a/apps/minifront/src/components/ibc/ibc-out/chain-selector.tsx +++ b/apps/minifront/src/components/ibc/ibc-out/chain-selector.tsx @@ -6,7 +6,6 @@ import { SelectValue, } from '@penumbra-zone/ui/components/ui/select'; import { cn } from '@penumbra-zone/ui/lib/utils'; -import { useState } from 'react'; import { AllSlices } from '../../../state'; import { Chain } from '@penumbra-labs/registry'; import { useStoreShallow } from '../../../utils/use-store-shallow'; @@ -20,7 +19,6 @@ const chainSelectorSelector = (state: AllSlices) => ({ export const ChainSelector = () => { const { chain, setChain } = useStoreShallow(chainSelectorSelector); const chains = useChains(); - const [openSelect, setOpenSelect] = useState(false); return (
@@ -28,10 +26,8 @@ export const ChainSelector = () => {