FuelLabs / fuels-ts

Fuel Network Typescript SDK
https://docs.fuel.network/docs/fuels-ts/
Apache License 2.0
44.35k stars 1.33k forks source link

Experiment with `fuels` entry point #2490

Open arboleya opened 1 month ago

arboleya commented 1 month ago

TL;DR

The idea is to provide a facade API composed of interconnected factory methods.

The aim is to shortcut the interaction with fuel nodes by:

  1. Providing a single fuels entry point within the fuels library (aka a provider factory)
  2. The returned object from the previous step should contain module initializers for:
    1. wallet
    2. contract
    3. predicate
    4. script
  3. From there, most methods can be chained and used fluidly.

This proposal can enable:

  1. Intuitive API that doesn't require memorization
  2. Concise onliners to be easily composed and exchanged
  3. Seamless usage across different platforms (node, browser)
  4. Easily digestible code snippets to be used in documentation
  5. Etc.

A public API crafted around this idea can narrow the user journey to what matters, hiding away most of the internal implementation details (not all) and paving the way for broader refactorings down the road, in which we can change or swap underlying pieces without changing the public APIs, reducing the occurrences of breaking changes.

Pseudo-code

[!IMPORTANT] All code samples in this page are pesude code.

API

Here's a rough example of how it would look.

// single import from fuels, the rest comes from the dApps
import { fuels, TESTNET_NETWORK_URL } from 'fuels';

// typegen'd classes
import {
  MyContractFactory,
  MyContract2Factory,
  MyContract3Factory,
} from './sway-api'; 

// usage example
const options: fuels.Options = { ... };

(await fuels(TESTNET_NETWORK_URL, options))
  .contract(MyContractFactory)
  .addContracts([ MyContract2Factory, MyContract3Factory ])
    .get_counter()
    .callParams({
      forward: CoinQuantityLike;
      gasLimit: BigNumberish;
    })
    .txParams({
      gasPrice: BigNumberish;
      gas limit: BigNumberish;
      maturity?: number;
      maxFee?: BigNumberish;
      witnessLimit?: BigNumberish;
      variableOutputs: number;
    })
    .<get|dryRun|simulate|call>();

Promises

The fuels entry point is a provider factory that returns a promise.

The promise is resolved after initializing the connection with the node.

From there, the user has everything to move on.

import { fuels, DEVNET_NETWORK_URL } from 'fuels';

import { MyContractFactory } from './sway-api';

fuels(DEVNET_NETWORK_URL)
  .then(({ contract ) => {
    const counter = contract(MyContractFactory).fns.getCounter().get();
    console.log({ counter });
  });

Callbacks (+ transfer example)

The same can be seamlessly achieved with callbacks.

import { fuels, DEVNET_NETWORK_URL } from 'fuels';

import { MyContractFactory } from './sway-api';

fuels(DEVNET_NETWORK_URL, async ({ wallet, nodeInfo }) => {

  const assetId = nodeInfo.getBaseAssetId();
  const privateKey = 'abc..xyz';
  const amount = 10;
  const address = '0x...';

  const transfer = await wallet({ privateKey }).transfer(address, amount, assetId);

  const balance = await wallet({ address }).getBalance(assetId);

  expect(transfer.abc).toEqual(xyz);
  expect(balance.toNumber()).toBeGreaterOrEqualThan(amount);

});

Oneliners

import { fuels, TESTNET_NETWORK_URL } from 'fuels';

import { MyContract, MyPredicate, MyScript } from './sway-api';

// reading block info
(await fuels(TESTNET_NETWORK_URL)).getBlock();

// provider's methods
(await fuels(TESTNET_NETWORK_URL)).fetchChain();
(await fuels(TESTNET_NETWORK_URL)).<otherProviderMethod>();

// wallet
(await fuels(TESTNET_NETWORK_URL)).wallet("0x...").getBalance();

// contract
(await fuels(TESTNET_NETWORK_URL)).contract(MyContract).fns.get_counter().get();

// predicate
(await fuels(TESTNET_NETWORK_URL)).predicate(MyPredicate).transfer();

// script
(await fuels(TESTNET_NETWORK_URL)).script(MyScript).run();

Normalized TX cost estimation

import { fuels, TESTNET_NETWORK_URL } from 'fuels';

import { MyContract, MyPredicate, MyScript } from './sway-api';

const client = await fuels(TESTNET_NETWORK_URL);

const cost1 = await client.contract(MyContract).fns.get_counter().estimateCost();
const cost2 = await client.predicate(MyPredicate).fns.main().estimateCost();
const cost3 = await client.script(MyScript).fns.main().estimateCost();

console.log({ cost1, cost2, cost3 });

Custom TX Builder

TBD

danielbate commented 1 week ago

Also assigning myself as I've been investigating the tx element in #2735