Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: return correct operations from coin and message inputs #2782

Merged
merged 23 commits into from
Jul 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
c5049e5
fix: ensure correct message input is returned based on spec
maschad Jul 17, 2024
201fa2f
docs: add changeset
maschad Jul 17, 2024
8003404
Merge branch 'master' into mc/fix/ensure-correct-input-message-returned
maschad Jul 18, 2024
f92c0c5
test: temp release to npm
maschad Jul 23, 2024
189cec2
Merge branch 'master' into mc/fix/ensure-correct-input-message-returned
maschad Jul 23, 2024
c659691
fix: use amounts to determine what message should be returned
maschad Jul 24, 2024
63e827d
fix: return correct coinInput and InputMessage operations
maschad Jul 24, 2024
a9cb052
docs: update changeset
maschad Jul 24, 2024
351f883
Merge branch 'master' into mc/fix/ensure-correct-input-message-returned
maschad Jul 24, 2024
d126fcb
docs: update changeset
maschad Jul 24, 2024
3818684
lint: resolve linting issues
maschad Jul 24, 2024
3b96d98
Merge branch 'master' into mc/fix/ensure-correct-input-message-returned
Torres-ssf Jul 25, 2024
7004319
fix: return correct input based on receipt assetId and baseAssetId
maschad Jul 25, 2024
b69a225
test: remove unnecessary only from test
maschad Jul 25, 2024
1224862
test: remove unnecessary parameter from test
maschad Jul 25, 2024
d5554fd
Merge branch 'master' into mc/fix/ensure-correct-input-message-returned
maschad Jul 26, 2024
2a5abea
test: add a fuel-gauge test for MessageInput
maschad Jul 26, 2024
f93436d
Merge branch 'master' into mc/fix/ensure-correct-input-message-returned
maschad Jul 26, 2024
3114f11
remove unnecessary only
maschad Jul 26, 2024
81e5f70
Merge branch 'master' into mc/fix/ensure-correct-input-message-returned
maschad Jul 29, 2024
66658b5
Merge branch 'master' into mc/fix/ensure-correct-input-message-returned
maschad Jul 30, 2024
1cc09e3
Merge branch 'master' into mc/fix/ensure-correct-input-message-returned
maschad Jul 30, 2024
17c2ca2
Merge branch 'master' into mc/fix/ensure-correct-input-message-returned
maschad Jul 30, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/thin-pumpkins-sell.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@fuel-ts/account": patch
---

fix: return correct operations from coin and message inputs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { ZeroBytes32 } from '@fuel-ts/address/configs';
import type { InputCoin } from '@fuel-ts/transactions';
import { bn } from '@fuel-ts/math';
import type { InputCoin, InputMessage } from '@fuel-ts/transactions';
import { ASSET_A } from '@fuel-ts/utils/test-utils';

import {
Expand Down Expand Up @@ -116,8 +117,33 @@ describe('transaction-summary/input', () => {
expect(getInputFromAssetId([inputCoin1, inputCoin2], ZeroBytes32)).toStrictEqual(inputCoin1);
expect(getInputFromAssetId([inputCoin1, inputCoin2], ASSET_A)).toStrictEqual(inputCoin2);

expect(getInputFromAssetId([MOCK_INPUT_MESSAGE], ZeroBytes32)).toStrictEqual(
expect(getInputFromAssetId([MOCK_INPUT_MESSAGE], ZeroBytes32, true)).toStrictEqual(
MOCK_INPUT_MESSAGE
);
});

it('should ensure getInputFromAssetId returns the correct coinInput thats greater than 0 for default assetId', () => {
const coinInput1: InputCoin = {
...MOCK_INPUT_COIN,
amount: bn(100),
assetId: ZeroBytes32,
};

const coinInput2: InputCoin = {
...MOCK_INPUT_COIN,
amount: bn(0),
assetId: ZeroBytes32,
};

expect(getInputFromAssetId([coinInput1, coinInput2], ZeroBytes32)).toEqual(coinInput1);
});

it('Should return the correct input message for withdrawals', () => {
const inputMessage: InputMessage = {
...MOCK_INPUT_MESSAGE,
amount: bn(100),
};

expect(getInputFromAssetId([inputMessage], ZeroBytes32, true)).toEqual(inputMessage);
});
});
36 changes: 26 additions & 10 deletions packages/account/src/providers/transaction-summary/input.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export function getInputsByType<T = Input>(inputs: Input[], type: InputType) {
}

