Skip to content
Open
Show file tree
Hide file tree
Changes from 3 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
5 changes: 5 additions & 0 deletions .changeset/full-chicken-beg.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'openzeppelin-solidity': patch
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
'openzeppelin-solidity': patch
'openzeppelin-solidity': minor

---

Implements ERC6909Pausable
33 changes: 33 additions & 0 deletions contracts/token/ERC6909/extensions/ERC6909Pausable.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.5.0) (token/ERC6909/extensions/ERC6909Pausable.sol)

pragma solidity ^0.8.24;

import {ERC6909} from "../ERC6909.sol";
import {Pausable} from "../../../utils/Pausable.sol";

/**
* @dev ERC-6909 token with pausable token transfers, minting and burning.
*
* Useful for scenarios such as preventing trades until the end of an evaluation
* period, or having an emergency switch for freezing all token transfers in the
* event of a large bug.
*
* IMPORTANT: This contract does not include public pause and unpause functions. In
* addition to inheriting this contract, you must define both functions, invoking the
* {Pausable-_pause} and {Pausable-_unpause} internal functions, with appropriate
* access control, e.g. using {AccessControl} or {Ownable}. Not doing so will
* make the contract pause mechanism of the contract unreachable, and thus unusable.
*/
abstract contract ERC6909Pausable is ERC6909, Pausable {
/**
* @dev See {ERC6909-_update}.
*
* Requirements:
*
* - the contract must not be paused.
*/
function _update(address from, address to, uint256 ids, uint256 values) internal virtual override whenNotPaused {
super._update(from, to, ids, values);
}
}
81 changes: 81 additions & 0 deletions test/token/ERC6909/extensions/ERC6909Pausable.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
const { ethers } = require('hardhat');
const { expect } = require('chai');
const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers');
const { shouldBehaveLikeERC6909 } = require('../ERC6909.behavior');

async function fixture() {
const [holder, operator, recipient, other] = await ethers.getSigners();
const token = await ethers.deployContract('$ERC6909Pausable');
return { token, holder, operator, recipient, other };
}

describe('ERC6909Pausable', function () {
const firstTokenId = 37n;
const firstTokenValue = 42n;
const secondTokenId = 19842n;
const secondTokenValue = 23n;

beforeEach(async function () {
Object.assign(this, await loadFixture(fixture));
});

shouldBehaveLikeERC6909();

describe('when token is paused', function () {
beforeEach(async function () {
await this.token.connect(this.holder).setOperator(this.operator, true);
await this.token.$_mint(this.holder, firstTokenId, firstTokenValue);
await this.token.$_pause();
});

it('reverts when trying to transfer', async function () {
await expect(
this.token.connect(this.holder).transfer(this.recipient, firstTokenId, firstTokenValue),
).to.be.revertedWithCustomError(this.token, 'EnforcedPause');
});

it('reverts when trying to transferFrom from operator', async function () {
await expect(
this.token.connect(this.operator).transferFrom(this.holder, this.recipient, firstTokenId, firstTokenValue),
).to.be.revertedWithCustomError(this.token, 'EnforcedPause');
});

it('reverts when trying to mint', async function () {
await expect(this.token.$_mint(this.holder, secondTokenId, secondTokenValue)).to.be.revertedWithCustomError(
this.token,
'EnforcedPause',
);
});

it('reverts when trying to burn', async function () {
await expect(this.token.$_burn(this.holder, firstTokenId, firstTokenValue)).to.be.revertedWithCustomError(
this.token,
'EnforcedPause',
);
});

describe('setOperator', function () {
it('approves an operator', async function () {
await this.token.connect(this.holder).setOperator(this.other, true);
expect(await this.token.isOperator(this.holder, this.other)).to.be.true;
});

it('disapproves an operator', async function () {
await this.token.connect(this.holder).setOperator(this.other, false);
expect(await this.token.isOperator(this.holder, this.other)).to.be.false;
});
});
Comment thread
arr00 marked this conversation as resolved.

describe('balanceOf', function () {
it('returns the token value owned by the given address', async function () {
expect(await this.token.balanceOf(this.holder, firstTokenId)).to.equal(firstTokenValue);
});
});

describe('isOperator', function () {
it('returns the approval of the operator', async function () {
expect(await this.token.isOperator(this.holder, this.operator)).to.be.true;
});
});
});
});