Skip to content

Commit

Permalink
feat(dw): add sign requests for plugins (#2829)
Browse files Browse the repository at this point in the history
* feat(dw): add sign requests for plugins

* fix(dw): dapp connection

* refactor(dw): communication

* fix(dw): acrivity table spacing

* feat(dw): reject sign request
  • Loading branch information
javadkh2 authored Jan 24, 2025
1 parent a8d709d commit a9f9e11
Show file tree
Hide file tree
Showing 13 changed files with 432 additions and 108 deletions.
38 changes: 21 additions & 17 deletions packages/apps/dev-wallet-example/src/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@ const sleep = (time: number) =>
}, time);
});

const walletOrigin = () =>
(window as any).walletUrl || 'https://wallet.kadena.io';
const walletOrigin = () => (window as any).walletUrl || 'http://localhost:4173';
const walletUrl = () => `${walletOrigin()}`;
const walletName = 'Dev-Wallet';
const appName = 'Dev Wallet Example';
Expand Down Expand Up @@ -114,9 +113,7 @@ interface IState {
};
accounts: Array<{
address: string;
keyset: {
guard: { keys: string[]; pred: 'keys-all' | 'keys-any' | 'keys-2' };
};
guard: { keys: string[]; pred: 'keys-all' | 'keys-any' | 'keys-2' };
alias: string;
contract: string;
chains: Array<{ chainId: ChainId; balance: string }>;
Expand Down Expand Up @@ -166,7 +163,7 @@ export default function Home() {
});
addLog(status);
setProfile((status.payload as any).profile);
// close();
close();
}}
>
{profile
Expand All @@ -189,7 +186,8 @@ export default function Home() {
});
addLog(response);
setState(response.payload as IState);
// close();
console.log(response.payload);
close();
}}
>
GET_STATUS
Expand All @@ -209,7 +207,7 @@ export default function Home() {
<div>Alias: {account.alias}</div>
<div>Contract: {account.contract}</div>
<div>overallBalance: {account.overallBalance}</div>
<div>Guard: {JSON.stringify(account.keyset.guard)}</div>
<div>Guard: {JSON.stringify(account.guard)}</div>
</li>
))}
</ul>
Expand All @@ -223,34 +221,40 @@ export default function Home() {
onClick={async () => {
if (!state) return;
const { message, focus, close } = await getWalletConnection();
const accounts = state.accounts.filter(
({ overallBalance }) => +overallBalance > 0,
);
if (accounts.length < 2) {
setLog(['Not enough accounts with balance']);
}
const tx = transferAllCommand({
sender: {
account: state.accounts[0].address,
publicKeys: state.accounts[0].keyset.guard.keys,
account: accounts[0].address,
publicKeys: accounts[0].guard.keys,
},
receiver: {
account: state.accounts[1].address,
keyset: state.accounts[1].keyset.guard,
account: accounts[1].address,
keyset: accounts[1].guard,
},
chainId: state.accounts[0].chains[0].chainId,
amount: state.accounts[0].chains[0].balance,
contract: state.accounts[0].contract,
chainId: accounts[0].chains[0].chainId,
amount: accounts[0].chains[0].balance,
contract: accounts[0].contract,
});
focus();
const response = await message(
'SIGN_REQUEST',
createTransaction(tx()) as any,
);
console.log(response);
const payload: {
status: 'signed' | 'rejected';
transaction?: ICommand;
} = response.payload as any;
debugger;
if (payload && payload.status === 'signed') {
setSignedTx(payload.transaction as ICommand);
}
addLog(response);
// close();
close();
}}
>
Transfer balance from account 1 to account 2
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"shortName": "Pact Console",
"description": "A console for interacting remotely with Pact on different networks (read-only)",
"permissions": [
"network-list"
"GET_NETWORK_LIST"
]
}
]
10 changes: 5 additions & 5 deletions packages/apps/dev-wallet/src/App/Layout/useRightAside.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useLayout } from '@kadena/kode-ui/patterns';
import { useEffect, useState } from 'react';
import { useCallback, useEffect, useState } from 'react';

