Skip to content

Conversation

@MikaelVallenet
Copy link
Member

@MikaelVallenet MikaelVallenet commented Dec 1, 2025

a package that handle subscriptions either with native coins or grc20 coins.
a public API with a way to create service where users can subscribe and top up their account.
Services owners can check the status of a user subscription to give access or not to a service.

The coins part is from payrolls realm available here: #3432
I added unit test for the coins handling part that is the most "critical" one.

⚠️ This realm is set to private to allow upgrade without proxy pattern and is meant to be an example of what is possible not developed in a production goal, for now at least

@github-actions github-actions bot added the 🧾 package/realm Tag used for new Realms or Packages. label Dec 1, 2025
@Gno2D2 Gno2D2 added the review/triage-pending PRs opened by external contributors that are waiting for the 1st review label Dec 1, 2025
@Gno2D2
Copy link
Collaborator

Gno2D2 commented Dec 1, 2025

🛠 PR Checks Summary

🔴 Pending initial approval by a review team member, or review from tech-staff

Manual Checks (for Reviewers):
  • IGNORE the bot requirements for this PR (force green CI check)
Read More

🤖 This bot helps streamline PR reviews by verifying automated checks and providing guidance for contributors and reviewers.

✅ Automated Checks (for Contributors):

🟢 Maintainers must be able to edit this pull request (more info)
🔴 Pending initial approval by a review team member, or review from tech-staff

☑️ Contributor Actions:
  1. Fix any issues flagged by automated checks.
  2. Follow the Contributor Checklist to ensure your PR is ready for review.
    • Add new tests, or document why they are unnecessary.
    • Provide clear examples/screenshots, if necessary.
    • Update documentation, if required.
    • Ensure no breaking changes, or include BREAKING CHANGE notes.
    • Link related issues/PRs, where applicable.
☑️ Reviewer Actions:
  1. Complete manual checks for the PR, including the guidelines and additional checks if applicable.
📚 Resources:
Debug
Automated Checks
Maintainers must be able to edit this pull request (more info)

If

🟢 Condition met
└── 🟢 And
    ├── 🟢 The base branch matches this pattern: ^master$
    └── 🟢 The pull request was created from a fork (head branch repo: MikaelVallenet/gno)

Then

🟢 Requirement satisfied
└── 🟢 Maintainer can modify this pull request

Pending initial approval by a review team member, or review from tech-staff

If

🟢 Condition met
└── 🟢 And
    ├── 🟢 The base branch matches this pattern: ^master$
    └── 🟢 Not (🔴 Pull request author is a member of the team: tech-staff)

Then

🔴 Requirement not satisfied
└── 🔴 If
    ├── 🔴 Condition
    │   └── 🔴 Or
    │       ├── 🔴 At least one of these user(s) reviewed the pull request: [jefft0 leohhhn n0izn0iz notJoon omarsy x1unix] (with state "APPROVED")
    │       ├── 🔴 At least 1 user(s) of the team tech-staff reviewed pull request
    │       └── 🔴 This pull request is a draft
    └── 🔴 Else
        └── 🔴 And
            ├── 🟢 This label is applied to pull request: review/triage-pending
            └── 🔴 On no pull request

Manual Checks
**IGNORE** the bot requirements for this PR (force green CI check)

If

🟢 Condition met
└── 🟢 On every pull request

Can be checked by

  • Any user with comment edit permission

@codecov
Copy link

codecov bot commented Dec 1, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

Copy link
Contributor

@Davphla Davphla left a comment

Choose a reason for hiding this comment

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

I think we should:

  • Add a README for explaining what the realm is about + main public function
  • Add a dashboard in the UI to explain how to use the service (Exemple: add subscription)
    (I can do a PR for this additions)
  • You should add events for important operation
  • There's some inconsistency, when sometime you use panic(...) and sometimes panic(errors.New(...))

// XXX: this part is 100% from payrolls realm from N0izN0iz <3
// Payrolls realm: https://github.com/gnolang/gno/pull/3432

// Coins transforms coins into subscriptions coins, prefixing their denom them with their type. Examples: "ugnot" -> "/native/ugnot", "gno.land/r/demo/foo20" -> "/grc20/gno.land/r/demo/foo20"
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this should be move to its own standalone package
/p/samcrew/coins/normalizedCoins.gno
Or something like that


func sendCoins(dst address, coins chain.Coins) {
if len(coins) == 0 {
return
Copy link
Contributor

Choose a reason for hiding this comment

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

Instead of returning silently, we should return an error, a status or a panic

return ""
}

func humanDuration(d time.Duration) string {
Copy link
Contributor

Choose a reason for hiding this comment

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

This should be moved to its own package. This may be useful in other situations.
Maybe we can create /p/samcrew/renderutils, and add more later on.

out[j].Amount = coin.Amount
out[j].Denom = "/grc20/" + coin.Denom
}
return out, nil
Copy link
Contributor

Choose a reason for hiding this comment

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

error is always == to nil
If both len == 0, we should return an error

// Payrolls realm: https://github.com/gnolang/gno/pull/3432

// Coins transforms coins into subscriptions coins, prefixing their denom them with their type. Examples: "ugnot" -> "/native/ugnot", "gno.land/r/demo/foo20" -> "/grc20/gno.land/r/demo/foo20"
func Coins(cur realm, native chain.Coins, grc20 chain.Coins) (chain.Coins, error) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
func Coins(cur realm, native chain.Coins, grc20 chain.Coins) (chain.Coins, error) {
func PrefixCoins(cur realm, native chain.Coins, grc20 chain.Coins) (chain.Coins, error) {

Or NormalizeCoins, FormatCoins

// - serviceName: the name of the service to subscribe to
// - fqName: the fully qualified name of the GRC20 token to use for payment (empty string for native coins)
// - amount: the amount of GRC20 tokens to deposit initially (0 for native coins)
func Subscribe(cur realm, serviceName string, fqName string, amount int64) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Amount can be negative, even if it's verified after through coinsHasPositive, I think it is better to verify beforehand explicitly

natSend := banker.OriginSend()
grc20Send := chain.Coins{}
if amount != 0 {
grc20Send = chain.NewCoins(chain.NewCoin("/grc20/"+fqName, amount))
Copy link
Contributor

Choose a reason for hiding this comment

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

grc20send token are never actually transferred
// - amount: the amount of GRC20 tokens to deposit initially (0 for native coins)


// ServiceClaimVault allows the owner of a service to claim the funds in the service's vault
// - displayName: the name of the service
func ServiceClaimVault(cur realm, displayName string) {
Copy link
Contributor

Choose a reason for hiding this comment

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

This does not update lastChargedAt

// Topup allows a user to top up their subscription balance using native coins or GRC20 tokens
// - serviceName: the name of the service to top up
// - fqName: the fully qualified name of the GRC20 token to use for top up (empty string for native coins)
// - amount: the amount of GRC20 tokens to deposit (0 for native coins)
Copy link
Contributor

Choose a reason for hiding this comment

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

This comment should be change, as it is possible to topup native and grc20 in one tx

sub := subValue.(*subscription)
due, _ := sub.dueAmount(svc.renewalPeriod, svc.price)
if coinsHasPositive(due) {
sendCoins(svc.owner.Owner(), due)
Copy link
Contributor

Choose a reason for hiding this comment

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

You don't update lastChargedAt

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🧾 package/realm Tag used for new Realms or Packages. review/triage-pending PRs opened by external contributors that are waiting for the 1st review

Projects

Status: In Progress

Development

Successfully merging this pull request may close these issues.

3 participants