Skip to content

Commit

Permalink
v1.19.32
Browse files Browse the repository at this point in the history
  • Loading branch information
mytonwalletorg committed May 30, 2024
1 parent ee6a96a commit b13ce10
Show file tree
Hide file tree
Showing 56 changed files with 946 additions and 298 deletions.
1 change: 1 addition & 0 deletions changelogs/1.19.32.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": "1.19.31",
"version": "1.19.32",
"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 @@
1.19.31
1.19.32
4 changes: 4 additions & 0 deletions src/api/blockchains/ton/constants.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { ApiParsedPayload, ApiWalletVersion } from '../../types';
import type { ContractInfo, ContractName } from './types';
import { Workchain } from '../../types';

export const TOKEN_TRANSFER_TONCOIN_AMOUNT = 100000000n; // 0.1 TON
export const TOKEN_TRANSFER_TONCOIN_FORWARD_AMOUNT = 1n; // 0.000000001 TON
Expand Down Expand Up @@ -29,6 +30,9 @@ export const ALL_WALLET_VERSIONS: ApiWalletVersion[] = [
'simpleR1', 'simpleR2', 'simpleR3', 'v2R1', 'v2R2', 'v3R1', 'v3R2', 'v4R2',
];

export const WORKCHAIN = Workchain.BaseChain;
export const TRANSFER_TIMEOUT_SEC = 60; // 1 min.

