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

feat: auth, authz, bank e2e tests for fiattokenfactory #373

Merged
merged 2 commits into from
Oct 17, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
325 changes: 325 additions & 0 deletions e2e/fiat_tf_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@ import (

"cosmossdk.io/math"
fiattokenfactorytypes "github.com/circlefin/noble-fiattokenfactory/x/fiattokenfactory/types"
"github.com/cosmos/cosmos-sdk/crypto/keyring"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/noble-assets/noble/e2e"
"github.com/strangelove-ventures/interchaintest/v8"
"github.com/strangelove-ventures/interchaintest/v8/ibc"
"github.com/stretchr/testify/require"
)

Expand Down Expand Up @@ -1521,3 +1523,326 @@ func TestFiatTFBurn(t *testing.T) {
require.NoError(t, err, "error getting balance")
require.EqualValues(t, expectedAmount, bal.Int64())
}

func TestFiatTFAuth(t *testing.T) {
if testing.Short() {
t.Skip()
}
t.Parallel()

ctx := context.Background()

nw := e2e.NobleSpinUp(t, ctx, true)
noble := nw.Chain
val := noble.Validators[0]

// ACTION: Send non TF token but pay fee in TF token while the TF is paused
// EXPECTED: Request fails; TF is paused

originalAmount := math.OneInt()
w := interchaintest.GetAndFundTestUsers(t, ctx, "default", originalAmount, noble, noble)
alice := w[0] // 1ustake
bob := w[1] // 1ustake

mintAmount := int64(100)
_, err := val.ExecTx(ctx, nw.FiatTfRoles.Minter.KeyName(), "fiat-tokenfactory", "mint", alice.FormattedAddress(), fmt.Sprintf("%duusdc", mintAmount))
require.NoError(t, err)

e2e.PauseFiatTF(t, ctx, val, nw.FiatTfRoles.Pauser)

sendAmount := 1
uusdcFee := int64(5)
_, err = val.ExecTx(ctx, alice.KeyName(), "bank", "send", alice.KeyName(), bob.FormattedAddress(), fmt.Sprintf("%dustake", sendAmount), "--fees", fmt.Sprintf("%duusdc", uusdcFee))
require.ErrorContains(t, err, "the chain is paused")

uStakebal, err := noble.GetBalance(ctx, alice.FormattedAddress(), "ustake")
require.NoError(t, err)
require.Equal(t, originalAmount, uStakebal)

uusdcBal, err := noble.GetBalance(ctx, alice.FormattedAddress(), "uusdc")
require.NoError(t, err)
require.Equal(t, math.NewInt(mintAmount), uusdcBal)

Choose a reason for hiding this comment

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

What's the expected behaviour for the fees here? Are fees still charged (and so alice's USDC balance drops), or is the transaction dropped before fees are charged so the balance remains? Is this worth adding an assertion on?

Copy link
Member Author

Choose a reason for hiding this comment

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

Good call. The fee is not taken.
Added a check in: 93925c1

e2e.UnpauseFiatTF(t, ctx, val, nw.FiatTfRoles.Pauser)

// ACTION: Send non TF token but pay fee in TF token while the sender is blacklisted
// EXPECTED: Request fails

e2e.BlacklistAccount(t, ctx, val, nw.FiatTfRoles.Blacklister, alice)

_, err = val.ExecTx(ctx, alice.KeyName(), "bank", "send", alice.KeyName(), bob.FormattedAddress(), fmt.Sprintf("%dustake", sendAmount), "--fees", fmt.Sprintf("%duusdc", uusdcFee))
require.ErrorContains(t, err, fmt.Sprintf("an address (%s) is blacklisted and can not send tokens: unauthorized", alice.FormattedAddress()))

bal, err := noble.GetBalance(ctx, alice.FormattedAddress(), "ustake")
require.NoError(t, err)
require.Equal(t, originalAmount, bal)

Choose a reason for hiding this comment

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

same question about checking the usdc balance

Copy link
Member Author

Choose a reason for hiding this comment

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

Added check in: 93925c1

uusdcBal, err = noble.GetBalance(ctx, alice.FormattedAddress(), "uusdc")
require.NoError(t, err)
require.Equal(t, math.NewInt(mintAmount), uusdcBal)

e2e.UnblacklistAccount(t, ctx, val, nw.FiatTfRoles.Blacklister, alice)

// ACTION: Successfully send non TF token but pay fee in TF token
// EXPECTED: Success; Fee withdrawn from users balance

