Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .changeset/adapter-evm-wallet-refactor.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
'@openzeppelin/contracts-ui-builder-adapter-evm': patch
---

Refactor wallet interface architecture for consistency

- Move `EvmWalletConnectionStatus` interface to `wallet/types.ts` for better organization
- Add `convertWagmiToEvmStatus` utility function to eliminate code duplication
- Fix chainId type conversion in execution config validation to handle both string and number types
- Update wallet barrel exports to include new types and utilities
- Maintain structural compatibility with base `WalletConnectionStatus` interface
11 changes: 11 additions & 0 deletions .changeset/adapter-solana-wallet-refactor.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
'@openzeppelin/contracts-ui-builder-adapter-solana': patch
---

Refactor wallet interface architecture for consistency

- Move `SolanaWalletConnectionStatus` interface to `wallet/types.ts` for better organization
- Update wallet connection functions to use two-parameter callback signature
- Maintain structural compatibility with base `WalletConnectionStatus` interface
- Align wallet directory structure with other adapters for architectural consistency
- Update barrel exports to include new wallet types
13 changes: 13 additions & 0 deletions .changeset/adapter-stellar-wallet-refactor.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
'@openzeppelin/contracts-ui-builder-adapter-stellar': patch
---

Fix Freighter wallet popup loop and refactor wallet interface architecture

- **Fix critical bug**: Replace aggressive polling with event-driven wallet connection approach in `StellarWalletUiRoot`
- **Eliminate infinite Freighter popups** that occurred when clicking wallet connection button
- Add `stellarWalletImplementationManager` singleton pattern following EVM adapter architecture
- Update adapter to use new implementation manager and return `StellarWalletConnectionStatus`
- Move wallet interfaces (`StellarWalletConnectionStatus`, `StellarConnectionStatusListener`) to `wallet/types.ts`
- Implement event subscriptions with minimal fallback polling (5-minute intervals instead of 1-second)
- Align wallet directory structure with EVM adapter for architectural consistency
9 changes: 9 additions & 0 deletions .changeset/clear-trees-buy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
'@openzeppelin/contracts-ui-builder-renderer': minor
'@openzeppelin/contracts-ui-builder-app': minor
'@openzeppelin/contracts-ui-builder-types': minor
'@openzeppelin/contracts-ui-builder-utils': minor
'@openzeppelin/contracts-ui-builder-ui': minor
---

support for new BytesField component with validation
5 changes: 5 additions & 0 deletions .changeset/curly-vans-beg.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@openzeppelin/contracts-ui-builder-ui': patch
---

allow zero values in ArrayField required validation
5 changes: 5 additions & 0 deletions .changeset/lucky-corners-repair.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@openzeppelin/contracts-ui-builder-adapter-evm': patch
---

remove broken address validation from field configs
5 changes: 5 additions & 0 deletions .changeset/polite-words-laugh.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@openzeppelin/contracts-ui-builder-ui': patch
---

snapshot pre-append and fallback setValue to fix add-item with default 0 in ArrayField
10 changes: 10 additions & 0 deletions .changeset/react-core-wallet-ui-fix.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
'@openzeppelin/contracts-ui-builder-react-core': patch
---

Fix wallet UI mounting issue when switching between ecosystems

- Fix React key generation bug in `WalletStateProvider` that prevented Stellar wallet UI from mounting after switching from EVM networks
- Update key generation to use `${ecosystem}-${networkId}` format for proper component unmounting/mounting
- Remove unused `lastFullUiKitConfiguration` property from `ExtendedContractAdapter` interface
- Ensure proper wallet UI reconciliation when switching between different blockchain ecosystems
5 changes: 5 additions & 0 deletions .changeset/small-books-divide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@openzeppelin/contracts-ui-builder-adapter-evm': patch
---

Update EVM adapter execution strategy interface to use TxStatus instead of string
8 changes: 8 additions & 0 deletions .changeset/tame-ghosts-lay.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
'@openzeppelin/contracts-ui-builder-renderer': minor
'@openzeppelin/contracts-ui-builder-app': minor
'@openzeppelin/contracts-ui-builder-types': minor
'@openzeppelin/contracts-ui-builder-ui': minor
---

