Skip to content

Commit

Permalink
Merge branch 'master' into st/fix/unset-cached-resource-for-failed-tx
Browse files Browse the repository at this point in the history
  • Loading branch information
Torres-ssf committed Aug 7, 2024
2 parents e05c96c + 4e82ad4 commit 78c3794
Show file tree
Hide file tree
Showing 16 changed files with 207 additions and 25 deletions.
6 changes: 6 additions & 0 deletions .changeset/honest-fishes-mix.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@fuel-ts/account": patch
"@fuel-ts/errors": patch
---

chore: handle exceeding maximum inputs when funding a transaction
5 changes: 5 additions & 0 deletions .changeset/wet-elephants-impress.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"create-fuels": patch
---

chore: add infra to support multiple `create fuels` templates
4 changes: 4 additions & 0 deletions apps/docs/src/guide/creating-a-fuel-dapp/options.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ bunx --bun create-fuels@{{fuels}} --bun [project-name] [options]

:::

## `--template <template-name>`

Specifies the template to use for your project. The available templates are: `nextjs`.

## `--pnpm`

Notifies the tool to use pnpm as the package manager to install the necessary dependencies.
Expand Down
4 changes: 4 additions & 0 deletions apps/docs/src/guide/errors/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -298,3 +298,7 @@ The purpose of the lock function is to provide a way to ensure that the implemen
In cases where the error hasn't been mapped yet, this code will be used.