export enum OpCode {
Comment = 0,
Encrypted = 0x2167da4b,
Expand Down
1 change: 1 addition & 0 deletions src/api/blockchains/ton/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export {
decryptComment,
waitUntilTransactionAppears,
fixTokenActivitiesAddressForm,
submitTransferWithDiesel,
} from './transactions';
export {
getAccountBalance,
Expand Down
140 changes: 122 additions & 18 deletions src/api/blockchains/ton/transactions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
beginCell, Cell, external, internal, SendMode, storeMessage,
} from '@ton/core';

import type { DieselStatus } from '../../../global/types';
import type {
ApiActivity,
ApiAnyDisplayError,
Expand All @@ -20,16 +21,18 @@ import type {
ApiCheckTransactionDraftResult,
ApiSubmitMultiTransferResult,
ApiSubmitTransferResult,
ApiSubmitTransferWithDieselResult,
ApiTransactionExtra,
TonTransferParams,
} from './types';
import type { TonWallet } from './util/tonCore';
import { ApiCommonError, ApiTransactionDraftError, ApiTransactionError } from '../../types';

import { ONE_TON, TONCOIN_SLUG } from '../../../config';
import { DIESEL_ADDRESS, ONE_TON, TONCOIN_SLUG } from '../../../config';
import { parseAccountId } from '../../../util/account';
import { bigintMultiplyToNumber } from '../../../util/bigint';
import { compareActivities } from '../../../util/compareActivities';
import { fromDecimal } from '../../../util/decimals';
import { buildCollectionByKey, omit } from '../../../util/iteratees';
import { isValidLedgerComment } from '../../../util/ledger/utils';
import { logDebugError } from '../../../util/logs';
Expand All @@ -54,6 +57,7 @@ import {
toBase64Address,
} from './util/tonCore';
import { fetchStoredAccount, fetchStoredAddress } from '../../common/accounts';
import { callBackendGet } from '../../common/backend';
import { updateTransactionMetadata } from '../../common/helpers';
import { base64ToBytes, isKnownStakingPool } from '../../common/utils';
import { ApiServerError, handleServerError } from '../../errors';
Expand All @@ -62,12 +66,14 @@ import { fetchKeyPair, fetchPrivateKey } from './auth';
import {
ATTEMPTS, FEE_FACTOR, STAKE_COMMENT, UNSTAKE_COMMENT,
} from './constants';
import { buildTokenTransfer, parseTokenTransaction, resolveTokenBySlug } from './tokens';
import {
buildTokenTransfer, findTokenByMinter, parseTokenTransaction, resolveTokenBySlug,
} from './tokens';
import {
getContractInfo, getWalletBalance, getWalletInfo, pickAccountWallet,
} from './wallet';

const DEFAULT_EXPIRE_AT_TIMEOUT_SEC = 120; // 2 min (extended for diesel payment)
const DEFAULT_EXPIRE_AT_TIMEOUT_SEC = 600; // 10 min
const GET_TRANSACTIONS_LIMIT = 50;
const GET_TRANSACTIONS_MAX_LIMIT = 100;
const WAIT_TRANSFER_TIMEOUT = 120000; // 2 min
Expand Down Expand Up @@ -161,6 +167,7 @@ export async function checkTransactionDraft(
}

const account = await fetchStoredAccount(accountId);
const { address } = account;
const isLedger = !!account.ledger;

if (isLedger && !isLedgerAllowed) {
Expand All @@ -174,6 +181,8 @@ export async function checkTransactionDraft(
data = commentToBytes(data);
}

let tokenBalance: bigint | undefined;

if (!tokenAddress) {
if (
data
Expand All @@ -198,7 +207,6 @@ export async function checkTransactionDraft(
data = packBytesAsSnake(data);
}
} else {
const address = await fetchStoredAddress(accountId);
const tokenAmount: bigint = amount;
let tokenWallet: OpenedContract<JettonWallet>;
({
Expand All @@ -208,7 +216,7 @@ export async function checkTransactionDraft(
payload: data,
} = await buildTokenTransfer(network, tokenAddress, address, toAddress, amount, data));

const tokenBalance = await tokenWallet!.getJettonBalance();
tokenBalance = await tokenWallet!.getJettonBalance();
if (tokenBalance < tokenAmount!) {
return {
...result,
Expand All @@ -232,6 +240,17 @@ export async function checkTransactionDraft(
: balance >= amount + realFee;

if (!isEnoughBalance) {
if (network === 'mainnet' && tokenAddress) {
const { decimals } = findTokenByMinter(tokenAddress)!;
const { status: dieselStatus, amount: dieselAmount } = await estimateDiesel(address, tokenAddress);
result.dieselStatus = dieselStatus;
result.dieselAmount = dieselAmount ? fromDecimal(dieselAmount, decimals) : undefined;

if (dieselStatus !== 'not-available') {
return result;
}
}

return {
...result,
error: ApiTransactionDraftError.InsufficientBalance,
Expand All @@ -247,6 +266,13 @@ export async function checkTransactionDraft(
}
}

function estimateDiesel(address: string, tokenAddress: string) {
return callBackendGet<{
status: DieselStatus;
amount?: string;
}>('/diesel/estimate', { address, tokenAddress });
}

export async function checkToAddress(network: ApiNetwork, toAddress: string) {
const result: {
addressName?: string;
Expand Down Expand Up @@ -333,17 +359,9 @@ export async function submitTransfer(options: {
let encryptedComment: string | undefined;

if (typeof data === 'string') {
if (!data) {
data = undefined;
} else if (isBase64Data) {
data = parseBase64(data);
} else if (shouldEncrypt) {
const toPublicKey = (await getWalletPublicKey(network, toAddress))!;
data = await encryptMessageComment(data, publicKey, toPublicKey, secretKey, fromAddress);
encryptedComment = Buffer.from(data.slice(4)).toString('base64');
} else {
data = commentToBytes(data);
}
({ payload: data, encryptedComment } = await stringToPayload({
network, toAddress, fromAddress, data, secretKey, publicKey, shouldEncrypt, isBase64Data,
}));
}

if (!tokenAddress) {
Expand Down Expand Up @@ -392,10 +410,95 @@ export async function submitTransfer(options: {
}
}

export function resolveTransactionError(error: any): ApiAnyDisplayError {
export async function submitTransferWithDiesel(options: {
accountId: string;
password: string;
toAddress: string;
amount: bigint;
data?: AnyPayload;
tokenAddress: string;
shouldEncrypt?: boolean;
dieselAmount: bigint;
}): Promise<ApiSubmitTransferWithDieselResult> {
const {
toAddress,
amount,
accountId,
password,
tokenAddress,
shouldEncrypt,
dieselAmount,
} = options;

let { data } = options;

const { network } = parseAccountId(accountId);

const [account, keyPair] = await Promise.all([
fetchStoredAccount(accountId),
fetchKeyPair(accountId, password),
]);

const { address: fromAddress } = account;
const { publicKey, secretKey } = keyPair!;

let encryptedComment: string | undefined;

if (typeof data === 'string') {
({ payload: data, encryptedComment } = await stringToPayload({
network, toAddress, fromAddress, data, secretKey, publicKey, shouldEncrypt,
}));
}

const messages: TonTransferParams[] = [
omit(await buildTokenTransfer(network, tokenAddress, fromAddress, toAddress, amount, data), ['tokenWallet']),
omit(await buildTokenTransfer(network, tokenAddress, fromAddress, DIESEL_ADDRESS, dieselAmount), ['tokenWallet']),
];

const result = await submitMultiTransfer(accountId, password, messages, undefined, true);

return { ...result, encryptedComment };
}

async function stringToPayload({
network, toAddress, data, shouldEncrypt, publicKey, secretKey, fromAddress, isBase64Data,
}: {
network: ApiNetwork;
data: string;
shouldEncrypt?: boolean;
toAddress: string;
publicKey: Uint8Array;
secretKey: Uint8Array;
fromAddress: string;
isBase64Data?: boolean;
}): Promise<{
payload?: Uint8Array | Cell;
encryptedComment?: string;
}> {
let payload: Uint8Array | Cell | undefined;
let encryptedComment: string | undefined;

if (!data) {
payload = undefined;
} else if (isBase64Data) {
payload = parseBase64(data);
} else if (shouldEncrypt) {
const toPublicKey = (await getWalletPublicKey(network, toAddress))!;
payload = await encryptMessageComment(data, publicKey, toPublicKey, secretKey, fromAddress);
encryptedComment = Buffer.from(data.slice(4)).toString('base64');
} else {
payload = commentToBytes(data);
}

return { payload, encryptedComment };
}

export function resolveTransactionError(error: any): ApiAnyDisplayError | string {
if (error instanceof ApiServerError) {
if (error.message.includes('exitcode=35,')) {
return ApiTransactionError.IncorrectDeviceTime;
} else if (error.statusCode === 400) {
return error.message;
} else if (error.displayError) {
return error.displayError;
}
Expand Down Expand Up @@ -844,7 +947,8 @@ async function retrySendBoc(network: ApiNetwork, boc: string, timestamp: number)
while (Date.now() < waitUntil) {
const error = await tonClient.sendFile(boc).catch((err) => String(err));

if (!error || !error.includes('exitcode=33')) {
// Errors mean seqno change and not enough of balance
if (!error || (!error.includes('exitcode=33') && !error.includes('inbound external message rejected by account'))) {
await pause(WAIT_TRANSFER_PAUSE);
} else {
break;
Expand Down
7 changes: 7 additions & 0 deletions src/api/blockchains/ton/types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { Cell } from '@ton/core';

import type { DieselStatus } from '../../../global/types';
import type {
ApiAnyDisplayError, ApiParsedPayload, ApiTransaction, ApiWalletInfo, ApiWalletVersion,
} from '../../types';
Expand Down Expand Up @@ -116,4 +117,10 @@ export type ApiCheckTransactionDraftResult = {
isBounceable?: boolean;
isMemoRequired?: boolean;
error?: ApiAnyDisplayError;
dieselStatus?: DieselStatus;
dieselAmount?: bigint;
};

export type ApiSubmitTransferWithDieselResult = ApiSubmitMultiTransferResult & {
encryptedComment?: string;
};
6 changes: 3 additions & 3 deletions src/api/blockchains/ton/util/tonCore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import { WalletContractV4 } from '@ton/ton/dist/wallets/WalletContractV4';

import type { ApiNetwork, ApiWalletVersion } from '../../../types';
import type { TokenTransferBodyParams } from '../types';
import { WORKCHAIN } from '../../../types';

import {
DEFAULT_TIMEOUT,
Expand All @@ -30,9 +29,10 @@ import { JettonWallet } from '../contracts/JettonWallet';
import { hexToBytes } from '../../../common/utils';
import { getEnvironment } from '../../../environment';
import {
DEFAULT_IS_BOUNCEABLE, JettonOpCode, LiquidStakingOpCode, OpCode,
DEFAULT_IS_BOUNCEABLE, JettonOpCode, LiquidStakingOpCode, OpCode, WORKCHAIN,
} from '../constants';
import { dieselSendBoc } from './diesel';
import { generateQueryId } from './index';

import { TonClient } from './TonClient';

Expand Down Expand Up @@ -163,7 +163,7 @@ export function buildTokenTransferBody(params: TokenTransferBodyParams) {

let builder = new Builder()
.storeUint(JettonOpCode.Transfer, 32)
.storeUint(queryId || 0, 64)
.storeUint(queryId || generateQueryId(), 64)
.storeCoins(tokenAmount)
.storeAddress(Address.parse(toAddress))
.storeAddress(Address.parse(responseAddress))
Expand Down
3 changes: 1 addition & 2 deletions src/api/blockchains/ton/wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { beginCell, storeStateInit } from '@ton/core';
import type { ApiNetwork, ApiWalletVersion } from '../../types';
import type { ContractInfo, WalletInfo } from './types';
import type { TonWallet } from './util/tonCore';
import { WORKCHAIN } from '../../types';

import { DEFAULT_WALLET_VERSION } from '../../../config';
import { parseAccountId } from '../../../util/account';
Expand All @@ -15,7 +14,7 @@ import {
} from './util/tonCore';
import { fetchStoredAccount, fetchStoredAddress } from '../../common/accounts';
import { base64ToBytes, hexToBytes, sha256 } from '../../common/utils';
import { ALL_WALLET_VERSIONS, KnownContracts } from './constants';
import { ALL_WALLET_VERSIONS, KnownContracts, WORKCHAIN } from './constants';

export const isAddressInitialized = withCacheAsync(
async (network: ApiNetwork, walletOrAddress: TonWallet | string) => {
Expand Down
4 changes: 3 additions & 1 deletion src/api/common/backend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ export async function callBackendPost<T>(path: string, data: AnyLiteral, options
},
body: JSON.stringify(data),
});
handleFetchErrors(response, isAllowBadRequest ? [BAD_REQUEST_CODE] : undefined);

await handleFetchErrors(response, isAllowBadRequest ? [BAD_REQUEST_CODE] : undefined);

return response.json();
}

Expand Down
6 changes: 3 additions & 3 deletions src/api/common/cache.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import type { ApiStakingCommonData } from '../types';

type Cache = { stakedAt?: number };
export type AccountCache = { stakedAt?: number };

let stakingCommonCache: ApiStakingCommonData;
const accountCache: Record<string, Cache> = {};
const accountCache: Record<string, AccountCache> = {};

export function getAccountCache(accountId: string, address: string) {
return accountCache[`${accountId}:${address}`] ?? {};
}

export function updateAccountCache(accountId: string, address: string, partial: Partial<Cache>) {
export function updateAccountCache(accountId: string, address: string, partial: Partial<AccountCache>) {
const key = `${accountId}:${address}`;
accountCache[key] = { ...accountCache[key], ...partial };
}
Expand Down
Loading

0 comments on commit b13ce10

Please sign in to comment.