Loyalty programs are popular tools for businesses to enhance customer retention and increase engagement. A blockchain-based loyalty points system offers transparency, security, and ease of use for both the issuer and the customer.
Step 1: Scaffold a New Blockchain
Create a new blockchain project using Ignite CLI:
ignite scaffold chain loyaltychain --no-module && cd loyaltychain
Step 2: Scaffold the Loyalty Module
Generate the loyalty module with:
ignite scaffold module loyalty
Step 3: Define Data Structures for Points and Rewards
Scaffold the data structures required for managing loyalty points and rewards. For example, define a Point
type to represent loyalty points and a Reward
type to represent redeemable items or services:
ignite scaffold map Point owner balance:int --module loyalty
ignite scaffold map Reward item points:int --module loyalty
Step 4: Implement Points Issuance and Redemption Functionality
The IssuePoints
function will be responsible for crediting loyalty points to a user's account. This operation will typically be called by the business logic layer after a customer performs an action that earns them points, like making a purchase.
In your x/loyalty/keeper/keeper.go
, implement the logic for issuing loyalty points:
package keeper
import (
// ...
errorsmod "cosmossdk.io/errors"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"loyaltychain/x/loyalty/types"
)
// IssuePoints credits points to the owner's balance.
func (k Keeper) IssuePoints(ctx sdk.Context, owner string, amount int) error {
if amount <= 0 {
return errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "amount must be positive")
}
// Convert the owner string to an sdk.AccAddress
_, err := sdk.AccAddressFromBech32(owner)
if err != nil {
return errorsmod.Wrap(sdkerrors.ErrInvalidAddress, "owner address is invalid")
}
// Convert amount to int32
amountInt32 := int32(amount)
// Get the current balance or initialize a new one
pointBalance, found := k.GetPoint(ctx, owner)
if !found {
pointBalance = types.Point{
Owner: owner,
Balance: 0,
}
}
// Update the balance
pointBalance.Balance += amountInt32
// Set the updated balance back to the store
k.SetPoint(ctx, pointBalance)
return nil
}
The RedeemReward
function will allow users to exchange their loyalty points for rewards. The function will check if the user has enough points to redeem the requested reward and then deduct the appropriate amount from their balance.
Implement the logic for redeeming rewards with loyalty points:
// RedeemReward exchanges points for a specified reward item.
func (k Keeper) RedeemReward(ctx sdk.Context, owner string, rewardItem string) error {
// Convert the owner string to an sdk.AccAddress
_, err := sdk.AccAddressFromBech32(owner)
if err != nil {
return errorsmod.Wrap(sdkerrors.ErrInvalidAddress, "owner address is invalid")
}
// Check if the reward exists
reward, found := k.GetReward(ctx, rewardItem)
if !found {
return errorsmod.Wrap(types.ErrRewardNotFound, "reward does not exist")
}
// Get the owner's point balance
pointBalance, found := k.GetPoint(ctx, owner)
if !found || pointBalance.Balance < reward.Points {
return errorsmod.Wrap(types.ErrInsufficientPoints, "insufficient points to redeem reward")
}
// Deduct the points for the reward
pointBalance.Balance -= reward.Points
// Set the updated balance back to the store
k.SetPoint(ctx, pointBalance)
// Emit an event for the reward redemption
ctx.EventManager().EmitEvent(
sdk.NewEvent(string(types.EventTypeRedeemReward),
sdk.NewAttribute(string(types.AttributeKeyReward), rewardItem),
sdk.NewAttribute(string(types.AttributeKeyOwner), owner),
sdk.NewAttribute(string(types.AttributeKeyPoints), fmt.Sprintf("%d", reward.Points)),
),
)
return nil
}
Add the two new error types to the x/loyalty/types/errors.go
ErrInvalidSigner = sdkerrors.Register(ModuleName, 1100, "expected gov account as only signer for proposal message")
ErrSample = sdkerrors.Register(ModuleName, 1101, "sample error")
ErrRewardNotFound = sdkerrors.Register(ModuleName, 1102, "Rewards Not Found")
ErrInsufficientPoints = sdkerrors.Register(ModuleName, 1103, "Insufficient Points")
Add your EventTypes and Attribute Keys to the x/loyalty/types/types.go
package types
type EventType string
type AttributeKey string
const (
EventTypeRedeemReward EventType = "RedeemReward"
AttributeKeyReward AttributeKey = "reward"
AttributeKeyOwner AttributeKey = "owner"
AttributeKeyPoints AttributeKey = "points"
)
Step 5: Add CLI and gRPC Configurations
Ensure your module's CLI commands and gRPC endpoints are properly configured for managing loyalty points and redeeming rewards.
Step 6: Test the Module
After implementing the module, test issuing points and redeeming rewards using CLI commands or gRPC clients.
With these steps, you have built the foundation for a Loyalty Points and Rewards module. This module can be further expanded to include features like tier-based rewards, time-limited offers, and integration with point-of-sale systems.
or
or
By clicking below, you agree to our terms of service.
New to HackMD? Sign up
Syntax | Example | Reference | |
---|---|---|---|
# Header | Header | 基本排版 | |
- Unordered List |
|
||
1. Ordered List |
|
||
- [ ] Todo List |
|
||
> Blockquote | Blockquote |
||
**Bold font** | Bold font | ||
*Italics font* | Italics font | ||
~~Strikethrough~~ | |||
19^th^ | 19th | ||
H~2~O | H2O | ||
++Inserted text++ | Inserted text | ||
==Marked text== | Marked text | ||
[link text](https:// "title") | Link | ||
 | Image | ||
`Code` | Code |
在筆記中貼入程式碼 | |
```javascript var i = 0; ``` |
|
||
:smile: | ![]() |
Emoji list | |
{%youtube youtube_id %} | Externals | ||
$L^aT_eX$ | LaTeX | ||
:::info This is a alert area. ::: |
This is a alert area. |
On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?
Please give us some advice and help us improve HackMD.
Do you want to remove this version name and description?
Syncing