Skip to content

Commit

Permalink
feat(rwa): add Ecko wallet functionality (#2884)
Browse files Browse the repository at this point in the history
  • Loading branch information
sstraatemans authored Feb 25, 2025
1 parent 30d9f4b commit 9650313
Show file tree
Hide file tree
Showing 18 changed files with 293 additions and 84 deletions.
4 changes: 2 additions & 2 deletions packages/apps/rwa-demo/src/app/(app)/SideBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ export const SideBar: FC = () => {
setTheme(newTheme);
};

const handleLogout = () => {
logout();
const handleLogout = async () => {
await logout();
router.push('/login');
};

Expand Down
10 changes: 5 additions & 5 deletions packages/apps/rwa-demo/src/app/(loggedout)/login/page.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
'use client';
import { ChainweaverWalletConnect } from '@/components/ChainweaverWalletConnect/ChainweaverWalletConnect';
import { EckoWalletConnect } from '@/components/EckoWalletConnect/EckoWalletConnect';
import { useAccount } from '@/hooks/account';
import { Button, Stack, Text } from '@kadena/kode-ui';
import { CardContentBlock, CardFooterGroup } from '@kadena/kode-ui/patterns';

const Home = () => {
const { login, accounts, selectAccount } = useAccount();
const handleConnect = async () => {
await login();
};
const { accounts, selectAccount } = useAccount();

return (
<>
Expand Down Expand Up @@ -43,7 +42,8 @@ const Home = () => {
</CardContentBlock>
{!accounts && (
<CardFooterGroup>
<Button onPress={handleConnect}>Connect</Button>
<EckoWalletConnect />
<ChainweaverWalletConnect />
</CardFooterGroup>
)}
</>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
'use client';
import { WALLETTYPES } from '@/constants';
import { useGetAccountKDABalance } from '@/hooks/getAccountKDABalance';
import type { IAgentHookProps } from '@/hooks/getAgentRoles';
import { useGetAgentRoles } from '@/hooks/getAgentRoles';
Expand All @@ -8,14 +9,20 @@ import { isComplianceOwner } from '@/services/isComplianceOwner';
import { isFrozen } from '@/services/isFrozen';
import { isInvestor } from '@/services/isInvestor';
import { isOwner } from '@/services/isOwner';

import { getAccountCookieName } from '@/utils/getAccountCookieName';

import { chainweaverAccountLogin } from '@/utils/walletTransformers/chainweaver/login';
import { chainweaverAccountLogout } from '@/utils/walletTransformers/chainweaver/logout';
import { chainweaverSignTx } from '@/utils/walletTransformers/chainweaver/signTx';
import { eckoAccountLogin } from '@/utils/walletTransformers/ecko/login';
import { eckoAccountLogout } from '@/utils/walletTransformers/ecko/logout';
import { eckoSignTx } from '@/utils/walletTransformers/ecko/signTx';
import type { ICommand, IUnsignedCommand } from '@kadena/client';
import { useRouter } from 'next/navigation';
import type { FC, PropsWithChildren } from 'react';
import { createContext, useCallback, useEffect, useState } from 'react';
import type { IWalletAccount } from './AccountType';
import type { IState } from './utils';
import { getWalletConnection } from './utils';

interface IAccountError {
message: string;
Expand All @@ -26,7 +33,7 @@ export interface IAccountContext {
accounts?: IWalletAccount[];
error?: IAccountError;
isMounted: boolean;
login: () => void;
login: (type: keyof typeof WALLETTYPES) => void;
logout: () => void;
sign: (tx: IUnsignedCommand) => Promise<ICommand | undefined>;
isAgent: boolean;
Expand Down Expand Up @@ -122,41 +129,51 @@ export const AccountProvider: FC<PropsWithChildren> = ({ children }) => {
router.replace('/');
};

const login = useCallback(async () => {
const { message, focus, close } = await getWalletConnection();
focus();
const response = await message('CONNECTION_REQUEST', {
name: 'RWA-demo',
});
const login = useCallback(
async (type: keyof typeof WALLETTYPES) => {
let tempAccount;
switch (type) {
case WALLETTYPES.ECKO:
tempAccount = await eckoAccountLogin();
break;
case WALLETTYPES.CHAINWEAVER:
const result = await chainweaverAccountLogin();
if (result.length > 1) {
setAccounts(result);
return;
} else if (result.length === 1) {
tempAccount = result[0];
}
}

if ((response.payload as any).status !== 'accepted') {
return;
}
const { payload } = (await message('GET_STATUS', {
name: 'RWA-demo',
})) as { payload: IState };
if (tempAccount) {
setAccounts(undefined);
setAccount(tempAccount);
localStorage.setItem(
getAccountCookieName(),
JSON.stringify(tempAccount),
);

if (payload.accounts.length > 1) {
setAccounts(payload.accounts);
close();
return;
}
router.replace('/');
}
},

setAccounts(undefined);
setAccount(payload.accounts[0]);
localStorage.setItem(
getAccountCookieName(),
JSON.stringify(payload.accounts[0]),
);
close();
router.replace('/');
}, [router]);
[router],
);

const logout = useCallback(() => {
const logout = useCallback(async () => {
switch (account?.walletType) {
case WALLETTYPES.ECKO:
await eckoAccountLogout();
break;
case WALLETTYPES.CHAINWEAVER:
await chainweaverAccountLogout();
break;
}
localStorage.removeItem(getAccountCookieName());
setAccount(undefined);
router.replace('/');
}, []);
}, [account]);

useEffect(() => {
const storage = localStorage.getItem(getAccountCookieName());
Expand Down Expand Up @@ -205,15 +222,12 @@ export const AccountProvider: FC<PropsWithChildren> = ({ children }) => {
}, [account?.address]);

const sign = async (tx: IUnsignedCommand): Promise<ICommand | undefined> => {
const { message, close } = await getWalletConnection();
const response = await message('SIGN_REQUEST', tx as any);
const payload: {
status: 'signed' | 'rejected';
transaction?: ICommand;
} = response.payload as any;

close();
return payload.transaction;
switch (account?.walletType) {
case WALLETTYPES.ECKO:
return await eckoSignTx(tx);
case WALLETTYPES.CHAINWEAVER:
return await chainweaverSignTx(tx);
}
};

return (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { WALLETTYPES } from '@/constants';
import type { BuiltInPredicate, ChainId } from '@kadena/client';

export type PvString = string;
Expand Down Expand Up @@ -96,11 +97,13 @@ export type Guard =

export interface IWalletAccount {
address: string;
publicKey: string;
guard: Guard;
alias: string;
contract: string;
chains: Array<{ chainId: ChainId; balance: string }>;
overallBalance: string;
walletType: keyof typeof WALLETTYPES;
}

export function isKeysetGuard(guard: Guard): guard is KeysetGuard {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { WALLETTYPES } from '@/constants';
import { useAccount } from '@/hooks/account';
import { Button } from '@kadena/kode-ui';
import type { FC } from 'react';

export const ChainweaverWalletConnect: FC = () => {
const { login } = useAccount();
const handleConnect = async () => {
await login(WALLETTYPES.CHAINWEAVER);
};

return <Button onPress={handleConnect}>Chainweaver Connect</Button>;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { WALLETTYPES } from '@/constants';
import { useAccount } from '@/hooks/account';
import { Button } from '@kadena/kode-ui';
import type { FC } from 'react';
import { useEffect, useState } from 'react';
import type { IECKOWindow } from './eckotypes';

export const EckoWalletConnect: FC = () => {
const [isMounted, setIsMounted] = useState(false);
const { login } = useAccount();
const handleConnect = async () => {
await login(WALLETTYPES.ECKO);
};

useEffect(() => {
const { kadena } = window as IECKOWindow;
const isKadena = Boolean(kadena && kadena.isKadena);
setIsMounted(isKadena);
}, []);

return (
<Button onPress={handleConnect} isDisabled={!isMounted}>
Ecko Connect
</Button>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// ecko has no types, so this kadena object is as found in the ecko repo.
export type IECKOWindow = Window &
typeof globalThis & {
kadena: {
isKadena: boolean;
on: (name: string, callback: () => {}) => Promise<any>;
request: (options: {
method:
| 'kda_connect'
| 'kda_disconnect'
| 'kda_checkStatus'
| 'kda_checkIsConnected'
| 'kda_getNetwork'
| 'kda_getChain'
| 'kda_getSelectedAccount'
| 'kda_requestAccount'
| 'kda_requestSign'
| 'kda_requestQuickSign'
| 'kda_sendKadena';
networkId: string;
data?: any;
}) => Promise<any>;
};
};

export interface IEckoConnectResult {
message: 'string';
status: 'success' | 'fail';
account: {
account: null | string;
publicKey: null | string;
};
}
11 changes: 5 additions & 6 deletions packages/apps/rwa-demo/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,9 @@ export const LOCALSTORAGE_ACCOUNTS = 'accounts';
export const INFINITE_COMPLIANCE = -1;
export const NETWORK_POLLING_RATE = 1000 * 30; /* 30 sec */

// todo: this is temporary for devnet
export const ADMIN = {
account: 'k:a56eb32f7860bfc4e19ec4d4ac0b2dd6c9a96f3c72d607847416253422a2b1b7',
publicKey: 'a56eb32f7860bfc4e19ec4d4ac0b2dd6c9a96f3c72d607847416253422a2b1b7',
};

export const ALLOWEDFILETYPES = () => ['text/csv'];

export const WALLETTYPES = {
CHAINWEAVER: 'CHAINWEAVER',
ECKO: 'ECKO',
} as const;
28 changes: 0 additions & 28 deletions packages/apps/rwa-demo/src/services/paused.ts

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import type { IWalletAccount } from '@/components/AccountProvider/AccountType';
import type { IState } from '@/components/TransactionsProvider/utils';
import { getWalletConnection } from '@/components/TransactionsProvider/utils';
import { WALLETTYPES } from '@/constants';

export const chainweaverAccountLogin = async (): Promise<IWalletAccount[]> => {
const { message, focus, close } = await getWalletConnection();
focus();
const response = await message('CONNECTION_REQUEST', {
name: 'RWA-demo',
});

if ((response.payload as any).status !== 'accepted') {
return [];
}
const { payload } = (await message('GET_STATUS', {
name: 'RWA-demo',
})) as { payload: IState };

close();

return (
payload.accounts.map((account) => ({
...account,
walletType: WALLETTYPES.CHAINWEAVER,
})) ?? []
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import type { IECKOWindow } from '@/components/EckoWalletConnect/eckotypes';
import { env } from '@/utils/env';

export const chainweaverAccountLogout = async (): Promise<void> => {
const { kadena } = window as IECKOWindow;
const isKadena = Boolean(kadena && kadena.isKadena);
if (!isKadena) return;

// eslint-disable-next-line @typescript-eslint/no-floating-promises
kadena.request({
method: 'kda_disconnect',
networkId: env.NETWORKID,
});
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { getWalletConnection } from '@/utils/getWalletConnection/getWalletConnection';
import type { ICommand, IUnsignedCommand } from '@kadena/client';

export const chainweaverSignTx = async (tx: IUnsignedCommand) => {
const { message, close } = await getWalletConnection();
console.log({ tx });
const response = await message('SIGN_REQUEST', tx as any);
const payload: {
status: 'signed' | 'rejected';
transaction?: ICommand;
} = response.payload as any;

close();

return payload.transaction;
};
Loading

0 comments on commit 9650313

Please sign in to comment.