/** @hidden */
export function getInputsCoin(inputs: Input[]) {
export function getInputsCoin(inputs: Input[]): InputCoin[] {
return getInputsByType<InputCoin>(inputs, InputType.Coin);
}

Expand All @@ -33,16 +33,32 @@ export function getInputsContract(inputs: Input[]) {
}

/** @hidden */
export function getInputFromAssetId(inputs: Input[], assetId: string) {
function findCoinInput(inputs: Input[], assetId: string): InputCoin | undefined {
const coinInputs = getInputsCoin(inputs);
const messageInputs = getInputsMessage(inputs);
const coinInput = coinInputs.find((i) => i.assetId === assetId);
// TODO: should include assetId in InputMessage as well. for now we're mocking ETH
const messageInput = messageInputs.find(
(_) => assetId === '0x0000000000000000000000000000000000000000000000000000000000000000'
);

return coinInput || messageInput;
return coinInputs.find((i) => i.assetId === assetId);
}

/** @hidden */
function findMessageInput(inputs: Input[]): InputMessage | undefined {
return getInputsMessage(inputs)?.[0];
}
/** @hidden */
export function getInputFromAssetId(
inputs: Input[],
assetId: string,
isBaseAsset = false
): InputCoin | InputMessage | undefined {
const coinInput = findCoinInput(inputs, assetId);
if (coinInput) {
return coinInput;
}

if (isBaseAsset) {
return findMessageInput(inputs);
}

// #TODO: we should throw an error here if we are unable to return a valid input
return undefined;
}

/** @hidden */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ describe('operations', () => {
outputs: [MOCK_OUTPUT_CONTRACT, MOCK_OUTPUT_VARIABLE, MOCK_OUTPUT_CHANGE],
receipts,
maxInputs: bn(255),
baseAssetId: ZeroBytes32,
});

expect(operations.length).toEqual(1);
Expand Down Expand Up @@ -150,6 +151,7 @@ describe('operations', () => {
},
rawPayload: MOCK_TRANSACTION_RAWPAYLOAD,
maxInputs: bn(255),
baseAssetId: ZeroBytes32,
});

expect(operations.length).toEqual(1);
Expand All @@ -162,6 +164,7 @@ describe('operations', () => {
outputs: [MOCK_OUTPUT_COIN, MOCK_OUTPUT_CHANGE],
receipts: [MOCK_RECEIPT_RETURN, MOCK_RECEIPT_SCRIPT_RESULT],
maxInputs: bn(255),
baseAssetId: ZeroBytes32,
});

expect(operations.length).toEqual(0);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ export function getWithdrawFromFuelOperations({

const withdrawFromFuelOperations = messageOutReceipts.reduce(
(prevWithdrawFromFuelOps, receipt) => {
const input = getInputFromAssetId(inputs, baseAssetId);
const input = getInputFromAssetId(inputs, baseAssetId, true);
if (input) {
const inputAddress = getInputAccountAddress(input);
const newWithdrawFromFuelOps = addOperation(prevWithdrawFromFuelOps, {
Expand Down Expand Up @@ -239,9 +239,10 @@ export function getContractCallOperations({
abiMap,
rawPayload,
maxInputs,
baseAssetId,
}: InputOutputParam &
ReceiptParam &
Pick<GetOperationParams, 'abiMap' | 'maxInputs'> &
Pick<GetOperationParams, 'abiMap' | 'maxInputs' | 'baseAssetId'> &
RawPayloadParam): Operation[] {
const contractCallReceipts = getReceiptsCall(receipts);
const contractOutputs = getOutputsContract(outputs);
Expand All @@ -252,7 +253,10 @@ export function getContractCallOperations({
if (contractInput) {
const newCallOps = contractCallReceipts.reduce((prevContractCallOps, receipt) => {
if (receipt.to === contractInput.contractID) {
const input = getInputFromAssetId(inputs, receipt.assetId);
// # TODO: This is a temporary fix to ensure that the base assetId is used when the assetId is ZeroBytes32
// The assetId is returned as ZeroBytes32 if the contract call has no assets in it (see https://github.com/FuelLabs/fuel-core/issues/1941)
const assetId = receipt.assetId === ZeroBytes32 ? baseAssetId : receipt.assetId;
const input = getInputFromAssetId(inputs, assetId, assetId === baseAssetId);
if (input) {
const inputAddress = getInputAccountAddress(input);
const calls = [];
Expand Down Expand Up @@ -497,6 +501,7 @@ export function getOperations({
abiMap,
rawPayload,
maxInputs,
baseAssetId,
}),
...getWithdrawFromFuelOperations({ inputs, receipts, baseAssetId }),
];
Expand Down
10 changes: 1 addition & 9 deletions packages/account/src/test-utils/wallet-config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,15 +84,7 @@ describe('WalletsConfig', () => {
() => new WalletsConfig(hexlify(randomBytes(32)), { ...configOptions, amountPerCoin: -1 }),
new FuelError(
FuelError.CODES.INVALID_INPUT_PARAMETERS,
'Amount per coin must be greater than zero.'
)
);

await expectToThrowFuelError(
() => new WalletsConfig(hexlify(randomBytes(32)), { ...configOptions, amountPerCoin: 0 }),
new FuelError(
FuelError.CODES.INVALID_INPUT_PARAMETERS,
'Amount per coin must be greater than zero.'
'Amount per coin must be greater than or equal to zero.'
)
);
});
Expand Down
4 changes: 2 additions & 2 deletions packages/account/src/test-utils/wallet-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,10 +167,10 @@ export class WalletsConfig {
'Number of coins per asset must be greater than zero.'
);
}
if (amountPerCoin <= 0) {
if (amountPerCoin < 0) {
throw new FuelError(
FuelError.CODES.INVALID_INPUT_PARAMETERS,
'Amount per coin must be greater than zero.'
'Amount per coin must be greater than or equal to zero.'
);
}
}
Expand Down
42 changes: 41 additions & 1 deletion packages/fuel-gauge/src/transaction-summary.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {
AddressType,
OperationName,
} from 'fuels';
import { ASSET_A, ASSET_B, launchTestNode } from 'fuels/test-utils';
import { ASSET_A, ASSET_B, launchTestNode, TestMessage } from 'fuels/test-utils';

import { MultiTokenContractAbi__factory, TokenContractAbi__factory } from '../test/typegen';
import MultiTokenContractAbiHex from '../test/typegen/contracts/MultiTokenContractAbi.hex';
Expand Down Expand Up @@ -591,5 +591,45 @@ describe('TransactionSummary', () => {
recipients: allRecipients,
});
});

it('should ensure that transfer operations are assembled correctly if only seeded with a MessageInput (SPENDABLE MESSAGE)', async () => {
const testMessage = new TestMessage({ amount: 1000000, data: '' });

using launched = await launchTestNode({
contractsConfigs: [
{
deployer: MultiTokenContractAbi__factory,
bytecode: MultiTokenContractAbiHex,
},
],
walletsConfig: {
amountPerCoin: 0,
messages: [testMessage],
},
});
const {
contracts: [contract],
provider,
wallets: [wallet],
} = launched;

const amount = 100;

const tx1 = await wallet.transferToContract(contract.id, amount);

const { operations } = await tx1.waitForResult();

expect(operations).toHaveLength(1);

validateTransferOperation({
operations,
sender: wallet.address,
fromType: AddressType.account,
toType: AddressType.contract,
recipients: [
{ address: contract.id, quantities: [{ amount, assetId: provider.getBaseAssetId() }] },
],
});
});
});
});
Loading