Skip to content

Commit

Permalink
Add filledPercentage to SwapOrderTransactionInfo
Browse files Browse the repository at this point in the history
  • Loading branch information
fmrsabino committed Mar 22, 2024
1 parent e8efc95 commit 5b816cb
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 0 deletions.
7 changes: 7 additions & 0 deletions src/routes/transactions/entities/swap-order-info.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,16 @@ export abstract class SwapOrderTransactionInfo extends TransactionInfo {
@ApiProperty({ description: 'The timestamp when the order expires' })
expiresTimestamp: number;

@ApiProperty({ description: 'The filled percentage of the order' })
filledPercentage: string;

protected constructor(args: {
status: 'open' | 'fulfilled' | 'cancelled' | 'expired';
orderKind: 'buy' | 'sell';
sellToken: TokenInfo;
buyToken: TokenInfo;
expiresTimestamp: number;
filledPercentage: string;
}) {
super(TransactionInfoType.SwapOrder, null, null);
this.type = TransactionInfoType.SwapOrder;
Expand All @@ -65,6 +69,7 @@ export abstract class SwapOrderTransactionInfo extends TransactionInfo {
this.sellToken = args.sellToken;
this.buyToken = args.buyToken;
this.expiresTimestamp = args.expiresTimestamp;
this.filledPercentage = args.filledPercentage;
}
}

Expand Down Expand Up @@ -94,6 +99,7 @@ export class FulfilledSwapOrderTransactionInfo extends SwapOrderTransactionInfo
expiresTimestamp: number;
surplusFeeLabel: string | null;
executionPriceLabel: string;
filledPercentage: string;
}) {
super({ ...args, status: 'fulfilled' });
this.status = 'fulfilled';
Expand All @@ -120,6 +126,7 @@ export class DefaultSwapOrderTransactionInfo extends SwapOrderTransactionInfo {
buyToken: TokenInfo;
expiresTimestamp: number;
limitPriceLabel: string;
filledPercentage: string;
}) {
super(args);
this.status = args.status;
Expand Down
59 changes: 59 additions & 0 deletions src/routes/transactions/mappers/common/swap-order.mapper.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ describe('Swap Order Mapper tests', () => {
symbol: buyToken.symbol,
},
expiresTimestamp: order.validTo,
filledPercentage: expect.any(String),
surplusLabel: expectedSurplus,
executionPriceLabel: expectedExecutionPrice,
humanDescription: null,
Expand Down Expand Up @@ -157,6 +158,7 @@ describe('Swap Order Mapper tests', () => {
symbol: buyToken.symbol,
},
expiresTimestamp: order.validTo,
filledPercentage: expect.any(String),
limitPriceLabel: expectedLimitPriceDescription,
humanDescription: null,
richDecodedInfo: null,
Expand Down Expand Up @@ -380,4 +382,61 @@ describe('Swap Order Mapper tests', () => {
).toHaveBeenCalledWith(transaction, dataSize, chainId, null, null);
},
);

it.each([
// [executedAmount, amount, expectedFilledPercentage]
[1000, 1000, '100.00'],
[0, 1000, '0.00'],
[500, 1000, '50.00'],
[350, 1050, '33.33'],
])(
'should calculate the filled percentage correctly for buy orders',
async (executedAmount, amount, expected) => {
const chainId = faker.string.numeric();
const transaction = multisigTransactionBuilder().build();
const buyToken = tokenBuilder().with('decimals', 0).build();
const sellToken = tokenBuilder().build();
let order = orderBuilder()
.with(
'status',
faker.helpers.arrayElement([
'open',
'fulfilled',
'cancelled',
'expired',
]),
)
.build();
if (order.kind === 'buy') {
order = {
...order,
executedBuyAmount: BigInt(executedAmount),
buyAmount: BigInt(amount),
};
} else if (order.kind === 'sell') {
order = {
...order,
executedSellAmount: BigInt(executedAmount),
sellAmount: BigInt(amount),
};
} else {
throw new Error('Invalid order kind');
}
setPreSignatureDecoderMock.getOrderUid.mockReturnValue(
order.uid as `0x${string}`,
);
swapsRepositoryMock.getOrder.mockResolvedValue(order);
tokenRepositoryMock.getToken.mockImplementation(({ address }) => {
if (address === order.buyToken) return Promise.resolve(buyToken);
if (address === order.sellToken) return Promise.resolve(sellToken);
return Promise.reject(new Error(`Token ${address} not found.`));
});

const result = await target.mapSwapOrder(chainId, transaction, 0);

expect(result).toMatchObject({
filledPercentage: expected,
});
},
);
});
25 changes: 25 additions & 0 deletions src/routes/transactions/mappers/common/swap-order.mapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,29 @@ export class SwapOrderMapper {
return `1 ${sellToken.token.symbol} = ${ratio} ${buyToken.token.symbol}`;
}

/**
* Returns the filled percentage of an order.
* The percentage is calculated as the ratio of the executed amount to the total amount.
*
* @param order - The order to calculate the filled percentage for.
* @private
*/
private _getFilledPercentage(order: Order): string {
let executed: number;
let total: number;
if (order.kind === 'buy') {
executed = Number(order.executedBuyAmount);
total = Number(order.buyAmount);
} else if (order.kind === 'sell') {
executed = Number(order.executedSellAmount);
total = Number(order.sellAmount);
} else {
throw new Error('Unknown order kind');
}

return ((executed / total) * 100).toFixed(2).toString();
}

private _mapFulfilledOrderStatus(args: {
buyToken: TokenAmount;
sellToken: TokenAmount;
Expand All @@ -194,6 +217,7 @@ export class SwapOrderMapper {
args.sellToken,
args.buyToken,
),
filledPercentage: this._getFilledPercentage(args.order),
});
}

Expand Down Expand Up @@ -228,6 +252,7 @@ export class SwapOrderMapper {
buyToken: args.buyToken.toTokenInfo(),
expiresTimestamp: args.order.validTo,
limitPriceLabel: this._getLimitPriceLabel(args.sellToken, args.buyToken),
filledPercentage: this._getFilledPercentage(args.order),
});
}
}
Expand Down

0 comments on commit 5b816cb

Please sign in to comment.