Skip to content

Commit

Permalink
Sign message (#18)
Browse files Browse the repository at this point in the history
* Add signMessage
  • Loading branch information
arhtudormorar authored Aug 30, 2024
1 parent 9a9f281 commit 69bb844
Show file tree
Hide file tree
Showing 18 changed files with 744 additions and 442 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

- [Added signMessage](https://github.com/multiversx/mx-sdk-dapp-core/pull/18)

## [[0.0.0-alpha.9]](https://github.com/multiversx/mx-sdk-dapp-core)] - 2024-08-29
- [CrossWindow login](https://github.com/multiversx/mx-sdk-dapp-core/pull/13)

## [[v0.0.0-alpha.8]](https://github.com/multiversx/mx-sdk-dapp-core/pull/16) - 2024-08-27
- [Added sdk-web-wallet-cross-window-provider as peer dependency](https://github.com/multiversx/mx-sdk-dapp-core/pull/14)
- [Generic login + ExtensionProvider login](https://github.com/multiversx/mx-sdk-dapp-core/pull/12)
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
"@multiversx/sdk-metamask-provider": "0.0.5",
"@multiversx/sdk-native-auth-client": "^1.0.8",
"@multiversx/sdk-opera-provider": "1.0.0-alpha.1",
"@multiversx/sdk-wallet": "4.5.1",
"@multiversx/sdk-wallet-connect-provider": "4.1.2",
"@multiversx/sdk-web-wallet-provider": "3.2.1",
"@types/lodash": "^4.17.4",
Expand Down
64 changes: 0 additions & 64 deletions src/core/methods/init/init.ts

This file was deleted.

46 changes: 46 additions & 0 deletions src/core/methods/initApp/initApp.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { initStore } from 'store/store';
import { defaultStorageCallback } from 'store/storage';
import { setNativeAuthConfig } from 'store/actions/config/configActions';
import { initializeNetwork } from 'store/actions';
import { NativeAuthConfigType } from 'services/nativeAuth/nativeAuth.types';
import { getDefaultNativeAuthConfig } from 'services/nativeAuth/methods/getDefaultNativeAuthConfig';
import { InitAppType } from './initApp.types';

const defaultInitAppProps = {
storage: {
getStorageCallback: defaultStorageCallback
}
};

/**
* Initializes the dApp with the given configuration.
* @param props - The configuration for the dApp initialization.
*
* @example
* ```ts
initApp({
nativeAuth: true,
environment: EnvironmentsEnum.devnet
});
* ```
* */
export const initApp = async ({
storage = defaultInitAppProps.storage,
dAppConfig
}: InitAppType) => {
initStore(storage.getStorageCallback);

if (dAppConfig?.nativeAuth) {
const nativeAuthConfig: NativeAuthConfigType =
typeof dAppConfig.nativeAuth === 'boolean'
? getDefaultNativeAuthConfig()
: dAppConfig.nativeAuth;

setNativeAuthConfig(nativeAuthConfig);
}

await initializeNetwork({
customNetworkConfig: dAppConfig.network,
environment: dAppConfig.environment
});
};
48 changes: 48 additions & 0 deletions src/core/methods/initApp/initApp.types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { StorageCallback } from 'store/storage';
import { CustomNetworkType } from 'types/network.types';
import { EnvironmentsEnum } from 'types/enums.types';
import { NativeAuthConfigType } from 'services/nativeAuth/nativeAuth.types';

type BaseDappConfigType = {
/**
* The native auth configuration for the dApp.
* If set to `true`, will fallback on default configuration.
* If set to `false`, will disable native auth.
* If set to `NativeAuthConfigType`, will set the native auth configuration.
*/
nativeAuth?: boolean | NativeAuthConfigType;
};

export type EnvironmentDappConfigType = BaseDappConfigType & {
/**
* If passed in, will automatically initialize the network with the given environment.
*/
environment: EnvironmentsEnum;
network?: CustomNetworkType;
};

export type CustomNetworkDappConfigType = BaseDappConfigType & {
/**
* Can override the network configuration, e.g. for sovereign shards.
* Must include `apiAddress` if provided.
*/
network: CustomNetworkType & { apiAddress: string };
environment?: never;
};

export type DappConfigType =
| EnvironmentDappConfigType
| CustomNetworkDappConfigType;

export type InitAppType = {
/**
* The storage configuration for the dApp.
*/
storage?: {
/**
* The callback to get the storage (custom storage).
*/
getStorageCallback: StorageCallback;
};
dAppConfig: DappConfigType;
};
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ import { setLoginToken } from 'store/actions/loginInfo/loginInfoActions';
import { IProvider } from 'core/providers/types/providerFactory.types';
import { loginAction } from 'store/actions';
import { AccountType } from 'types/account.types';
import { getImpersonatedAccountDetails } from './getImpersonatedAccountDetails';
import { getAccountFromToken } from './getAccountFromToken';
import { getLatestNonce } from 'core/methods/account/getLatestNonce';

export async function impersonateAccount({
export async function extractAccountFromToken({
loginToken,
extraInfoData,
address,
Expand All @@ -20,33 +20,33 @@ export async function impersonateAccount({
address: string;
provider: IProvider;
}) {
const impersonationDetails = await getImpersonatedAccountDetails({
const accountDetails = await getAccountFromToken({
originalLoginToken: loginToken,
extraInfoData,
address
});

if (impersonationDetails.modifiedLoginToken) {
setLoginToken(impersonationDetails.modifiedLoginToken);
if (accountDetails.modifiedLoginToken) {
setLoginToken(accountDetails.modifiedLoginToken);
}

if (impersonationDetails.account) {
if (accountDetails.account) {
loginAction({
address: impersonationDetails.address,
address: accountDetails.address,
providerType: provider.getType()
});

const newAccount: AccountType = {
...impersonationDetails.account,
nonce: getLatestNonce(impersonationDetails.account)
...accountDetails.account,
nonce: getLatestNonce(accountDetails.account)
};

setAccount(newAccount);
return {
...impersonationDetails,
...accountDetails,
account: newAccount
};
}

return impersonationDetails;
return accountDetails;
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { getAccount } from 'utils/account/getAccount';
import { getModifiedLoginToken } from './getModifiedLoginToken';

interface GetImpersonatedAccountDetailsType {
interface GetAccountFromTokenType {
address: string;
originalLoginToken?: string;
extraInfoData: {
Expand All @@ -10,11 +10,11 @@ interface GetImpersonatedAccountDetailsType {
};
}

export const getImpersonatedAccountDetails = async ({
export const getAccountFromToken = async ({
originalLoginToken,
extraInfoData,
address
}: GetImpersonatedAccountDetailsType) => {
}: GetAccountFromTokenType) => {
const modifiedLoginToken = await getModifiedLoginToken({
loginToken: originalLoginToken,
extraInfoData
Expand Down
35 changes: 7 additions & 28 deletions src/core/methods/login/login.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { NativeAuthConfigType } from 'services/nativeAuth/nativeAuth.types';
import { getIsLoggedIn } from 'core/methods/account/getIsLoggedIn';
import { getAddress } from 'core/methods/account/getAddress';
import { loginAction, logoutAction } from 'store/actions';
import { impersonateAccount } from './helpers/impersonateAccount';
import { extractAccountFromToken } from './helpers/extractAccountFromToken';
import { SECOND_LOGIN_ATTEMPT_ERROR } from 'constants/errorMessages.constants';
import { getCallbackUrl } from './helpers/getCallbackUrl';

Expand All @@ -38,28 +38,6 @@ async function loginWithoutNativeToken(provider: IProvider) {
};
}

async function tryImpersonateAccount({
loginToken,
extraInfoData,
address,
provider
}: {
loginToken: string;
extraInfoData: {
multisig?: string;
impersonate?: string;
};
address: string;
provider: IProvider;
}) {
return await impersonateAccount({
loginToken,
extraInfoData,
address,
provider
});
}

async function loginWithNativeToken(
provider: IProvider,
nativeAuthConfig: NativeAuthConfigType
Expand Down Expand Up @@ -104,12 +82,13 @@ async function loginWithNativeToken(
signature,
nativeAuthToken
});

loginAction({
address,
providerType: provider.getType()
});

const impersonationDetails = await tryImpersonateAccount({
const accountDetails = await extractAccountFromToken({
loginToken,
extraInfoData: {
multisig: loginResult?.multisig,
Expand All @@ -119,19 +98,19 @@ async function loginWithNativeToken(
provider
});

if (impersonationDetails.account) {
setAccount(impersonationDetails.account);
if (accountDetails.account) {
setAccount(accountDetails.account);
} else {
logoutAction();
console.error('Failed to fetch account');
throw new Error('Failed to fetch account');
}

return {
address: impersonationDetails?.address || address,
address: accountDetails?.address || address,
signature,
nativeAuthToken,
loginToken: impersonationDetails?.modifiedLoginToken || loginToken,
loginToken: accountDetails?.modifiedLoginToken || loginToken,
nativeAuthConfig
};
}
Expand Down
8 changes: 8 additions & 0 deletions src/core/methods/signMessage/getVerifier.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { Address } from '@multiversx/sdk-core/out';
import { UserPublicKey, UserVerifier } from '@multiversx/sdk-wallet';

export const getVerifier = (address: string) => {
const publicKey = new UserPublicKey(Address.fromString(address).pubkey());

return new UserVerifier(publicKey);
};
48 changes: 48 additions & 0 deletions src/core/methods/signMessage/signMessage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { SignableMessage, Address } from '@multiversx/sdk-core';
import { getAccountProvider } from 'core/providers';
import { getProviderType } from 'core/providers/helpers/utils';
import { CrossWindowProvider } from 'lib/sdkWebWalletCrossWindowProvider';
import { Nullable } from 'types';
import { getAddress } from '../account/getAddress';
import { addOriginToLocationPath } from 'utils/window/addOriginToLocationPath';
import { ProviderTypeEnum } from 'core/providers/types/providerFactory.types';

export interface SignMessageType {
message: string;
callbackRoute?: string;
options?: {
hasConsentPopup?: boolean;
};
}

// TODO: upgrade to Message
export const signMessage = async ({
message,
callbackRoute,
options
}: SignMessageType): Promise<Nullable<SignableMessage>> => {
const address = getAddress();
const provider = getAccountProvider();
const providerType = getProviderType(provider);

const callbackUrl = addOriginToLocationPath(callbackRoute);
const signableMessage = new SignableMessage({
address: new Address(address),
message: Buffer.from(message, 'ascii')
});

if (
options?.hasConsentPopup &&
providerType === ProviderTypeEnum.crossWindow
) {
(provider as unknown as CrossWindowProvider).setShouldShowConsentPopup(
true
);
}

const signedMessage = await provider.signMessage(signableMessage, {
callbackUrl: encodeURIComponent(callbackUrl)
});

return signedMessage;
};
Loading

0 comments on commit 69bb844

Please sign in to comment.