Skip to content

Commit

Permalink
docs: custom transactions with predicates (#2864)
Browse files Browse the repository at this point in the history
* doc: add test and doc for predicate custom transaction

* chore: changeset

* docs: typo

* docs: fix workding

Co-authored-by: Anderson Arboleya <[email protected]>

* docs: fix wording

Co-authored-by: Anderson Arboleya <[email protected]>

* docs: move test launcher out of snippet

* docs: fix context imports

* docs: fix import

* docs: use import

* test: fix provider

* docs: spacing

* chore: fix imports

---------

Co-authored-by: Sérgio Torres <[email protected]>
Co-authored-by: Anderson Arboleya <[email protected]>
  • Loading branch information
3 people committed Aug 6, 2024
1 parent 751d638 commit 2f0a176
Show file tree
Hide file tree
Showing 4 changed files with 111 additions and 0 deletions.
4 changes: 4 additions & 0 deletions .changeset/long-deers-behave.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
---

docs: custom transactions with predicates
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
// eslint-disable-next-line @typescript-eslint/no-unused-vars
import { Provider, ScriptTransactionRequest, Wallet, TESTNET_NETWORK_URL } from 'fuels';
import { launchTestNode } from 'fuels/test-utils';

import { ConfigurablePinAbi__factory as PredicateFactory } from '../../../test/typegen';
import type { ConfigurablePinAbiInputs as PredicateInputs } from '../../../test/typegen';

/**
* @group node
* @group browser
*/
describe('Predicate Custom Transactions', () => {
it('uses a predicate in a custom transaction', async () => {
using launched = await launchTestNode();
const {
wallets: [testSender, testReceiver],
provider: testProvider,
} = launched;

const SENDER_PVT_KEY = testSender.privateKey;
const RECEIVER_ADDRESS = testReceiver.address;
// eslint-disable-next-line @typescript-eslint/no-shadow
const TESTNET_NETWORK_URL = testProvider.url;

await Provider.create(TESTNET_NETWORK_URL);

const initialRecieverBalance = await testReceiver.getBalance(testProvider.getBaseAssetId());

// #region predicate-custom-transaction
// #import { Provider, ScriptTransactionRequest, TESTNET_NETWORK_URL, Wallet };
// #context import { PredicateFactory } from 'path/to/typegen/outputs';
// #context import type { PredicateInputs } from 'path/to/typegen/outputs';
// #context import { SENDER_PVT_KEY, RECEIVER_ADDRESS } from 'path/to/my/env/file';

// Setup
const provider = await Provider.create(TESTNET_NETWORK_URL);
const sender = Wallet.fromPrivateKey(SENDER_PVT_KEY, provider);
const receiver = Wallet.fromAddress(RECEIVER_ADDRESS, provider);
const assetId = provider.getBaseAssetId();
const amountToFundPredicate = 300_000;
const amountToReceiver = 100_000;

// Instantiate the predicate using valid predicate data, aka the pin we need
// to send the funds to the receiver
const predicateData: PredicateInputs = [1337];
const predicate = PredicateFactory.createInstance(provider, predicateData);

// Fund the predicate, so that we can send these funds via predicate logic
// to the receiver
const fundPredicateTx = await sender.transfer(
predicate.address,
amountToFundPredicate,
assetId
);
await fundPredicateTx.waitForResult();
const initialPredicateBalance = await predicate.getBalance(assetId);

// Instantiate the script request
const customRequest = new ScriptTransactionRequest();

// Get the predicate resources that we would like to transfer
const predicateResources = await predicate.getResourcesToSpend([
{ assetId, amount: amountToReceiver },
]);

// Add the resources for the transfer of the asset to the receiver. The resources
// adds the required inputs, and the output is for the transfer to the receiver address
customRequest.addResources(predicateResources);
customRequest.addCoinOutput(receiver.address, amountToReceiver, assetId);

// Estimate the transaction cost and fund accordingly
const txCost = await predicate.getTransactionCost(customRequest);
customRequest.gasLimit = txCost.gasUsed;
customRequest.maxFee = txCost.maxFee;
await predicate.fund(customRequest, txCost);

// Submit the transaction and await it's result
const predicateTx = await predicate.sendTransaction(customRequest);
await predicateTx.waitForResult();
// #endregion predicate-custom-transaction

expect(initialPredicateBalance.toNumber()).toEqual(amountToFundPredicate);
const { isStatusSuccess } = await predicateTx.waitForResult();
expect(isStatusSuccess).toBe(true);
const finalReceiverBalance = await receiver.getBalance(assetId);
expect(finalReceiverBalance.toNumber()).toEqual(
initialRecieverBalance.add(amountToReceiver).toNumber()
);
});
});
4 changes: 4 additions & 0 deletions apps/docs/.vitepress/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,10 @@ export default defineConfig({
text: 'Methods',
link: '/guide/predicates/methods',
},
{
text: 'Custom Transactions',
link: '/guide/predicates/custom-transactions',
},
],
},
{
Expand Down
13 changes: 13 additions & 0 deletions apps/docs/src/guide/predicates/custom-transactions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Custom Transactions

Utilizing predicate logic unlocks a wide range of possibilities for your dApps when creating transactions. Therefore, pairing predicates with custom transactions can help you achieve more complex use cases. This can be achieved by instantiating a custom transaction, appending the predicate resources, and submitting the transaction via a successfully validated predicate.

Custom transactions can be shaped via a `ScriptTransactionRequest` instance. For more information on crafting custom transactions and the methods available to them, please refer to the [Transaction Request](../transactions/transaction-request.md) guide.

However, this guide will demonstrate how to use a predicate in a custom transaction. Consider the following predicate, where a configurable pin must be used to validate the predicate and unlock the funds:

<<< @/../../docs-snippets/test/fixtures/forc-projects/configurable-pin/src/main.sw#predicate-with-configurable-pin-1{rust:line-numbers}

We can interact with the above and include it in a custom transaction like so:

<<< @/../../docs-snippets/src/guide/predicates/predicate-custom-transactions.test.ts#predicate-custom-transaction{ts:line-numbers}

0 comments on commit 2f0a176

Please sign in to comment.