Skip to content

SignData Feature #352

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

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
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
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
export interface SignDataRpcRequest {
method: 'signData';
params: [
{
schema_crc: number;
cell: string;
}
];
params: [string];
id: string;
}
4 changes: 3 additions & 1 deletion packages/protocol/src/models/feature.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,6 @@ export type SendTransactionFeature = {
maxMessages: number;
extraCurrencySupported?: boolean;
};
export type SignDataFeature = { name: 'SignData' };

export type SignDataType = 'text' | 'binary' | 'cell';
export type SignDataFeature = { name: 'SignData'; types: SignDataType[] };
1 change: 1 addition & 0 deletions packages/protocol/src/models/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export {
Feature,
SendTransactionFeatureDeprecated,
SendTransactionFeature,
SignDataType,
SignDataFeature
} from './feature';
export { CHAIN } from './CHAIN';
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ export {
SEND_TRANSACTION_ERROR_CODES
} from './send-transaction-rpc-response';
export {
SignDataPayload,
SignDataPayloadText,
SignDataPayloadBinary,
SignDataPayloadCell,
SignDataRpcResponse,
SignDataRpcResponseSuccess,
SignDataRpcResponseError,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,34 @@ import { WalletResponseTemplateError } from './wallet-response-template';
export type SignDataRpcResponse = SignDataRpcResponseSuccess | SignDataRpcResponseError;

export interface SignDataRpcResponseSuccess {
id: string;
result: {
signature: string;
timestamp: string;
address: string;
timestamp: number;
domain: string;
payload: SignDataPayload;
};
id: string;
}

export type SignDataPayload = SignDataPayloadText | SignDataPayloadBinary | SignDataPayloadCell;

export type SignDataPayloadText = {
type: 'text';
text: string;
};
export type SignDataPayloadBinary = {
type: 'binary';
bytes: string;
};
export type SignDataPayloadCell = {
type: 'cell';
schema: string;
cell: string;
};

export interface SignDataRpcResponseError extends WalletResponseTemplateError {
error: { code: SIGN_DATA_ERROR_CODES; message: string; data?: unknown };
error: { code: SIGN_DATA_ERROR_CODES; message: string };
id: string;
}

Expand Down
3 changes: 3 additions & 0 deletions packages/sdk/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,9 @@ List of events:
* `transaction-sent-for-signature`: when a user sends a transaction for signature.
* `transaction-signed`: when a user successfully signs a transaction.
* `transaction-signing-failed`: when a user cancels transaction signing or there is an error during the signing process.
* `sign-data-request-initiated`: when a user initiates a request to sign data.
* `sign-data-request-completed`: when a user successfully signs data.
* `sign-data-request-failed`: when a user cancels data signing or there is an error during the signing process.

If you want to track user actions, you can subscribe to the window events with prefix `ton-connect-`:

Expand Down
17 changes: 15 additions & 2 deletions packages/sdk/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ export {
createTransactionSentForSignatureEvent,
createTransactionSigningFailedEvent,
createTransactionSignedEvent,
createDataSentForSignatureEvent,
createDataSigningFailedEvent,
createDataSignedEvent,
createRequestVersionEvent,
createResponseVersionEvent,
createVersionInfo
Expand All @@ -43,6 +46,10 @@ export type {
TransactionSignedEvent,
TransactionSentForSignatureEvent,
TransactionSigningFailedEvent,
DataSigningEvent,
DataSignedEvent,
DataSentForSignatureEvent,
DataSigningFailedEvent,
SdkActionEvent,
RequestVersionEvent,
ResponseVersionEvent,
Expand All @@ -60,15 +67,21 @@ export {
DeviceInfo,
Feature,
SendTransactionFeature,
SignDataFeature,
SendTransactionFeatureDeprecated,
SignDataFeature,
SignDataType,
SignDataPayload,
SignDataPayloadText,
SignDataPayloadBinary,
SignDataPayloadCell,
TonProofItemReply,
TonProofItemReplySuccess,
TonProofItemReplyError,
ConnectItemReplyError,
CONNECT_ITEM_ERROR_CODES,
CONNECT_EVENT_ERROR_CODES,
SEND_TRANSACTION_ERROR_CODES
SEND_TRANSACTION_ERROR_CODES,
SIGN_DATA_ERROR_CODES
} from '@tonconnect/protocol';
export { toUserFriendlyAddress } from './utils/address';
export { checkRequiredWalletFeatures } from './utils/feature-support';
Expand Down
1 change: 1 addition & 0 deletions packages/sdk/src/models/methods/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from './connect';
export * from './send-transaction';
export * from './sign-data';
1 change: 1 addition & 0 deletions packages/sdk/src/models/methods/sign-data/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { SignDataResponse } from './sign-data-response';
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { SignDataPayload } from '@tonconnect/protocol';

export type SignDataResponse = {
signature: string;
address: string;
timestamp: number;
domain: string;
payload: SignDataPayload;
};
6 changes: 5 additions & 1 deletion packages/sdk/src/models/wallet/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,8 @@ export {
isWalletInfoRemote,
isWalletInfoInjected
} from './wallet-info';
export { RequiredFeatures, RequiredSendTransactionFeature } from './require-feature';
export {
RequiredFeatures,
RequiredSendTransactionFeature,
RequiredSignDataFeature
} from './require-feature';
14 changes: 14 additions & 0 deletions packages/sdk/src/models/wallet/require-feature.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { SignDataType } from '@tonconnect/protocol';

/**
* Required features for wallets.
*/
Expand All @@ -6,8 +8,10 @@ export type RequiredFeatures = {
* Required features for the send transaction feature.
*/
sendTransaction?: RequiredSendTransactionFeature;
signData?: RequiredSignDataFeature;
};


/**
* Required features for the send transaction feature.
*/
Expand All @@ -22,3 +26,13 @@ export type RequiredSendTransactionFeature = {
*/
extraCurrencyRequired?: boolean;
};

/**
* Required features for the sign data feature.
*/
export type RequiredSignDataFeature = {
/**
* Supported sign data types.
*/
types: SignDataType[];
};
45 changes: 45 additions & 0 deletions packages/sdk/src/parsers/sign-data-parser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import {
CONNECT_EVENT_ERROR_CODES,
SIGN_DATA_ERROR_CODES,
SignDataPayload,
SignDataRpcRequest,
SignDataRpcResponseError,
SignDataRpcResponseSuccess
} from '@tonconnect/protocol';
import { BadRequestError, TonConnectError, UnknownAppError, UserRejectsError } from 'src/errors';
import { UnknownError } from 'src/errors/unknown.error';
import { SignDataResponse } from 'src/models/methods';
import { RpcParser } from 'src/parsers/rpc-parser';
import { WithoutId } from 'src/utils/types';

const signDataErrors: Partial<Record<CONNECT_EVENT_ERROR_CODES, typeof TonConnectError>> = {
[SIGN_DATA_ERROR_CODES.UNKNOWN_ERROR]: UnknownError,
[SIGN_DATA_ERROR_CODES.USER_REJECTS_ERROR]: UserRejectsError,
[SIGN_DATA_ERROR_CODES.BAD_REQUEST_ERROR]: BadRequestError,
[SIGN_DATA_ERROR_CODES.UNKNOWN_APP_ERROR]: UnknownAppError
};

class SignDataParser extends RpcParser<'signData'> {
convertToRpcRequest(payload: SignDataPayload): WithoutId<SignDataRpcRequest> {
return {
method: 'signData',
params: [JSON.stringify(payload)]
};
}

parseAndThrowError(response: WithoutId<SignDataRpcResponseError>): never {
let ErrorConstructor: typeof TonConnectError = UnknownError;

if (response.error.code in signDataErrors) {
ErrorConstructor = signDataErrors[response.error.code] || UnknownError;
}

throw new ErrorConstructor(response.error.message);
}

convertFromRpcResponse(rpcResponse: WithoutId<SignDataRpcResponseSuccess>): SignDataResponse {
return rpcResponse.result;
}
}

export const signDataParser = new SignDataParser();
11 changes: 10 additions & 1 deletion packages/sdk/src/ton-connect.interface.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { TonConnectError } from 'src/errors';
import { Account, Wallet, WalletConnectionSource, WalletConnectionSourceHTTP } from 'src/models';
import { SendTransactionRequest, SendTransactionResponse } from 'src/models/methods';
import { SendTransactionRequest, SendTransactionResponse, SignDataResponse } from 'src/models/methods';
import { ConnectAdditionalRequest } from 'src/models/methods/connect/connect-additional-request';
import { WalletInfo } from 'src/models/wallet/wallet-info';
import { WalletConnectionSourceJS } from 'src/models/wallet/wallet-connection-source';
import { SignDataPayload } from '@tonconnect/protocol';

export interface ITonConnect {
/**
Expand Down Expand Up @@ -92,4 +93,12 @@ export interface ITonConnect {
transaction: SendTransactionRequest,
onRequestSent?: () => void
): Promise<SendTransactionResponse>;

signData(
data: SignDataPayload,
options?: {
onRequestSent?: () => void;
signal?: AbortSignal;
}
): Promise<SignDataResponse>
}
50 changes: 48 additions & 2 deletions packages/sdk/src/ton-connect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import {
ConnectRequest,
Feature,
SendTransactionRpcResponseSuccess,
SignDataPayload,
SignDataRpcResponseSuccess,
TonAddressItemReply,
TonProofItemReply,
WalletEvent
Expand All @@ -25,7 +27,11 @@ import {
WalletConnectionSourceHTTP,
WalletInfo
} from 'src/models';
import { SendTransactionRequest, SendTransactionResponse } from 'src/models/methods';
import {
SendTransactionRequest,
SendTransactionResponse,
SignDataResponse
} from 'src/models/methods';
import { ConnectAdditionalRequest } from 'src/models/methods/connect/connect-additional-request';
import { TonConnectOptions } from 'src/models/ton-connect-options';
import {
Expand All @@ -34,6 +40,7 @@ import {
} from 'src/models/wallet/wallet-connection-source';
import { connectErrorsParser } from 'src/parsers/connect-errors-parser';
import { sendTransactionParser } from 'src/parsers/send-transaction-parser';
import { signDataParser } from 'src/parsers/sign-data-parser';
import { BridgeProvider } from 'src/provider/bridge/bridge-provider';
import { InjectedProvider } from 'src/provider/injected/injected-provider';
import { Provider } from 'src/provider/provider';
Expand All @@ -45,7 +52,8 @@ import { WalletsListManager } from 'src/wallets-list-manager';
import { WithoutIdDistributive } from 'src/utils/types';
import {
checkSendTransactionSupport,
checkRequiredWalletFeatures
checkRequiredWalletFeatures,
checkSignDataSupport
} from 'src/utils/feature-support';
import { callForSuccess } from 'src/utils/call-for-success';
import { logDebug, logError } from 'src/utils/log';
Expand Down Expand Up @@ -474,6 +482,44 @@ export class TonConnect implements ITonConnect {
return result;
}

public async signData(
data: SignDataPayload,
options?: {
onRequestSent?: () => void;
signal?: AbortSignal;
}
): Promise<SignDataResponse> {
const abortController = createAbortController(options?.signal);
if (abortController.signal.aborted) {
throw new TonConnectError('Data sending was aborted');
}

this.checkConnection();
checkSignDataSupport(this.wallet!.device.features, { requiredTypes: [data.type] });

this.tracker.trackDataSentForSignature(this.wallet, data);

const response = await this.provider!.sendRequest(signDataParser.convertToRpcRequest(data));

if (signDataParser.isError(response)) {
this.tracker.trackDataSigningFailed(
this.wallet,
data,
response.error.message,
response.error.code
);
return signDataParser.parseAndThrowError(response);
}

const result = signDataParser.convertFromRpcResponse(
response as SignDataRpcResponseSuccess
);

this.tracker.trackDataSigned(this.wallet, data, result);

return result;
}

/**
* Disconnect form thw connected wallet and drop current session.
*/
Expand Down
Loading