Skip to content

Commit

Permalink
Mahmoud/eng 3111 integrate with stacks raw hex signing on extension (#…
Browse files Browse the repository at this point in the history
…652)

* init tx hex handling

* added stx hex tx signing support

* integrate with connect core utils

* fix stx tx serialization

* handle sign-hex for dapp interactions

* update core version

* add auh to tx-creation

* fix pending txs handling

* fix type issues

* update core version

* revert removing signMultipleTransactions

* update core version

* remove unused package aliases

* Tim/eng 3475 nonce not updating correctly after switching accounts (#725)

* fix: regenerate the unsignedTx with correct stxAddress after switch account

* fix: minor cleanup

* fix tx-request account switching

* updated core version

* added type checking and default values for feeMultipliers

---------

Co-authored-by: Mahmoud Aboelenein <mahmoud@secretkeylabs.com>

---------

Co-authored-by: Tim Man <tim@secretkeylabs.com>
  • Loading branch information
m-aboelenein and teebszet committed Dec 27, 2023
1 parent 4855207 commit 65d33e3
Show file tree
Hide file tree
Showing 28 changed files with 700 additions and 1,867 deletions.
2,077 changes: 401 additions & 1,676 deletions package-lock.json

Large diffs are not rendered by default.

7 changes: 3 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,10 @@
"@ledgerhq/hw-transport-webusb": "^6.27.13",
"@phosphor-icons/react": "^2.0.10",
"@react-spring/web": "^9.6.1",
"@secretkeylabs/xverse-core": "6.0.1",
"@stacks/connect": "^6.10.2",
"@stacks/encryption": "4.3.5",
"@secretkeylabs/xverse-core": "7.0.0",
"@stacks/connect": "7.4.1",
"@stacks/stacks-blockchain-api-types": "6.1.1",
"@stacks/transactions": "4.3.8",
"@stacks/transactions": "6.9.0",
"@tanstack/query-sync-storage-persister": "^4.29.1",
"@tanstack/react-query": "^4.29.3",
"@tanstack/react-query-devtools": "^4.29.3",
Expand Down
22 changes: 13 additions & 9 deletions src/app/components/confirmStxTransactionComponent/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -129,9 +129,10 @@ interface Props {
isAsset?: boolean;
title?: string;
subTitle?: string;
hasSignatures?: boolean;
}

function ConfirmStxTransationComponent({
function ConfirmStxTransactionComponent({
initialStxTransactions,
loading,
isSponsored,
Expand All @@ -142,6 +143,7 @@ function ConfirmStxTransationComponent({
onConfirmClick,
onCancelClick,
skipModal = false,
hasSignatures = false,
}: Props) {
const { t } = useTranslation('translation', { keyPrefix: 'CONFIRM_TRANSACTION' });
const { t: signatureRequestTranslate } = useTranslation('translation', {
Expand Down Expand Up @@ -280,7 +282,7 @@ function ConfirmStxTransationComponent({
try {
const signedTxs = await signLedgerStxTransaction({
transport,
transactionBuffer: initialStxTransactions[0].serialize(),
transactionBuffer: Buffer.from(initialStxTransactions[0].serialize()),
addressIndex: selectedAccount.deviceAccountIndex,
});
setIsTxApproved(true);
Expand Down Expand Up @@ -334,12 +336,14 @@ function ConfirmStxTransationComponent({
{isSponsored ? (
<SponsoredInfoText>{t('SPONSORED_TX_INFO')}</SponsoredInfoText>
) : (
<Button onClick={onAdvancedSettingClick}>
<>
<ButtonImage src={SettingIcon} />
<ButtonText>{t('ADVANCED_SETTING')}</ButtonText>
</>
</Button>
!hasSignatures && (
<Button onClick={onAdvancedSettingClick}>
<>
<ButtonImage src={SettingIcon} />
<ButtonText>{t('ADVANCED_SETTING')}</ButtonText>
</>
</Button>
)
)}
<TransactionSettingAlert
visible={openTransactionSettingModal}
Expand Down Expand Up @@ -407,4 +411,4 @@ function ConfirmStxTransationComponent({
);
}

export default ConfirmStxTransationComponent;
export default ConfirmStxTransactionComponent;
8 changes: 4 additions & 4 deletions src/app/components/postCondition/postConditionView/index.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
/* eslint-disable no-nested-ternary */
import { StoreState } from '@stores/index';
import { useSelector } from 'react-redux';
import {
addressToString,
FungibleConditionCode,
NonFungibleConditionCode,
PostCondition,
} from '@stacks/transactions';
import { StoreState } from '@stores/index';
import { useTranslation } from 'react-i18next';
import { useSelector } from 'react-redux';

import TransferAmountComponent from '@components/transferAmountComponent';
import { getNameFromPostCondition, getSymbolFromPostCondition } from './helper';
Expand Down Expand Up @@ -36,9 +36,9 @@ function PostConditionsView({ postCondition, amount, icon }: Props) {
return t('TRANSFER_LESS');
case FungibleConditionCode.LessEqual:
return t('TRANSFER_LESS_EQUAL');
case NonFungibleConditionCode.DoesNotOwn:
case NonFungibleConditionCode.Sends:
return t('TRANSFER_DOES_NOT_OWN');
case NonFungibleConditionCode.Owns:
case NonFungibleConditionCode.DoesNotSend:
return t('TRANSFER_OWN');
default:
return '';
Expand Down
2 changes: 1 addition & 1 deletion src/app/components/transactionSetting/editNonce.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ function EditNonce({ nonce, setNonce }: Props) {

useEffect(() => {
setNonce(nonceInput);
}, [nonceInput]);
}, [nonceInput, setNonce]);

return (
<NonceContainer>
Expand Down
25 changes: 21 additions & 4 deletions src/app/components/transactionsRequests/ContractCallRequest.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import AccountHeaderComponent from '@components/accountHeader';
import ConfirmStxTransationComponent from '@components/confirmStxTransactionComponent';
import ConfirmStxTransactionComponent from '@components/confirmStxTransactionComponent';
import InfoContainer from '@components/infoContainer';
import FtPostConditionCard from '@components/postCondition/ftPostConditionCard';
import NftPostConditionCard from '@components/postCondition/nftPostConditionCard';
Expand All @@ -11,15 +11,18 @@ import {
addressToString,
Args,
broadcastSignedTransaction,
buf2hex,
Coin,
ContractFunction,
extractFromPayload,
isMultiSig,
} from '@secretkeylabs/xverse-core';
import { ContractCallPayload } from '@stacks/connect';
import {
ClarityType,
cvToJSON,
cvToString,
MultiSigSpendingCondition,
PostConditionType,
SomeCV,
StacksTransaction,
Expand Down Expand Up @@ -85,6 +88,12 @@ export default function ContractCallRequest(props: ContractCallRequestProps) {
const [hasTabClosed, setHasTabClosed] = useState(false);
const { t } = useTranslation('translation');

// SignTransaction Params
const isMultiSigTx = isMultiSig(unsignedTx);
const hasSignatures =
isMultiSigTx &&
(unsignedTx.auth.spendingCondition as MultiSigSpendingCondition).fields?.length > 0;

useOnOriginTabClose(tabId, () => {
setHasTabClosed(true);
window.scrollTo({ top: 0, behavior: 'smooth' });
Expand Down Expand Up @@ -175,7 +184,7 @@ export default function ContractCallRequest(props: ContractCallRequestProps) {
finalizeTxSignature({
requestPayload: requestToken,
tabId,
data: { txId: broadcastResult, txRaw: tx[0].serialize().toString('hex') },
data: { txId: broadcastResult, txRaw: buf2hex(tx[0].serialize()) },
});
navigate('/tx-status', {
state: {
Expand Down Expand Up @@ -208,6 +217,13 @@ export default function ContractCallRequest(props: ContractCallRequestProps) {
browserTx: true,
},
});
} else if (isMultiSigTx) {
finalizeTxSignature({
requestPayload: requestToken,
tabId,
data: { txId: '', txRaw: buf2hex(unsignedTx.serialize()) },
});
window.close();
} else {
broadcastTx(transactions, attachment);
}
Expand Down Expand Up @@ -248,13 +264,14 @@ export default function ContractCallRequest(props: ContractCallRequestProps) {
return (
<>
<AccountHeaderComponent disableMenuOption disableAccountSwitch />
<ConfirmStxTransationComponent
<ConfirmStxTransactionComponent
initialStxTransactions={[unsignedTx]}
onConfirmClick={confirmCallback}
onCancelClick={cancelCallback}
loading={false}
title={request.functionName}
subTitle={`Requested by ${request.appDetails?.name}`}
hasSignatures={hasSignatures}
>
<>
{hasTabClosed && (
Expand All @@ -272,7 +289,7 @@ export default function ContractCallRequest(props: ContractCallRequestProps) {
/>
{functionArgsView()}
</>
</ConfirmStxTransationComponent>
</ConfirmStxTransactionComponent>
</>
);
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import DownloadImage from '@assets/img/webInteractions/ArrowLineDown.svg';
import AccountHeaderComponent from '@components/accountHeader';
import ConfirmStxTransationComponent from '@components/confirmStxTransactionComponent';
import ConfirmStxTransactionComponent from '@components/confirmStxTransactionComponent';
import InfoContainer from '@components/infoContainer';
import StxPostConditionCard from '@components/postCondition/stxPostConditionCard';
import TransactionDetailComponent from '@components/transactionDetailComponent';
import useNetworkSelector from '@hooks/useNetwork';
import useOnOriginTabClose from '@hooks/useOnTabClosed';
import { broadcastSignedTransaction } from '@secretkeylabs/xverse-core';
import { PostCondition, StacksTransaction } from '@stacks/transactions';
import { broadcastSignedTransaction, buf2hex, isMultiSig } from '@secretkeylabs/xverse-core';
import { MultiSigSpendingCondition, PostCondition, StacksTransaction } from '@stacks/transactions';
import { useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useNavigate } from 'react-router-dom';
Expand Down Expand Up @@ -111,6 +111,12 @@ export default function ContractDeployRequest(props: ContractDeployRequestProps)
const [loaderForBroadcastingTx, setLoaderForBroadcastingTx] = useState<boolean>(false);
const navigate = useNavigate();

// SignTransaction Params
const isMultiSigTx = isMultiSig(unsignedTx);
const hasSignatures =
isMultiSigTx &&
(unsignedTx.auth.spendingCondition as MultiSigSpendingCondition).fields?.length > 0;

useOnOriginTabClose(tabId, () => {
setHasTabClosed(true);
window.scrollTo({ top: 0, behavior: 'smooth' });
Expand All @@ -124,7 +130,7 @@ export default function ContractDeployRequest(props: ContractDeployRequestProps)
finalizeTxSignature({
requestPayload: requestToken,
tabId,
data: { txId: broadcastResult, txRaw: tx[0].serialize().toString('hex') },
data: { txId: broadcastResult, txRaw: buf2hex(tx[0].serialize()) },
});
navigate('/tx-status', {
state: {
Expand Down Expand Up @@ -160,6 +166,13 @@ export default function ContractDeployRequest(props: ContractDeployRequestProps)
browserTx: true,
},
});
} else if (isMultiSigTx) {
finalizeTxSignature({
requestPayload: requestToken,
tabId,
data: { txId: '', txRaw: buf2hex(unsignedTx.serialize()) },
});
window.close();
} else {
broadcastTx(txs);
}
Expand Down Expand Up @@ -199,13 +212,14 @@ export default function ContractDeployRequest(props: ContractDeployRequestProps)
return (
<>
<AccountHeaderComponent disableMenuOption disableAccountSwitch />
<ConfirmStxTransationComponent
<ConfirmStxTransactionComponent
initialStxTransactions={[unsignedTx!]}
onConfirmClick={confirmCallback}
onCancelClick={cancelCallback}
loading={loaderForBroadcastingTx}
isSponsored={sponsored}
title={t('DEPLOY_CONTRACT_REQUEST.DEPLOY_CONTRACT')}
hasSignatures={hasSignatures}
>
{hasTabClosed && (
<InfoContainer
Expand Down Expand Up @@ -233,7 +247,7 @@ export default function ContractDeployRequest(props: ContractDeployRequestProps)
</Button>
</DownloadButtonContainer>
</DownloadContainer>
</ConfirmStxTransationComponent>
</ConfirmStxTransactionComponent>
</>
);
}
4 changes: 2 additions & 2 deletions src/app/hooks/queries/useStxPendingTxData.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { fetchStxPendingTxData } from '@secretkeylabs/xverse-core';
import { fetchStxPendingTxData, StxPendingTxData } from '@secretkeylabs/xverse-core';
import { StoreState } from '@stores/index';
import { useQuery } from '@tanstack/react-query';
import { useSelector } from 'react-redux';
Expand All @@ -9,7 +9,7 @@ const useStxPendingTxData = () => {
const selectedNetwork = useNetworkSelector();
const result = useQuery({
queryKey: ['stx-pending-transaction', { stxAddress, selectedNetwork }],
queryFn: () => fetchStxPendingTxData(stxAddress, selectedNetwork),
queryFn: (): Promise<StxPendingTxData> => fetchStxPendingTxData(stxAddress, selectedNetwork),
});
return result;
};
Expand Down
1 change: 1 addition & 0 deletions src/app/hooks/queries/useStxWalletData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { useDispatch } from 'react-redux';
import useNetworkSelector from '../useNetwork';
import useWalletSelector from '../useWalletSelector';

// TODO refactor: no need to put this in store. use this hook instead
export const useStxWalletData = () => {
const dispatch = useDispatch();
const { stxAddress } = useWalletSelector();
Expand Down
41 changes: 41 additions & 0 deletions src/app/hooks/useStxTransactionRequest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { txPayloadToRequest } from '@secretkeylabs/xverse-core';
import { deserializeTransaction } from '@stacks/transactions';
import { decodeToken } from 'jsontokens';
import { useLocation } from 'react-router-dom';

const useStxTransactionRequest = () => {
const { search } = useLocation();
const params = new URLSearchParams(search);
const requestToken = params.get('request') ?? '';
const request = decodeToken(requestToken) as any;
const tabId = params.get('tabId') ?? '0';
const stacksTransaction = request.payload.txHex
? deserializeTransaction(request.payload.txHex!)
: undefined;

const getPayload = () => {
if (stacksTransaction) {
const txPayload = txPayloadToRequest(
stacksTransaction,
request.payload.stxAddress,
request.payload.attachment,
);
return {
...request.payload,
...txPayload,
};
}
return request.payload;
};

const txPayload = getPayload();

return {
payload: txPayload,
stacksTransaction,
tabId,
requestToken,
};
};

export default useStxTransactionRequest;
17 changes: 0 additions & 17 deletions src/app/hooks/useTransationRequest.ts

This file was deleted.

8 changes: 4 additions & 4 deletions src/app/screens/confirmFtTransaction/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ConfirmStxTransactionState, LedgerTransactionType } from '@common/types/ledger';
import ConfirmStxTransationComponent from '@components/confirmStxTransactionComponent';
import ConfirmStxTransactionComponent from '@components/confirmStxTransactionComponent';
import TransferMemoView from '@components/confirmStxTransactionComponent/transferMemoView';
import RecipientComponent from '@components/recipientComponent';
import BottomBar from '@components/tabBar';
Expand Down Expand Up @@ -73,7 +73,7 @@ function ConfirmFtTransaction() {
if (isLedgerAccount(selectedAccount)) {
const type: LedgerTransactionType = 'STX';
const state: ConfirmStxTransactionState = {
unsignedTx: unsignedTx.serialize(),
unsignedTx: Buffer.from(unsignedTx.serialize()),
type,
recipients: [{ address: recepientAddress, amountMicrostacks: new BigNumber(amount) }],
fee: new BigNumber(unsignedTx.auth.spendingCondition.fee.toString()),
Expand All @@ -100,7 +100,7 @@ function ConfirmFtTransaction() {
return (
<>
<TopRow title={t('CONFIRM_TX')} onClick={handleBackButtonClick} />
<ConfirmStxTransationComponent
<ConfirmStxTransactionComponent
initialStxTransactions={[unsignedTx]}
loading={isLoading}
onConfirmClick={handleOnConfirmClick}
Expand All @@ -116,7 +116,7 @@ function ConfirmFtTransaction() {
/>
<TransactionDetailComponent title={t('NETWORK')} value={network.type} />
{memo && <TransferMemoView memo={memo} />}
</ConfirmStxTransationComponent>
</ConfirmStxTransactionComponent>
<BottomBar tab="dashboard" />
</>
);
Expand Down
Loading

0 comments on commit 65d33e3

Please sign in to comment.