diff --git a/src/components/Settings.tsx b/src/components/Settings.tsx index 8c17edf..fa8e87e 100644 --- a/src/components/Settings.tsx +++ b/src/components/Settings.tsx @@ -93,19 +93,18 @@ const Settings = (props: { accountId: string }) => { return; } - await createMCTransactionDB(txn.toXDR(), jwt); + const response = await createMCTransactionDB(txn.toXDR(), jwt); - addTxnNotification({ - title: 'Success', - message: 'Add a signer transaction has been submitted', - type: TxnResponse.Success, - timestamp: Date.now(), - }); + if (response?.id != null) { + addTxnNotification({ + title: 'Success', + message: 'Change threshold transaction has been submitted', + type: TxnResponse.Success, + timestamp: Date.now(), + }); + } } catch (err) { handleErrors('Error in changing threshold', err); - useLoadingModal.setAction({ - type: 'CLOSE', - }); } finally { useLoadingModal.setAction({ type: 'CLOSE', @@ -126,18 +125,19 @@ const Settings = (props: { accountId: string }) => { if (!jwt) { return; } - await createMCTransactionDB(txn.toXDR(), jwt); - addTxnNotification({ - title: 'Success', - message: 'Add a signer transaction has been submitted', - type: TxnResponse.Success, - timestamp: Date.now(), - }); + + const response = await createMCTransactionDB(txn.toXDR(), jwt); + + if (response?.id != null) { + addTxnNotification({ + title: 'Success', + message: 'Add a signer transaction has been submitted', + type: TxnResponse.Success, + timestamp: Date.now(), + }); + } } catch (err) { handleErrors('Error in adding signer', err); - useLoadingModal.setAction({ - type: 'CLOSE', - }); } finally { useLoadingModal.setAction({ type: 'CLOSE', @@ -161,13 +161,20 @@ const Settings = (props: { accountId: string }) => { if (!jwt) { return; } - await createMCTransactionDB(txn.toXDR(), jwt); - useLoadingModal.setAction({ - type: 'CLOSE', - }); + const response = await createMCTransactionDB(txn.toXDR(), jwt); + + if (response?.id != null) { + addTxnNotification({ + title: 'Success', + message: 'Remove a signer transaction has been submitted', + type: TxnResponse.Success, + timestamp: Date.now(), + }); + } } catch (err) { handleErrors('Error in removing signer', err); + } finally { useLoadingModal.setAction({ type: 'CLOSE', }); @@ -247,7 +254,7 @@ const Settings = (props: { accountId: string }) => { })}> { - handleChangeThreshold(data?.threshold); + handleChangeThreshold(data); }}> = { [MultiSigTransactionStatus.Executed]: 3, }; +const StatusBadgeMap: Record = { + [MultiSigTransactionStatus.Executable]: 'Active', + [MultiSigTransactionStatus.Pending]: 'Pending', + [MultiSigTransactionStatus.Executed]: 'Approved', + [MultiSigTransactionStatus.Rejected]: 'Cancelled', +}; + const Transactions = ({ address }: ITransactionsProps) => { const [jwt] = useMCStore((s) => [s.jwt]); const { getJwtToken } = useMC(); @@ -68,7 +75,6 @@ const Transactions = ({ address }: ITransactionsProps) => { offset: Math.max(pagination.offset - 1, 0), limit: 5, search: debouncedSearchTerm, - multicliqueAccountAddress: `${address}`, }, jwt ); @@ -121,70 +127,78 @@ const Transactions = ({ address }: ITransactionsProps) => { {!listTransactions.pending && listTransactions.fulfilled && !listTransactions.value?.results?.length && } - {listTransactions?.value?.results?.map((item, index) => { - return ( - - setActiveAccordion(activeAccordion === index ? null : index) - } - color='base' - expanded={index === activeAccordion}> - -
{item.callFunc}
-
- {dayjs(item.createdAt).format('MMMM D, YYYY - h:mm:ss A')} -
- - -
- -
-
-
Call hash:
-
- {/* TODO: update hash */} - {truncateMiddle(item.preimageHash, 16, 3)} + {!listTransactions.pending && + listTransactions?.value?.results?.map((item, index) => { + return ( + + setActiveAccordion(activeAccordion === index ? null : index) + } + color='base' + expanded={index === activeAccordion}> + +
{item.callFunc}
+
+ {dayjs(item.createdAt).format('MMMM D, YYYY - h:mm:ss A')} +
+ + +
+ +
+
+
Call hash:
+
+ {item.preimageHash} +
+
+ {/* TODO: update hash */} + {truncateMiddle(item.preimageHash, 16, 3)} +
+ + +
- - -
-
-
- - {['Created', 'Approved', 'Executed'].map( - (step, stepIndex) => ( - - {step} - - ) - )} - -
Can be executed once threshold is reached
-
- - - {/* TODO add execute button */} +
+ + {['Created', 'Approved', 'Executed'].map( + (step, stepIndex) => ( + + {step} + + ) + )} + +
Can be executed once threshold is reached
+
+ + + {/* TODO add execute button */} +
-
- - - ); - })} + + + ); + })}
{!listTransactions.pending && Boolean(listTransactions.value?.results?.length) && ( diff --git a/src/hooks/useMC.ts b/src/hooks/useMC.ts index 582e272..496ad0b 100644 --- a/src/hooks/useMC.ts +++ b/src/hooks/useMC.ts @@ -16,6 +16,7 @@ import { isValidXDR, numberToU32ScVal, toBase64, + toScValBytes, } from '@/utils'; import { signBlob, signTransaction } from '@stellar/freighter-api'; import * as SorobanClient from 'soroban-client'; @@ -560,7 +561,7 @@ const useMC = () => { currentWalletAccount?.publicKey, coreAddress, 'add_signer', - accountToScVal(signerAddress) + toScValBytes(signerAddress) ); return txn; }; @@ -576,7 +577,7 @@ const useMC = () => { currentWalletAccount?.publicKey, coreAddress, 'remove_signer', - accountToScVal(signerAddress) + toScValBytes(signerAddress) ); return txn; }; @@ -618,6 +619,11 @@ const useMC = () => { }, jwtToken ); + + if (!mcTxnRes?.preimageHash) { + throw new Error('Error creating a Multiclique Transaction'); + } + const signedHash = await signBlob(mcTxnRes.preimageHash, { accountToSign: currentWalletAccount?.publicKey, }); @@ -631,7 +637,7 @@ const useMC = () => { name: currentWalletAccount.publicKey, address: currentWalletAccount.publicKey, }, - signature: signedHash, + signature: toBase64(signedHash), }, ], }, diff --git a/src/services/transaction.ts b/src/services/transaction.ts index eb4b3ce..e419524 100644 --- a/src/services/transaction.ts +++ b/src/services/transaction.ts @@ -130,7 +130,7 @@ export const patchMultiCliqueTransaction = async ( const body = JSON.stringify(keysToSnakeCase(payload)); const response = await fetch( - `${SERVICE_URL}/multiclique/transactions/${id}`, + `${SERVICE_URL}/multiclique/transactions/${id}/`, { method: 'PATCH', body, diff --git a/src/types/multiCliqueAccount.ts b/src/types/multiCliqueAccount.ts index 4016b08..a540a98 100644 --- a/src/types/multiCliqueAccount.ts +++ b/src/types/multiCliqueAccount.ts @@ -24,3 +24,8 @@ export type MultiCliquePolicy = { name: string; active: boolean; }; + +export interface Signature { + signature: string; + signatory: Signatory; +} diff --git a/src/types/multisigTransaction.ts b/src/types/multisigTransaction.ts index f884f80..dc8c905 100644 --- a/src/types/multisigTransaction.ts +++ b/src/types/multisigTransaction.ts @@ -1,5 +1,5 @@ import type { CamelCaseObject } from '@/utils/transformer'; -import type { Signatory } from './multiCliqueAccount'; +import type { Signatory, Signature } from './multiCliqueAccount'; export enum MultiSigTransactionStatus { Pending = 'PENDING', @@ -14,8 +14,8 @@ export interface RawMultisigTransaction { preimage_hash: string; call_func: string; call_args: Record; - approvals: { signature: string; signatory: Signatory }[]; - rejections: { signature: string; signatory: Signatory }[]; + approvals?: Signature[]; + rejections?: Signature[]; status: MultiSigTransactionStatus; executed_at: string; created_at: string; diff --git a/src/utils/index.ts b/src/utils/index.ts index 218335d..79e0c13 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -1,4 +1,4 @@ -import { DAO_UNITS, XLM_UNITS } from '@/config'; +import { DAO_UNITS,XLM_UNITS } from '@/config'; import BigNumber from 'bignumber.js'; import * as SorobanClient from 'soroban-client'; // @ts-ignore @@ -97,6 +97,9 @@ export const toBase64 = (str: string): string => { export const accountToScVal = (account: string) => new SorobanClient.Address(account).toScVal(); +export const toScValBytes = (value: string) => + SorobanClient.xdr.ScVal.scvBytes(new SorobanClient.Address(value).toBuffer()); + export const isValidXDR = (xdrString: string, networkParaphrase: string) => { try { SorobanClient.TransactionBuilder.fromXDR(xdrString, networkParaphrase);