Skip to content

Commit

Permalink
fix!: ensure getOperations returns operation Transfer Asset for t…
Browse files Browse the repository at this point in the history
…ransfer to contract transactions (#1400)
  • Loading branch information
Torres-ssf authored Nov 8, 2023
1 parent 15c3669 commit 54e613c
Show file tree
Hide file tree
Showing 5 changed files with 159 additions and 28 deletions.
5 changes: 5 additions & 0 deletions .changeset/flat-brooms-accept.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@fuel-ts/providers": minor
---

ensure transfer to contract transactions returns operation transfer asset
62 changes: 62 additions & 0 deletions packages/providers/src/transaction-summary/operations.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
MOCK_RECEIPT_RETURN_DATA_1,
MOCK_RECEIPT_RETURN_DATA_2,
MOCK_RECEIPT_SCRIPT_RESULT,
MOCK_RECEIPT_TRANSFER,
MOCK_RECEIPT_TRANSFER_OUT,
MOCK_TRANSACTION_RAWPAYLOAD,
} from '../../test/fixtures/transaction-summary';
Expand Down Expand Up @@ -261,6 +262,36 @@ describe('operations', () => {
const operations = getTransferOperations({
inputs: [MOCK_INPUT_COIN, MOCK_INPUT_COIN],
outputs: [MOCK_OUTPUT_COIN, MOCK_OUTPUT_CHANGE],
receipts: [],
});
expect(operations.length).toEqual(1);

expect(operations[0]).toStrictEqual(expected);
});

