Skip to content

Commit 7bbc6a6

Browse files
committed
feat: improve sign message
1 parent 3b14e6f commit 7bbc6a6

23 files changed

+2999
-50
lines changed

.env.example

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID='8b62fc124fa933f467984b1a1e4ff124'
2+
ALCHEMY_API_KEY='sERMzQpN-DgFrd43w-AmhqC2z94i4IiI'
3+
4+
5+
NEXT_PUBLIC_SAFE_GLOBAL_API_KEY='eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzYWZlLWF1dGgtc2VydmljZSIsInN1YiI6ImJlNTg4MDYxOTJlODQyYThhZmZhMmEwOTI1ODNlYThjXzQyYTExOWNiNWM4YjQzOWViMzQyZGNiMzVlOTIyMGY3Iiwia2V5IjoiYmU1ODgwNjE5MmU4NDJhOGFmZmEyYTA5MjU4M2VhOGNfNDJhMTE5Y2I1YzhiNDM5ZWIzNDJkY2IzNWU5MjIwZjciLCJhdWQiOlsic2FmZS1hdXRoLXNlcnZpY2UiXSwiZXhwIjoxOTEyMTQ4MTk5LCJkYXRhIjp7fX0.cQJF4sLZiJjyKNo-v6k6wFlP4bKPO_Z3aRhmE6OqFaQenLWcrUWZTAOc1yweuEuCykowI85T8E1OrZeMMf8B7g'

app/transactions/new/page.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import { Card, CardContent } from '@/components/ui/card';
55
import { Coins } from 'lucide-react';
66
import { useSelectedSafeAddress } from '@/providers/SafeProvider';
77
import { TransactionProcess } from '@/components/Transactions/process/TransactionProcess';
8+
import { TransactionProcessV2Demo } from '@/components/Transactions/process-v2/demo';
9+
import { TransactionProcessV2 } from '@/components/Transactions/process-v2/TransactionProcessV2';
810

