Skip to content

Commit

Permalink
feat: @wallet supported
Browse files Browse the repository at this point in the history
  • Loading branch information
siandreev committed Oct 5, 2023
1 parent 2bf986f commit 7edcf07
Show file tree
Hide file tree
Showing 7 changed files with 122 additions and 66 deletions.
1 change: 1 addition & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
TELEGRAM_BOT_TOKEN=<YOUR TOKEN HERE>
TELEGRAM_BOT_LINK=<YOUR TG BOT LINK HERE, E.G. https://t.me/ton_connect_example_bot>
MANIFEST_URL=https://ton-connect.github.io/demo-dapp-with-react-ui/tonconnect-manifest.json

# 24h
Expand Down
14 changes: 7 additions & 7 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"stop:daemon": "pm2 stop tgbot && pm2 delete tgbot"
},
"dependencies": {
"@tonconnect/sdk": "^2.1.3",
"@tonconnect/sdk": "^3.0.0-beta.1",
"dotenv": "^16.0.3",
"node-telegram-bot-api": "^0.61.0",
"qrcode": "^1.5.1",
Expand Down
63 changes: 28 additions & 35 deletions src/commands-handlers.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,10 @@
import {
CHAIN,
isWalletInfoRemote,
toUserFriendlyAddress,
UserRejectsError
} from '@tonconnect/sdk';
import { CHAIN, isTelegramUrl, toUserFriendlyAddress, UserRejectsError } from '@tonconnect/sdk';
import { bot } from './bot';
import { getWallets } from './ton-connect/wallets';
import { getWallets, getWalletInfo } from './ton-connect/wallets';
import QRCode from 'qrcode';
import TelegramBot from 'node-telegram-bot-api';
import { getConnector } from './ton-connect/connector';
import { pTimeout, pTimeoutException } from './utils';
import { addTGReturnStrategy, buildUniversalKeyboard, pTimeout, pTimeoutException } from './utils';

let newConnectRequestListenersMap = new Map<number, () => void>();

Expand All @@ -27,11 +22,12 @@ export async function handleConnectCommand(msg: TelegramBot.Message): Promise<vo

await connector.restoreConnection();
if (connector.connected) {
const connectedName =
(await getWalletInfo(connector.wallet!.device.appName))?.name ||
connector.wallet!.device.appName;
await bot.sendMessage(
chatId,
`You have already connect a ${
connector.wallet!.device.appName
} wallet\nYour address: ${toUserFriendlyAddress(
`You have already connect ${connectedName} wallet\nYour address: ${toUserFriendlyAddress(
connector.wallet!.account.address,
connector.wallet!.account.chain === CHAIN.TESTNET
)}\n\n Disconnect wallet firstly to connect a new one`
Expand All @@ -44,7 +40,9 @@ export async function handleConnectCommand(msg: TelegramBot.Message): Promise<vo
if (wallet) {
await deleteMessage();

await bot.sendMessage(chatId, `${wallet.device.appName} wallet connected successfully`);
const walletName =
(await getWalletInfo(wallet.device.appName))?.name || wallet.device.appName;
await bot.sendMessage(chatId, `${walletName} wallet connected successfully`);
unsubscribe();
newConnectRequestListenersMap.delete(chatId);
}
Expand All @@ -55,22 +53,11 @@ export async function handleConnectCommand(msg: TelegramBot.Message): Promise<vo
const link = connector.connect(wallets);
const image = await QRCode.toBuffer(link);

const keyboard = await buildUniversalKeyboard(link, wallets);

const botMessage = await bot.sendPhoto(chatId, image, {
reply_markup: {
inline_keyboard: [
[
{
text: 'Open Wallet',
url: `https://ton-connect.github.io/open-tc?connect=${encodeURIComponent(
link
)}`
},
{
text: 'Choose a Wallet',
callback_data: JSON.stringify({ method: 'chose_wallet' })
}
]
]
inline_keyboard: [keyboard]
}
});

Expand Down Expand Up @@ -101,8 +88,6 @@ export async function handleSendTXCommand(msg: TelegramBot.Message): Promise<voi
return;
}

const wallets = await connector.getWallets();

pTimeout(
connector.sendTransaction({
validUntil: Math.round(
Expand Down Expand Up @@ -136,20 +121,26 @@ export async function handleSendTXCommand(msg: TelegramBot.Message): Promise<voi
.finally(() => connector.pauseConnection());

let deeplink = '';
const walletInfo = wallets.find(wallet => wallet.name === connector.wallet!.device.appName);
if (walletInfo && isWalletInfoRemote(walletInfo)) {
const walletInfo = await getWalletInfo(connector.wallet!.device.appName);
if (walletInfo) {
deeplink = walletInfo.universalLink;
}

if (isTelegramUrl(deeplink)) {
const url = new URL(deeplink);
url.searchParams.append('startattach', 'tonconnect');
deeplink = addTGReturnStrategy(url.toString(), process.env.TELEGRAM_BOT_LINK!);
}

await bot.sendMessage(
chatId,
`Open ${connector.wallet!.device.appName} and confirm transaction`,
`Open ${walletInfo?.name || connector.wallet!.device.appName} and confirm transaction`,
{
reply_markup: {
inline_keyboard: [
[
{
text: 'Open Wallet',
text: `Open ${walletInfo?.name || connector.wallet!.device.appName}`,
url: deeplink
}
]
Expand Down Expand Up @@ -186,11 +177,13 @@ export async function handleShowMyWalletCommand(msg: TelegramBot.Message): Promi
return;
}

const walletName =
(await getWalletInfo(connector.wallet!.device.appName))?.name ||
connector.wallet!.device.appName;

await bot.sendMessage(
chatId,
`Connected wallet: ${
connector.wallet!.device.appName
}\nYour address: ${toUserFriendlyAddress(
`Connected wallet: ${walletName}\nYour address: ${toUserFriendlyAddress(
connector.wallet!.account.address,
connector.wallet!.account.chain === CHAIN.TESTNET
)}`
Expand Down
42 changes: 19 additions & 23 deletions src/connect-wallet-menu.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import TelegramBot, { CallbackQuery } from 'node-telegram-bot-api';
import { getWallets } from './ton-connect/wallets';
import { getWalletInfo, getWallets } from './ton-connect/wallets';
import { bot } from './bot';
import { getConnector } from './ton-connect/connector';
import QRCode from 'qrcode';
import * as fs from 'fs';
import { isTelegramUrl, isWalletInfoRemote } from '@tonconnect/sdk';
import { addTGReturnStrategy, buildUniversalKeyboard } from './utils';

export const walletMenuCallbacks = {
chose_wallet: onChooseWalletClick,
Expand All @@ -18,7 +20,7 @@ async function onChooseWalletClick(query: CallbackQuery, _: string): Promise<voi
inline_keyboard: [
wallets.map(wallet => ({
text: wallet.name,
callback_data: JSON.stringify({ method: 'select_wallet', data: wallet.name })
callback_data: JSON.stringify({ method: 'select_wallet', data: wallet.appName })
})),
[
{
Expand Down Expand Up @@ -47,22 +49,11 @@ async function onOpenUniversalQRClick(query: CallbackQuery, _: string): Promise<

await editQR(query.message!, link);

const keyboard = await buildUniversalKeyboard(link, wallets);

await bot.editMessageReplyMarkup(
{
inline_keyboard: [
[
{
text: 'Open Wallet',
url: `https://ton-connect.github.io/open-tc?connect=${encodeURIComponent(
link
)}`
},
{
text: 'Choose a Wallet',
callback_data: JSON.stringify({ method: 'chose_wallet' })
}
]
]
inline_keyboard: [keyboard]
},
{
message_id: query.message?.message_id,
Expand All @@ -75,19 +66,24 @@ async function onWalletClick(query: CallbackQuery, data: string): Promise<void>
const chatId = query.message!.chat.id;
const connector = getConnector(chatId);

const wallets = await getWallets();

const selectedWallet = wallets.find(wallet => wallet.name === data);
const selectedWallet = await getWalletInfo(data);
if (!selectedWallet) {
return;
}

const link = connector.connect({
let buttonLink = connector.connect({
bridgeUrl: selectedWallet.bridgeUrl,
universalLink: selectedWallet.universalLink
});

await editQR(query.message!, link);
let qrLink = buttonLink;

if (isTelegramUrl(selectedWallet.universalLink)) {
buttonLink = addTGReturnStrategy(buttonLink, process.env.TELEGRAM_BOT_LINK!);
qrLink = addTGReturnStrategy(qrLink, 'none');
}

await editQR(query.message!, qrLink);

await bot.editMessageReplyMarkup(
{
Expand All @@ -98,8 +94,8 @@ async function onWalletClick(query: CallbackQuery, data: string): Promise<void>
callback_data: JSON.stringify({ method: 'chose_wallet' })
},
{
text: `Open ${data}`,
url: link
text: `Open ${selectedWallet.name}`,
url: buttonLink
}
]
]
Expand Down
5 changes: 5 additions & 0 deletions src/ton-connect/wallets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,8 @@ export async function getWallets(): Promise<WalletInfoRemote[]> {
const wallets = await walletsListManager.getWallets();
return wallets.filter(isWalletInfoRemote);
}

export async function getWalletInfo(walletAppName: string): Promise<WalletInfoRemote | undefined> {
const wallets = await getWallets();
return wallets.find(wallet => wallet.appName.toLowerCase() === walletAppName.toLowerCase());
}
61 changes: 61 additions & 0 deletions src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
import { encodeTelegramUrlParameters, isTelegramUrl, WalletInfoRemote } from '@tonconnect/sdk';
import { InlineKeyboardButton } from 'node-telegram-bot-api';

export const AT_WALLET_APP_NAME = 'telegram-wallet';

export const pTimeoutException = Symbol();

export function pTimeout<T>(
Expand All @@ -11,3 +16,59 @@ export function pTimeout<T>(
new Promise((_r, rej) => (timer = setTimeout(rej, time, exception)))
]).finally(() => clearTimeout(timer)) as Promise<T>;
}

export function addTGReturnStrategy(link: string, strategy: string): string {
const parsed = new URL(link);
parsed.searchParams.append('ret', strategy);
link = parsed.toString();

const lastParam = link.slice(link.lastIndexOf('&') + 1);
return link.slice(0, link.lastIndexOf('&')) + '-' + encodeTelegramUrlParameters(lastParam);
}

export function convertDeeplinkToUniversalLink(link: string, walletUniversalLink: string): string {
const search = new URL(link).search;
const url = new URL(walletUniversalLink);

if (isTelegramUrl(walletUniversalLink)) {
const startattach = 'tonconnect-' + encodeTelegramUrlParameters(search.slice(1));
url.searchParams.append('startattach', startattach);
} else {
url.search = search;
}

return url.toString();
}

export async function buildUniversalKeyboard(
link: string,
wallets: WalletInfoRemote[]
): Promise<InlineKeyboardButton[]> {
const atWallet = wallets.find(wallet => wallet.appName.toLowerCase() === AT_WALLET_APP_NAME);
const atWalletLink = atWallet
? addTGReturnStrategy(
convertDeeplinkToUniversalLink(link, atWallet?.universalLink),
process.env.TELEGRAM_BOT_LINK!
)
: undefined;

const keyboard = [
{
text: 'Choose a Wallet',
callback_data: JSON.stringify({ method: 'chose_wallet' })
},
{
text: 'Open Link',
url: `https://ton-connect.github.io/open-tc?connect=${encodeURIComponent(link)}`
}
];

if (atWalletLink) {
keyboard.unshift({
text: '@wallet',
url: atWalletLink
});
}

return keyboard;
}

0 comments on commit 7edcf07

Please sign in to comment.