Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for Unstoppable domains #2953

Draft
wants to merge 2 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@
"@cardano-foundation/ledgerjs-hw-app-cardano": "5.0.0",
"@iohk-jormungandr/wallet-js": "0.5.0-pre7",
"@ledgerhq/hw-transport-node-hid": "5.51.1",
"@unstoppabledomains/resolution": "^7.1.4",
"aes-js": "3.1.2",
"bech32": "2.0.0",
"bignumber.js": "9.0.1",
Expand Down
5 changes: 4 additions & 1 deletion source/common/types/address-introspection.types.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { Domain } from 'domain';

export type IntrospectAddressRequest = {
input: string;
};
export type AddressStyle = 'Byron' | 'Icarus' | 'Shelley';
export type AddressStyle = 'Byron' | 'Icarus' | 'Shelley' | DomainAddress;
export type AddressType = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 14 | 15;
export type ChainPointer = {
slot_num: number;
Expand All @@ -18,6 +20,7 @@ export type ByronAddress = AddressBase & {
address_root: string;
derivation_path: string;
};
export type DomainAddress = 'ENS' | 'UNS';
export type IcarusAddress = AddressBase & {
address_root: string;
};
Expand Down
59 changes: 59 additions & 0 deletions source/common/utils/unsResolution.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { Resolution, ResolutionError } from '@unstoppabledomains/resolution';
import { defineMessages } from 'react-intl';
// import globalMessages from '../../renderer/app/i18n/global-messages';

const messages = defineMessages({
unsupported: {
id: 'wallet.transaction.send.domain.unsupported',
defaultMessage: '!!!Unsupported Domain',
description: 'Unsupported Domain Error',
},
notFound: {
id: 'wallet.transaction.send.domain.recordNotFound',
defaultMessage: '!!!No Cardano record found for this domain',
description: 'Not Found domain error',
},
unregistered: {
id: 'wallet.transaction.send.domain.unregistered',
defaultMessage: '!!!Domain not registered',
description: 'Domain not registered error',
},
networkLabel: {
id: 'wallet.receive.pdf.networkLabel',
defaultMessage: '!!!Cardano Network:',
description: 'PDF networkLabel',
},
});

// enum ErrorMessage {
// NOT_FOUND = 'domainNotFound',
// UNREGISTED = 'domainUnregistered',
// UNSUPPORTED = 'domainUnsupported',
// }

export const resolveUNSAddress = async (unsAddress: string, intl: any) => {
try {
return await new Resolution().addr(unsAddress, 'ADA');
} catch (error) {
switch (error.code) {
case 'UnsupportedDomain':
throw new Error(
intl.formatMessage(messages.wallet.transaction.send.domain.invalid)
);
case 'RecordNotFound':
throw new Error(
intl.formatMessage(
messages.wallet.transaction.send.domain.recordNotFound
)
);
case 'UnregisteredDomain':
throw new Error(
intl.formatMessage(
messages.wallet.transaction.send.domain.unregistered
)
);
default:
throw new Error('Error resolving domain');
}
}
};
2 changes: 2 additions & 0 deletions source/main/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ module.exports = {
new webpack.DefinePlugin(
Object.assign(
{
devContentSecurityPolicy:
"connect-src 'self' ws://localhost:3000/cpp 'unsafe-eval'",
'process.env.API_VERSION': JSON.stringify(
process.env.API_VERSION || 'dev'
),
Expand Down
26 changes: 26 additions & 0 deletions source/main/windows/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ type WindowOptionsType = {
width: number;
height: number;
webPreferences: {
webSecurity: boolean;
nodeIntegration: boolean;
webviewTag: boolean;
enableRemoteModule: boolean;
Expand All @@ -45,6 +46,7 @@ export const createMainWindow = (locale: string, windowBounds?: Rectangle) => {
height: 870,
...windowBounds,
webPreferences: {
webSecurity: false,
nodeIntegration: isTest,
webviewTag: false,
// @ts-ignore ts-migrate(2322) FIXME: Type '{ nodeIntegration: boolean; webviewTag: fals... Remove this comment to see the full error message
Expand All @@ -62,6 +64,30 @@ export const createMainWindow = (locale: string, windowBounds?: Rectangle) => {

// Construct new BrowserWindow
const window = new BrowserWindow(windowOptions);

window.webContents.session.webRequest.onBeforeSendHeaders(
(details, callback) => {
callback({ requestHeaders: { Origin: '*', ...details.requestHeaders } });
}
);

window.webContents.session.webRequest.onHeadersReceived(
(details, callback) => {
callback({
responseHeaders: {
'Content-Security-Policy':
"connect-src 'https://mainnet.infura.io' 'https://polygon-mainnet.infura.io' 'unsafe-eval';",
'Access-Control-Allow-Origin': [
'https://mainnet.infura.io',
'https://polygon-mainnet.infura.io',
'*',
],
...details.responseHeaders,
},
});
}
);

rendererErrorHandler.setup(window, createMainWindow);
const { minWindowsWidth, minWindowsHeight } = getContentMinimumSize(window);
window.setMinimumSize(minWindowsWidth, minWindowsHeight);
Expand Down
12 changes: 11 additions & 1 deletion source/renderer/app/components/wallet/WalletSendForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,13 +65,15 @@ type Props = {
selectedAsset: Asset | null | undefined;
isLoadingAssets: boolean;
isDialogOpen: (...args: Array<any>) => any;
isDomainAddress: (address: string) => boolean;
isRestoreActive: boolean;
isHardwareWallet: boolean;
hwDeviceStatus: HwDeviceStatus;
onSubmit: (...args: Array<any>) => any;
onUnsetActiveAsset: (...args: Array<any>) => any;
onExternalLinkClick: (...args: Array<any>) => any;
isAddressFromSameWallet: boolean;
resolveDomain: (value: string) => string;
tokenFavorites: Record<string, boolean>;
walletName: string;
onTokenPickerDialogOpen: (...args: Array<any>) => any;
Expand Down Expand Up @@ -335,7 +337,7 @@ class WalletSendForm extends Component<Props, State> {
value: '',
validators: [
async ({ field, form }) => {
const { value } = field;
let { value } = field;

if (value === null || value === '') {
this.resetTransactionFee();
Expand All @@ -346,6 +348,14 @@ class WalletSendForm extends Component<Props, State> {
];
}

if (value.split('.').length > 1) {
try {
value = await this.props.resolveDomain(value);
} catch (error) {
return [false, error.message];
}
}

const isValid = await this.props.addressValidator(value);

if (isValid && this.isAddressFromSameWallet()) {
Expand Down
20 changes: 20 additions & 0 deletions source/renderer/app/components/wallet/send-form/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,26 @@ export default defineMessages({
defaultMessage: '!!!Ada',
description: 'Label for the "Ada" input in the wallet send form.',
},
domainUnsupported: {
id: 'wallet.send.form.unsupported',
defaultMessage: '!!!Unsupported Domain',
description: 'Unsupported Domain Error',
},
domainNotFound: {
id: 'wallet.send.form.recordNotFound',
defaultMessage: '!!!No Cardano record found for this domain',
description: 'Not Found domain error',
},
domainUnregistered: {
id: 'wallet.send.form.unregistered',
defaultMessage: '!!!Domaon not registered',
description: 'Domain not registered error',
},
networkLabel: {
id: 'wallet.receive.pdf.networkLabel',
defaultMessage: '!!!Cardano Network:',
description: 'PDF networkLabel',
},
removeLabel: {
id: 'wallet.send.form.button.removeLabel',
defaultMessage: '!!!Remove',
Expand Down
9 changes: 8 additions & 1 deletion source/renderer/app/containers/wallet/WalletSendPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,12 @@ class WalletSendPage extends Component<Props> {
hardwareWallets,
assets: assetsStore,
} = stores;
const { isValidAddress, isAddressFromSameWallet } = wallets;
const {
isValidAddress,
isAddressFromSameWallet,
isDomainAddress,
resolveDomain,
} = wallets;
const { validateAmount, validateAssetAmount } = transactions;
const { hwDeviceStatus } = hardwareWallets;
const hasAssetsEnabled = WALLET_ASSETS_ENABLED;
Expand Down Expand Up @@ -154,13 +159,15 @@ class WalletSendPage extends Component<Props> {
selectedAsset={selectedAsset}
isLoadingAssets={isLoadingAssets}
isDialogOpen={uiDialogs.isOpen}
isDomainAddress={isDomainAddress}
isRestoreActive={wallet.isRestoring}
isHardwareWallet={isHardwareWallet}
hwDeviceStatus={hwDeviceStatus}
onSubmit={() => this.submit(isHardwareWallet, wallet.id)}
onUnsetActiveAsset={unsetActiveAsset.trigger}
onExternalLinkClick={app.openExternalLink}
isAddressFromSameWallet={isAddressFromSameWallet}
resolveDomain={resolveDomain}
tokenFavorites={favorites}
walletName={walletName}
onTokenPickerDialogOpen={this.openTokenPickerDialog}
Expand Down
4 changes: 4 additions & 0 deletions source/renderer/app/i18n/locales/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -1243,6 +1243,10 @@
"wallet.transaction.receiverLabel": "Receiver",
"wallet.transaction.rewards.from": "From rewards",
"wallet.transaction.sent": "{transactionsType} sent",
"wallet.transaction.send.domain.invalid": "Invalid Domain",
"wallet.transaction.send.domain.recordNotFound": "No Cardano record found for this domain",
"wallet.transaction.send.domain.unregistered": "Domain is not registered",
"wallet.transaction.send.domain.unsupported": "Domain is not supported",
"wallet.transaction.state.confirmed": "Transaction confirmed",
"wallet.transaction.state.confirmedHeading": "Confirmed",
"wallet.transaction.state.failed": "Transaction failed",
Expand Down
22 changes: 21 additions & 1 deletion source/renderer/app/stores/WalletsStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { logger } from '../utils/logging';
import { ROUTES } from '../routes-config';
import { formattedWalletAmount } from '../utils/formatters';
import { ellipsis } from '../utils/strings';
import { resolveUNSAddress } from '../../../common/utils/unsResolution';
import {
bech32EncodePublicKey,
isReceiverAddressType,
Expand Down Expand Up @@ -1138,10 +1139,21 @@ export default class WalletsStore extends Store {
}
});
};

isDomainAddress = (address: string): Boolean => {
return address.split('.').length > 0;
};

isValidAddress = async (address: string) => {
const { network } = this.environment;
const expectedNetworkTag = get(NetworkMagics, [network]);
const validAddressStyles: AddressStyle[] = ['Byron', 'Icarus', 'Shelley'];
const validAddressStyles: AddressStyle[] = [
'Byron',
'Icarus',
'Shelley',
'ENS',
'UNS',
];
this.isAddressFromSameWallet = false;

if (!expectedNetworkTag) {
Expand Down Expand Up @@ -1241,6 +1253,14 @@ export default class WalletsStore extends Store {
this.stores.transactions.transactionsRequests = [];
this.isAddressFromSameWallet = false;
};

@action
resolveDomain = async (address: string) => {
const { currentLocale } = this.stores.profile;
const intl = i18nContext(currentLocale);
return (address = await resolveUNSAddress(address, intl));
};

@action
_importWalletFromFile = async (params: WalletImportFromFileParams) => {
const { filePath, walletName, spendingPassword } = params;
Expand Down
Loading