@openzeppelin/contracts-ui-builder-adapter-stellar
Version:
Stellar Adapter for Contracts UI Builder
261 lines (234 loc) • 8.54 kB
text/typescript
import {
testStellarRpcConnection,
validateStellarRpcEndpoint,
} from 'packages/adapter-stellar/src/configuration';
import type {
AvailableUiKit,
Connector,
ContractAdapter,
ContractFunction,
ContractSchema,
ExecutionConfig,
ExecutionMethodDetail,
FieldType,
FormFieldType,
FormValues,
FunctionParameter,
RelayerDetails,
RelayerDetailsRich,
StellarNetworkConfig,
UiKitConfiguration,
UserRpcProviderConfig,
} from '@openzeppelin/contracts-ui-builder-types';
import { isStellarNetworkConfig } from '@openzeppelin/contracts-ui-builder-types';
import { logger } from '@openzeppelin/contracts-ui-builder-utils';
// Import functions from modules
import {
getStellarSupportedExecutionMethods,
validateStellarExecutionConfig,
} from './configuration/execution';
import { getStellarExplorerAddressUrl, getStellarExplorerTxUrl } from './configuration/explorer';
import {
generateStellarDefaultField,
getStellarCompatibleFieldTypes,
mapStellarParameterTypeToFieldType,
} from './mapping';
import { isStellarViewFunction, queryStellarViewFunction } from './query';
import { formatStellarTransactionData, signAndBroadcastStellarTransaction } from './transaction';
import { formatStellarFunctionResult } from './transform';
import { isValidAddress as isStellarValidAddress } from './utils';
import {
connectStellarWallet,
disconnectStellarWallet,
getStellarAvailableConnectors,
getStellarWalletConnectionStatus,
supportsStellarWalletConnection,
// onStellarWalletConnectionChange, // Placeholder if needed later
} from './wallet';
/**
* Stellar-specific adapter implementation using explicit method delegation.
*
* NOTE: Contains placeholder implementations for most functionalities.
*/
export class StellarAdapter implements ContractAdapter {
readonly networkConfig: StellarNetworkConfig;
readonly initialAppServiceKitName: UiKitConfiguration['kitName'];
constructor(networkConfig: StellarNetworkConfig) {
if (!isStellarNetworkConfig(networkConfig)) {
throw new Error('StellarAdapter requires a valid Stellar network configuration.');
}
this.networkConfig = networkConfig;
this.initialAppServiceKitName = 'custom';
logger.info(
'StellarAdapter',
`Adapter initialized for network: ${networkConfig.name} (ID: ${networkConfig.id})`
);
}
// --- Contract Loading --- //
public getContractDefinitionInputs(): FormFieldType[] {
return [
{
id: 'contractAddress',
name: 'contractAddress',
label: 'Contract ID',
type: 'blockchain-address',
validation: { required: true },
placeholder: 'G...',
helperText: 'Enter the Stellar contract ID.',
},
];
}
async loadContract(artifacts: FormValues): Promise<ContractSchema> {
// Stellar doesn't have a standardized on-chain contract interface like an ABI.
// The 'source' would need to be a custom JSON descriptor provided by the user.
// This is a placeholder for a more complex implementation.
if (typeof artifacts.contractAddress !== 'string') {
throw new Error('A contract address must be provided.');
}
return {
name: 'StellarContract',
address: artifacts.contractAddress,
ecosystem: 'stellar',
functions: [],
events: [],
};
}
getWritableFunctions(contractSchema: ContractSchema): ContractSchema['functions'] {
return contractSchema.functions.filter((fn: ContractFunction) => fn.modifiesState);
}
// --- Type Mapping & Field Generation --- //
mapParameterTypeToFieldType(parameterType: string): FieldType {
return mapStellarParameterTypeToFieldType(parameterType);
}
getCompatibleFieldTypes(parameterType: string): FieldType[] {
return getStellarCompatibleFieldTypes(parameterType);
}
generateDefaultField<T extends FieldType = FieldType>(
parameter: FunctionParameter
): FormFieldType<T> {
return generateStellarDefaultField(parameter);
}
// --- Transaction Formatting & Execution --- //
public formatTransactionData(
contractSchema: ContractSchema,
functionId: string,
submittedInputs: Record<string, unknown>,
fields: FormFieldType[]
): unknown {
return formatStellarTransactionData(contractSchema, functionId, submittedInputs, fields);
}
async signAndBroadcast(
transactionData: unknown,
executionConfig?: ExecutionConfig
): Promise<{ txHash: string }> {
return signAndBroadcastStellarTransaction(transactionData, executionConfig);
}
// NOTE: waitForTransactionConfirmation? is optional in the interface.
// Since the imported function is currently undefined, we omit the method here.
// If implemented in ./transaction/sender.ts later, add the method back:
// async waitForTransactionConfirmation?(...) { ... }
// --- View Function Querying --- //
isViewFunction(functionDetails: ContractFunction): boolean {
return isStellarViewFunction(functionDetails);
}
// Implement queryViewFunction with the correct signature from ContractAdapter
async queryViewFunction(
contractAddress: string,
functionId: string,
params: unknown[] = [],
contractSchema?: ContractSchema
): Promise<unknown> {
return queryStellarViewFunction(
contractAddress,
functionId,
this.networkConfig,
params,
contractSchema
);
}
formatFunctionResult(decodedValue: unknown, functionDetails: ContractFunction): string {
return formatStellarFunctionResult(decodedValue, functionDetails);
}
// --- Wallet Interaction --- //
supportsWalletConnection(): boolean {
return supportsStellarWalletConnection();
}
async getAvailableConnectors(): Promise<Connector[]> {
return getStellarAvailableConnectors();
}
async connectWallet(
connectorId: string
): Promise<{ connected: boolean; address?: string; error?: string }> {
return connectStellarWallet(connectorId);
}
async disconnectWallet(): Promise<{ disconnected: boolean; error?: string }> {
return disconnectStellarWallet();
}
getWalletConnectionStatus(): { isConnected: boolean; address?: string; chainId?: string } {
return getStellarWalletConnectionStatus();
}
// Optional: onWalletConnectionChange(...) implementation would go here
// --- Configuration & Metadata --- //
async getSupportedExecutionMethods(): Promise<ExecutionMethodDetail[]> {
return getStellarSupportedExecutionMethods();
}
async validateExecutionConfig(config: ExecutionConfig): Promise<true | string> {
return validateStellarExecutionConfig(config);
}
// Implement getExplorerUrl with the correct signature from ContractAdapter
getExplorerUrl(address: string): string | null {
return getStellarExplorerAddressUrl(address, this.networkConfig);
}
// Implement getExplorerTxUrl with the correct signature from ContractAdapter
getExplorerTxUrl?(txHash: string): string | null {
if (getStellarExplorerTxUrl) {
return getStellarExplorerTxUrl(txHash, this.networkConfig);
}
return null;
}
// --- Validation --- //
isValidAddress(address: string): boolean {
return isStellarValidAddress(address);
}
public async getAvailableUiKits(): Promise<AvailableUiKit[]> {
return [
{
id: 'custom',
name: 'OpenZeppelin Custom',
configFields: [],
},
];
}
public async getRelayers(_serviceUrl: string, _accessToken: string): Promise<RelayerDetails[]> {
logger.warn('StellarAdapter', 'getRelayers is not implemented for the Stellar adapter yet.');
return Promise.resolve([]);
}
public async getRelayer(
_serviceUrl: string,
_accessToken: string,
_relayerId: string
): Promise<RelayerDetailsRich> {
logger.warn('StellarAdapter', 'getRelayer is not implemented for the Stellar adapter yet.');
return Promise.resolve({} as RelayerDetailsRich);
}
/**
* @inheritdoc
*/
public async validateRpcEndpoint(rpcConfig: UserRpcProviderConfig): Promise<boolean> {
// TODO: Implement Stellar-specific RPC validation when needed
return validateStellarRpcEndpoint(rpcConfig);
}
/**
* @inheritdoc
*/
public async testRpcConnection(rpcConfig: UserRpcProviderConfig): Promise<{
success: boolean;
latency?: number;
error?: string;
}> {
// TODO: Implement Stellar-specific RPC validation when needed
return testStellarRpcConnection(rpcConfig);
}
}
// Also export as default to ensure compatibility with various import styles
export default StellarAdapter;