_, err = val.ExecTx(ctx, alice.KeyName(), "bank", "send", alice.KeyName(), bob.FormattedAddress(), fmt.Sprintf("%dustake", sendAmount), "--fees", fmt.Sprintf("%duusdc", uusdcFee))
require.NoError(t, err)

bobBalStake, err := noble.GetBalance(ctx, bob.FormattedAddress(), "ustake")
require.NoError(t, err)
require.Equal(t, originalAmount.Add(math.NewInt(int64(sendAmount))), bobBalStake)

aliceBalUusdc, err := noble.GetBalance(ctx, alice.FormattedAddress(), "uusdc")
require.NoError(t, err)
require.EqualValues(t, mintAmount-uusdcFee, aliceBalUusdc.Int64())
}

func TestFiatTFAuthzGrant(t *testing.T) {
if testing.Short() {
t.Skip()
}
t.Parallel()

ctx := context.Background()

nw := e2e.NobleSpinUp(t, ctx, true)
noble := nw.Chain
val := noble.Validators[0]

// ACTION: Grant an authz SEND using a TF token while TF is paused
// EXPECTED: Request fails

w := interchaintest.GetAndFundTestUsers(t, ctx, "default", math.OneInt(), noble, noble)
granter1 := w[0]
grantee1 := w[1]

e2e.PauseFiatTF(t, ctx, val, nw.FiatTfRoles.Pauser)

_, err := val.AuthzGrant(ctx, granter1, grantee1.FormattedAddress(), "send", "--spend-limit=100uusdc")
require.ErrorContains(t, err, "can not perform token authorizations: the chain is paused")

e2e.UnpauseFiatTF(t, ctx, val, nw.FiatTfRoles.Pauser)

// ACTION: Grant an authz SEND using a TF token to a grantee who is blacklisted
// EXPECTED: Success;

w = interchaintest.GetAndFundTestUsers(t, ctx, "default", math.OneInt(), noble, noble)
granter2 := w[0]
grantee2 := w[1]

e2e.BlacklistAccount(t, ctx, val, nw.FiatTfRoles.Blacklister, grantee2)

res, err := val.AuthzGrant(ctx, granter2, grantee2.FormattedAddress(), "send", "--spend-limit=100uusdc")
require.NoError(t, err)
require.Zero(t, res.Code)

// ACTION: Grant an authz SEND using a TF token from a granter who is blacklisted
// EXPECTED: Success;

w = interchaintest.GetAndFundTestUsers(t, ctx, "default", math.OneInt(), noble, noble)
granter3 := w[0]
grantee3 := w[1]

e2e.BlacklistAccount(t, ctx, val, nw.FiatTfRoles.Blacklister, granter3)

res, err = val.AuthzGrant(ctx, granter3, grantee3.FormattedAddress(), "send", "--spend-limit=100uusdc")
require.NoError(t, err)
require.Zero(t, res.Code)
}
Comment on lines +1639 to +1652
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider Validating Authorization Grants

After granting authorization (lines 1636-1652), it's good practice to verify that the authorization has been correctly recorded.

Add assertions to confirm that the grantee has the expected authorization:

res, err := val.AuthzListGrants(ctx, granter2, grantee2.FormattedAddress())
require.NoError(t, err)
require.Contains(t, res.Grants, /* expected grant details */)

This ensures that the authorization grant is not only successful but also properly registered in the system.