export function useRightAside() {
const [expanded, setExpanded] = useState(false);
Expand All @@ -13,18 +13,18 @@ export function useRightAside() {

return [
expanded,
() => {
useCallback(() => {
if (isRightAsideExpanded && !expanded) {
throw new Error('Right aside is already open with a different panel');
}
setIsRightAsideExpanded(true);
setExpanded(true);
},
() => {
}, [isRightAsideExpanded, expanded, setIsRightAsideExpanded]),
useCallback(() => {
if (expanded) {
setIsRightAsideExpanded(false);
setExpanded(false);
}
},
}, [expanded, setIsRightAsideExpanded]),
] as [isExpanded: boolean, expand: () => void, close: () => void];
}
11 changes: 7 additions & 4 deletions packages/apps/dev-wallet/src/App/routes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { AccountDiscovery } from '@/pages/account-discovery/account-dsicovery';
import { AccountPage } from '@/pages/account/account';
import { MigrateAccount } from '@/pages/account/migrate-account/migrate-account';
import { ActivitiesPage } from '@/pages/activities/activities';
import { Connect } from '@/pages/connect/connect';
import { ConnectPage } from '@/pages/connect/connect';
import { Contacts } from '@/pages/contacts/contacts';
import { CreateAccount } from '@/pages/create-account/create-account';
import { FungiblePage } from '@/pages/fungible/fungible';
Expand All @@ -31,7 +31,7 @@ import { KeepPasswordPolicy } from '@/pages/settings/keep-password-policy/keep-p
import { RevealPhrase } from '@/pages/settings/reveal-phrase/reveal-phrase';
import { Settings } from '@/pages/settings/settings';
import { SignatureBuilder } from '@/pages/signature-builder/signature-builder';
import { SignRequest } from '@/pages/transaction/sign-request';
import { SignRequestPage } from '@/pages/transaction/sign-request';
import { TransactionPage } from '@/pages/transaction/Transaction';
import { Transfer } from '@/pages/transfer/transfer';
import { ImportChainweaverExport } from '@/pages/wallet-recovery/import-chainweaver-export/import-chainweaver-export';
Expand Down Expand Up @@ -119,7 +119,7 @@ export const Routes: FC = () => {
<Route path="/" element={<HomePage />} />
<Route path="/sig-builder" element={<SignatureBuilder />} />
<Route path="/networks" element={<Networks />} />
<Route path="/connect/:requestId" element={<Connect />} />
<Route path="/connect/:requestId" element={<ConnectPage />} />
<Route path="/key-management/:tab" element={<KeysPage />} />
<Route path="/create-account" element={<CreateAccount />} />
<Route
Expand Down Expand Up @@ -153,7 +153,10 @@ export const Routes: FC = () => {
>
<Route path="/plugins" element={<Plugins />} />
</Route>
<Route path="/sign-request/:requestId" element={<SignRequest />} />
<Route
path="/sign-request/:requestId"
element={<SignRequestPage />}
/>
</Route>
</Route>
<Route element={<LayoutFull />}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,27 @@ import {
FC,
PropsWithChildren,
createContext,
useCallback,
useContext,
useEffect,
useState,
} from 'react';
import { useWallet } from '../wallet/wallet.hook';

type Message = {
export type Message = {
id: string;
type: RequestType;
payload: unknown;
};

type RequestType =
export type UiRequiredRequest = 'CONNECTION_REQUEST' | 'SIGN_REQUEST';

export type RequestType =
| UiRequiredRequest
| 'GET_STATUS'
| 'CONNECTION_REQUEST'
| 'SIGN_REQUEST'
| 'PAYMENT_REQUEST'
| 'UNLOCK_REQUEST'
| 'GET_NETWORK_LIST';
| 'GET_NETWORK_LIST'
| 'GET_ACCOUNTS';

type Request = Message & {
resolve: (data: unknown) => void;
reject: (error: unknown) => void;
Expand All @@ -35,14 +37,13 @@ const messageHandle = (
) => Promise<{ payload: unknown } | { error: unknown }>,
) => {
const cb = async (event: MessageEvent) => {
if (event.data.type === type && event.source) {
if (event.data.type === type && event.source && event.origin !== 'null') {
const payload = await handler(event.data);
event.source.postMessage(
{ id: event.data.id, type: event.data.type, ...payload },
// TODO: use sessionId of plugins, since 'null' happens for the iframe plugins that we need more proper handling
{ targetOrigin: event.origin === 'null' ? '*' : event.origin },
{ targetOrigin: event.origin },
);
if (window.opener && event.origin && event.origin !== 'null') {
if (window.opener && event.origin) {
window.opener.focus();
}
}
Expand Down Expand Up @@ -71,23 +72,38 @@ export const CommunicationProvider: FC<
}
>,
) => () => void;
uiLoader?: (route: string) => void;
uiLoader?: (requestId: string, requestType: UiRequiredRequest) => void;
}>
> = ({ children, handle = messageHandle, uiLoader }) => {
const { setOrigin } = useGlobalState();
const [requests] = useState(() => new Map<string, Request>());
const routeNavigate = usePatchedNavigate();
const navigate =
uiLoader ||
((route: string) => {
setOrigin(route);
routeNavigate(route);
});
const { isUnlocked, accounts, profile, networks, activeNetwork } =
const defaultUiLoader = useCallback(
(requestId: string, requestType: UiRequiredRequest) => {
const routeMap = {
CONNECTION_REQUEST: '/connect',
PAYMENT_REQUEST: '/payment',
SIGN_REQUEST: '/sign-request',
};
const route = routeMap[requestType];
if (!route) return;
const path = `${route}/${requestId}`;
setOrigin(path);
routeNavigate(path);
},
[routeNavigate, setOrigin],
);
const loadUiComponent = useCallback(
(requestId: string, requestType: UiRequiredRequest) => {
const loader = uiLoader || defaultUiLoader;
return loader(requestId, requestType);
},
[defaultUiLoader, uiLoader],
);
const { isUnlocked, accounts, profile, networks, activeNetwork, keySources } =
useWallet();

useEffect(() => {
console.log('CommunicationProvider mounted', isUnlocked);
const createRequest = (data: Message) =>
new Promise<{ payload: unknown } | { error: unknown }>((resolve) => {
const request = {
Expand All @@ -108,23 +124,44 @@ export const CommunicationProvider: FC<
requests.delete(data.id);
});

const handleRequest = (type: RequestType, route: string) =>
const handleUIRequiredRequest = (type: UiRequiredRequest) =>
handle(type, async (payload) => {
console.log('handleRequest', type, payload);
const request = createRequest(payload);
setOrigin(`${route}/${payload.id}`);
navigate(`${route}/${payload.id}`);
loadUiComponent(payload.id, type);
return request;
});
const handlers = [
handleRequest('CONNECTION_REQUEST', '/connect'),
handleRequest('PAYMENT_REQUEST', '/payment'),
handleRequest('SIGN_REQUEST', '/sign-request'),
handleUIRequiredRequest('CONNECTION_REQUEST'),
handleUIRequiredRequest('SIGN_REQUEST'),
handle('GET_STATUS', async () => {
if (!isUnlocked || !profile) return { payload: { isUnlocked: false } };
const { uuid, name, accentColor } = profile;
const accountsToSend = accounts.map(
({ address, alias, overallBalance, chains, guard }) => ({
address,
alias,
overallBalance,
chains,
guard,
}),
);
return {
payload: {
isUnlocked: isUnlocked,
...(isUnlocked ? { profile, accounts } : {}),
profile: {
uuid,
name,
accentColor,
authMode: profile.options?.authMode,
},
accounts: accountsToSend,
keySources: keySources.map(({ uuid, source, keys }) => ({
uuid,
source,
keys,
})),
networks,
activeNetwork,
},
};
}),
Expand All @@ -133,19 +170,36 @@ export const CommunicationProvider: FC<
payload: isUnlocked ? networks : [],
};
}),
handle('GET_ACCOUNTS', async () => {
return {
payload: isUnlocked
? accounts.map(
({ address, alias, overallBalance, chains, guard }) => ({
address,
alias,
overallBalance,
chains,
guard,
}),
)
: [],
};
}),
];
return () => {
handlers.forEach((unsubscribe) => unsubscribe());
};
}, [
navigate,
loadUiComponent,
requests,
isUnlocked,
accounts,
profile,
networks,
activeNetwork,
setOrigin,
handle,
keySources,
]);

return (
Expand Down
Loading

0 comments on commit a9f9e11

Please sign in to comment.