English
EnglishRussian

Improvements

Subscribe to account changes

In our example, we simply queried maxBet from the dice contract. But in reality, the dice can be played by several players at the same time, and this value can change often. Therefore, after getting the value, we need to subscribe transactions on the account and update this value:


export const TokenDiceContractAddress = new Address("0:19128985f2d034a0a7b8dad5b23946aff3e63fe68c13243feb58124cef9acbb6"); const tokenDiceContract = new provider.Contract(TokenDiceAbi, TokenDiceContractAddress); const {value0: maxBet} = await tokenDiceContract.methods.maxBet().call(); setMaxBet(maxBet); // some logic const subscriber = new provider.Subscriber(); subscriber.states(tokenDiceContract.address).on(async (update) => { // Account state was changed! // Fetch max bet again const {value0: maxBet} = await tokenDiceContract.methods.maxBet().call(); setMaxBet(maxBet); // some logic // you need to add some concurency controll there, in case tokenDiceContract have a lot // transactions in parallel. // You can use update.state.lastTransactionId.lt - monotonic counter of account // transaction time. });

You should do the same with the user's token balance to update it dynamically when the state of the TokenWallet changes.



Account caching

Every time you make a request to the smart contract, the inpage-provider goes online to query the complete state of the account and executes this request locally by running TVM. However, if you only need to make static queries or don't care about the latest status, you can cache the state once and run the queries very quickly.


For example, we make three queries to the TokenRoot contract to get the user's decimal, symbol, and TokenWallet address. Let's rewrite these queries using the cache:


const tokenRootContract = new provider.Contract(TokenRootAbi, TokenRootContractAddress); const {state: tokenRootFullState} = await tokenRootContract.getFullState(); const { value0: userTokenWalletAddress } = await tokenRootContract.methods.walletOf({answerId: 0, walletOwner: userAddress}).call({cachedState: tokenRootFullState}); const { value0: decimals } = await tokenRootContract.methods.decimals({answerId: 0}).call({cachedState: tokenRootFullState}); const { value0: symbol } = await tokenRootContract.methods.symbol({answerId: 0}).call({cachedState: tokenRootFullState});


Waiting for call chain to complete

When we rolled a die, we waited until the entire chain of calls was complete before displaying the result. But really, the only thing the user cares about is the result of the transaction in which the die was rolled. He does not have to wait until the contract returns him the change in Venom (+ 1 transaction), or transfer tokens and then return the change (+ 2 transactions). So let us just wait until this transaction happens:


// before // let playTx; // const subscriber = new provider.Subscriber(); // await subscriber.trace(firstTx).tap(tx_in_tree => { // if (tx_in_tree.account.equals(TokenDiceContractAddress)) { // playTx = tx_in_tree; // } // }).finished(); // let events = await tokenDiceContract.decodeTransactionEvents({transaction: playTx}); // after const subscriber = new provider.Subscriber(); const playTx = await subscriber.trace(firstTx).filter(tx_in_tree => { return tx_in_tree.account.equals(TokenDiceContractAddress); }).first(); let events = await tokenDiceContract.decodeTransactionEvents({transaction: playTx}); // ...


Token Transfer Optimization

When we transfer tokens in our application, we always set the deployWalletValue parameter to 0.1. This means that TokenWallet creates a message to deploy the destination wallet before creating an outgoing message to transfer tokens. This is a common practice when you don't know if the recipient's wallet has been deployed, but it creates an additional transaction.


In our case, we know for sure that both wallets are deployed at the time of the game (the player's wallet and the dice wallet), so we can set deployWalletValue to 0 and not have to spend extra money on fees.


// before // await userTokenWalletContract.methods.transfer({ // amount: '1000000000', // recipient: tokenDiceContract.address, // deployWalletValue: '100000000', //0.1 Venom // remainingGasTo: userAddress, // notify: true, // payload: payload // }).send({ // amount: "1500000000", // bounce: true, // from: userAddress // }) // after await userTokenWalletContract.methods.transfer({ amount: '1000000000', recipient: tokenDiceContract.address, deployWalletValue: '0', //already deployed remainingGasTo: userAddress, notify: true, payload: payload }).send({ amount: "1500000000", bounce: true, from: userAddress })