Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feature(mobile): Top up relayer with zero TON balance
Browse files Browse the repository at this point in the history
voloshinskii committed Apr 18, 2024
1 parent 1a98b87 commit 6997d44
Showing 8 changed files with 78 additions and 27 deletions.
10 changes: 10 additions & 0 deletions packages/@core-js/src/BatteryAPI/BatteryGenerated.ts
Original file line number Diff line number Diff line change
@@ -21,6 +21,11 @@ export interface Status {
}

export interface Config {
/**
* with zero balance it is possible to transfer some jettons (stablecoins, jusdt, etc) to this address to refill the balance. Such transfers would be paid by Battery Service.
* @example "0:07331e629e39d006d86a8cc7659c10a97c671f7535dc8b7f251a1a944dda348e"
*/
fund_receiver: string;
/**
* when building a message to transfer an NFT or Jetton, use this address to send excess funds back to Battery Service.
* @example "0:da6b1b6663a0e4d18cc8574ccd9db5296e367dd9324706f3bbd9eb1cd2caf0bf"
@@ -31,6 +36,11 @@ export interface Config {
export interface Balance {
/** @example "10.250" */
balance: string;
/**
* reserved amount in units (TON/USD)
* @example "0.3"
*/
reserved: string;
/** @example "usd" */
units: BalanceUnitsEnum;
}
15 changes: 12 additions & 3 deletions packages/mobile/src/blockchain/wallet.ts
Original file line number Diff line number Diff line change
@@ -38,6 +38,7 @@ import { createTonApiInstance } from '$wallet/utils';
import { config } from '$config';
import { toNano } from '$utils';
import { BatterySupportedTransaction } from '$wallet/managers/BatteryManager';
import { compareAddresses } from '$utils/address';

const TonWeb = require('tonweb');

@@ -329,8 +330,14 @@ export class TonWallet {
boc: string,
params?,
withRelayer = true,
forceRelayer = false,
): Promise<[BigNumber, boolean]> {
const { emulateResult, battery } = await emulateBoc(boc, params, withRelayer);
const { emulateResult, battery } = await emulateBoc(
boc,
params,
withRelayer,
forceRelayer,
);
return [new BigNumber(emulateResult.event.extra).multipliedBy(-1), battery];
}

@@ -427,6 +434,7 @@ export class TonWallet {
tk.wallet.battery.state.data.supportedTransactions[
BatterySupportedTransaction.Jetton
],
compareAddresses(address, tk.wallet.battery.fundReceiver),
);

return [Ton.fromNano(feeNano.toString()), isBattery];
@@ -473,7 +481,7 @@ export class TonWallet {
}

const excessesAccount = sendWithBattery
? await tk.wallet.battery.getExcessesAccount()
? tk.wallet.battery.excessesAccount
: tk.wallet.address.ton.raw;

const boc = this.createJettonTransfer({
@@ -497,6 +505,7 @@ export class TonWallet {
tk.wallet.battery.state.data.supportedTransactions[
BatterySupportedTransaction.Jetton
],
compareAddresses(address, tk.wallet.battery.fundReceiver),
);
feeNano = fee;
isBattery = battery;
@@ -632,7 +641,7 @@ export class TonWallet {
? AddressFormatter.isBounceable(address)
: false,
});
const [feeNano, isBattery] = await this.calcFee(boc);
const [feeNano, isBattery] = await this.calcFee(boc, undefined, true);
return [Ton.fromNano(feeNano.toString()), isBattery];
}