func TestFiatTFAuthzSend(t *testing.T) {
if testing.Short() {
t.Skip()
}
t.Parallel()

ctx := context.Background()

nw := e2e.NobleSpinUp(t, ctx, true)
noble := nw.Chain
val := noble.Validators[0]

// setup
w := interchaintest.GetAndFundTestUsers(t, ctx, "default", math.OneInt(), noble, noble, noble)
granter := w[0]
grantee := w[1]
receiver := w[2]

mintAmount := int64(100)
_, err := val.ExecTx(ctx, nw.FiatTfRoles.Minter.KeyName(), "fiat-tokenfactory", "mint", granter.FormattedAddress(), fmt.Sprintf("%duusdc", mintAmount))
require.NoError(t, err, "error minting")

res, err := val.AuthzGrant(ctx, granter, grantee.FormattedAddress(), "send", "--spend-limit=100uusdc")
require.NoError(t, err)
require.Zero(t, res.Code)

sendAmount := int64(5)
nestedCmd := []string{
noble.Config().Bin,
"tx", "bank", "send", granter.FormattedAddress(), receiver.FormattedAddress(), fmt.Sprintf("%duusdc", sendAmount),
"--from", granter.FormattedAddress(), "--generate-only",
"--chain-id", noble.GetNode().Chain.Config().ChainID,
"--node", noble.GetNode().Chain.GetRPCAddress(),
"--home", noble.GetNode().HomeDir(),
"--keyring-backend", keyring.BackendTest,
"--output", "json",
"--yes",
}

// ACTION: Execute an authz SEND using a TF token from a grantee who is blacklisted
// EXPECTED: Request fails; Even though grantee is acting on behalf of the granter,
// the granter still cannot execute `send` due to being blacklisted
// Status:
// Granter1 has authorized Grantee1 to send 100usdc from their wallet

e2e.BlacklistAccount(t, ctx, val, nw.FiatTfRoles.Blacklister, grantee)

_, err = val.AuthzExec(ctx, grantee, nestedCmd)
require.ErrorContains(t, err, fmt.Sprintf("an address (%s) is blacklisted and can not authorize tokens: unauthorized", receiver.FormattedAddress()))
Comment on lines +1693 to +1702
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Correct Error Message Check for Blacklisted Grantee

In TestFiatTFAuthzSend, when the grantee is blacklisted (line 1699), the error check references the receiver's address instead of the grantee's:

require.ErrorContains(t, err, fmt.Sprintf("an address (%s) is blacklisted and can not authorize tokens: unauthorized", receiver.FormattedAddress()))

Since the grantee (grantee) is blacklisted, the error message should reference the grantee's address.

Apply this diff to correct the error message check:

- require.ErrorContains(t, err, fmt.Sprintf("an address (%s) is blacklisted and can not authorize tokens: unauthorized", receiver.FormattedAddress()))
+ require.ErrorContains(t, err, fmt.Sprintf("an address (%s) is blacklisted and can not authorize tokens: unauthorized", grantee.FormattedAddress()))

This ensures that the test accurately validates the error message corresponding to the blacklisted grantee.


bal, err := noble.GetBalance(ctx, receiver.FormattedAddress(), "uusdc")
require.NoError(t, err)
require.True(t, bal.IsZero())

e2e.UnblacklistAccount(t, ctx, val, nw.FiatTfRoles.Blacklister, grantee)

// ACTION: Execute an authz SEND using a TF token from a granter who is blacklisted
// EXPECTED: Request fails; Granter is blacklisted
// Status:
// Granter1 has authorized Grantee1 to send 100usdc from their wallet

e2e.BlacklistAccount(t, ctx, val, nw.FiatTfRoles.Blacklister, granter)

_, err = val.AuthzExec(ctx, grantee, nestedCmd)
require.ErrorContains(t, err, fmt.Sprintf("an address (%s) is blacklisted and can not send tokens: unauthorized", granter.FormattedAddress()))

bal, err = noble.GetBalance(ctx, receiver.FormattedAddress(), "uusdc")
require.NoError(t, err)
require.True(t, bal.IsZero())

e2e.UnblacklistAccount(t, ctx, val, nw.FiatTfRoles.Blacklister, granter)

// ACTION: Execute an authz SEND using a TF token to a receiver who is blacklisted
// EXPECTED: Request fails; Granter is blacklisted
// Status:
// Granter1 has authorized Grantee1 to send 100usdc from their wallet

e2e.BlacklistAccount(t, ctx, val, nw.FiatTfRoles.Blacklister, receiver)

_, err = val.AuthzExec(ctx, grantee, nestedCmd)
require.ErrorContains(t, err, fmt.Sprintf("an address (%s) is blacklisted and can not receive tokens: unauthorized", receiver.FormattedAddress()))

bal, err = noble.GetBalance(ctx, receiver.FormattedAddress(), "uusdc")
require.NoError(t, err)
require.True(t, bal.IsZero())

e2e.UnblacklistAccount(t, ctx, val, nw.FiatTfRoles.Blacklister, receiver)

// ACTION: Execute an authz SEND using a TF token while the TF is paused
// EXPECTED: Request fails; chain is paused
// Status:
// Granter1 has authorized Grantee1 to send 100usdc from their wallet

e2e.PauseFiatTF(t, ctx, val, nw.FiatTfRoles.Pauser)

_, err = val.AuthzExec(ctx, grantee, nestedCmd)
require.ErrorContains(t, err, "the chain is paused")

bal, err = noble.GetBalance(ctx, receiver.FormattedAddress(), "uusdc")
require.NoError(t, err)
require.True(t, bal.IsZero())

e2e.UnpauseFiatTF(t, ctx, val, nw.FiatTfRoles.Pauser)

Comment on lines +1747 to +1757
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Ensure Proper Cleanup After Paused State

In TestFiatTFAuthzSend, after testing the paused state (lines 1747-1757), ensure that the TF is unpaused before proceeding to other tests to avoid side effects.

Add a call to e2e.UnpauseFiatTF if not already done, or confirm that the TF is unpaused at the end of the test.

// Ensure TF is unpaused after test
e2e.UnpauseFiatTF(t, ctx, val, nw.FiatTfRoles.Pauser)

// ACTION: Happy path: Execute an authz SEND using a TF token
// EXPECTED: Success; authz send is successful.
// Status:
// Granter1 has authorized Grantee1 to send 100usdc from their wallet

_, err = val.AuthzExec(ctx, grantee, nestedCmd)
require.NoError(t, err)

bal, err = noble.GetBalance(ctx, receiver.FormattedAddress(), "uusdc")
require.NoError(t, err)
require.Equal(t, math.NewInt(sendAmount), bal)

}

