Skip to content

Commit

Permalink
change transaction sending to user's wallet
Browse files Browse the repository at this point in the history
  • Loading branch information
gicha committed Jun 24, 2024
1 parent ad3af2c commit 51abc7b
Show file tree
Hide file tree
Showing 7 changed files with 146 additions and 155 deletions.
9 changes: 4 additions & 5 deletions src/contexts/UserContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import Toaster from '@/components/ui/Toaster';
import { getUserTonPrivateKey, isUseApi, isUseTon } from '@/hooks/useTelegramStorage';
import { initBlockchainLogic } from '@/lib/contract/blockchain';
import { getMonthPeriodData, updateMonthPeriodData } from '@/lib/contract/data';
import { waitForAction } from '@/lib/contract/utils';
import { derivePublicKey } from '@/lib/encryption';
import { getFirstDayOfLastPeriod } from '@/utils/periodDates';

Expand Down Expand Up @@ -63,7 +62,7 @@ export const UserProvider: React.FC<UserProviderProps> = ({ children }) => {
const [farmingSessionLoading, setFarmingSessionLoading] = useState(true);

const initData = useInitData();
const { address, connected, wallet } = useTonConnect();
const { send, address, connected, wallet } = useTonConnect();

const fetchUser = async (telegramId: string): Promise<User | null> => {
try {
Expand Down Expand Up @@ -152,7 +151,7 @@ export const UserProvider: React.FC<UserProviderProps> = ({ children }) => {
}

const publicKey = derivePublicKey(tonPrivateKey);
await initBlockchainLogic(storedAddress!, publicKey);
await initBlockchainLogic(send, storedAddress!, publicKey);
dates = await getMonthPeriodData(storedAddress!, tonPrivateKey!);
}
const sortedMenstruationData = dates.sort((a: Date, b: Date) => a.getTime() - b.getTime());
Expand Down Expand Up @@ -236,8 +235,8 @@ export const UserProvider: React.FC<UserProviderProps> = ({ children }) => {
}

const publicKey = derivePublicKey(tonPrivateKey);
await initBlockchainLogic(storedAddress!, publicKey);
const result = await updateMonthPeriodData(storedAddress!, changes, tonPrivateKey!);
await initBlockchainLogic(send, storedAddress!, publicKey);
const result = await updateMonthPeriodData(send, storedAddress!, changes, tonPrivateKey!);

if (result.success) {
setMenstruations(result.data!);
Expand Down
70 changes: 53 additions & 17 deletions src/hooks/useTonConnect.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,21 @@
import { Sender, SenderArguments } from '@ton/core';
import { Cell, Contract, beginCell, storeStateInit } from '@ton/core';
import { CHAIN } from '@tonconnect/protocol';
import { Wallet, useTonAddress, useTonConnectUI, useTonWallet } from '@tonconnect/ui-react';
import {
SendTransactionResponse,
Wallet,
useTonAddress,
useTonConnectUI,
useTonWallet,
} from '@tonconnect/ui-react';

export interface UserTransaction {
contract: Contract;
amount: bigint;
payload?: Cell | null;
}

export function useTonConnect(): {
sender: Sender;
send: (transactions: UserTransaction[]) => Promise<SendTransactionResponse>;
connected: boolean;
wallet: string | null;
walletObject: Wallet | null;
Expand All @@ -14,21 +26,45 @@ export function useTonConnect(): {
const wallet = useTonWallet();
const address = useTonAddress();

return {
sender: {
send: async (args: SenderArguments) => {
tonConnectUI.sendTransaction({
messages: [
{
address: args.to.toString(),
amount: args.value.toString(),
payload: args.body?.toBoc().toString('base64'),
},
],
validUntil: Date.now() + 5 * 60 * 1000, // 5 minutes for user to approve
});
const send = (transactions: UserTransaction[]) => {
return tonConnectUI.sendTransaction(
{
messages: transactions.map((transaction) => {
const address = transaction.contract.address.toString();
const amount = transaction.amount.toString();
const init = transaction.contract.init;
const payload = transaction.payload?.toBoc().toString('base64');
if (init == null) {
return {
address,
amount,
payload,
};
}
const stateInit = beginCell()
.store(storeStateInit(init))
.endCell()
.toBoc()
.toString('base64');
return {
address,
amount,
stateInit,
payload,
};
}),
from: wallet?.account.address,
network: wallet?.account.chain,
validUntil: Date.now() + 5 * 60 * 1000, // 5 minutes for user to approve
},
},
{
modals: 'all',
}
);
};

return {
send,
connected: !!wallet?.account.address,
wallet: wallet?.account.address ?? null,
walletObject: wallet ?? null,
Expand Down
107 changes: 58 additions & 49 deletions src/lib/contract/blockchain.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { toNano } from '@ton/ton';
import { Cell, beginCell, toNano } from '@ton/ton';
import { SendTransactionResponse } from '@tonconnect/ui-react';

import { UserTransaction } from '@/hooks/useTonConnect';
import { waitForAction } from '@/lib/contract/utils';
import { Account } from 'src/ton_client/tact_Account';
import { Account, storeDeploy, storeSetPublicKey } from 'src/ton_client/tact_Account';

import { getBotSender } from './bot';
import { tonClient } from './tonClient';

let blockchainLogicInited = false;
Expand All @@ -18,20 +19,44 @@ export async function isContractDeployed(userAddress: string) {
return await tonClient.isContractDeployed(contract.address);
}

export async function initBlockchainLogic(userAddress: string, publicKey: string) {
export async function initBlockchainLogic(
send: (transactions: UserTransaction[]) => Promise<SendTransactionResponse>,
userAddress: string,
publicKey: string
) {
if (!blockchainLogicInited) {
if (initing === null) {
initing = new Promise(async (resolve) => {
try {
const contract = await Account.fromInit(userAddress);
const deployed = await isDeployed(userAddress);
const deployTransaction = {
contract,
amount: toNano('0.008'),
payload: getDeployPayload(),
};
const publicKeyTransaction = {
contract,
amount: toNano('0.008'),
payload: getPublicKeyPayload(publicKey),
};
console.log('Deployed:', deployed);
if (!deployed) {
await deployContract(userAddress);
}
const contractPublicKey = await getPublicKey(userAddress);
if (contractPublicKey !== publicKey) {
await setPublicKey(userAddress, publicKey);
const res = await send([deployTransaction, publicKeyTransaction]);
console.log('Deploy response:', res);
} else {
const contractPublicKey = await getPublicKey(userAddress);
console.log('Contract public key:', contractPublicKey);
console.log('Public key:', publicKey);
const savedKeyIsEqual = contractPublicKey === publicKey;
console.log('Saved key is equal:', savedKeyIsEqual);
if (!savedKeyIsEqual) {
await send([publicKeyTransaction]);
}
}
console.log('Waiting for blockchain logic init');
await waitForAction(async () => await isInited(userAddress, publicKey));
console.log('Blockchain logic inited');
blockchainLogicInited = true;
} catch (e) {
console.error('Failed to init blockchain logic', e);
Expand All @@ -44,45 +69,33 @@ export async function initBlockchainLogic(userAddress: string, publicKey: string
}
}

async function deployContract(userAddress: string) {
const sender = await getBotSender();
const contract = await Account.fromInit(userAddress);
const openedContract = tonClient.open(contract);
console.log('Contract deploying');
await openedContract.send(
sender,
{ value: toNano('0.008') },
{
$$type: 'Deploy',
queryId: BigInt(0),
}
);
console.log('Contract deployment initiated');

//TODO: get rid of it?
await waitForAction(() => tonClient.isContractDeployed(contract.address));
console.log('Contract deployed');
console.log('Address:', contract.address.toString());
function getDeployPayload(): Cell {
return beginCell()
.store(
storeDeploy({
$$type: 'Deploy',
queryId: BigInt(0),
})
)
.endCell();
}

async function setPublicKey(userAddress: string, publicKey: string) {
const sender = await getBotSender();
const contract = await Account.fromInit(userAddress);
const openedContract = tonClient.open(contract);
console.log('Setting public key...');
await openedContract.send(
sender,
{ value: toNano('0.008') },
{
$$type: 'SetPublicKey',
publicKey,
}
);
console.log('Public key set');
function getPublicKeyPayload(publicKey: string): Cell {
return beginCell()
.store(
storeSetPublicKey({
$$type: 'SetPublicKey',
publicKey,
})
)
.endCell();
}

//TODO: get rid of it?
await waitForAction(async () => (await openedContract.getPublicKey()) === publicKey);
console.log('Public key set');
async function isInited(userAddress: string, expectedPublicKey: string) {
const deployed = await isDeployed(userAddress);
if (!deployed) return false;
const publicKey = await getPublicKey(userAddress);
return publicKey === expectedPublicKey;
}

export async function getPublicKey(userAddress: string): Promise<string> {
Expand All @@ -96,7 +109,3 @@ export async function isDeployed(userAddress: string): Promise<boolean> {
const contract = await Account.fromInit(userAddress);
return await tonClient.isContractDeployed(contract.address);
}

export async function deploy(userAddress: string) {
await deployContract(userAddress);
}
21 changes: 0 additions & 21 deletions src/lib/contract/bot.ts

This file was deleted.

40 changes: 24 additions & 16 deletions src/lib/contract/data.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
import { Address, Dictionary, toNano } from '@ton/ton';
import { Address, Dictionary, beginCell, toNano } from '@ton/ton';
import { SendTransactionResponse } from '@tonconnect/ui-react';
import assert from 'assert';
import axios from 'axios';

import { decryptData, encryptData, derivePublicKey } from '@/lib/encryption';
import { Account, PeriodDataItem } from 'src/ton_client/tact_Account';
import { UserTransaction } from '@/hooks/useTonConnect';
import { decryptData, derivePublicKey, encryptData } from '@/lib/encryption';
import { Account, PeriodDataItem, storeUpdateMonthPeriodData } from 'src/ton_client/tact_Account';
import { MonthPeriodData } from 'src/ton_client/tact_MonthPeriodData';

import { isContractDeployed } from './blockchain';
import { getBotSender } from './bot';
import { tonClient } from './tonClient';
import { getMonthDataUpdates, waitForAction } from './utils';

export async function updateMonthPeriodData(
send: (transactions: UserTransaction[]) => Promise<SendTransactionResponse>,
userAddress: string,
changes: { date: Date; action: 'add' | 'delete' }[],
privateKey: string
Expand All @@ -38,7 +40,7 @@ export async function updateMonthPeriodData(
}
expectedDateArray.push(...toAdd);
const publicKey = derivePublicKey(privateKey);
const result = await runUpdateMonthPeriodData(userAddress, toAdd, toDelete, privateKey);
const result = await runUpdateMonthPeriodData(send, userAddress, toAdd, toDelete, privateKey);
if (!result.success) {
return { success: false, error: result.error, statusCode: result.statusCode };
}
Expand Down Expand Up @@ -66,6 +68,7 @@ export async function updateMonthPeriodData(
}

export async function runUpdateMonthPeriodData(
send: (transactions: UserTransaction[]) => Promise<SendTransactionResponse>,
userAddress: string,
toAdd: Date[],
toDelete: Date[],
Expand All @@ -78,7 +81,6 @@ export async function runUpdateMonthPeriodData(
const publicKey = derivePublicKey(privateKey);
const dataOwnerAddress = Address.parse(userAddress);
console.log('Saving users health data');
const sender = await getBotSender();
const contract = await Account.fromInit(userAddress);
const openedContract = tonClient.open(contract);
const months = getMonthDataUpdates(toAdd, toDelete);
Expand Down Expand Up @@ -115,17 +117,23 @@ export async function runUpdateMonthPeriodData(
});
}
try {
await openedContract.send(
sender,
{ value: toNano('0.04') },
await send([
{
$$type: 'UpdateMonthPeriodData',
accessedAddress: dataOwnerAddress,
monthIndex: BigInt(monthIndex),
toAdd: toAddDict,
toDelete: toDeleteDict,
}
);
contract,
amount: toNano('0.03'),
payload: beginCell()
.store(
storeUpdateMonthPeriodData({
$$type: 'UpdateMonthPeriodData',
accessedAddress: dataOwnerAddress,
monthIndex: BigInt(monthIndex),
toAdd: toAddDict,
toDelete: toDeleteDict,
})
)
.endCell(),
},
]);
} catch (error) {
if (axios.isAxiosError(error)) {
console.error('Error sending update month period data:', error);
Expand Down
2 changes: 1 addition & 1 deletion src/lib/contract/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ export interface OneMonthDataUpdate {

export async function waitForAction(
checkCondition: () => Promise<boolean>,
attempts = 50,
attempts = 120,
sleepDuration = 500
) {
if (attempts <= 0) {
Expand Down
Loading

0 comments on commit 51abc7b

Please sign in to comment.