New FormField UI component for enum type handling and representation
10 changes: 10 additions & 0 deletions .changeset/types-wallet-interface-enhancement.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
'@openzeppelin/contracts-ui-builder-types': patch
---

Enhance base wallet connection status interface

- Enhance `WalletConnectionStatus` interface with additional universal properties (isConnecting, isDisconnected, isReconnecting, status, connector)
- Remove chain-specific properties (addresses, chain) from base interface to maintain chain-agnostic design
- Support inheritance pattern for chain-specific extensions while preserving structural typing compatibility
- Enable richer wallet UX data across all adapters without sacrificing architectural principles
5 changes: 5 additions & 0 deletions .changeset/whole-bees-press.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@openzeppelin/contracts-ui-builder-adapter-evm': patch
---

clean up redundant ternary in array field validation
4 changes: 2 additions & 2 deletions packages/adapter-evm/src/abi/loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ import type {
ContractDefinitionMetadata,
ContractSchema,
EvmNetworkConfig,
FormValues,
ProxyInfo,
} from '@openzeppelin/contracts-ui-builder-types';
import { logger, simpleHash } from '@openzeppelin/contracts-ui-builder-utils';

import { detectProxyFromAbi, getAdminAddress, getImplementationAddress } from '../proxy/detection';
import type { AbiItem, TypedEvmNetworkConfig } from '../types';
import type { EvmContractArtifacts } from '../types/artifacts';
import { loadAbiFromEtherscan } from './etherscan';
import { transformAbiToSchema } from './transformer';

