Skip to content

Commit

Permalink
fix: getOperation for Transfer Asset (#1619)
Browse files Browse the repository at this point in the history
  • Loading branch information
Torres-ssf authored Jan 15, 2024
1 parent db1df6b commit 14aebe7
Show file tree
Hide file tree
Showing 3 changed files with 186 additions and 47 deletions.
5 changes: 5 additions & 0 deletions .changeset/wet-dots-marry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@fuel-ts/providers": minor
---

Made getOperations to consider multiple assets transfer for a Transfer Asset operation
128 changes: 127 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 @@ -722,6 +723,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 @@ -749,6 +753,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 @@ -808,6 +830,7 @@ describe('operations', () => {
JSON.parse(JSON.stringify(baseOperations[0].assetsSent))
);
});

it('should stack when same asset is added', () => {
const baseOperations = addOperation([], OPERATION_CONTRACT_CALL);
const operationsAddedSameAsset = addOperation(baseOperations, OPERATION_CONTRACT_CALL);
Expand All @@ -819,7 +842,8 @@ 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]', () => {
const DIF_ASSET_ID = '0x0012300000000000000000000000000000000001';
const operationTwoAssets: Operation = {
...OPERATION_CONTRACT_CALL,
Expand Down Expand Up @@ -848,6 +872,108 @@ describe('operations', () => {
);
expect(operationsAddedSameAsset[0].assetsSent?.[1]?.assetId).toEqual(DIF_ASSET_ID);
});

it('ensure operation asset transfer stacks multiple assetSents between same addresses', () => {
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', () => {
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
);
});

it('should always not stack for contract calls', () => {
const baseOperations = addOperation([], OPERATION_CONTRACT_CALL);
const operationsAddedSameContractCall = addOperation(baseOperations, OPERATION_CONTRACT_CALL);
Expand Down
100 changes: 54 additions & 46 deletions packages/providers/src/transaction-summary/operations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,16 +103,27 @@ 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);

// Getting assets from op2 that don't exist in op1
const filteredAssets = assets2.filter(
(asset2) => !assets1.some((asset1) => asset1.assetId === asset2.assetId)
);

// Merge assets that already exist in op1
const mergedAssets = assets1.map((asset1) => {
// Find matching asset in op2
const matchingAsset = assets2.find((asset2) => asset2.assetId === asset1.assetId);
if (!matchingAsset) {
// No matching asset found, return asset1
return asset1;
}
// Matching asset found, merge amounts
const mergedAmount = bn(asset1.amount).add(matchingAsset.amount);
return { ...asset1, amount: mergedAmount };
});

// Return merged assets from op1 with filtered assets from op2
return mergedAssets.concat(filteredAssets);
};

/** @hidden */
Expand All @@ -128,43 +139,37 @@ 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;
}

let newOp = { ...op };

// 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,
};
}
const allOperations = [...operations];

// Verifying if the operation to add already exists.
const index = allOperations.findIndex((op) => isSameOperation(op, toAdd));

if (allOperations[index]) {
// Existent operation, we want to edit it.
const existentOperation = { ...allOperations[index] };

if (toAdd.assetsSent?.length) {
/**
* If the assetSent already exists, we call 'mergeAssets' to merge possible
* entries of the same 'assetId', otherwise we just add the new 'assetSent'.
*/
existentOperation.assetsSent = existentOperation.assetsSent?.length
? mergeAssets(existentOperation, toAdd)
: toAdd.assetsSent;
}

// 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.calls?.length) {
// We need to stack the new call(s) with the possible existent ones.
existentOperation.calls = [...(existentOperation.calls || []), ...toAdd.calls];
}

return newOp;
})
.filter(Boolean) as Operation[];
allOperations[index] = existentOperation;
} else {
// New operation, we can simply add it.
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 +377,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 +394,9 @@ export function getTransferOperations({
amount: output.amount,
},
],
});
};

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

0 comments on commit 14aebe7

Please sign in to comment.