From 688ab0a0aba69cce252a4a56a7605c8be485c40d Mon Sep 17 00:00:00 2001
From: Leon Hudak <33522493+leohhhn@users.noreply.github.com>
Date: Thu, 9 Nov 2023 18:48:07 +0100
Subject: [PATCH] feat: add p/demo/pausable (#1328)
## Description
This PR adds the `Pausable` package to p/demo, allowing realms to have
pausability. It relies on the already existing `Ownable` package for
access control, #1314.
Inspired by OpenZeppelin's
[Pausable](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v4.9.3/contracts/security/Pausable.sol).
Contributors' checklist...
- [x] Added new tests, or not needed, or not feasible
- [x] Provided an example (e.g. screenshot) to aid review or the PR is
self-explanatory
- [x] Updated the official documentation or not needed
- [x] No breaking changes were made, or a `BREAKING CHANGE: xxx` message
was included in the description
- [x] Added references to related issues and PRs
- [ ] Provided any useful hints for running manual tests
- [ ] Added new benchmarks to [generated
graphs](https://gnoland.github.io/benchmarks), if any. More info
[here](https://github.com/gnolang/gno/blob/master/.benchmarks/README.md).
---------
Co-authored-by: Morgan Bazalgette
---
examples/gno.land/p/demo/pausable/gno.mod | 3 +
.../gno.land/p/demo/pausable/pausable.gno | 49 ++++++++++++
.../p/demo/pausable/pausable_test.gno | 77 +++++++++++++++++++
3 files changed, 129 insertions(+)
create mode 100644 examples/gno.land/p/demo/pausable/gno.mod
create mode 100644 examples/gno.land/p/demo/pausable/pausable.gno
create mode 100644 examples/gno.land/p/demo/pausable/pausable_test.gno
diff --git a/examples/gno.land/p/demo/pausable/gno.mod b/examples/gno.land/p/demo/pausable/gno.mod
new file mode 100644
index 00000000000..08c7a4f7e5f
--- /dev/null
+++ b/examples/gno.land/p/demo/pausable/gno.mod
@@ -0,0 +1,3 @@
+module gno.land/p/demo/pausable
+
+require gno.land/p/demo/ownable v0.0.0-latest
diff --git a/examples/gno.land/p/demo/pausable/pausable.gno b/examples/gno.land/p/demo/pausable/pausable.gno
new file mode 100644
index 00000000000..eae3456ba61
--- /dev/null
+++ b/examples/gno.land/p/demo/pausable/pausable.gno
@@ -0,0 +1,49 @@
+package pausable
+
+import "gno.land/p/demo/ownable"
+
+type Pausable struct {
+ *ownable.Ownable
+ paused bool
+}
+
+// New returns a new Pausable struct with non-paused state as default
+func New() *Pausable {
+ return &Pausable{
+ Ownable: ownable.New(),
+ paused: false,
+ }
+}
+
+// NewFromOwnable is the same as New, but with a pre-existing top-level ownable
+func NewFromOwnable(ownable *ownable.Ownable) *Pausable {
+ return &Pausable{
+ Ownable: ownable,
+ paused: false,
+ }
+}
+
+// IsPaused checks if Pausable is paused
+func (p Pausable) IsPaused() bool {
+ return p.paused
+}
+
+// Pause sets the state of Pausable to true, meaning all pausable functions are paused
+func (p *Pausable) Pause() error {
+ if err := p.CallerIsOwner(); err != nil {
+ return err
+ }
+
+ p.paused = true
+ return nil
+}
+
+// Unpause sets the state of Pausable to false, meaning all pausable functions are resumed
+func (p *Pausable) Unpause() error {
+ if err := p.CallerIsOwner(); err != nil {
+ return err
+ }
+
+ p.paused = false
+ return nil
+}
diff --git a/examples/gno.land/p/demo/pausable/pausable_test.gno b/examples/gno.land/p/demo/pausable/pausable_test.gno
new file mode 100644
index 00000000000..cc95c457573
--- /dev/null
+++ b/examples/gno.land/p/demo/pausable/pausable_test.gno
@@ -0,0 +1,77 @@
+package pausable
+
+import (
+ "std"
+ "testing"
+
+ "gno.land/p/demo/ownable"
+)
+
+var (
+ firstCaller = std.Address("g1l9aypkr8xfvs82zeux486ddzec88ty69lue9de")
+ secondCaller = std.Address("g127jydsh6cms3lrtdenydxsckh23a8d6emqcvfa")
+)
+
+func TestNew(t *testing.T) {
+ std.TestSetOrigCaller(firstCaller)
+
+ result := New()
+
+ if result.paused != false {
+ t.Fatalf("Expected result to be unpaused, got %t\n", result.paused)
+ }
+
+ if result.Owner() != firstCaller {
+ t.Fatalf("Expected %s, got %s\n", firstCaller, result.Owner())
+ }
+}
+
+func TestNewFromOwnable(t *testing.T) {
+ std.TestSetOrigCaller(firstCaller)
+ o := ownable.New()
+
+ std.TestSetOrigCaller(secondCaller)
+ result := NewFromOwnable(o)
+
+ if result.Owner() != firstCaller {
+ t.Fatalf("Expected %s, got %s\n", firstCaller, result.Owner())
+ }
+}
+
+func TestSetUnpaused(t *testing.T) {
+ std.TestSetOrigCaller(firstCaller)
+
+ result := New()
+ result.Unpause()
+
+ if result.IsPaused() {
+ t.Fatalf("Expected result to be unpaused, got %t\n", result.IsPaused())
+ }
+}
+
+func TestSetPaused(t *testing.T) {
+ std.TestSetOrigCaller(firstCaller)
+
+ result := New()
+ result.Pause()
+
+ if !result.IsPaused() {
+ t.Fatalf("Expected result to be paused, got %t\n", result.IsPaused())
+ }
+}
+
+func TestIsPaused(t *testing.T) {
+ std.TestSetOrigCaller(firstCaller)
+
+ result := New()
+
+ if result.IsPaused() {
+ t.Fatalf("Expected result to be unpaused, got %t\n", result.IsPaused())
+ }
+
+ result.Pause()
+
+ if !result.IsPaused() {
+ t.Fatalf("Expected result to be paused, got %t\n", result.IsPaused())
+ }
+}