Expand Down Expand Up @@ -60,7 +60,7 @@ export interface ContractLoadOptions {
* Returns enhanced result with schema source information and automatic proxy detection.
*/
export async function loadEvmContract(
artifacts: FormValues,
artifacts: EvmContractArtifacts,
networkConfig: EvmNetworkConfig,
options: ContractLoadOptions = {}
): Promise<EvmContractLoadResult> {
Expand Down
64 changes: 38 additions & 26 deletions packages/adapter-evm/src/adapter.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import type { GetAccountReturnType } from '@wagmi/core';
import { type TransactionReceipt } from 'viem';
import React from 'react';

Expand All @@ -15,17 +14,18 @@ import type {
ExecutionMethodDetail,
FieldType,
FormFieldType,
FormValues,
FunctionParameter,
NativeConfigLoader,
NetworkConfig,
ProxyInfo,
RelayerDetails,
RelayerDetailsRich,
TransactionStatusUpdate,
TxStatus,
UiKitConfiguration,
UserExplorerConfig,
UserRpcProviderConfig,
WalletConnectionStatus,
} from '@openzeppelin/contracts-ui-builder-types';
import { logger } from '@openzeppelin/contracts-ui-builder-utils';

Expand All @@ -37,18 +37,6 @@ import { loadInitialConfigFromAppService } from './wallet/hooks/useUiKitConfig';
import { generateRainbowKitConfigFile } from './wallet/rainbowkit/config-generator';
import { generateRainbowKitExportables } from './wallet/rainbowkit/export-service';
import { resolveFullUiKitConfiguration } from './wallet/services/configResolutionService';
import { getResolvedWalletComponents } from './wallet/utils';
import {
connectAndEnsureCorrectNetwork,
disconnectEvmWallet,
evmSupportsWalletConnection,
getEvmAvailableConnectors,
getEvmWalletConnectionStatus,
} from './wallet/utils/connection';
import {
getEvmWalletImplementation,
getInitializedEvmWalletImplementation,
} from './wallet/utils/walletImplementationManager';

import { loadEvmContract } from './abi';
import {
Expand Down Expand Up @@ -78,7 +66,19 @@ import {
import { formatEvmFunctionResult } from './transform';
import { TypedEvmNetworkConfig } from './types';
import type { WriteContractParameters } from './types';
import { isValidEvmAddress } from './utils';
import { isValidEvmAddress, validateAndConvertEvmArtifacts } from './utils';
import {
connectAndEnsureCorrectNetwork,
convertWagmiToEvmStatus,
disconnectEvmWallet,
evmSupportsWalletConnection,
EvmWalletConnectionStatus,
getEvmAvailableConnectors,
getEvmWalletConnectionStatus,
getEvmWalletImplementation,
getInitializedEvmWalletImplementation,
getResolvedWalletComponents,
} from './wallet';

/**
* Type guard to check if a network config is a TypedEvmNetworkConfig
Expand Down Expand Up @@ -126,15 +126,17 @@ export class EvmAdapter implements ContractAdapter {
/**
* @inheritdoc
*/
public async loadContract(artifacts: FormValues): Promise<ContractSchema> {
public async loadContract(source: string | Record<string, unknown>): Promise<ContractSchema> {
// Convert generic input to EVM-specific artifacts
const artifacts = validateAndConvertEvmArtifacts(source);
const result = await loadEvmContract(artifacts, this.networkConfig);
return result.schema;
}

/**
* @inheritdoc
*/
public async loadContractWithMetadata(artifacts: FormValues): Promise<{
public async loadContractWithMetadata(source: string | Record<string, unknown>): Promise<{
schema: ContractSchema;
source: 'fetched' | 'manual';
contractDefinitionOriginal?: string;
Expand All @@ -148,6 +150,8 @@ export class EvmAdapter implements ContractAdapter {
proxyInfo?: ProxyInfo;
}> {
try {
// Convert generic input to EVM-specific artifacts
const artifacts = validateAndConvertEvmArtifacts(source);
const result = await loadEvmContract(artifacts, this.networkConfig);

return {
Expand Down Expand Up @@ -212,7 +216,7 @@ export class EvmAdapter implements ContractAdapter {
public async signAndBroadcast(
transactionData: unknown,
executionConfig: ExecutionConfig,
onStatusChange: (status: string, details: TransactionStatusUpdate) => void,
onStatusChange: (status: TxStatus, details: TransactionStatusUpdate) => void,
runtimeApiKey?: string
): Promise<{ txHash: string }> {
const walletImplementation = await getEvmWalletImplementation();
Expand Down Expand Up @@ -400,20 +404,19 @@ export class EvmAdapter implements ContractAdapter {
/**
* @inheritdoc
*/
public getWalletConnectionStatus(): { isConnected: boolean; address?: string; chainId?: string } {
public getWalletConnectionStatus(): EvmWalletConnectionStatus {
const status = getEvmWalletConnectionStatus();
return {
isConnected: status.isConnected,
address: status.address,
chainId: status.chainId?.toString(),
};
return convertWagmiToEvmStatus(status);
}

/**
* @inheritdoc
*/
public onWalletConnectionChange(
callback: (account: GetAccountReturnType, prevAccount: GetAccountReturnType) => void
callback: (
currentStatus: WalletConnectionStatus,
previousStatus: WalletConnectionStatus
) => void
): () => void {
const walletImplementation = getInitializedEvmWalletImplementation();
if (!walletImplementation) {
Expand All @@ -423,7 +426,16 @@ export class EvmAdapter implements ContractAdapter {
);
return () => {};
}
return walletImplementation.onWalletConnectionChange(callback);
return walletImplementation.onWalletConnectionChange(
(currentWagmiStatus, previousWagmiStatus) => {
// Convert wagmi's GetAccountReturnType to the rich adapter interface
// This preserves all the enhanced UX capabilities from wagmi
const current = convertWagmiToEvmStatus(currentWagmiStatus);
const previous = convertWagmiToEvmStatus(previousWagmiStatus);

callback(current, previous);
}
);
}

/**
Expand Down
15 changes: 12 additions & 3 deletions packages/adapter-evm/src/configuration/execution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,17 +59,26 @@ async function _validateMultisigConfig(
*/
export async function validateEvmExecutionConfig(
config: ExecutionConfig,
walletStatus: { isConnected: boolean; address?: string; chainId?: string }
walletStatus: { isConnected: boolean; address?: string; chainId?: string | number }
): Promise<true | string> {
logger.info(SYSTEM_LOG_TAG, 'Validating EVM execution config:', { config, walletStatus });

// Normalize chainId to string for validation functions
const normalizedWalletStatus = {
...walletStatus,
chainId:
typeof walletStatus.chainId === 'number'
? walletStatus.chainId.toString()
: walletStatus.chainId,
};

switch (config.method) {
case 'eoa':
return validateEoaConfig(config as EoaExecutionConfig, walletStatus);
return validateEoaConfig(config as EoaExecutionConfig, normalizedWalletStatus);
case 'relayer':
return validateRelayerConfig(config as RelayerExecutionConfig);
case 'multisig':
return _validateMultisigConfig(config as MultisigExecutionConfig, walletStatus);
return _validateMultisigConfig(config as MultisigExecutionConfig, normalizedWalletStatus);
default: {
const unknownMethod = (config as ExecutionConfig).method;
logger.warn(
Expand Down
4 changes: 4 additions & 0 deletions packages/adapter-evm/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,5 +43,9 @@ export { evmAdapterConfig } from './config';
export type { TypedEvmNetworkConfig, WriteContractParameters } from './types';
export type { EvmRelayerTransactionOptions } from './transaction/relayer';

// Re-export adapter-specific types
export type { EvmContractArtifacts } from './types/artifacts';
export { isEvmContractArtifacts } from './types/artifacts';

// Export abi module for comparison functionality
export { abiComparisonService } from './abi';
27 changes: 4 additions & 23 deletions packages/adapter-evm/src/mapping/field-generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import type {
} from '@openzeppelin/contracts-ui-builder-types';
import { getDefaultValueForType } from '@openzeppelin/contracts-ui-builder-utils';

import { isValidEvmAddress } from '../utils';
import { mapEvmParamTypeToFieldType } from './type-mapper';

/**
Expand All @@ -30,26 +29,8 @@ function extractArrayElementType(parameterType: string): string | null {
* Get default validation rules for a parameter type.
* Only includes serializable validation rules - no custom functions.
*/
function getDefaultValidationForType(parameterType: string): FieldValidation {
const validation: FieldValidation = { required: true };

// Add specific validation rules based on the parameter type
if (parameterType === 'blockchain-address') {
return {
...validation,
// Use the imported isValidEvmAddress method for direct validation
// NOTE: FieldValidation type doesn't officially support `custom`. This relies
// on React Hook Form's `validate` prop potentially picking this up downstream.
// Consider alternative validation approaches if this proves problematic.
custom: (value: unknown): boolean | string => {
if (value === '') return true; // Empty values handled by required
if (typeof value !== 'string') return 'Address must be a string';
return isValidEvmAddress(value) ? true : 'Invalid address format';
},
} as FieldValidation & { custom?: (value: unknown) => boolean | string }; // Cast to include custom
}

return validation;
function getDefaultValidationForType(): FieldValidation {
return { required: true };
}

/**
Expand All @@ -67,7 +48,7 @@ export function generateEvmDefaultField<T extends FieldType = FieldType>(
placeholder: `Enter ${parameter.displayName || parameter.name || parameter.type}`,
helperText: parameter.description || '',
defaultValue: getDefaultValueForType(fieldType) as FieldValue<T>,
validation: getDefaultValidationForType(parameter.type),
validation: getDefaultValidationForType(),
width: 'full',
};

Expand All @@ -83,7 +64,7 @@ export function generateEvmDefaultField<T extends FieldType = FieldType>(
elementType: elementFieldType,
elementFieldConfig: {
type: elementFieldType,
validation: getDefaultValidationForType(elementType),
validation: getDefaultValidationForType(),
placeholder: `Enter ${elementType}`,
},
};
Expand Down
3 changes: 2 additions & 1 deletion packages/adapter-evm/src/transaction/eoa.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
EoaExecutionConfig,
ExecutionConfig,
TransactionStatusUpdate,
TxStatus,
} from '@openzeppelin/contracts-ui-builder-types';
import { logger } from '@openzeppelin/contracts-ui-builder-utils';

Expand All @@ -25,7 +26,7 @@ export class EoaExecutionStrategy implements ExecutionStrategy {
transactionData: WriteContractParameters,
executionConfig: ExecutionConfig,
walletImplementation: WagmiWalletImplementation,
onStatusChange: (status: string, details: TransactionStatusUpdate) => void,
onStatusChange: (status: TxStatus, details: TransactionStatusUpdate) => void,
// runtimeApiKey is unused in EOA strategy but required by the interface
_runtimeApiKey?: string
): Promise<{ txHash: string }> {
Expand Down
Loading