diff --git a/.changeset/famous-buttons-greet.md b/.changeset/famous-buttons-greet.md new file mode 100644 index 00000000..be818a25 --- /dev/null +++ b/.changeset/famous-buttons-greet.md @@ -0,0 +1,5 @@ +--- +'@moonbeam-network/xcm-builder': patch +--- + +Apply correct transferrable balance formula diff --git a/packages/builder/src/balance/BalanceBuilder.test.ts b/packages/builder/src/balance/BalanceBuilder.test.ts index 6f53fa01..556dcc63 100644 --- a/packages/builder/src/balance/BalanceBuilder.test.ts +++ b/packages/builder/src/balance/BalanceBuilder.test.ts @@ -2,8 +2,16 @@ import { describe, expect, it } from 'vitest'; import { TypeRegistry, U128 } from '@polkadot/types'; +import { + FrameSystemAccountInfo, + PalletBalancesAccountData, +} from '@polkadot/types/lookup'; import { SubstrateQueryConfig } from '../types/substrate/SubstrateQueryConfig'; -import { BalanceBuilder } from './BalanceBuilder'; +import { + BalanceBuilder, + calculateSystemAccountBalance, +} from './BalanceBuilder'; +import { PalletBalancesAccountDataOld } from './BalanceBuilder.interfaces'; function balanceOf(number: number | string): U128 { return new U128(new TypeRegistry(), number); @@ -193,4 +201,79 @@ describe('balanceBuilder', () => { }); }); }); + + describe('calculateSystemAccountBalance', () => { + it('should correctly calculate balance with PalletBalancesAccountData', async () => { + const response = { + data: { + flags: balanceOf(0), + free: balanceOf(1000), + frozen: balanceOf(300), + reserved: balanceOf(200), + } as PalletBalancesAccountData, + } as FrameSystemAccountInfo; + + const result = await calculateSystemAccountBalance(response); + expect(result).toBe(BigInt(900)); + }); + + it('should correctly calculate balance with PalletBalancesAccountDataOld', async () => { + const response = { + data: { + feeFrozen: balanceOf(0), + free: balanceOf(1000), + miscFrozen: balanceOf(300), + reserved: balanceOf(200), + } as PalletBalancesAccountDataOld, + } as unknown as FrameSystemAccountInfo; + + const result = await calculateSystemAccountBalance(response); + expect(result).toBe(BigInt(900)); + }); + + it('should handle when reserved is greater than frozen', async () => { + const response = { + data: { + flags: balanceOf(0), + free: balanceOf(1000), + frozen: balanceOf(300), + reserved: balanceOf(400), + } as PalletBalancesAccountData, + } as FrameSystemAccountInfo; + + const result = await calculateSystemAccountBalance(response); + expect(result).toBe(BigInt(1000)); + }); + + it('should return 0 when all balances are zero', async () => { + const response = { + data: { + flags: balanceOf(0), + free: balanceOf(0), + frozen: balanceOf(0), + reserved: balanceOf(0), + } as PalletBalancesAccountData, + } as FrameSystemAccountInfo; + + const result = await calculateSystemAccountBalance(response); + expect(result).toBe(BigInt(0)); + }); + + it('should handle large numbers correctly', async () => { + const largeNumber = '123456789012345678901234567890'; + const response = { + data: { + flags: balanceOf(0), + free: balanceOf(largeNumber), + frozen: balanceOf(largeNumber), + reserved: balanceOf(100), + } as PalletBalancesAccountData, + } as FrameSystemAccountInfo; + + const result = await calculateSystemAccountBalance(response); + expect(result).toBe( + BigInt(largeNumber) - (BigInt(largeNumber) - BigInt(100)), + ); + }); + }); }); diff --git a/packages/builder/src/balance/BalanceBuilder.ts b/packages/builder/src/balance/BalanceBuilder.ts index 165261e7..7675165d 100644 --- a/packages/builder/src/balance/BalanceBuilder.ts +++ b/packages/builder/src/balance/BalanceBuilder.ts @@ -164,7 +164,10 @@ export async function calculateSystemAccountBalance( const balance = response.data as PalletBalancesAccountData & PalletBalancesAccountDataOld; + const free = BigInt(balance.free.toString()); const frozen = balance.miscFrozen ?? balance.frozen; + const frozenMinusReserved = BigInt(frozen.sub(balance.reserved).toString()); + const locked = frozenMinusReserved < 0n ? 0n : frozenMinusReserved; - return BigInt(balance.free.sub(frozen).add(balance.reserved).toString()); + return free - locked; }