Choose a reason for hiding this comment

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

Is there any value in adding a happy path test here (no accounts blacklisted, chain not paused)?

Copy link
Member Author

Choose a reason for hiding this comment

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

I think so, mainly to validate the test. Added:
b345e59 (#373)


func TestFiatTFBankSend(t *testing.T) {
if testing.Short() {
t.Skip()
}
t.Parallel()

ctx := context.Background()

nw := e2e.NobleSpinUp(t, ctx, true)
noble := nw.Chain
val := noble.Validators[0]

w := interchaintest.GetAndFundTestUsers(t, ctx, "default", math.OneInt(), noble, noble)
alice := w[0]
bob := w[1]

mintAmount := int64(100)
_, err := val.ExecTx(ctx, nw.FiatTfRoles.Minter.KeyName(), "fiat-tokenfactory", "mint", alice.FormattedAddress(), fmt.Sprintf("%duusdc", mintAmount))
require.NoError(t, err, "error minting")

// ACTION: Send TF token while TF is paused
// EXPECTED: Request fails; token not sent

e2e.PauseFiatTF(t, ctx, val, nw.FiatTfRoles.Pauser)

amountToSend := ibc.WalletAmount{
Address: bob.FormattedAddress(),
Denom: "uusdc",
Amount: math.OneInt(),
}
err = noble.SendFunds(ctx, alice.KeyName(), amountToSend)
require.ErrorContains(t, err, "the chain is paused")

bobBal, err := noble.GetBalance(ctx, bob.FormattedAddress(), "uusdc")
require.NoError(t, err, "error getting balance")
require.True(t, bobBal.IsZero())

e2e.UnpauseFiatTF(t, ctx, val, nw.FiatTfRoles.Pauser)

// ACTION: Send TF token while FROM address is blacklisted
// EXPECTED: Request fails; token not sent

e2e.BlacklistAccount(t, ctx, val, nw.FiatTfRoles.Blacklister, alice)

err = noble.SendFunds(ctx, alice.KeyName(), amountToSend)
require.ErrorContains(t, err, fmt.Sprintf("an address (%s) is blacklisted and can not send tokens", alice.FormattedAddress()))

bobBal, err = noble.GetBalance(ctx, bob.FormattedAddress(), "uusdc")
require.NoError(t, err, "error getting balance")
require.True(t, bobBal.IsZero())

e2e.UnblacklistAccount(t, ctx, val, nw.FiatTfRoles.Blacklister, alice)

// ACTION: Send TF token while TO address is blacklisted
// EXPECTED: Request fails; token not sent

e2e.BlacklistAccount(t, ctx, val, nw.FiatTfRoles.Blacklister, bob)

err = noble.SendFunds(ctx, alice.KeyName(), amountToSend)
require.ErrorContains(t, err, fmt.Sprintf("an address (%s) is blacklisted and can not receive tokens", bob.FormattedAddress()))

bobBal, err = noble.GetBalance(ctx, bob.FormattedAddress(), "uusdc")
require.NoError(t, err, "error getting balance")
require.True(t, bobBal.IsZero())

e2e.UnblacklistAccount(t, ctx, val, nw.FiatTfRoles.Blacklister, bob)

// ACTION: Successfully send TF token
// EXPECTED: Success

err = noble.SendFunds(ctx, alice.KeyName(), amountToSend)
require.NoError(t, err, "error sending funds")

bobBal, err = noble.GetBalance(ctx, bob.FormattedAddress(), "uusdc")
require.NoError(t, err, "error getting balance")
require.Equal(t, amountToSend.Amount, bobBal)
}
Loading