diff --git a/src/app/pages/rpc-stx-transfer-stx/rpc-stx-transfer-stx.tsx b/src/app/pages/rpc-stx-transfer-stx/rpc-stx-transfer-stx.tsx
new file mode 100644
index 0000000000..0eea4b7d95
--- /dev/null
+++ b/src/app/pages/rpc-stx-transfer-stx/rpc-stx-transfer-stx.tsx
@@ -0,0 +1,29 @@
+import { isDefined } from '@leather.io/utils';
+
+import { StacksHighFeeWarningContainer } from '@app/features/stacks-high-fee-warning/stacks-high-fee-warning-container';
+import { StacksTransactionSigner } from '@app/features/stacks-transaction-request/stacks-transaction-signer';
+import { useBreakOnNonCompliantEntity } from '@app/query/common/compliance-checker/compliance-checker.query';
+import { useCurrentStacksAccount } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks';
+
+import { useRpcStxTransferStx } from './use-rpc-stx-transfer-stx';
+
+export function RpcStxTransferStx() {
+ const { params, onCancel } = useRpcStxTransferStx();
+
+ // Grab account details from store
+ const currentStacksAccount = useCurrentStacksAccount();
+ useBreakOnNonCompliantEntity([currentStacksAccount?.address, params.recipient].filter(isDefined));
+
+ return (
+
+
+
+ );
+}
diff --git a/src/app/pages/rpc-stx-transfer-stx/use-rpc-stx-transfer-stx.ts b/src/app/pages/rpc-stx-transfer-stx/use-rpc-stx-transfer-stx.ts
new file mode 100644
index 0000000000..779dbc6da9
--- /dev/null
+++ b/src/app/pages/rpc-stx-transfer-stx/use-rpc-stx-transfer-stx.ts
@@ -0,0 +1,86 @@
+import { useMemo } from 'react';
+
+import { makeUnsignedSTXTokenTransfer } from '@stacks/transactions';
+
+import { RpcErrorCode, type StxTransferStxRequestParams } from '@leather.io/rpc';
+
+import { logger } from '@shared/logger';
+import { makeRpcErrorResponse, makeRpcSuccessResponse } from '@shared/rpc/rpc-methods';
+import { closeWindow } from '@shared/utils';
+
+import { useDefaultRequestParams } from '@app/common/hooks/use-default-request-search-params';
+import { initialSearchParams } from '@app/common/initial-search-params';
+import { useStacksBroadcastTransaction } from '@app/features/stacks-transaction-request/hooks/use-stacks-broadcast-transaction';
+
+export function useRpcStxTransferStxParams() {
+ const { origin, tabId } = useDefaultRequestParams();
+ const requestId = initialSearchParams.get('requestId');
+ const params = initialSearchParams.get('params');
+
+ if (!origin || !params || !requestId) throw new Error('Invalid params');
+
+ return useMemo(
+ () => ({
+ origin,
+ tabId: tabId ?? 0,
+ requestId,
+ params: JSON.parse(decodeURIComponent(params)),
+ // params: stxTransferStxRequestParamsSchema.parse(JSON.parse(decodeURIComponent(params))),
+ }),
+ [origin, tabId, requestId, params]
+ );
+}
+
+export function useRpcStxTransferStx() {
+ const { origin, params, requestId, tabId } = useRpcStxTransferStxParams();
+ const { stacksBroadcastTransaction } = useStacksBroadcastTransaction({ token: '' });
+
+ return useMemo(
+ () => ({
+ origin,
+ // TODO: replace with schema infererence
+ params: params as StxTransferStxRequestParams,
+ async onSignStacksTransaction(fee: number, nonce: number) {
+ const tx = await makeUnsignedSTXTokenTransfer({
+ recipient: params.recipient,
+ amount: params.amount,
+ memo: params.memo,
+ publicKey: params.publicKey,
+ });
+
+ if (!tx) return logger.error('No stacks transaction to sign');
+
+ tx.setFee(fee);
+ tx.setNonce(nonce);
+
+ const result = await stacksBroadcastTransaction(tx);
+ if (!result) throw new Error('Error broadcasting stacks transaction');
+
+ chrome.tabs.sendMessage(
+ tabId,
+ makeRpcSuccessResponse('stx_transferStx', {
+ id: requestId,
+ result: {
+ txid: result.txid,
+ transaction: result.transaction.serialize(),
+ } as any,
+ })
+ );
+ closeWindow();
+ },
+ onCancel() {
+ chrome.tabs.sendMessage(
+ tabId,
+ makeRpcErrorResponse('stx_transferStx', {
+ id: requestId,
+ error: {
+ message: 'User denied signing stacks transaction',
+ code: RpcErrorCode.USER_REJECTION,
+ },
+ })
+ );
+ },
+ }),
+ [origin, params, requestId, stacksBroadcastTransaction, tabId]
+ );
+}
diff --git a/src/app/routes/rpc-routes.tsx b/src/app/routes/rpc-routes.tsx
index d5dde60ddb..5b0cfb653a 100644
--- a/src/app/routes/rpc-routes.tsx
+++ b/src/app/routes/rpc-routes.tsx
@@ -14,6 +14,7 @@ import { RpcSignPsbtSummary } from '@app/pages/rpc-sign-psbt/rpc-sign-psbt-summa
import { RpcStacksMessageSigning } from '@app/pages/rpc-sign-stacks-message/rpc-sign-stacks-message';
import { RpcSignStacksTransaction } from '@app/pages/rpc-sign-stacks-transaction/rpc-sign-stacks-transaction';
import { RpcStxCallContract } from '@app/pages/rpc-stx-call-contract/rpc-stx-call-contract';
+import { RpcStxTransferStx } from '@app/pages/rpc-stx-transfer-stx/rpc-stx-transfer-stx';
import { AccountGate } from '@app/routes/account-gate';
import { SuspenseLoadingSpinner } from './app-routes';
@@ -96,5 +97,17 @@ export const rpcRequestRoutes = (
{ledgerStacksTxSigningRoutes}
} />
+
+
+
+
+ }
+ >
+ {ledgerStacksTxSigningRoutes}
+ } />
+
>
);
diff --git a/src/background/messaging/rpc-message-handler.ts b/src/background/messaging/rpc-message-handler.ts
index 99e8361028..928859019e 100644
--- a/src/background/messaging/rpc-message-handler.ts
+++ b/src/background/messaging/rpc-message-handler.ts
@@ -1,4 +1,4 @@
-import { RpcErrorCode } from '@leather.io/rpc';
+import { RpcErrorCode, stxTransferStxMethodName } from '@leather.io/rpc';
import { WalletRequests, makeRpcErrorResponse } from '@shared/rpc/rpc-methods';
@@ -18,11 +18,14 @@ import {
} from './rpc-methods/sign-stacks-message';
import { rpcStxCallContract } from './rpc-methods/stx-call-contract';
import { rpcStxGetAddresses } from './rpc-methods/stx-get-addresses';
+import { rpcStxTransferStx } from './rpc-methods/stx-transfer-stx';
import { rpcSupportedMethods } from './rpc-methods/supported-methods';
export async function rpcMessageHandler(message: WalletRequests, port: chrome.runtime.Port) {
listenForOriginTabClose({ tabId: port.sender?.tab?.id });
+ console.log(message);
+
switch (message.method) {
case 'open': {
await rpcOpen(message, port);
@@ -82,10 +85,15 @@ export async function rpcMessageHandler(message: WalletRequests, port: chrome.ru
break;
}
+ case stxTransferStxMethodName: {
+ await rpcStxTransferStx(message, port);
+ break;
+ }
+
default:
chrome.tabs.sendMessage(
getTabIdFromPort(port),
- makeRpcErrorResponse('' as any, {
+ makeRpcErrorResponse(message.method, {
id: message.id,
error: {
code: RpcErrorCode.METHOD_NOT_FOUND,
diff --git a/src/background/messaging/rpc-methods/stx-transfer-stx.ts b/src/background/messaging/rpc-methods/stx-transfer-stx.ts
new file mode 100644
index 0000000000..00efcbb4ba
--- /dev/null
+++ b/src/background/messaging/rpc-methods/stx-transfer-stx.ts
@@ -0,0 +1,34 @@
+import { RpcErrorCode, type StxTransferStxRequest } from '@leather.io/rpc';
+
+import { RouteUrls } from '@shared/route-urls';
+import { makeRpcErrorResponse } from '@shared/rpc/rpc-methods';
+
+import {
+ type RequestParams,
+ listenForPopupClose,
+ makeSearchParamsWithDefaults,
+ triggerRequestWindowOpen,
+} from '../messaging-utils';
+
+export async function rpcStxTransferStx(message: StxTransferStxRequest, port: chrome.runtime.Port) {
+ const requestParams: RequestParams = [
+ ['params', encodeURIComponent(JSON.stringify(message.params))],
+ ['requestId', message.id],
+ ];
+
+ const { urlParams, tabId } = makeSearchParamsWithDefaults(port, requestParams);
+
+ const { id } = await triggerRequestWindowOpen(RouteUrls.RpcStxTransferStx, urlParams);
+
+ listenForPopupClose({
+ tabId,
+ id,
+ response: makeRpcErrorResponse('stx_transferStx', {
+ id: message.id,
+ error: {
+ code: RpcErrorCode.USER_REJECTION,
+ message: 'User rejected the Stacks transaction signing request',
+ },
+ }),
+ });
+}
diff --git a/src/background/monitors/address-monitors/bitcoin-transaction-monitor.ts b/src/background/monitors/address-monitors/bitcoin-transaction-monitor.ts
index 0f6323c8cc..c802be1678 100644
--- a/src/background/monitors/address-monitors/bitcoin-transaction-monitor.ts
+++ b/src/background/monitors/address-monitors/bitcoin-transaction-monitor.ts
@@ -131,8 +131,6 @@ export function createBitcoinTransactionMonitor(addresses: MonitoredAddress[]):
await handleTransactionMessage(message);
} else if (message['conversions']) {
_btcPriceUsd = readMempooWsBtcPriceUsd(message['conversions']);
- } else {
- logger.debug('Unrecognized Message Type: ', event.data);
}
}
diff --git a/src/shared/route-urls.ts b/src/shared/route-urls.ts
index 61453389bc..69515ff6e5 100644
--- a/src/shared/route-urls.ts
+++ b/src/shared/route-urls.ts
@@ -107,4 +107,5 @@ export enum RouteUrls {
// Request routes stacks
RpcSignStacksTransaction = '/sign-stacks-transaction',
RpcStxCallContract = '/stx-call-contract',
+ RpcStxTransferStx = '/stx-transfer-stx',
}
diff --git a/src/shared/rpc/rpc-methods.ts b/src/shared/rpc/rpc-methods.ts
index f37f55ad0d..7040775c4c 100644
--- a/src/shared/rpc/rpc-methods.ts
+++ b/src/shared/rpc/rpc-methods.ts
@@ -24,3 +24,8 @@ export function makeRpcErrorResponse(
) {
return { jsonrpc: '2.0', ...response } as LeatherRpcMethodMap[T]['response'];
}
+
+// makeRpcSuccessResponse('stx_transferStx', {
+// id: '123',
+// result: {},
+// });