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: getOperation for Transfer Asset #1619

Merged
merged 14 commits into from
Jan 15, 2024
Merged
123 changes: 122 additions & 1 deletion packages/providers/src/transaction-summary/operations.test.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { getRandomB256 } from '@fuel-ts/address';
import { bn } from '@fuel-ts/math';
import { ReceiptType, TransactionType } from '@fuel-ts/transactions';

Expand Down Expand Up @@ -698,6 +699,9 @@ describe('operations', () => {

const fromAddress = getInputAccountAddress(coinInput[0]);

const assetA = '0x0101010101010101010101010101010101010101010101010101010101010101';
const assetB = '0x0202020202020202020202020202020202020202020202020202020202020202';

const OPERATION_CONTRACT_CALL = {
name: OperationName.contractCall,
from: {
Expand Down Expand Up @@ -725,6 +729,24 @@ describe('operations', () => {
],
};

const OPERATION_TRANSFER = {
name: OperationName.transfer,
from: {
type: 1,
address: getRandomB256(),
},
to: {
type: 1,
address: getRandomB256(),
},
assetsSent: [
{
assetId: '0x0101010101010101010101010101010101010101010101010101010101010101',
amount: bn(100),
},
],
};

it('should just add operation when its the first one', () => {
const operations = addOperation([], OPERATION_CONTRACT_CALL);
expect(operations.length).toEqual(1);
Expand Down Expand Up @@ -795,7 +817,7 @@ describe('operations', () => {
OPERATION_CONTRACT_CALL.assetsSent[0].assetId
);
});
it('should stack when same asset is added together with a different asset', () => {
it('should stack when same asset is added together with a different asset [CONTRACT-CALL]', () => {
Torres-ssf marked this conversation as resolved.
Show resolved Hide resolved
const DIF_ASSET_ID = '0x0012300000000000000000000000000000000001';
const operationTwoAssets: Operation = {
...OPERATION_CONTRACT_CALL,
Expand Down Expand Up @@ -824,6 +846,105 @@ describe('operations', () => {
);
expect(operationsAddedSameAsset[0].assetsSent?.[1]?.assetId).toEqual(DIF_ASSET_ID);
});
it('ensure operation asset transfer stacks multiple assetSents between same addresses', () => {
Torres-ssf marked this conversation as resolved.
Show resolved Hide resolved
const operationOne: Operation = {
...OPERATION_TRANSFER,
assetsSent: [
{
assetId: assetA,
amount: bn(100),
},
],
};

const operationTwo: Operation = {
...OPERATION_TRANSFER,
assetsSent: [
{
assetId: assetB,
amount: bn(200),
},
],
};

const baseOperations = addOperation([], operationOne);
const stackedOperation = addOperation(baseOperations, operationTwo);

expect(stackedOperation.length).toEqual(1);
expect(stackedOperation[0].assetsSent?.length).toEqual(2);
expect(stackedOperation[0].assetsSent?.[0]?.amount.valueOf()).toEqual(
operationOne.assetsSent?.[0]?.amount.valueOf()
);
expect(stackedOperation[0].assetsSent?.[0]?.assetId).toEqual(
operationOne.assetsSent?.[0].assetId
);
expect(stackedOperation[0].assetsSent?.[1]?.amount.valueOf()).toEqual(
operationTwo.assetsSent?.[0]?.amount.valueOf()
);
expect(stackedOperation[0].assetsSent?.[1]?.assetId).toEqual(
operationTwo.assetsSent?.[0].assetId
);
});
it('ensure operation asset transfer does not stack multiple assetSents between different addresses', () => {
Torres-ssf marked this conversation as resolved.
Show resolved Hide resolved
const fromOne = getRandomB256();
const fromTwo = getRandomB256();
const toAddress2 = getRandomB256();

const operationOne: Operation = {
...OPERATION_TRANSFER,
from: {
address: fromOne,
type: 1,
},
to: {
address: toAddress2,
type: 1,
},
assetsSent: [
{
assetId: assetA,
amount: bn(100),
},
],
};

const operationTwo: Operation = {
...OPERATION_TRANSFER,
from: {
address: fromTwo,
type: 1,
},
to: {
address: toAddress2,
type: 1,
},
assetsSent: [
{
assetId: assetB,
amount: bn(200),
},
],
};

const baseOperation = addOperation([], operationOne);
const multipleOperations = addOperation(baseOperation, operationTwo);

expect(multipleOperations.length).toEqual(2);
expect(multipleOperations[0].assetsSent?.length).toEqual(1);
expect(multipleOperations[0].assetsSent?.[0]?.amount.valueOf()).toEqual(
operationOne.assetsSent?.[0]?.amount.valueOf()
);
expect(multipleOperations[0].assetsSent?.[0]?.assetId).toEqual(
operationOne.assetsSent?.[0].assetId
);
expect(multipleOperations[1].assetsSent?.length).toEqual(1);
expect(multipleOperations[1].assetsSent?.[0]?.amount.valueOf()).toEqual(
operationTwo.assetsSent?.[0]?.amount.valueOf()
);
expect(multipleOperations[1].assetsSent?.[0]?.assetId).toEqual(
operationTwo.assetsSent?.[0].assetId
);
});
Torres-ssf marked this conversation as resolved.
Show resolved Hide resolved
it('should always not stack for contract calls', () => {
const baseOperations = addOperation([], OPERATION_CONTRACT_CALL);
const operationsAddedSameContractCall = addOperation(baseOperations, OPERATION_CONTRACT_CALL);
Expand Down
82 changes: 38 additions & 44 deletions packages/providers/src/transaction-summary/operations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,16 +103,21 @@ export function getReceiptsMessageOut(receipts: TransactionResultReceipt[]) {
const mergeAssets = (op1: Operation, op2: Operation) => {
const assets1 = op1.assetsSent || [];
const assets2 = op2.assetsSent || [];
const filtered = assets2.filter((c) => !assets1.some(hasSameAssetId(c)));
return assets1
.map((coin) => {
const asset = assets2.find(hasSameAssetId(coin));
if (!asset) {
return coin;
}
return { ...coin, amount: bn(coin.amount).add(asset.amount) };
})
.concat(filtered);

const filteredAssets = assets2.filter(
(asset2) => !assets1.some((asset1) => asset1.assetId === asset2.assetId)
);

const mergedAssets = assets1.map((asset1) => {
const matchingAsset = assets2.find((asset2) => asset2.assetId === asset1.assetId);
if (!matchingAsset) {
return asset1;
}
const mergedAmount = bn(asset1.amount).add(matchingAsset.amount);
return { ...asset1, amount: mergedAmount };
});

return mergedAssets.concat(filteredAssets);
};

/** @hidden */
Expand All @@ -128,43 +133,29 @@ function isSameOperation(a: Operation, b: Operation) {

/** @hidden */
export function addOperation(operations: Operation[], toAdd: Operation) {
const ops = operations
.map((op) => {
// if it's not same operation, don't change. we just wanna stack the same operation
if (!isSameOperation(op, toAdd)) {
return null;
}
const allOperations = [...operations];

let newOp = { ...op };
const index = allOperations.findIndex((op) => isSameOperation(op, toAdd));

// if it's adding new assets
if (toAdd.assetsSent?.length) {
// if prev op had assets, merge them. Otherwise just add the new assets
newOp = {
...newOp,
assetsSent: op.assetsSent?.length ? mergeAssets(op, toAdd) : toAdd.assetsSent,
};
}
if (allOperations[index]) {
const existentOperation = { ...allOperations[index] };

// if it's adding new calls,
if (toAdd.calls?.length) {
/*
[] for calls we don't stack as grouping is not desired.
we wanna show all calls in the same operation
with each respective assets, amounts, functions, arguments.
*/
newOp = {
...newOp,
calls: [...(op.calls || []), ...(toAdd.calls || [])],
};
}
if (toAdd.assetsSent?.length) {
existentOperation.assetsSent = existentOperation.assetsSent?.length
? mergeAssets(existentOperation, toAdd)
: toAdd.assetsSent;
}

if (toAdd.calls?.length) {
existentOperation.calls = [...(existentOperation.calls || []), ...(toAdd.calls || [])];
}

return newOp;
})
.filter(Boolean) as Operation[];
allOperations[index] = existentOperation;
} else {
allOperations.push(toAdd);
}

// if this operation didn't exist before just add it to the end
return ops.length ? ops : [...operations, toAdd];
return allOperations;
}

/** @hidden */
Expand Down Expand Up @@ -372,7 +363,8 @@ export function getTransferOperations({
const input = getInputFromAssetId(inputs, output.assetId);
if (input) {
const inputAddress = getInputAccountAddress(input);
operations = addOperation(operations, {

const operationToAdd: Operation = {
name: OperationName.transfer,
from: {
type: AddressType.account,
Expand All @@ -388,7 +380,9 @@ export function getTransferOperations({
amount: output.amount,
},
],
});
};

operations = addOperation(operations, operationToAdd);
}
});
}
Expand Down
Loading