Skip to content
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
2 changes: 1 addition & 1 deletion apps/desktop/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@tonkeeper/desktop",
"license": "Apache-2.0",
"version": "4.3.1",
"version": "4.3.3",
"description": "Your desktop wallet on The Open Network",
"main": ".webpack/main",
"repository": {
Expand Down
3 changes: 3 additions & 0 deletions apps/desktop/src/app/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ import { CryptoStrategyInstaller } from '@tonkeeper/uikit/dist/components/pro/Cr
import { localesList } from '@tonkeeper/locales/localesList';
import { useAppCountryInfo } from '@tonkeeper/uikit/dist/state/country';
import { SecureWalletNotification } from '@tonkeeper/uikit/dist/components/desktop/SecureWalletNotification';
import { DesktopCancelLegacySubscriptionBanner } from '@tonkeeper/uikit/dist/components/legacy-plugins/DesktopCancelLegacySubscriptionBanner';

const queryClient = new QueryClient({
defaultOptions: {
Expand Down Expand Up @@ -225,6 +226,7 @@ const WalletLayout = styled.div`
`;

const WalletLayoutBody = styled.div`
position: relative;
flex: 1;
display: flex;
max-height: calc(100% - ${desktopHeaderContainerHeight});
Expand Down Expand Up @@ -426,6 +428,7 @@ const WalletContent = () => {
<MemoryScroll />
</Wrapper>
</WalletRoutingWrapper>
<DesktopCancelLegacySubscriptionBanner />
</WalletLayoutBody>
</WalletLayout>
);
Expand Down
2 changes: 1 addition & 1 deletion apps/extension/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@tonkeeper/extension",
"version": "4.3.1",
"version": "4.3.3",
"author": "Ton APPS UK Limited <[email protected]>",
"description": "Your extension wallet on The Open Network",
"dependencies": {
Expand Down
2 changes: 1 addition & 1 deletion apps/mobile/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@tonkeeper/mobile",
"version": "4.3.1",
"version": "4.3.3",
"license": "Apache-2.0",
"description": "Your tablet wallet on The Open Network",
"type": "module",
Expand Down
2 changes: 2 additions & 0 deletions apps/mobile/src/app/app-content/WideContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import { SplashScreen } from '@capacitor/splash-screen';
import { useRealtimeUpdatesInvalidation } from '@tonkeeper/uikit/dist/hooks/realtime';
import DashboardPage from '@tonkeeper/uikit/dist/desktop-pages/dashboard';
import { SecureWalletNotification } from '@tonkeeper/uikit/dist/components/desktop/SecureWalletNotification';
import { DesktopCancelLegacySubscriptionBanner } from '@tonkeeper/uikit/dist/components/legacy-plugins/DesktopCancelLegacySubscriptionBanner';

const FullSizeWrapper = styled(Container)`
max-width: 800px;
Expand Down Expand Up @@ -191,6 +192,7 @@ const WalletContent = () => {
<MemoryScroll />
</Wrapper>
</WalletRoutingWrapper>
<DesktopCancelLegacySubscriptionBanner />
</WalletLayoutBody>
</WalletLayout>
);
Expand Down
2 changes: 1 addition & 1 deletion apps/web/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@tonkeeper/web",
"version": "4.3.1",
"version": "4.3.3",
"author": "Ton APPS UK Limited <[email protected]>",
"description": "Your web wallet on The Open Network",
"dependencies": {
Expand Down
5 changes: 5 additions & 0 deletions apps/web/src/AppDesktop.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ import {
import { DesktopMultisigOrdersPage } from "@tonkeeper/uikit/dist/desktop-pages/multisig-orders/DesktopMultisigOrders";
import { UrlTonConnectSubscription } from "./components/UrlTonConnectSubscription";
import { useRealtimeUpdatesInvalidation } from '@tonkeeper/uikit/dist/hooks/realtime';
import {
DesktopCancelLegacySubscriptionBanner
} from "@tonkeeper/uikit/dist/components/legacy-plugins/DesktopCancelLegacySubscriptionBanner";

const DesktopAccountSettingsPage = React.lazy(
() => import('@tonkeeper/uikit/dist/desktop-pages/settings/DesktopAccountSettingsPage')
Expand Down Expand Up @@ -137,6 +140,7 @@ const WalletLayout = styled.div`
`;

const WalletLayoutBody = styled.div`
position: relative;
flex: 1;
display: flex;
max-height: calc(100% - ${desktopHeaderContainerHeight});
Expand Down Expand Up @@ -280,6 +284,7 @@ const WalletContent = () => {
<MemoryScroll />
</Wrapper>
</WalletRoutingWrapper>
<DesktopCancelLegacySubscriptionBanner />
</WalletLayoutBody>
</WalletLayout>
);
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "tonkeeper-web",
"version": "4.3.1",
"version": "4.3.2",
"repository": {
"type": "git",
"url": "https://github.com/tonkeeper/tonkeeper-web.git"
Expand Down
178 changes: 172 additions & 6 deletions packages/core/src/entries/tonConnect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -199,10 +199,19 @@ const signDataFeatureSchema = z.object({
});
export type SignDataFeature = z.infer<typeof signDataFeatureSchema>;

const subscriptionFeatureSchema = z.object({
name: z.literal('Subscription'),
versions: z.object({
v2: z.boolean()
})
});
export type SubscriptionFeature = z.infer<typeof subscriptionFeatureSchema>;

export const featureSchema = z.union([
sendTransactionFeatureDeprecatedSchema,
sendTransactionFeatureSchema,
signDataFeatureSchema
signDataFeatureSchema,
subscriptionFeatureSchema
]);
export type Feature = z.infer<typeof featureSchema>;

Expand Down Expand Up @@ -268,9 +277,78 @@ const keyPairSchema = z.object({
});
export type KeyPair = z.infer<typeof keyPairSchema>;

const rpcMethodSchema = z.enum(['disconnect', 'sendTransaction', 'signData']);
const rpcMethodSchema = z.enum([
'disconnect',
'sendTransaction',
'signData',
'createSubscriptionV2',
'cancelSubscriptionV2'
]);
export type RpcMethod = z.infer<typeof rpcMethodSchema>;

const createSubscriptionV2RpcRequestSchema = z.object({
id: z.string(),
method: z.literal('createSubscriptionV2'),
params: z.tuple([z.string()])
});
export type CreateSubscriptionV2RpcRequest = z.infer<typeof createSubscriptionV2RpcRequestSchema>;

const cancelSubscriptionV2RpcRequestSchema = z.object({
id: z.string(),
method: z.literal('cancelSubscriptionV2'),
params: z.tuple([z.string()])
});
export type CancelSubscriptionV2RpcRequest = z.infer<typeof cancelSubscriptionV2RpcRequestSchema>;

export enum SUBSCRIPTION_V2_ERROR_CODES {
UNKNOWN_ERROR = 0,
BAD_REQUEST_ERROR = 1,
UNKNOWN_APP_ERROR = 100,
USER_REJECTS_ERROR = 300,
METHOD_NOT_SUPPORTED = 400,
EXTENSION_NOT_FOUND = 404
}

const createSubscriptionV2RpcResponseSuccessSchema = z.object({
id: z.string(),
result: z.object({
boc: z.string()
})
});
export type CreateSubscriptionV2RpcResponseSuccess = z.infer<
typeof createSubscriptionV2RpcResponseSuccessSchema
>;

const createSubscriptionV2RpcResponseErrorSchema = z.object({
id: z.string(),
error: z.object({
code: z.nativeEnum(SUBSCRIPTION_V2_ERROR_CODES),
message: z.string()
})
});
export type CreateSubscriptionV2RpcResponseError = z.infer<
typeof createSubscriptionV2RpcResponseErrorSchema
>;

const cancelSubscriptionV2RpcResponseSuccessSchema = z.object({
id: z.string(),
result: z.object({})
});
export type CancelSubscriptionV2RpcResponseSuccess = z.infer<
typeof cancelSubscriptionV2RpcResponseSuccessSchema
>;

const cancelSubscriptionV2RpcResponseErrorSchema = z.object({
id: z.string(),
error: z.object({
code: z.nativeEnum(SUBSCRIPTION_V2_ERROR_CODES),
message: z.string()
})
});
export type CancelSubscriptionV2RpcResponseError = z.infer<
typeof cancelSubscriptionV2RpcResponseErrorSchema
>;

export enum SEND_TRANSACTION_ERROR_CODES {
UNKNOWN_ERROR = 0,
BAD_REQUEST_ERROR = 1,
Expand Down Expand Up @@ -365,7 +443,9 @@ export type SignDataRequestPayload = z.infer<typeof signDataRequestPayloadSchema
const rpcRequestsSchema = z.object({
sendTransaction: sendTransactionRpcRequestSchema,
signData: signDataRpcRequestSchema,
disconnect: disconnectRpcRequestSchema
disconnect: disconnectRpcRequestSchema,
createSubscriptionV2: createSubscriptionV2RpcRequestSchema,
cancelSubscriptionV2: cancelSubscriptionV2RpcRequestSchema
});
export type RpcRequests = z.infer<typeof rpcRequestsSchema>;

Expand Down Expand Up @@ -411,6 +491,14 @@ const rpcResponsesSchema = z.object({
disconnect: z.object({
error: disconnectRpcResponseErrorSchema,
success: disconnectRpcResponseSuccessSchema
}),
createSubscriptionV2: z.object({
error: createSubscriptionV2RpcResponseErrorSchema,
success: createSubscriptionV2RpcResponseSuccessSchema
}),
cancelSubscriptionV2: z.object({
error: cancelSubscriptionV2RpcResponseErrorSchema,
success: cancelSubscriptionV2RpcResponseSuccessSchema
})
});
export type RpcResponses = z.infer<typeof rpcResponsesSchema>;
Expand All @@ -424,7 +512,11 @@ export const walletResponseSchema = z.union([
signDataRpcResponseSuccessSchema,
signDataRpcResponseErrorSchema,
disconnectRpcResponseSuccessSchema,
disconnectRpcResponseErrorSchema
disconnectRpcResponseErrorSchema,
createSubscriptionV2RpcResponseSuccessSchema,
createSubscriptionV2RpcResponseErrorSchema,
cancelSubscriptionV2RpcResponseSuccessSchema,
cancelSubscriptionV2RpcResponseErrorSchema
]);

export type WalletResponse<T extends RpcMethod> = WalletResponseSuccess<T> | WalletResponseError<T>;
Expand All @@ -439,7 +531,9 @@ assertTypesEqual<WalletMessage, z.infer<typeof walletMessageSchema>>(true);
export const appRequestSchema = z.union([
sendTransactionRpcRequestSchema,
signDataRpcRequestSchema,
disconnectRpcRequestSchema
disconnectRpcRequestSchema,
createSubscriptionV2RpcRequestSchema,
cancelSubscriptionV2RpcRequestSchema
]);
export type AppRequest<T extends RpcMethod> = RpcRequests[T];
assertTypesEqual<AppRequest<RpcMethod>, z.infer<typeof appRequestSchema>>(true);
Expand Down Expand Up @@ -476,9 +570,39 @@ export interface SignDatAppRequest<
payload: SignDataRequestPayload;
}

export interface CreateSubscriptionV2AppRequest<
T extends AccountConnection['type'] = AccountConnection['type']
> {
id: string;
connection: T extends 'http'
? AccountConnectionHttp
: T extends 'injected'
? AccountConnectionInjected
: AccountConnection;
kind: 'createSubscriptionV2';
payload: CreateSubscriptionV2Payload;
}

export interface CancelSubscriptionV2AppRequest<
T extends AccountConnection['type'] = AccountConnection['type']
> {
id: string;
connection: T extends 'http'
? AccountConnectionHttp
: T extends 'injected'
? AccountConnectionInjected
: AccountConnection;
kind: 'cancelSubscriptionV2';
payload: CancelSubscriptionV2Payload;
}

export type TonConnectAppRequestPayload<
T extends AccountConnection['type'] = AccountConnection['type']
> = SendTransactionAppRequest<T> | SignDatAppRequest<T>;
> =
| SendTransactionAppRequest<T>
| SignDatAppRequest<T>
| CreateSubscriptionV2AppRequest<T>
| CancelSubscriptionV2AppRequest<T>;

export interface InjectedWalletInfo {
name: string;
Expand All @@ -497,3 +621,45 @@ export interface ITonConnectInjectedBridge {
send<T extends RpcMethod>(message: AppRequest<T>): Promise<WalletResponse<T>>;
listen(callback: (event: WalletEvent) => void): () => void;
}

export type SubscriptionMetadataSource = z.infer<typeof subscriptionMetadataSchema>;

export const subscriptionMetadataSchema = z.object({
logo: z.string().url(),
name: z.string(),
description: z.string(),
link: z.string().url(),
tos: z.string().url(),
merchant: z.string(),
website: z.string().url()
});

export const subscriptionSchema = z.object({
beneficiary: z.string(),
id: z.number(),
period: z.number().int().positive(),
amount: z.string(),
first_charge_date: z.number(),
withdraw_address: z.string(),
withdraw_msg_body: z.string(),
metadata: subscriptionMetadataSchema
});

export const createSubscriptionV2PayloadSchema = z.object({
validUntil: z.number(),
subscription: subscriptionSchema,
from: rawAddressSchema.optional(),
network: tonConnectNetworkSchema,
valid_until: z.number()
});

export const cancelSubscriptionV2PayloadSchema = z.object({
validUntil: z.number(),
extensionAddress: z.string(),
from: rawAddressSchema.optional(),
network: tonConnectNetworkSchema,
valid_until: z.number()
});

export type CreateSubscriptionV2Payload = z.infer<typeof createSubscriptionV2PayloadSchema>;
export type CancelSubscriptionV2Payload = z.infer<typeof cancelSubscriptionV2PayloadSchema>;
Original file line number Diff line number Diff line change
Expand Up @@ -158,8 +158,6 @@ export class SubscriptionEncoder {
private encodeWithdrawMsgBody(message?: string): Cell {
if (!message) return Cell.EMPTY;

const boc = Buffer.from(message, 'hex');

return Cell.fromBoc(boc)[0];
return beginCell().storeBuffer(Buffer.from(message, 'utf8')).endCell();
}
}
Loading
Loading