diff --git a/.github/README.md b/.github/README.md
index 8e866fa..ecacb0e 100644
--- a/.github/README.md
+++ b/.github/README.md
@@ -2,4 +2,6 @@
Ondo's US Dollar Yield Token on Noble
+[![codecov](https://img.shields.io/codecov/c/gh/noble-assets/aura?token=U9QRRF82W7&labelColor=black)](https://codecov.io/gh/noble-assets/aura)
+
This repository includes the `x/aura` Cosmos SDK module implementation.
diff --git a/x/aura/keeper/keeper.go b/x/aura/keeper/keeper.go
index c87815d..86196c2 100644
--- a/x/aura/keeper/keeper.go
+++ b/x/aura/keeper/keeper.go
@@ -7,6 +7,7 @@ import (
"cosmossdk.io/collections"
"cosmossdk.io/core/event"
"cosmossdk.io/core/store"
+ "cosmossdk.io/errors"
"cosmossdk.io/log"
"github.com/cosmos/cosmos-sdk/codec"
@@ -77,8 +78,10 @@ func NewKeeper(
return keeper
}
-// SendRestrictionFn checks every USDY transfer on the Noble chain to and checks
-// if transfers are currently paused.
+// SendRestrictionFn executes the following checks against all USDY transfers:
+// - Is the module currently paused?
+// - If we're not minting, check the sender against the blocklist.
+// - If we're not burning, check the recipient against the blocklist.
func (k *Keeper) SendRestrictionFn(ctx context.Context, fromAddr, toAddr sdk.AccAddress, amt sdk.Coins) (newToAddr sdk.AccAddress, err error) {
if amount := amt.AmountOf(k.Denom); !amount.IsZero() {
paused, _ := k.Paused.Get(ctx)
@@ -86,7 +89,27 @@ func (k *Keeper) SendRestrictionFn(ctx context.Context, fromAddr, toAddr sdk.Acc
return toAddr, fmt.Errorf("%s transfers are paused", k.Denom)
}
- // TODO(@john): Discuss with Ondo the checks needed here.
+ if !fromAddr.Equals(types.ModuleAddress) {
+ has, err := k.BlockedAddresses.Has(ctx, fromAddr)
+ if err != nil {
+ return toAddr, errors.Wrap(err, "unable to retrieve blocked address")
+ }
+ if has {
+ address, _ := k.accountKeeper.AddressCodec().BytesToString(fromAddr)
+ return toAddr, fmt.Errorf("%s is blocked from sending %s", address, k.Denom)
+ }
+ }
+
+ if !toAddr.Equals(types.ModuleAddress) {
+ has, err := k.BlockedAddresses.Has(ctx, toAddr)
+ if err != nil {
+ return toAddr, errors.Wrap(err, "unable to retrieve blocked address")
+ }
+ if has {
+ address, _ := k.accountKeeper.AddressCodec().BytesToString(toAddr)
+ return toAddr, fmt.Errorf("%s is blocked from receiving %s", address, k.Denom)
+ }
+ }
}
return toAddr, nil
diff --git a/x/aura/keeper/keeper_test.go b/x/aura/keeper/keeper_test.go
new file mode 100644
index 0000000..5371b81
--- /dev/null
+++ b/x/aura/keeper/keeper_test.go
@@ -0,0 +1,64 @@
+package keeper_test
+
+import (
+ "testing"
+
+ "cosmossdk.io/math"
+ sdk "github.com/cosmos/cosmos-sdk/types"
+ "github.com/noble-assets/aura/utils"
+ "github.com/noble-assets/aura/utils/mocks"
+ "github.com/stretchr/testify/require"
+)
+
+func TestSendRestriction(t *testing.T) {
+ keeper, ctx := mocks.AuraKeeper(t)
+ from, to := utils.TestAccount(), utils.TestAccount()
+
+ // ACT: Attempt to send 1 USDC.
+ _, err := keeper.SendRestrictionFn(ctx, from.Bytes, to.Bytes, sdk.NewCoins(sdk.NewCoin(
+ "uusdc", math.NewInt(1_000_000),
+ )))
+ // ASSERT: Action should succeed as it's not USDY.
+ require.NoError(t, err)
+
+ // ACT: Attempt to send 1 USDY.
+ _, err = keeper.SendRestrictionFn(ctx, from.Bytes, to.Bytes, sdk.NewCoins(sdk.NewCoin(
+ keeper.Denom, ONE,
+ )))
+ // ASSERT: Action should succeed.
+ require.NoError(t, err)
+
+ // ARRANGE: Set paused state to true.
+ require.NoError(t, keeper.Paused.Set(ctx, true))
+
+ // ACT: Attempt to send 1 USDY when paused.
+ _, err = keeper.SendRestrictionFn(ctx, from.Bytes, to.Bytes, sdk.NewCoins(sdk.NewCoin(
+ keeper.Denom, ONE,
+ )))
+ // ASSERT: Action should succeed.
+ require.ErrorContains(t, err, "ausdy transfers are paused")
+
+ // ARRANGE: Set paused state to false.
+ require.NoError(t, keeper.Paused.Set(ctx, false))
+ // ARRANGE: Block from address.
+ require.NoError(t, keeper.BlockedAddresses.Set(ctx, from.Bytes, true))
+
+ // ACT: Attempt to send 1 USDY from blocklisted address.
+ _, err = keeper.SendRestrictionFn(ctx, from.Bytes, to.Bytes, sdk.NewCoins(sdk.NewCoin(
+ keeper.Denom, ONE,
+ )))
+ // ASSERT: Action should've failed due to blocked sender.
+ require.ErrorContains(t, err, "blocked from sending")
+
+ // ARRANGE: Unblock from address.
+ require.NoError(t, keeper.BlockedAddresses.Remove(ctx, from.Bytes))
+ // ARRANGE: Block to address.
+ require.NoError(t, keeper.BlockedAddresses.Set(ctx, to.Bytes, true))
+
+ // ACT: Attempt to send 1 USDY to blocklisted address.
+ _, err = keeper.SendRestrictionFn(ctx, from.Bytes, to.Bytes, sdk.NewCoins(sdk.NewCoin(
+ keeper.Denom, ONE,
+ )))
+ // ASSERT: Action should've failed due to blocked recipient.
+ require.ErrorContains(t, err, "blocked from receiving")
+}
diff --git a/x/aura/types/keys.go b/x/aura/types/keys.go
index b546c89..81398dd 100644
--- a/x/aura/types/keys.go
+++ b/x/aura/types/keys.go
@@ -1,7 +1,11 @@
package types
+import authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
+
const ModuleName = "aura"
+var ModuleAddress = authtypes.NewModuleAddress(ModuleName)
+
var (
PausedKey = []byte("paused")
BurnerPrefix = []byte("burner/")