If you believe you found a bug, please report the [issue](https://github.com/FuelLabs/fuels-ts/issues/new/choose) to the team.

### `MAX_INPUTS_EXCEEDED`

When the number of transaction inputs exceeds the maximum limit allowed by the blockchain.
58 changes: 56 additions & 2 deletions packages/account/src/providers/provider.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { BN, bn } from '@fuel-ts/math';
import type { Receipt } from '@fuel-ts/transactions';
import { InputType, ReceiptType, TransactionType } from '@fuel-ts/transactions';
import { DateTime, arrayify, sleep } from '@fuel-ts/utils';
import { ASSET_A } from '@fuel-ts/utils/test-utils';
import { ASSET_A, ASSET_B } from '@fuel-ts/utils/test-utils';
import { versions } from '@fuel-ts/versions';
import * as fuelTsVersionsMod from '@fuel-ts/versions';

Expand All @@ -25,6 +25,7 @@ import {
import { Wallet } from '../wallet';

import type { Coin } from './coin';
import { coinQuantityfy } from './coin-quantity';
import type { Message } from './message';
import type { ChainInfo, CursorPaginationArgs, NodeInfo } from './provider';
import Provider, {
Expand All @@ -35,7 +36,7 @@ import Provider, {
import type { ExcludeResourcesOption } from './resource';
import { isCoin } from './resource';
import type { CoinTransactionRequestInput } from './transaction-request';
import { ScriptTransactionRequest, CreateTransactionRequest } from './transaction-request';
import { CreateTransactionRequest, ScriptTransactionRequest } from './transaction-request';
import { TransactionResponse } from './transaction-response';
import type { SubmittedStatus } from './transaction-summary/types';
import * as gasMod from './utils/gas';
Expand Down Expand Up @@ -628,6 +629,59 @@ describe('Provider', () => {
});
});

it('should throws if max of inputs was exceeded', async () => {
const maxInputs = 2;
using launched = await setupTestProviderAndWallets({
nodeOptions: {
snapshotConfig: {
chainConfig: {
consensus_parameters: {
V1: {
tx_params: {
V1: {
max_inputs: maxInputs,
},
},
},
},
},
},
},
walletsConfig: {
amountPerCoin: 500_000,
},
});

const {
wallets: [sender, receiver],
provider,
} = launched;

const request = new ScriptTransactionRequest();

const quantities = [coinQuantityfy([1000, ASSET_A]), coinQuantityfy([500, ASSET_B])];
const resources = await sender.getResourcesToSpend(quantities);

request.addCoinOutput(receiver.address, 500, provider.getBaseAssetId());

const txCost = await sender.getTransactionCost(request);

request.gasLimit = txCost.gasUsed;
request.maxFee = txCost.maxFee;

request.addResources(resources);

await sender.fund(request, txCost);

await expectToThrowFuelError(
() => sender.sendTransaction(request),
new FuelError(
ErrorCode.MAX_INPUTS_EXCEEDED,
'The transaction exceeds the maximum allowed number of inputs for funding.'
)
);
});

it('can getBlocks', async () => {
using launched = await setupTestProviderAndWallets();
const blocksLenght = 5;
Expand Down
13 changes: 13 additions & 0 deletions packages/account/src/providers/provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -703,6 +703,15 @@ Supported fuel-core version: ${supportedVersion}.`
this.cache.set(transactionId, inputsToCache);
}

private validateTransaction(tx: TransactionRequest, consensusParameters: ConsensusParameters) {
if (bn(tx.inputs.length).gt(consensusParameters.txParameters.maxInputs)) {
throw new FuelError(
ErrorCode.MAX_INPUTS_EXCEEDED,
'The transaction exceeds the maximum allowed number of inputs for funding.'
);
}
}

/**
* Submits a transaction to the chain to be executed.
*
Expand All @@ -724,6 +733,10 @@ Supported fuel-core version: ${supportedVersion}.`
}
// #endregion Provider-sendTransaction

const { consensusParameters } = this.getChain();

this.validateTransaction(transactionRequest, consensusParameters);

const encodedTransaction = hexlify(transactionRequest.toTransactionBytes());

let abis: JsonAbisFromAllCalls | undefined;
Expand Down
1 change: 0 additions & 1 deletion packages/create-fuels/src/bin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { runScaffoldCli, setupProgram } from './cli';

runScaffoldCli({
program: setupProgram(),
templateName: 'nextjs',
args: process.argv,
})
.then(() => process.exit(0))
Expand Down
22 changes: 17 additions & 5 deletions packages/create-fuels/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@ import ora from 'ora';
import { join } from 'path';

import { tryInstallFuelUp } from './lib';
import { doesTemplateExist } from './lib/doesTemplateExist';
import { getPackageManager } from './lib/getPackageManager';
import { getPackageVersion } from './lib/getPackageVersion';
import type { ProgramOptions } from './lib/setupProgram';
import { defaultTemplate, templates } from './lib/setupProgram';
import type { Template, ProgramOptions } from './lib/setupProgram';
import { promptForProjectPath } from './prompts';
import { error, log } from './utils/logger';

Expand All @@ -39,18 +41,28 @@ function writeEnvFile(envFilePath: string) {

export const runScaffoldCli = async ({
program,
templateName = 'nextjs',
args = process.argv,
}: {
program: Command;
args: string[];
templateName: string;
}) => {
program.parse(args);
const opts = program.opts<ProgramOptions>();

const templateOfChoice = (opts.template ?? defaultTemplate) as Template;

if (!doesTemplateExist(templateOfChoice)) {
error(`Template '${templateOfChoice}' does not exist.`);
log();
log('Available templates:');
for (const template of templates) {
log(` - ${template}`);
}
process.exit(1);
}

let projectPath = program.args[0] ?? (await promptForProjectPath());

const opts = program.opts<ProgramOptions>();
const verboseEnabled = opts.verbose ?? false;
const packageManager = getPackageManager(opts);

Expand Down Expand Up @@ -93,7 +105,7 @@ export const runScaffoldCli = async ({

await mkdir(projectPath);

const templateDir = join(__dirname, '..', 'templates', templateName);
const templateDir = join(__dirname, '..', 'templates', templateOfChoice);
await cp(templateDir, projectPath, {
recursive: true,
filter: (filename) => !filename.includes('CHANGELOG.md'),
Expand Down
13 changes: 13 additions & 0 deletions packages/create-fuels/src/lib/doesTemplateExist.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { doesTemplateExist } from './doesTemplateExist';

/**
* @group node
*/
test('doesTemplateExist should return true if the template exists', () => {
expect(doesTemplateExist('nextjs')).toBeTruthy();
});

test('doesTemplateExist should return false if the template does not exist', () => {
// @ts-expect-error intentionally passing in a non-existent template
expect(doesTemplateExist('non-existent-template')).toBeFalsy();
});
4 changes: 4 additions & 0 deletions packages/create-fuels/src/lib/doesTemplateExist.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import type { Template } from './setupProgram';
import { templates } from './setupProgram';

export const doesTemplateExist = (templateName: Template): boolean => templates.has(templateName);
12 changes: 11 additions & 1 deletion packages/create-fuels/src/lib/setupProgram.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,22 @@ import { setupProgram } from './setupProgram';
describe('setupProgram', () => {
test('setupProgram takes in args properly', () => {
const program = setupProgram();
program.parse(['', '', 'test-project-name', '--pnpm', '--npm', '--bun']);
program.parse([
'',
'',
'test-project-name',
'--template',
'nextjs',
'--pnpm',
'--npm',
'--bun',
]);
expect(program.args[0]).toBe('test-project-name');
expect(program.opts().pnpm).toBe(true);
expect(program.opts().npm).toBe(true);
expect(program.opts().bun).toBe(true);
expect(program.opts().install).toBe(true);
expect(program.opts().template).toBe('nextjs');
});

test('setupProgram - no args', () => {
Expand Down
6 changes: 6 additions & 0 deletions packages/create-fuels/src/lib/setupProgram.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ import { Command } from 'commander';

import packageJson from '../../package.json';

export type Template = 'nextjs';
export const templates: Set<Template> = new Set(['nextjs']);
export const defaultTemplate: Template = 'nextjs';

export interface ProgramOptions {
contract?: boolean;
predicate?: boolean;
Expand All @@ -11,6 +15,7 @@ export interface ProgramOptions {
bun?: boolean;
verbose?: boolean;
install?: boolean;
template?: Template;
}

export const setupProgram = () => {
Expand All @@ -22,6 +27,7 @@ export const setupProgram = () => {
.option('--bun', 'Use bun to install dependencies')
.option('--verbose', 'Enable verbose logging')
.option('--no-install', 'Do not install dependencies')
.option('--template <template>', 'Specify a template to use', defaultTemplate)
.addHelpCommand()
.showHelpAfterError(true);
return program;
Expand Down
53 changes: 44 additions & 9 deletions packages/create-fuels/test/cli.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { mkdirSync, readFileSync } from 'fs';
import { join } from 'path';

import { runScaffoldCli, setupProgram } from '../src/cli';
import * as doesTemplateExistMod from '../src/lib/doesTemplateExist';
import { templates } from '../src/lib/setupProgram';

import type { ProjectPaths } from './utils/bootstrapProject';
import {
Expand All @@ -20,7 +22,7 @@ import { filterOriginalTemplateFiles, getAllFiles } from './utils/templateFiles'
* @group node
*/
describe('CLI', { timeout: 15_000 }, () => {
const { error } = mockLogger();
const { error, log } = mockLogger();
let paths: ProjectPaths;

beforeEach(() => {
Expand All @@ -39,11 +41,12 @@ describe('CLI', { timeout: 15_000 }, () => {
});

test('create-fuels extracts the template to the specified directory', async () => {
const args = generateArgv(paths.projectRoot);
const args = generateArgv({ projectName: paths.projectRoot, template: paths.templateName });

vi.spyOn(doesTemplateExistMod, 'doesTemplateExist').mockReturnValueOnce(true);

await runScaffoldCli({
program: setupProgram(),
templateName: paths.templateName,
args,
});

Expand All @@ -56,11 +59,12 @@ describe('CLI', { timeout: 15_000 }, () => {
});

test('create-fuels checks the versions on the fuel-toolchain file', async () => {
const args = generateArgv(paths.projectRoot);
const args = generateArgv({ projectName: paths.projectRoot, template: paths.templateName });

vi.spyOn(doesTemplateExistMod, 'doesTemplateExist').mockReturnValueOnce(true);

await runScaffoldCli({
program: setupProgram(),
templateName: paths.templateName,
args,
});

Expand All @@ -75,11 +79,16 @@ describe('CLI', { timeout: 15_000 }, () => {
});

test('should rewrite for the appropriate package manager', async () => {
const args = generateArgv(paths.projectRoot, 'bun');
const args = generateArgv({
projectName: paths.projectRoot,
packageManager: 'bun',
template: paths.templateName,
});

vi.spyOn(doesTemplateExistMod, 'doesTemplateExist').mockReturnValueOnce(true);

await runScaffoldCli({
program: setupProgram(),
templateName: paths.templateName,
args,
});

Expand All @@ -94,15 +103,16 @@ describe('CLI', { timeout: 15_000 }, () => {
});

test('create-fuels reports an error if the project directory already exists', async () => {
const args = generateArgv(paths.projectRoot);
const args = generateArgv({ projectName: paths.projectRoot, template: paths.templateName });

// Generate the project once
mkdirSync(paths.projectRoot, { recursive: true });

vi.spyOn(doesTemplateExistMod, 'doesTemplateExist').mockReturnValueOnce(true);

// Generate the project again
await runScaffoldCli({
program: setupProgram(),
templateName: paths.templateName,
args,
}).catch((e) => {
expect(e).toBeInstanceOf(Error);
Expand All @@ -112,4 +122,29 @@ describe('CLI', { timeout: 15_000 }, () => {
expect.stringContaining(`A folder already exists at ${paths.projectRoot}`)
);
});

test('create-fuels reports an error if the template does not exist', async () => {
const args = generateArgv({
projectName: paths.projectRoot,
template: 'non-existent-template',
});

vi.spyOn(doesTemplateExistMod, 'doesTemplateExist').mockReturnValueOnce(false);

await runScaffoldCli({
program: setupProgram(),
args,
}).catch((e) => {
expect(e).toBeInstanceOf(Error);
});

expect(error).toHaveBeenCalledWith(
expect.stringContaining(`Template 'non-existent-template' does not exist.`)
);
expect(log).toHaveBeenCalledWith();
expect(log).toHaveBeenCalledWith('Available templates:');
for (const template of templates) {
expect(log).toHaveBeenCalledWith(` - ${template}`);
}
});
});
Loading

0 comments on commit 78c3794

Please sign in to comment.