Skip to content

Fix DEX freeze bug#92

Merged
TxCorpi0x merged 14 commits into
masterfrom
fix-DEX-freeze-bug
Mar 27, 2026
Merged

Fix DEX freeze bug#92
TxCorpi0x merged 14 commits into
masterfrom
fix-DEX-freeze-bug

Conversation

@masihyeganeh

@masihyeganeh masihyeganeh commented Feb 23, 2026

Copy link
Copy Markdown
Contributor

Description

This pull request strengthens the enforcement of token freezing in the DEX (decentralized exchange) module. It ensures that frozen tokens cannot be locked or transferred via DEX operations, and updates both the implementation and tests to reflect the correct handling of spendable balances when tokens are frozen, locked by the DEX, or locked by the bank. The changes also add new integration tests to verify these behaviors.

Key changes:

DEX and Freezing Logic Improvements:

  • The calculation of spendable balance now correctly subtracts both DEX-locked and frozen amounts, ensuring that only truly spendable tokens can be locked or transferred. Previously, these were not always additive, which could allow frozen tokens to be used in DEX operations. [1] [2]
  • The validateCoinSpendable function is updated to use the new spendable calculation, preventing users from placing DEX orders or sending tokens that would utilize frozen balances.

Testing Enhancements:

  • Adds comprehensive integration tests (TestFrozenTokenEscapeViaDEX and TestFrozenTokenFreezeAfterOrderPlacement) to verify that frozen tokens cannot be locked or transferred via DEX, and that orders placed before freezing are still honored.
  • Updates unit tests to match the new spendable balance logic, ensuring tests expect the correct (stricter) outcomes when tokens are frozen and/or locked. [1] [2] [3] [4]

Interface and Documentation:

  • Extends the BankKeeper interface to include a SpendableCoin method, which is now used in the spendable balance calculations.
  • Adds a nolint:interfacebloat comment to the BankKeeper interface for clarity.

These changes together close a loophole where frozen tokens could be improperly used in DEX operations, and ensure that both the business logic and tests are aligned with the intended security model.

Reviewers checklist:

  • Try to write more meaningful comments with clear actions to be taken.
  • Nit-picking should be unblocking. Focus on core issues.

Authors checklist

  • Provide a concise and meaningful description
  • Review the code yourself first, before making the PR.
  • Annotate your PR in places that require explanation.
  • Think and try to split the PR to smaller PR if it is big.

This change is Reviewable

@masihyeganeh masihyeganeh requested a review from a team as a code owner February 23, 2026 11:37
@masihyeganeh masihyeganeh requested review from TxCorpi0x, metalarm10, miladz68 and ysv and removed request for a team February 23, 2026 11:37

@ysv ysv left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ysv reviewed 3 files and all commit messages, and made 3 comments.
Reviewable status: all files reviewed, 3 unresolved discussions (waiting on masihyeganeh, metalarm10, miladz68, and TxCorpi0x).


integration-tests/modules/dex_test.go line 3058 at r1 (raw file):