911
export default function NewTransactionPage() {
1012
const { selectedSafeAddress } = useSelectedSafeAddress();
@@ -29,7 +31,7 @@ export default function NewTransactionPage() {
2931

3032
return (
3133
<PageContainer>
32-
<TransactionProcess />
34+
<TransactionProcessV2 />
3335
</PageContainer>
3436
);
3537
}
Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
'use client';
2+
3+
import React, { useEffect } from 'react';
4+
import { TransactionBuilderProvider } from './core/TransactionBuilderProvider';
5+
import { useTransactionBuilder } from './hooks/useTransactionBuilder';
6+
import { TransactionFlowController } from './core/TransactionFlowController';
7+
import { TransactionType, BuildData } from './core/types';
8+
9+
// Import builders
10+
import { SendTokenBuilder, SendTokenBuilderLogic } from './builders/SendTokenBuilder';
11+
import { CustomContractBuilder, CustomContractBuilderLogic } from './builders/CustomContractBuilder';
12+
13+
// Import preview and confirm components
14+
import { TransactionPreviewPanel } from './preview/TransactionPreviewPanel';
15+
import { TransactionConfirmation } from './confirm/TransactionConfirmation';
16+
17+
// Import registry and register components
18+
import { ComponentRegistry, BuilderRegistry } from './builders/BuilderRegistry';
19+
20+
// Register all available builders (both logic and components)
21+
BuilderRegistry.register('send', () => new SendTokenBuilderLogic());
22+
BuilderRegistry.register('custom', () => new CustomContractBuilderLogic());
23+
24+
ComponentRegistry.register('send', SendTokenBuilder);
25+
ComponentRegistry.register('custom', CustomContractBuilder);
26+
27+
interface TransactionProcessV2Props {
28+
// For new transactions
29+
initialType?: TransactionType;
30+
31+
// For pending transactions
32+
pendingTransactionId?: string;
33+
pendingTransactionData?: Partial<BuildData>;
34+
35+
// Callbacks
36+
onComplete?: (transactionHash: string) => void;
37+
onCancel?: () => void;
38+
}
39+
40+
function TransactionProcessV2Content() {
41+
const { state, startBuilding, proceedToPreview } = useTransactionBuilder();
42+
43+
const renderBuildStep = () => {
44+
if (!state.transactionType) {
45+
// Show transaction type selector
46+
return (
47+
<div className='space-y-6'>
48+
<div className='grid gap-4 md:grid-cols-2'>
49+
<div
50+
className='flex flex-col items-center gap-3 p-6 border rounded-lg hover:border-primary/50 hover:text-primary transition-colors cursor-pointer'
51+
onClick={() => startBuilding('send')}
52+
>
53+
<div className='h-12 w-12 rounded-full bg-primary/10 flex items-center justify-center'>
54+
<svg className='h-6 w-6' fill='none' stroke='currentColor' viewBox='0 0 24 24'>
55+
<path
56+
strokeLinecap='round'
57+
strokeLinejoin='round'
58+
strokeWidth={2}
59+
d='M12 19l9 2-9-18-9 18 9-2zm0 0v-8'
60+
/>
61+
</svg>
62+
</div>
63+
<div className='text-center'>
64+
<div className='font-medium text-lg'>Send Token</div>
65+
<div className='text-sm text-muted-foreground'>Transfer ETH or tokens to another address</div>
66+
</div>
67+
</div>
68+
69+
<div
70+
className='flex flex-col items-center gap-3 p-6 border rounded-lg hover:border-primary/50 hover:text-primary transition-colors cursor-pointer'
71+
onClick={() => startBuilding('custom')}
72+
>
73+
<div className='h-12 w-12 rounded-full bg-primary/10 flex items-center justify-center'>
74+
<svg className='h-6 w-6' fill='none' stroke='currentColor' viewBox='0 0 24 24'>
75+
<path
76+
strokeLinecap='round'
77+
strokeLinejoin='round'
78+
strokeWidth={2}
79+
d='M10 20l4-16m4 4l4 4-4 4M6 16l-4-4 4-4'
80+
/>
81+
</svg>
82+
</div>
83+
<div className='text-center'>
84+
<div className='font-medium text-lg'>Custom Transaction</div>
85+
<div className='text-sm text-muted-foreground'>Build custom smart contract interactions</div>
86+
</div>
87+
</div>
88+
</div>
89+
</div>
90+
);
91+
}
92+
93+
// Render the appropriate builder component
94+
try {
95+
const BuilderComponent = ComponentRegistry.getComponent(state.transactionType);
96+
return (
97+
<BuilderComponent
98+
onComplete={async (data: BuildData) => {
99+
await proceedToPreview(data);
100+
}}
101+
initialData={state.buildData}
102+
errors={state.errors}
103+
isLoading={state.isLoading}
104+
/>
105+
);
106+
} catch (error) {
107+
return (
108+
<div className='text-center py-8'>
109+
<div className='text-destructive'>Error: Unknown transaction type "{state.transactionType}"</div>
110+
</div>
111+
);
112+
}
113+
};
114+
115+
const renderPreviewStep = () => {
116+
if (!state.buildData) {
117+
return (
118+
<div className='text-center py-8'>
119+
<div className='text-destructive'>No transaction data available for preview</div>
120+
</div>
121+
);
122+
}
123+
124+
return <TransactionPreviewPanel transactionData={state.buildData} />;
125+
};
126+
127+
const renderConfirmStep = () => {
128+
if (!state.buildData) {
129+
return (
130+
<div className='text-center py-8'>
131+
<div className='text-destructive'>No transaction data available for confirmation</div>
132+
</div>
133+
);
134+
}
135+
136+
return <TransactionConfirmation transactionData={state.buildData} />;
137+
};
138+
139+
const renderStepContent = () => {
140+
switch (state.currentStep) {
141+
case 'build':
142+
return renderBuildStep();
143+
case 'preview':
144+
return renderPreviewStep();
145+
case 'confirm':
146+
return renderConfirmStep();
147+
default:
148+
return null;
149+
}
150+
};
151+
152+
return <TransactionFlowController>{renderStepContent()}</TransactionFlowController>;
153+
}
154+
155+
export function TransactionProcessV2({
156+
initialType,
157+
pendingTransactionId,
158+
pendingTransactionData,
159+
onComplete,
160+
onCancel,
161+
}: TransactionProcessV2Props) {
162+
return (
163+
<TransactionBuilderProvider
164+
initialType={initialType}
165+
pendingTransactionId={pendingTransactionId}
166+
pendingTransactionData={pendingTransactionData}
167+
>
168+
<TransactionProcessV2Content />
169+
</TransactionBuilderProvider>
170+
);
171+
}
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
import { TransactionType } from '../core/types';
2+
import { ITransactionBuilder } from './base/BuilderInterface';
3+
4+
// Builder class registry
5+
export class BuilderRegistry {
6+
private static builders: Map<TransactionType, () => ITransactionBuilder> = new Map();
7+
8+
/**
9+
* Register a builder for a specific transaction type
10+
*/
11+
static register(type: TransactionType, builderFactory: () => ITransactionBuilder): void {
12+
this.builders.set(type, builderFactory);
13+
}
14+
15+
/**
16+
* Get a builder instance for a specific transaction type
17+
*/
18+
static getBuilder(type: TransactionType): ITransactionBuilder {
19+
const builderFactory = this.builders.get(type);
20+
if (!builderFactory) {
21+
throw new Error(`No builder registered for transaction type: ${type}`);
22+
}
23+
return builderFactory();
24+
}
25+
26+
/**
27+
* Get all registered transaction types
28+
*/
29+
static getRegisteredTypes(): TransactionType[] {
30+
return Array.from(this.builders.keys());
31+
}
32+
33+
/**
34+
* Check if a builder is registered for a specific type
35+
*/
36+
static hasBuilder(type: TransactionType): boolean {
37+
return this.builders.has(type);
38+
}
39+
40+
/**
41+
* Clear all registered builders (mainly for testing)
42+
*/
43+
static clear(): void {
44+
this.builders.clear();
45+
}
46+
}
47+
48+
// Component registry for React components
49+
export class ComponentRegistry {
50+
private static components: Map<TransactionType, React.ComponentType<any>> = new Map();
51+
52+
/**
53+
* Register a React component for a specific transaction type
54+
*/
55+
static register(type: TransactionType, component: React.ComponentType<any>): void {
56+
this.components.set(type, component);
57+
}
58+
59+
/**
60+
* Get a React component for a specific transaction type
61+
*/
62+
static getComponent(type: TransactionType): React.ComponentType<any> {
63+
const component = this.components.get(type);
64+
if (!component) {
65+
throw new Error(`No component registered for transaction type: ${type}`);
66+
}
67+
return component;
68+
}
69+
70+
/**
71+
* Check if a component is registered for a specific type
72+
*/
73+
static hasComponent(type: TransactionType): boolean {
74+
return this.components.has(type);
75+
}
76+
77+
/**
78+
* Clear all registered components (mainly for testing)
79+
*/
80+
static clear(): void {
81+
this.components.clear();
82+
}
83+
}
84+
85+
// Factory function to create builder instances
86+
export function createBuilder(type: TransactionType): ITransactionBuilder {
87+
return BuilderRegistry.getBuilder(type);
88+
}
89+
90+
// Factory function to get React components
91+
export function getBuilderComponent(type: TransactionType): React.ComponentType<any> {
92+
return ComponentRegistry.getComponent(type);
93+
}

0 commit comments

Comments
 (0)