Skip to content

Commit

Permalink
feat(suite): nostr send flow
Browse files Browse the repository at this point in the history
  • Loading branch information
martykan committed Dec 12, 2024
1 parent eb27ce5 commit b9db206
Show file tree
Hide file tree
Showing 8 changed files with 72 additions and 27 deletions.
4 changes: 2 additions & 2 deletions packages/connect-nostr/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,9 +152,9 @@ export class NostrClient extends PeerToPeerCommunicationClient<PeerToPeerCommuni

await this.relay.publish(signedEvent);

await promise;
const response = await promise;

return { success: true as const, response: promise };
return { success: true as const, response };
}

subscribe({ recipientPubkey }: { recipientPubkey: string }) {
Expand Down
13 changes: 9 additions & 4 deletions packages/suite/src/actions/suite/protocolActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,18 +26,23 @@ export const fillSendForm = (shouldFill: boolean): ProtocolAction => ({
payload: shouldFill,
});

const saveCoinProtocol = (scheme: Protocol, address: string, amount?: number): ProtocolAction => ({
const saveCoinProtocol = (
scheme: Protocol,
address: string,
amount?: number,
signature?: string,
): ProtocolAction => ({
type: PROTOCOL.SAVE_COIN_PROTOCOL,
payload: { scheme, address, amount },
payload: { scheme, address, amount, signature },
});

export const handleProtocolRequest = (uri: string) => (dispatch: Dispatch) => {
const protocol = getProtocolInfo(uri);

if (protocol && !('error' in protocol) && getNetworkSymbolForProtocol(protocol.scheme)) {
const { scheme, amount, address } = protocol as CoinProtocolInfo;
const { scheme, amount, address, signature } = protocol as CoinProtocolInfo;

dispatch(saveCoinProtocol(scheme, address, amount));
dispatch(saveCoinProtocol(scheme, address, amount, signature));
dispatch(
notificationsActions.addToast({
type: 'coin-scheme-protocol',
Expand Down
2 changes: 2 additions & 0 deletions packages/suite/src/reducers/suite/protocolReducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export interface SendFormState {
scheme: Protocol;
address: string;
amount?: number;
signature?: string;
}

type Autofill<T> = Partial<T> & {
Expand All @@ -33,6 +34,7 @@ const protocolReducer = (state: State = initialState, action: Action): State =>
draft.sendForm.address = action.payload.address;
draft.sendForm.scheme = action.payload.scheme;
draft.sendForm.amount = action.payload.amount;
draft.sendForm.signature = action.payload.signature;
draft.sendForm.shouldFill = false;
break;
case PROTOCOL.RESET:
Expand Down
4 changes: 4 additions & 0 deletions packages/suite/src/utils/suite/protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export type CoinProtocolInfo = {
scheme: Protocol;
address: string;
amount?: number;
signature?: string;
};

const removeLeadingTrailingSlashes = (text: string) => text.replace(/^\/{0,2}|\/$/g, '');
Expand Down Expand Up @@ -42,10 +43,13 @@ export const getProtocolInfo = (
const address =
removeLeadingTrailingSlashes(pathname) || removeLeadingTrailingSlashes(host);

const { signature } = params;

return {
scheme,
address,
amount,
signature,
};
}

Expand Down
11 changes: 9 additions & 2 deletions packages/suite/src/views/contacts/AddNewContactModal.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { useState } from 'react';

import { Column, Input, NewModal, Textarea } from '@trezor/components';
import { Checkbox, Column, Input, NewModal, Textarea } from '@trezor/components';
import { selectDevice } from '@suite-common/wallet-core';
import { spacings } from '@trezor/theme';

Expand All @@ -18,8 +18,9 @@ export const AddNewContactModal = ({ onClose }: AddNewContactModalProps) => {
const [errorMessage, setErrorMessage] = useState<string | null>(null);
const [label, setLabel] = useState('');
const [address, setAddress] = useState('');
const [requestAddress, setRequestAddress] = useState(false);

const addContact = useAddContact(onClose, label, address);
const addContact = useAddContact(onClose, label, address, requestAddress);
const onAddContact = async () => {
const { error } = await addContact();
setErrorMessage(error);
Expand Down Expand Up @@ -67,6 +68,12 @@ export const AddNewContactModal = ({ onClose }: AddNewContactModalProps) => {
label="Recipient's address or public key"
onChange={event => setAddress(event.target.value)}
/>
<Checkbox
isChecked={requestAddress}
onClick={() => setRequestAddress(!requestAddress)}
>
Request address from recipient
</Checkbox>
</Column>
</NewModal>
);
Expand Down
11 changes: 10 additions & 1 deletion packages/suite/src/views/contacts/ContactList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,21 @@ import { Contact } from '@suite-common/contacts';
import { Button, Card, Dropdown, Table, Text } from '@trezor/components';
import { spacings } from '@trezor/theme';

import { useDispatch } from 'src/hooks/suite';
import { handleProtocolRequest } from 'src/actions/suite/protocolActions';

const ContactItem = ({
contact: { address, label, receiveAddresses },
remove,
}: {
contact: Contact;
remove: () => void;
}) => {
const dispatch = useDispatch();
const prefillAddress = (ra: { address: string; signature: string }) => {
dispatch(handleProtocolRequest('test:' + ra.address + '?signature=' + ra.signature));
};

return (
<Table.Row>
<Table.Cell>
Expand All @@ -22,7 +30,8 @@ const ContactItem = ({
<Table.Cell>
<Text variant="tertiary" typographyStyle="hint" overflowWrap="anywhere">
{receiveAddresses?.map(ra => (
<a href={'bitcoin:' + ra.address} key={ra.address}>
/* eslint-disable-next-line jsx-a11y/anchor-is-valid */
<a href="#" onClick={() => prefillAddress(ra)} key={ra.address}>
{ra.address}
</a>
))}
Expand Down
42 changes: 28 additions & 14 deletions packages/suite/src/views/contacts/useAddContact.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,12 @@ import * as nostrActions from 'src/actions/suite/nostrActions';

import { useDispatch, useSelector } from '../../hooks/suite';

export const useAddContact = (onCloseModal: () => void, label: string, address: string) => {
export const useAddContact = (
onCloseModal: () => void,
label: string,
address: string,
requestAddress: boolean,
) => {
const dispatch = useDispatch();
const device = useSelector(selectDevice);

Expand Down Expand Up @@ -36,29 +41,38 @@ export const useAddContact = (onCloseModal: () => void, label: string, address:

const { signature } = response.payload;

const newAddress = await dispatch(
nostrActions.request({
kind: 9898,
tags: [['p', address]],
content: JSON.stringify({
type: 'address_request',
}),
}),
);
console.log('newAddress', newAddress);

const contact = {
address,
label,
signature,
deviceState,
receiveAddresses: [
{
address: newAddress.response.address,
signature: newAddress.response.signature,
address: 'a',
signature: 'b',
},
],
};

if (requestAddress) {
const newAddress = await dispatch(
nostrActions.request({
kind: 9898,
tags: [['p', address]],
content: JSON.stringify({
type: 'address_request',
}),
}),
);
console.log('newAddress', newAddress);
contact.receiveAddresses = [
{
address: newAddress.response.payload,
signature: newAddress.response.signature,
},
];
}

dispatch(contactsActions.addContact(contact));

onCloseModal();
Expand Down
12 changes: 8 additions & 4 deletions packages/suite/src/views/settings/SettingsDebug/Nostr.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import { useEffect, useState } from 'react';
import { useState } from 'react';

import TrezorConnect from '@trezor/connect';
import { Input, Button } from '@trezor/components';
import { Event } from '@trezor/connect-nostr';
import { selectDevice } from '@suite-common/wallet-core';

import { ActionColumn, SectionItem, TextColumn } from 'src/components/suite';
import { useSelector, useDispatch } from 'src/hooks/suite';
import * as nostrActions from 'src/actions/suite/nostrActions';

export const Nostr = () => {
const device = useSelector(selectDevice);
const [peerNpub, setPeerNpub] = useState<`npub1${string}` | undefined>(undefined);
const [addressRequestState, setAddressRequestState] = useState<'none' | 'pending' | 'success'>(
'none',
Expand Down Expand Up @@ -37,7 +38,7 @@ export const Nostr = () => {
dispatch(nostrActions.subscribe());
};

const handleDisconnectClick = async () => {
const handleDisconnectClick = () => {
dispatch(nostrActions.dispose());
};

Expand Down Expand Up @@ -73,8 +74,11 @@ export const Nostr = () => {

const handleAddressRequestResponse = async () => {
console.log('handleAddressRequestResponse', events);
if (!device) return;

const addressResponse = await TrezorConnect.getAddress({
device,
useEmptyPassphrase: device.useEmptyPassphrase,
coin: 'test',
path: "m/44'/1'/0'/0/0",
showOnTrezor: false,
Expand Down Expand Up @@ -117,7 +121,7 @@ export const Nostr = () => {
/>

<ActionColumn>
<Button onClick={() => handleAddressRequestResponse(content)}>
<Button onClick={() => handleAddressRequestResponse()}>
Send address back
</Button>
</ActionColumn>
Expand Down

0 comments on commit b9db206

Please sign in to comment.