From 43fbfc716f84f0ede9cef84196c713cbb64302ff Mon Sep 17 00:00:00 2001 From: Daniel Bate Date: Tue, 31 Dec 2024 16:44:18 +0000 Subject: [PATCH] docs: optimistic predicates --- apps/docs/.vitepress/config.ts | 4 ++ .../guide/optimizing-transactions/index.md | 2 + .../optimistic-predicates.md | 32 +++++++++ .../snippets/optimistic-predicates-after.ts | 70 +++++++++++++++++++ .../snippets/optimistic-predicates-before.ts | 32 +++++++++ 5 files changed, 140 insertions(+) create mode 100644 apps/docs/src/guide/optimizing-transactions/optimistic-predicates.md create mode 100644 apps/docs/src/guide/optimizing-transactions/snippets/optimistic-predicates-after.ts create mode 100644 apps/docs/src/guide/optimizing-transactions/snippets/optimistic-predicates-before.ts diff --git a/apps/docs/.vitepress/config.ts b/apps/docs/.vitepress/config.ts index 48af2bd360..9191bdaf0e 100644 --- a/apps/docs/.vitepress/config.ts +++ b/apps/docs/.vitepress/config.ts @@ -359,6 +359,10 @@ export default defineConfig({ text: 'Optimistic Contract Calls', link: '/guide/optimizing-transactions/optimistic-contract-calls', }, + { + text: 'Optimistic Predicates', + link: '/guide/optimizing-transactions/optimistic-predicates', + }, ], }, { diff --git a/apps/docs/src/guide/optimizing-transactions/index.md b/apps/docs/src/guide/optimizing-transactions/index.md index fd732c5baa..4fbc1e9dfa 100644 --- a/apps/docs/src/guide/optimizing-transactions/index.md +++ b/apps/docs/src/guide/optimizing-transactions/index.md @@ -12,5 +12,7 @@ Depending on how you are performing the transaction, all of the above may have b This can be mitigated by optimistically building the transaction before your user submits the transaction. Pre-preparation of the transaction can speed increases for your users of **~2x**. Check out the following guides on implementing optimistic transaction building: + - [Optimistic Transactions](./optimistic-transactions) - [Optimistic Contract Calls](./optimistic-contract-calls) +- [Optimistic Predicates](./optimistic-predicates) diff --git a/apps/docs/src/guide/optimizing-transactions/optimistic-predicates.md b/apps/docs/src/guide/optimizing-transactions/optimistic-predicates.md new file mode 100644 index 0000000000..e55e55e8a4 --- /dev/null +++ b/apps/docs/src/guide/optimizing-transactions/optimistic-predicates.md @@ -0,0 +1,32 @@ +# Optimistic Predicates + +Imagine we have an application that allows a user to transfer funds to another user given a validated predicate condition, here we'll use a pin number. + +```tsx + setPin(e.target.value)} +/> + setRecipientAddress(e.target.value)} +/> + +``` + +This would likely have the following handler function: + +<<< @./snippets/optimistic-predicates-before.ts#main{ts:line-numbers} + +Under the hood, the `transfer` call is making multiple calls to the network to both simulate and fund the transaction, then submitting it. This may give the appearance of slowness for users interacting with your application. + +This process can be optimized by optimistically building the transaction on page load, like so: + +<<< @./snippets/optimistic-transactions-after.ts#main{ts:line-numbers} + +> [!NOTE] +> Any change to the underlying transaction will require re-estimation and re-funding of the transaction. Otherwise the transaction could increase in size and therefore cost, causing the transaction to fail. +> +> For predicates, the data passed to validate it could potentially alter the amount of gas the predicate consumes. This may mean we need to re-estimate and re-fund the transaction. diff --git a/apps/docs/src/guide/optimizing-transactions/snippets/optimistic-predicates-after.ts b/apps/docs/src/guide/optimizing-transactions/snippets/optimistic-predicates-after.ts new file mode 100644 index 0000000000..8fd8c4aaa5 --- /dev/null +++ b/apps/docs/src/guide/optimizing-transactions/snippets/optimistic-predicates-after.ts @@ -0,0 +1,70 @@ +// #region main +import type { Account } from 'fuels'; +import { Provider, Wallet, ScriptTransactionRequest, Address } from 'fuels'; + +import { LOCAL_NETWORK_URL, WALLET_PVT_KEY_2 } from '../../../env'; +import { ConfigurablePin } from '../../../typegend'; + +const { info } = console; + +let provider: Provider; +let sender: Account; +let request: ScriptTransactionRequest; +let predicate: ConfigurablePin; + +// This is a generic page load function which should be called +// as soon as the user lands on the page +async function onPageLoad() { + // Initialize the provider and wallet + provider = await Provider.create(LOCAL_NETWORK_URL); + sender = Wallet.fromPrivateKey(WALLET_PVT_KEY_2, provider); + + // Instantiate the predicate and fund it + predicate = new ConfigurablePin({ provider }); + const fundTx = await sender.transfer( + predicate.address, + 500_000, + provider.getBaseAssetId() + ); + await fundTx.waitForResult(); + + // Create a new transaction request and add the predicate resources + request = new ScriptTransactionRequest(); + const resources = await predicate.getResourcesToSpend([ + [100_000, provider.getBaseAssetId()], + ]); + request.addResources(resources); + + // Estimate and fund the transaction, including the predicate gas used + const txCost = await predicate.getTransactionCost(request); + request.updatePredicateGasUsed(txCost.estimatedPredicates); + request.gasLimit = txCost.gasUsed; + request.maxFee = txCost.maxFee; + await predicate.fund(request, txCost); +} + +async function onTransferPressed(pin: number, recipientAddress: string) { + // When the user presses the transfer button, we add the output + // to the transaction request + request.addCoinOutput( + Address.fromString(recipientAddress), + 10_000, + provider.getBaseAssetId() + ); + // Then we must alter any existing predicate data that may have changed + const predicateWithData = new ConfigurablePin({ provider, data: [pin] }); + const requestWithData = + predicateWithData.populateTransactionPredicateData(request); + // And submit the transaction, ensuring that the dependencies are + // not re-estimated and making redundant calls to the network + const transaction = await sender.sendTransaction(requestWithData, { + estimateTxDependencies: false, + }); + info(`Transaction ID Submitted: ${transaction.id}`); + const result = await transaction.waitForResult(); + info(`Transaction ID Successful: ${result.id}`); +} +// #endregion main + +await onPageLoad(); +await onTransferPressed(1337, Wallet.generate().address.toString()); diff --git a/apps/docs/src/guide/optimizing-transactions/snippets/optimistic-predicates-before.ts b/apps/docs/src/guide/optimizing-transactions/snippets/optimistic-predicates-before.ts new file mode 100644 index 0000000000..a0b3e2f21c --- /dev/null +++ b/apps/docs/src/guide/optimizing-transactions/snippets/optimistic-predicates-before.ts @@ -0,0 +1,32 @@ +// #region main +import { Provider, Wallet } from 'fuels'; + +import { LOCAL_NETWORK_URL, WALLET_PVT_KEY } from '../../../env'; +import { ConfigurablePin } from '../../../typegend'; + +const { info } = console; + +async function onTransferPressed(pin: number, recipientAddress: string) { + // Initialize the provider and sender + const provider = await Provider.create(LOCAL_NETWORK_URL); + const sender = Wallet.fromPrivateKey(WALLET_PVT_KEY, provider); + + // Instantiate the predicate and fund it + const predicate = new ConfigurablePin({ provider, data: [pin] }); + const fundTx = await sender.transfer( + predicate.address, + 500_000, + provider.getBaseAssetId() + ); + await fundTx.waitForResult(); + + // Calling the transfer function will create the transaction, + // and then perform multiple network requests to fund, simulate and submit + const transaction = await predicate.transfer(recipientAddress, 10_000); + info(`Transaction ID Submitted: ${transaction.id}`); + const result = await transaction.waitForResult(); + info(`Transaction ID Successful: ${result.id}`); +} +// #endregion main + +await onTransferPressed(1337, Wallet.generate().address.toString());