Skip to content

Commit

Permalink
v3.0.24
Browse files Browse the repository at this point in the history
  • Loading branch information
mytonwalletorg committed Sep 30, 2024
1 parent b9ed080 commit 6f675fe
Show file tree
Hide file tree
Showing 10 changed files with 143 additions and 41 deletions.
1 change: 1 addition & 0 deletions changelogs/3.0.24.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Bug fixes and performance improvements
4 changes: 2 additions & 2 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
@@ -1,6 +1,6 @@
{
"name": "mytonwallet",
"version": "3.0.23",
"version": "3.0.24",
"description": "The most feature-rich web wallet and browser extension for TON – with support of multi-accounts, tokens (jettons), NFT, TON DNS, TON Sites, TON Proxy, and TON Magic.",
"main": "index.js",
"scripts": {
Expand Down
2 changes: 1 addition & 1 deletion public/version.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
3.0.23
3.0.24
10 changes: 6 additions & 4 deletions src/api/chains/ton/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,8 @@ export async function getWalletFromMnemonic(
mnemonic: string[],
network: ApiNetwork,
version?: ApiTonWalletVersion,
): Promise<ApiTonWallet> {
const { publicKey } = await mnemonicToKeyPair(mnemonic);
): Promise<ApiTonWallet & { lastTxId?: string }> {
const { publicKey } = await tonWebMnemonic.mnemonicToKeyPair(mnemonic);
return getWalletFromKeys(publicKey, network, version);
}

Expand All @@ -107,12 +107,13 @@ async function getWalletFromKeys(
publicKey: Uint8Array,
network: ApiNetwork,
version?: ApiTonWalletVersion,
): Promise<ApiTonWallet> {
): Promise<ApiTonWallet & { lastTxId?: string }> {
let wallet: TonWallet;
let lastTxId: string | undefined;
if (version) {
wallet = buildWallet(network, publicKey, version);
} else {
({ wallet, version } = await pickBestWallet(network, publicKey));
({ wallet, version, lastTxId } = await pickBestWallet(network, publicKey));
}

const address = toBase64Address(wallet.address, false, network);
Expand All @@ -124,6 +125,7 @@ async function getWalletFromKeys(
address,
version,
index: 0,
lastTxId,
};
}

Expand Down
5 changes: 2 additions & 3 deletions src/api/chains/ton/transactions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,6 @@ import {
FEE_FACTOR,
STAKE_COMMENT,
TINY_TOKEN_TRANSFER_AMOUNT,
TOKEN_TRANSFER_AMOUNT,
TRANSFER_TIMEOUT_SEC,
UNSTAKE_COMMENT,
} from './constants';
Expand Down Expand Up @@ -235,14 +234,14 @@ export async function checkTransactionDraft(
});

const realFee = await calculateFee(network, wallet, transaction, isWalletInitialized);
result.fee = bigintMultiplyToNumber(realFee, FEE_FACTOR) + (tokenAddress ? TOKEN_TRANSFER_AMOUNT : 0n);
result.fee = bigintMultiplyToNumber(realFee, FEE_FACTOR) + (tokenAddress ? amount : 0n);

const toncoinBalance = await getWalletBalance(network, wallet);

const isFullTonBalance = !tokenAddress && toncoinBalance === amount;
const isEnoughBalance = isFullTonBalance
? toncoinBalance > realFee
: toncoinBalance >= amount + result.fee;
: toncoinBalance >= result.fee;

if (
network === 'mainnet'
Expand Down
7 changes: 1 addition & 6 deletions src/api/chains/ton/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,9 @@ import type { Cell } from '@ton/core';

import type { DieselStatus } from '../../../global/types';
import type {
ApiAnyDisplayError, ApiParsedPayload, ApiTransaction, ApiWalletInfo,
ApiAnyDisplayError, ApiParsedPayload, ApiTransaction,
} from '../../types';
import type { ContractType } from './constants';
import type { TonWallet } from './util/tonCore';

export type ApiTonWalletVersion = 'simpleR1'
| 'simpleR2'
Expand Down Expand Up @@ -95,10 +94,6 @@ export type GetAddressInfoResponse = {
state: 'uninitialized' | 'active';
};

export type WalletInfo = ApiWalletInfo & {
wallet: TonWallet;
};

export type ApiSubmitTransferTonResult = {
toAddress: string;
amount: bigint;
Expand Down
96 changes: 91 additions & 5 deletions src/api/chains/ton/util/apiV3.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { ApiNetwork } from '../../../types';
import type { ApiNetwork, ApiWalletInfo } from '../../../types';
import type { ApiTransactionExtra } from '../types';

import {
Expand All @@ -7,14 +7,59 @@ import {
TONHTTPAPI_V3_TESTNET_API_URL,
} from '../../../../config';
import { fetchJson } from '../../../../util/fetch';
import { omitUndefined, split } from '../../../../util/iteratees';
import {
buildCollectionByKey, mapValues, omitUndefined, split,
} from '../../../../util/iteratees';
import { getEnvironment } from '../../../environment';
import { stringifyTxId } from './index';
import { toBase64Address } from './tonCore';
import { toBase64Address, toRawAddress } from './tonCore';

type AddressBook = Record<string, { user_friendly: string }>;

type AccountState = {
account_state_hash: string;
address: string;
balance: string;
code_boc: string;
code_hash: string;
data_boc: string;
data_hash: string;
frozen_hash: string;
last_transaction_hash: string;
last_transaction_lt: number;
status: string;
};

type WalletVersion = keyof typeof VERSION_MAP;

type WalletState = {
address: string;
balance: string;
code_hash: string;
is_signature_allowed: boolean;
is_wallet: boolean;
last_transaction_hash: string;
last_transaction_lt: number;
seqno: number;
status: string;
wallet_id: number;
wallet_type: WalletVersion;
};

const ADDRESS_BOOK_CHUNK_SIZE = 128;
const VERSION_MAP = {
'wallet v1 r1': 'simpleR1',
'wallet v1 r2': 'simpleR2',
'wallet v1 r3': 'simpleR3',
'wallet v2 r1': 'v2R1',
'wallet v2 r2': 'v2R2',
'wallet v3 r1': 'v3R1',
'wallet v3 r2': 'v3R2',
// 'wallet v4 r1': '', // Not used in production, wrapper is missing
'wallet v4 r2': 'v4R2',
// 'wallet v5 beta': '', // Not used in production, wrapper is missing
'wallet v5 r1': 'W5',
} as const;

export async function fetchTransactions(options: {
network: ApiNetwork;
Expand Down Expand Up @@ -138,7 +183,48 @@ export async function fixAddressFormat(network: ApiNetwork, address: string): Pr
return result.address_book[address];
}

function callApiV3(network: ApiNetwork, path: string, data?: AnyLiteral) {
export async function getWalletStates(network: ApiNetwork, addresses: string[]) {
const { wallets: states } = await callApiV3<{
addressBook: AddressBook;
wallets: WalletState[];
}>(network, '/walletStates', { address: addresses.join(',') });

const addressByRaw = Object.fromEntries(addresses.map((address) => [toRawAddress(address).toUpperCase(), address]));
for (const state of states) {
state.address = addressByRaw[state.address];
}
return buildCollectionByKey(states, 'address');
}

export async function getWalletInfos(network: ApiNetwork, addresses: string[]): Promise<Record<string, ApiWalletInfo>> {
const states = await getWalletStates(network, addresses);
return mapValues(states, (state) => {
return {
address: toBase64Address(state.address, false),
version: VERSION_MAP[state.wallet_type],
balance: BigInt(state.balance),
isInitialized: state.status === 'active',
lastTxId: state.last_transaction_lt && state.last_transaction_hash
? stringifyTxId({ lt: state.last_transaction_lt, hash: state.last_transaction_hash })
: undefined,
};
});
}

export async function getAccountStates(network: ApiNetwork, addresses: string[]) {
const { accounts: states } = await callApiV3<{
addressBook: AddressBook;
accounts: AccountState[];
}>(network, '/accountStates', { address: addresses.join(',') });

const addressByRaw = Object.fromEntries(addresses.map((address) => [toRawAddress(address), address]));
for (const state of states) {
state.address = addressByRaw[state.address];
}
return buildCollectionByKey(states, 'address');
}

function callApiV3<T = any>(network: ApiNetwork, path: string, data?: AnyLiteral) {
const { apiHeaders, tonhttpapiMainnetKey, tonhttpapiTestnetKey } = getEnvironment();
const baseUrl = network === 'testnet' ? TONHTTPAPI_V3_TESTNET_API_URL : TONHTTPAPI_V3_MAINNET_API_URL;
const apiKey = network === 'testnet' ? tonhttpapiTestnetKey : tonhttpapiMainnetKey;
Expand All @@ -148,7 +234,7 @@ function callApiV3(network: ApiNetwork, path: string, data?: AnyLiteral) {
...(apiKey && { 'X-Api-Key': apiKey }),
...apiHeaders,
},
});
}) as Promise<T>;
}

function msToSec(ms: number) {
Expand Down
33 changes: 20 additions & 13 deletions src/api/chains/ton/wallet.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import { beginCell, storeStateInit } from '@ton/core';

import type { ApiNetwork } from '../../types';
import type { ApiTonWalletVersion, ContractInfo, WalletInfo } from './types';
import type { ApiNetwork, ApiWalletInfo } from '../../types';
import type { ApiTonWalletVersion, ContractInfo } from './types';
import type { TonWallet } from './util/tonCore';

import { DEFAULT_WALLET_VERSION } from '../../../config';
import { parseAccountId } from '../../../util/account';
import { pick } from '../../../util/iteratees';
import { extractKey, findLast } from '../../../util/iteratees';
import withCacheAsync from '../../../util/withCacheAsync';
import { stringifyTxId } from './util';
import { getWalletInfos } from './util/apiV3';
import { fetchJettonBalances } from './util/tonapiio';
import {
getTonClient, toBase64Address, walletClassMap,
Expand Down Expand Up @@ -135,6 +136,7 @@ export async function pickBestWallet(network: ApiNetwork, publicKey: Uint8Array)
wallet: TonWallet;
version: ApiTonWalletVersion;
balance: bigint;
lastTxId?: string;
}> {
const allWallets = await getWalletVersionInfos(network, publicKey);
const defaultWallet = allWallets.filter(({ version }) => version === DEFAULT_WALLET_VERSION)[0];
Expand All @@ -151,7 +153,7 @@ export async function pickBestWallet(network: ApiNetwork, publicKey: Uint8Array)
return withBiggestBalance;
}

const withLastTx = allWallets.find(({ lastTxId }) => !!lastTxId);
const withLastTx = findLast(allWallets, ({ lastTxId }) => !!lastTxId);

if (withLastTx) {
return withLastTx;
Expand All @@ -167,23 +169,28 @@ export async function pickBestWallet(network: ApiNetwork, publicKey: Uint8Array)
return defaultWallet;
}

export function getWalletVersionInfos(
export async function getWalletVersionInfos(
network: ApiNetwork,
publicKey: Uint8Array,
versions: ApiTonWalletVersion[] = ALL_WALLET_VERSIONS,
): Promise<WalletInfo[]> {
return Promise.all(versions.map(async (version) => {
): Promise<(ApiWalletInfo & { wallet: TonWallet })[]> {
const items = versions.map((version) => {
const wallet = buildWallet(network, publicKey, version);
const address = toBase64Address(wallet.address, false, network);
const walletInfo = await getWalletInfo(network, wallet);
return { wallet, address, version };
});

const walletInfos = await getWalletInfos(network, extractKey(items, 'address'));

return items.map((item) => {
return {
wallet,
address,
version,
...pick(walletInfo, ['isInitialized', 'balance', 'lastTxId']),
...walletInfos[item.address] ?? {
balance: 0n,
isInitialized: false,
},
...item,
};
}));
});
}

export function getWalletVersions(
Expand Down
24 changes: 18 additions & 6 deletions src/api/methods/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import type {
ApiAccountWithMnemonic,
ApiLedgerAccount,
ApiNetwork,
ApiTonWallet,
ApiTxTimestamps,
} from '../types';
import { ApiCommonError } from '../types';
Expand Down Expand Up @@ -61,9 +62,10 @@ export async function importMnemonic(
version?: ApiTonWalletVersion,
) {
const isPrivateKey = isMnemonicPrivateKey(mnemonic);
const isBip39Mnemonic = validateBip39Mnemonic(mnemonic);
let isBip39Mnemonic = validateBip39Mnemonic(mnemonic);
const isTonMnemonic = await ton.validateMnemonic(mnemonic);

if (!isPrivateKey && !isBip39Mnemonic && !await validateMnemonic(mnemonic)) {
if (!isPrivateKey && !isBip39Mnemonic && !isTonMnemonic) {
throw new Error('Invalid mnemonic');
}

Expand All @@ -79,13 +81,21 @@ export async function importMnemonic(
}

let account: ApiAccountAny;
let tonWallet: ApiTonWallet & { lastTxId?: string } | undefined;
let tonAddress: string;
let tronAddress: string | undefined;

try {
if (isBip39Mnemonic && isTonMnemonic) {
tonWallet = await ton.getWalletFromMnemonic(mnemonic, network, version);
if (tonWallet.lastTxId) {
isBip39Mnemonic = false;
}
}

if (isBip39Mnemonic) {
const tronWallet = tron.getWalletFromBip39Mnemonic(network, mnemonic);
const tonWallet = await ton.getWalletFromBip39Mnemonic(network, mnemonic);
tonWallet = await ton.getWalletFromBip39Mnemonic(network, mnemonic);

tonAddress = tonWallet.address;
tronAddress = tronWallet.address;
Expand All @@ -96,9 +106,11 @@ export async function importMnemonic(
ton: tonWallet,
};
} else {
const tonWallet = isPrivateKey
? await ton.getWalletFromPrivateKey(mnemonic[0], network, version)
: await ton.getWalletFromMnemonic(mnemonic, network, version);
if (!tonWallet) {
tonWallet = isPrivateKey
? await ton.getWalletFromPrivateKey(mnemonic[0], network, version)
: await ton.getWalletFromMnemonic(mnemonic, network, version);
}
account = {
type: 'ton',
mnemonicEncrypted,
Expand Down

0 comments on commit 6f675fe

Please sign in to comment.