it('should ensure getTransferOperations return transfer operations from coin inputs (CONTRACT_TRANSFER)', () => {
const expected: Operation = {
assetsSent: [
{
amount: bn('0x3dc'),
assetId: '0x0000000000000000000000000000000000000000000000000000000000000000',
},
],
from: {
address: '0x3e7ddda4d0d3f8307ae5f1aed87623992c1c4decefec684936960775181b2302',
type: AddressType.account,
},
name: OperationName.transfer,
to: {
address: '0x0a98320d39c03337401a4e46263972a9af6ce69ec2f35a5420b1bd35784c74b1',
type: AddressType.contract,
},
};

const operations = getTransferOperations({
inputs: [MOCK_INPUT_CONTRACT, MOCK_INPUT_COIN, MOCK_INPUT_COIN],
outputs: [MOCK_OUTPUT_CONTRACT, MOCK_OUTPUT_CHANGE],
receipts: [MOCK_RECEIPT_TRANSFER],
});
expect(operations.length).toEqual(1);

Expand Down Expand Up @@ -289,16 +320,47 @@ describe('operations', () => {
const operations = getTransferOperations({
inputs: [MOCK_INPUT_MESSAGE, MOCK_INPUT_MESSAGE],
outputs: [MOCK_OUTPUT_COIN, MOCK_OUTPUT_CHANGE],
receipts: [],
});

expect(operations.length).toEqual(1);
expect(operations[0]).toStrictEqual(expected);
});

it('should ensure getTransferOperations return transfer operations from message inputs (CONTRACT_TRANSFER)', () => {
const expected: Operation = {
assetsSent: [
{
amount: bn('0x3dc'),
assetId: '0x0000000000000000000000000000000000000000000000000000000000000000',
},
],
from: {
address: '0x06300e686a5511c7ba0399fc68dcbe0ca2d8f54f7e6afea73c505dd3bcacf33b',
type: AddressType.account,
},
name: OperationName.transfer,
to: {
address: '0x0a98320d39c03337401a4e46263972a9af6ce69ec2f35a5420b1bd35784c74b1',
type: AddressType.contract,
},
};

const operations = getTransferOperations({
inputs: [MOCK_INPUT_CONTRACT, MOCK_INPUT_MESSAGE, MOCK_INPUT_MESSAGE],
outputs: [MOCK_OUTPUT_CONTRACT, MOCK_OUTPUT_CHANGE],
receipts: [MOCK_RECEIPT_TRANSFER],
});
expect(operations.length).toEqual(1);

expect(operations[0]).toStrictEqual(expected);
});

it('should ensure getTransferOperations return empty', () => {
const operations = getTransferOperations({
inputs: [MOCK_INPUT_CONTRACT, MOCK_INPUT_COIN],
outputs: [MOCK_OUTPUT_CONTRACT, MOCK_OUTPUT_VARIABLE, MOCK_OUTPUT_CHANGE],
receipts: [],
});

expect(operations.length).toEqual(0);
Expand Down
105 changes: 77 additions & 28 deletions packages/providers/src/transaction-summary/operations.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import { ErrorCode, FuelError } from '@fuel-ts/errors';
import { bn } from '@fuel-ts/math';
import { ReceiptType, type Output, TransactionType } from '@fuel-ts/transactions';
import { ReceiptType, TransactionType } from '@fuel-ts/transactions';
import type { Output } from '@fuel-ts/transactions';

import type {
TransactionResultReceipt,
TransactionResultCallReceipt,
TransactionResultMessageOutReceipt,
TransactionResultReceipt,
TransactionResultTransferOutReceipt,
TransactionResultTransferReceipt,
} from '../transaction-response';

import { getFunctionCall } from './call';
Expand All @@ -15,8 +17,14 @@ import {
getInputAccountAddress,
getInputContractFromIndex,
getInputsCoin,
getInputsContract,
} from './input';
import { getOutputsCoin, getOutputsContract, getOutputsContractCreated } from './output';
import {
getOutputsChange,
getOutputsCoin,
getOutputsContract,
getOutputsContractCreated,
} from './output';
import { AddressType, ChainName, OperationName, TransactionTypeName } from './types';
import type {
InputOutputParam,
Expand All @@ -26,6 +34,7 @@ import type {
ReceiptParam,
Operation,
GetOperationParams,
GetTransferOperationsParams,
} from './types';

/** @hidden */
Expand Down Expand Up @@ -311,34 +320,74 @@ export function getContractCallOperations({
}

/** @hidden */
export function getTransferOperations({ inputs, outputs }: InputOutputParam): Operation[] {
export function getTransferOperations({
inputs,
outputs,
receipts,
}: GetTransferOperationsParams): Operation[] {
const coinOutputs = getOutputsCoin(outputs);

const [transferReceipt] = getReceiptsByType<TransactionResultTransferReceipt>(
receipts,
ReceiptType.Transfer
);

let operations: Operation[] = [];
coinOutputs.forEach((output) => {
const input = getInputFromAssetId(inputs, output.assetId);

if (input) {
const inputAddress = getInputAccountAddress(input);
operations = addOperation(operations, {
name: OperationName.transfer,
from: {
type: AddressType.account,
address: inputAddress,
},
to: {
type: AddressType.account,
address: output.to.toString(),
},
assetsSent: [
{
assetId: output.assetId.toString(),
amount: output.amount,
// Possible transfer to contract
if (transferReceipt) {
const changeOutputs = getOutputsChange(outputs);
changeOutputs.forEach((output) => {
const { assetId } = output;
const [contractInput] = getInputsContract(inputs);
const utxo = getInputFromAssetId(inputs, assetId);

if (utxo && contractInput) {
const inputAddress = getInputAccountAddress(utxo);
operations = addOperation(operations, {
name: OperationName.transfer,
from: {
type: AddressType.account,
address: inputAddress,
},
],
});
}
});
to: {
type: AddressType.contract,
address: contractInput.contractID,
},
assetsSent: [
{
assetId: assetId.toString(),
amount: transferReceipt.amount,
},
],
});
}
});
} else {
coinOutputs.forEach((output) => {
const input = getInputFromAssetId(inputs, output.assetId);
if (input) {
const inputAddress = getInputAccountAddress(input);
operations = addOperation(operations, {
name: OperationName.transfer,
from: {
type: AddressType.account,
address: inputAddress,
},
to: {
type: AddressType.account,
address: output.to.toString(),
},
assetsSent: [
{
assetId: output.assetId.toString(),
amount: output.amount,
},
],
});
}
});
}

return operations;
}
Expand Down Expand Up @@ -408,13 +457,13 @@ export function getOperations({
if (isTypeCreate(transactionType)) {
return [
...getContractCreatedOperations({ inputs, outputs }),
...getTransferOperations({ inputs, outputs }),
...getTransferOperations({ inputs, outputs, receipts }),
];
}

if (isTypeScript(transactionType)) {
return [
...getTransferOperations({ inputs, outputs }),
...getTransferOperations({ inputs, outputs, receipts }),
...getContractCallOperations({
inputs,
outputs,
Expand Down
4 changes: 4 additions & 0 deletions packages/providers/src/transaction-summary/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,10 @@ export type RawPayloadParam = {

export type InputOutputParam = InputParam & OutputParam;

export interface GetTransferOperationsParams extends InputOutputParam {
receipts: TransactionResultReceipt[];
}

export type GetOperationParams = {
transactionType: TransactionType;
abiMap?: AbiMap;
Expand Down
11 changes: 11 additions & 0 deletions packages/providers/test/fixtures/transaction-summary.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import type {
TransactionResultReturnReceipt,
TransactionResultScriptResultReceipt,
TransactionResultTransferOutReceipt,
TransactionResultTransferReceipt,
} from '../../src';
import type {
AbiMap,
Expand Down Expand Up @@ -132,6 +133,16 @@ export const MOCK_RECEIPT_RETURN: TransactionResultReturnReceipt = {
is: bn('0x2868'),
};

export const MOCK_RECEIPT_TRANSFER: TransactionResultTransferReceipt = {
type: ReceiptType.Transfer,
from: '0x0000000000000000000000000000000000000000000000000000000000000000',
to: '0xaab4884920fa4d3a35fc2977cc442b0caddf87e001ef62321b6c02f5ab0f4115',
amount: bn(988),
assetId: '0x0000000000000000000000000000000000000000000000000000000000000000',
pc: bn(10360),
is: bn(10344),
};

export const MOCK_RECEIPT_TRANSFER_OUT: TransactionResultTransferOutReceipt = {
type: ReceiptType.TransferOut,
from: '0x0a98320d39c03337401a4e46263972a9af6ce69ec2f35a5420b1bd35784c74b1',
Expand Down

0 comments on commit 54e613c

Please sign in to comment.