Original file line number Diff line number Diff line change
@@ -118,7 +118,7 @@ export const SignRawModal = memo<SignRawModalProps>((props) => {
const boc = TransactionService.createTransfer(contract, {
messages: TransactionService.parseSignRawMessages(
params.messages,
isBattery ? await tk.wallet.battery.getExcessesAccount() : undefined,
isBattery ? tk.wallet.battery.excessesAccount : undefined,
),
seqno: await getWalletSeqno(wallet),
sendMode: 3,
2 changes: 1 addition & 1 deletion packages/mobile/src/core/NFTSend/NFTSend.tsx
Original file line number Diff line number Diff line change
@@ -291,7 +291,7 @@ export const NFTSend: FC<Props> = (props) => {
throw new CanceledActionError();
}

const excessesAccount = isBattery && (await wallet.battery.getExcessesAccount());
const excessesAccount = isBattery && tk.wallet.battery.excessesAccount;

const nftTransferMessages = [
internal({
Original file line number Diff line number Diff line change
@@ -471,7 +471,7 @@ export function useDeeplinkingResolvers() {
const excessesAccount =
!config.get('disable_battery_send') &&
tk.wallet.battery.state.data.balance !== '0'
? await tk.wallet.battery.getExcessesAccount()
? tk.wallet.battery.excessesAccount
: null;

await openSignRawModal(
3 changes: 3 additions & 0 deletions packages/mobile/src/wallet/Wallet/WalletContent.ts
Original file line number Diff line number Diff line change
@@ -108,6 +108,7 @@ export class WalletContent extends WalletBase {
this.tonProof,
this.batteryapi,
this.storage,
this.isTestnet,
);
this.cards = new CardsManager(
this.persistPath,
@@ -171,6 +172,7 @@ export class WalletContent extends WalletBase {
this.staking.load(),
this.subscriptions.load(),
this.battery.load(),
this.battery.loadBatteryConfig(),
this.activityList.load(),
this.cards.load(),
]);
@@ -185,6 +187,7 @@ export class WalletContent extends WalletBase {
this.staking.reload(),
this.subscriptions.reload(),
this.battery.load(),
this.battery.loadBatteryConfig(),
this.activityList.reload(),
this.cards.load(),
]);
47 changes: 37 additions & 10 deletions packages/mobile/src/wallet/managers/BatteryManager.ts
Original file line number Diff line number Diff line change
@@ -4,6 +4,7 @@ import { Storage } from '@tonkeeper/core/src/declarations/Storage';
import { State } from '@tonkeeper/core/src/utils/State';
import { TonProofManager } from '$wallet/managers/TonProofManager';
import { logger, NamespacedLogger } from '$logger';
import { config } from '$config';

export enum BatterySupportedTransaction {
Swap = 'swap',
@@ -15,6 +16,8 @@ export interface BatteryState {
isLoading: boolean;
balance?: string;
reservedBalance?: string;
excessesAccount?: string;
fundReceiver?: string;
supportedTransactions: Record<BatterySupportedTransaction, boolean>;
}

@@ -37,6 +40,7 @@ export class BatteryManager {
private tonProof: TonProofManager,
private batteryapi: BatteryAPI,
private storage: Storage,
private isTestnet: boolean,
) {
this.state.persist({
partialize: ({ balance, reservedBalance, supportedTransactions }) => ({
@@ -50,6 +54,14 @@ export class BatteryManager {
this.logger = logger.extend('BatteryManager');
}

get excessesAccount() {
return this.state.data.excessesAccount;
}

get fundReceiver() {
return this.state.data.fundReceiver;
}

public async fetchBalance() {
try {
if (!this.tonProof.tonProofToken) {
@@ -68,30 +80,31 @@ export class BatteryManager {
this.state.set({
isLoading: false,
balance: data.balance,
// @ts-expect-error reservedAmount will be implemented in API later. Remove then
reservedBalance: data.reservedBalance ?? '0',
reservedBalance: data.reserved ?? '0',
});
} catch (err) {
this.state.set({ isLoading: false });
return null;
}
}

public async getExcessesAccount() {
public async loadBatteryConfig() {
try {
if (!this.tonProof.tonProofToken) {
throw new Error('No proof token');
}

const data = await this.batteryapi.getConfig({
headers: {
'X-TonConnect-Auth': this.tonProof.tonProofToken,
},
});

return data.excess_account;
console.log(data);
return this.state.set({
excessesAccount: data.excess_account,
fundReceiver: data.fund_receiver,
});
} catch (err) {
return null;
this.logger.error(err);
}
}

@@ -220,21 +233,35 @@ export class BatteryManager {
}
}

public async emulate(boc: string): Promise<MessageConsequences> {
public async emulate(
boc: string,
): Promise<{ consequences: MessageConsequences; withBattery: boolean }> {
try {
if (!this.tonProof.tonProofToken) {
throw new Error('No proof token');
}

return await this.batteryapi.emulate.emulateMessageToWallet(
{ boc },
const res = await fetch(
`${config.get('batteryHost', this.isTestnet)}/wallet/emulate`,
{
method: 'POST',
body: JSON.stringify({ boc }),
headers: {
'Content-Type': 'application/json',
'X-TonConnect-Auth': this.tonProof.tonProofToken,
},
},
);

const data: MessageConsequences = await res.json();

const withBattery =
res.headers.get('supported-by-battery') === 'true' &&
res.headers.get('allowed-by-battery') === 'true';

return { consequences: data, withBattery };
} catch (err) {
console.log(err);
throw new Error(err);
}
}
24 changes: 13 additions & 11 deletions packages/shared/utils/blockchain.ts
Original file line number Diff line number Diff line change
@@ -22,12 +22,7 @@ export async function sendBoc(boc, attemptWithRelayer = true) {
) {
throw new Error('Battery disabled');
}
if (
!tk.wallet.battery?.state?.data?.balance ||
tk.wallet.battery.state.data.balance === '0'
) {
throw new Error('Zero balance');
}

return await tk.wallet.battery.sendMessage(boc);
} catch (err) {
return await tk.wallet.tonapi.blockchain.sendBlockchainMessage(
@@ -39,7 +34,12 @@ export async function sendBoc(boc, attemptWithRelayer = true) {
}
}

export async function emulateBoc(boc, params?, attemptWithRelayer = false) {
export async function emulateBoc(
boc,
params?,
attemptWithRelayer = false,
forceRelayer = false,
) {
try {
if (
!attemptWithRelayer ||
@@ -48,14 +48,16 @@ export async function emulateBoc(boc, params?, attemptWithRelayer = false) {
) {
throw new Error('Battery disabled');
}

if (
!tk.wallet.battery?.state?.data?.balance ||
tk.wallet.battery.state.data.balance === '0'
!forceRelayer &&
(!tk.wallet.battery?.state?.data?.balance ||
tk.wallet.battery.state.data.balance === '0')
) {
throw new Error('Zero balance');
}
const emulateResult = await tk.wallet.battery.emulate(boc);
return { emulateResult, battery: true };
const { consequences, withBattery } = await tk.wallet.battery.emulate(boc);
return { emulateResult: consequences, battery: withBattery };
} catch (err) {
const emulateResult = await tk.wallet.tonapi.wallet.emulateMessageToWallet({
boc,

0 comments on commit 6997d44

Please sign in to comment.