// via raw bank keeper. This test asserts the fix: the second order (which would
// lock frozen tokens) must be rejected.
func TestFrozenTokenEscapeViaDEX(t *testing.T) {

are these 2 tests from immunify bugreport ?

they are almost equal so I would keep 1 one of them. Either integration or keeper test


x/asset/ft/keeper/keeper_dex.go line 69 at r1 (raw file):

	for _, send := range actions.Send {
		// Validate frozen balances before sending to prevent transferring frozen tokens

does this part of code run during order execution ?

As far as I remember our plan was to make sure that if order is inside orderbook it will be guaranteed executable when matched. And I think this check brakes such logic

Lets discuss


integration-tests/modules/dex_test.go line 3230 at r1 (raw file):

		placeOrder2,
	)
	requireT.Error(err, "Second order of 400,000 must fail: it would lock frozen tokens; available spendable is 0")

maybe it makes sense to check that address is not able to lock 100k or 10k also.
Smaller portion of frozen/locked.

@masihyeganeh masihyeganeh left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@masihyeganeh made 3 comments.
Reviewable status: all files reviewed, 3 unresolved discussions (waiting on metalarm10, miladz68, TxCorpi0x, and ysv).


integration-tests/modules/dex_test.go line 3058 at r1 (raw file):

Previously, ysv (Yaroslav Savchuk) wrote…

are these 2 tests from immunify bugreport ?

they are almost equal so I would keep 1 one of them. Either integration or keeper test

Yes. kept the integration test only


integration-tests/modules/dex_test.go line 3230 at r1 (raw file):

Previously, ysv (Yaroslav Savchuk) wrote…

maybe it makes sense to check that address is not able to lock 100k or 10k also.
Smaller portion of frozen/locked.

Done.


x/asset/ft/keeper/keeper_dex.go line 69 at r1 (raw file):

Previously, ysv (Yaroslav Savchuk) wrote…

does this part of code run during order execution ?

As far as I remember our plan was to make sure that if order is inside orderbook it will be guaranteed executable when matched. And I think this check brakes such logic

Lets discuss

It was an extra safeguard for a defense in depth. I removed it and still both tests are passing. I also checked the original test and it failed

@miladz68 miladz68 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@miladz68 made 1 comment.
Reviewable status: 0 of 3 files reviewed, 4 unresolved discussions (waiting on masihyeganeh, metalarm10, TxCorpi0x, and ysv).


x/asset/ft/keeper/keeper_dex.go line 670 at r2 (raw file):

	// validate that we don't use frozen coins (if freezing feature is enabled)
	def, err := k.getDefinitionOrNil(ctx, coin.Denom)

Do I understand correctly that we check that frozen is not violated after a dex match happens ?

@ysv ysv requested a review from miladz68 February 24, 2026 13:05

@ysv ysv left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ysv made 2 comments and resolved 3 discussions.
Reviewable status: 0 of 3 files reviewed, 3 unresolved discussions (waiting on masihyeganeh, metalarm10, miladz68, and TxCorpi0x).


x/asset/ft/keeper/keeper_dex.go line 670 at r2 (raw file):

	// validate that we don't use frozen coins (if freezing feature is enabled)
	def, err := k.getDefinitionOrNil(ctx, coin.Denom)

@miladz68 @masihyeganeh
lets double check and make sure this is called ONLY on order placement step not inside matching


integration-tests/modules/dex_test.go line 3058 at r2 (raw file):

// via raw bank keeper. This test asserts the fix: the second order (which would
// lock frozen tokens) must be rejected.
func TestFrozenTokenEscapeViaDEX(t *testing.T) {

lets also add intgr or unit-test for the following logic:

  1. user has 100TKN availble
  2. user creates order for 80TKN sell
  3. issuer freezes 60/100 TKN
  4. DEX order matched
  5. Order should still be executed

(number are subject to change)

@masihyeganeh masihyeganeh left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@masihyeganeh made 1 comment.
Reviewable status: 0 of 4 files reviewed, 3 unresolved discussions (waiting on metalarm10, miladz68, TxCorpi0x, and ysv).


integration-tests/modules/dex_test.go line 3058 at r2 (raw file):

Previously, ysv (Yaroslav Savchuk) wrote…

lets also add intgr or unit-test for the following logic:

  1. user has 100TKN availble
  2. user creates order for 80TKN sell
  3. issuer freezes 60/100 TKN
  4. DEX order matched
  5. Order should still be executed

(number are subject to change)

Done.

@miladz68 miladz68 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@miladz68 made 1 comment.
Reviewable status: 0 of 5 files reviewed, 4 unresolved discussions (waiting on masihyeganeh, metalarm10, TxCorpi0x, and ysv).


x/asset/ft/keeper/keeper.go line 1017 at r3 (raw file):

	}

	// Spendable = balance - dexLocked - bankLocked - frozen (additive when freezing enabled)

should we check the spendable balance query ?

ysv
ysv previously approved these changes Feb 25, 2026

@ysv ysv left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ysv reviewed 5 files and all commit messages, made 1 comment, and resolved 2 discussions.
Reviewable status: all files reviewed, 3 unresolved discussions (waiting on masihyeganeh, metalarm10, and TxCorpi0x).


integration-tests/modules/dex_test.go line 3306 at r4 (raw file):

	requireT.NoError(err)

	// Setup: token with freezing, Alice has 100 TKN (1,000,000), Bob has quote

nit: maybe to match with comments we operate with millions of tokens

100 TKN = 100_000_000
80 TKN = 80_000_000

just easier to read

@miladz68 miladz68 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@miladz68 reviewed all commit messages and resolved 1 discussion.
Reviewable status: all files reviewed, 2 unresolved discussions (waiting on masihyeganeh, metalarm10, and TxCorpi0x).

@TxCorpi0x TxCorpi0x left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@TxCorpi0x partially reviewed 5 files, made 2 comments, and resolved 1 discussion.
Reviewable status: 3 of 6 files reviewed, 1 unresolved discussion (waiting on metalarm10, miladz68, and ysv).


integration-tests/modules/dex_test.go line 3306 at r4 (raw file):

Previously, ysv (Yaroslav Savchuk) wrote…

nit: maybe to match with comments we operate with millions of tokens

100 TKN = 100_000_000
80 TKN = 80_000_000

just easier to read

Done


x/asset/ft/keeper/keeper.go line 1017 at r3 (raw file):

Previously, miladz68 (milad) wrote…

should we check the spendable balance query ?

Done

@ysv ysv requested a review from miladz68 March 27, 2026 11:57

@ysv ysv left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ysv reviewed 3 files and all commit messages.
Reviewable status: all files reviewed, 1 unresolved discussion (waiting on metalarm10 and miladz68).

@TxCorpi0x TxCorpi0x dismissed miladz68’s stale review March 27, 2026 12:00

The changes have been made, but the comment is set as dismissed, since Milad is not available to resolve at the moment.

@metalarm10 metalarm10 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@metalarm10 reviewed 6 files and all commit messages.
Reviewable status: all files reviewed, 1 unresolved discussion (waiting on miladz68).

@TxCorpi0x TxCorpi0x merged commit ca7d2cc into master Mar 27, 2026
13 of 14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants