Skip to content

Commit 5ed53a9

Browse files
committed
feat: pausable transferable document
1 parent 1e3cae5 commit 5ed53a9

File tree

2 files changed

+125
-4
lines changed

2 files changed

+125
-4
lines changed

src/base/BaseTransferableDocumentStore.sol

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
pragma solidity >=0.8.23 <0.9.0;
44

55
import "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol";
6+
import "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol";
67
import { Strings } from "@openzeppelin/contracts/utils/Strings.sol";
78

89
import "../base/DocumentStoreAccessControl.sol";
@@ -13,6 +14,7 @@ import "../interfaces/IERC5192.sol";
1314
abstract contract BaseTransferableDocumentStore is
1415
DocumentStoreAccessControl,
1516
ERC721Upgradeable,
17+
PausableUpgradeable,
1618
IERC5192,
1719
ITransferableDocumentStoreErrors,
1820
ITransferableDocumentStore
@@ -26,8 +28,7 @@ abstract contract BaseTransferableDocumentStore is
2628
mapping(uint256 => bool) locked;
2729
}
2830

29-
// keccak256(abi.encode(uint256(keccak256("openattestation.storage.TransferableDocumentStore")) - 1)) &
30-
// ~bytes32(uint256(0xff))
31+
// keccak256(abi.encode(uint256(keccak256("openattestation.storage.TransferableDocumentStore")) - 1)) & ~bytes32(uint256(0xff))
3132
bytes32 private constant _DocumentStoreStorageSlot =
3233
0xe00db8ee8fc09b2a809fe10830715c77ed23b7661f93309e127fb70c1f33ef00;
3334

@@ -124,10 +125,26 @@ abstract contract BaseTransferableDocumentStore is
124125
return _isLocked(tokenId);
125126
}
126127

127-
function setBaseURI(string memory baseURI) public onlyRole(DEFAULT_ADMIN_ROLE) {
128+
function setBaseURI(string memory baseURI) public whenNotPaused onlyRole(DEFAULT_ADMIN_ROLE) {
128129
_getStorage().baseURI = baseURI;
129130
}
130131

132+
/**
133+
* @dev Pauses all token transfers.
134+
* @notice Requires the caller to be admin.
135+
*/
136+
function pause() external onlyRole(DEFAULT_ADMIN_ROLE) {
137+
_pause();
138+
}
139+
140+
/**
141+
* @dev Unpauses all token transfers.
142+
* @notice Requires the caller to be admin.
143+
*/
144+
function unpause() external onlyRole(DEFAULT_ADMIN_ROLE) {
145+
_unpause();
146+
}
147+
131148
function _isRevoked(uint256 tokenId) internal view returns (bool) {
132149
return _getStorage().revoked[tokenId];
133150
}
@@ -136,7 +153,11 @@ abstract contract BaseTransferableDocumentStore is
136153
return _getStorage().locked[tokenId];
137154
}
138155

139-
function _update(address to, uint256 tokenId, address auth) internal virtual override returns (address) {
156+
function _update(
157+
address to,
158+
uint256 tokenId,
159+
address auth
160+
) internal virtual override whenNotPaused returns (address) {
140161
address from = super._update(to, tokenId, auth);
141162
if (_isLocked(tokenId) && (from != address(0) && to != address(0))) {
142163
revert DocumentLocked(bytes32(tokenId));

test/TransferableDocumentStore.t.sol

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -498,3 +498,103 @@ contract TransferableDocumentStore_supportsInterface_Test is TranferableDocument
498498
assertTrue(documentStore.supportsInterface(type(IAccessControl).interfaceId));
499499
}
500500
}
501+
502+
contract TransferableDocumentStore_Pause_Test is TranferableDocumentStoreCommonTest {
503+
function testPause() public {
504+
vm.prank(owner);
505+
documentStore.pause();
506+
507+
assertTrue(documentStore.paused(), "Document store should be paused");
508+
}
509+
510+
function testUnpause() public {
511+
vm.prank(owner);
512+
documentStore.pause();
513+
vm.prank(owner);
514+
documentStore.unpause();
515+
516+
assertFalse(documentStore.paused(), "Document store should not be paused");
517+
}
518+
519+
function testPauseAsNonAdminRevert() public {
520+
address nonAdmin = vm.addr(69);
521+
522+
vm.expectRevert(
523+
abi.encodeWithSelector(
524+
IAccessControl.AccessControlUnauthorizedAccount.selector,
525+
nonAdmin,
526+
documentStore.DEFAULT_ADMIN_ROLE()
527+
)
528+
);
529+
530+
vm.prank(nonAdmin);
531+
documentStore.pause();
532+
}
533+
534+
function testUnpauseAsNonAdminRevert() public {
535+
address nonAdmin = vm.addr(69);
536+
537+
vm.expectRevert(
538+
abi.encodeWithSelector(
539+
IAccessControl.AccessControlUnauthorizedAccount.selector,
540+
nonAdmin,
541+
documentStore.DEFAULT_ADMIN_ROLE()
542+
)
543+
);
544+
545+
vm.prank(nonAdmin);
546+
documentStore.unpause();
547+
}
548+
}
549+
550+
contract TransferableDocumentStore_Pausable_Test is TransferableDocumentStore_Initializer {
551+
function setUp() public override {
552+
super.setUp();
553+
554+
vm.prank(owner);
555+
documentStore.pause();
556+
}
557+
558+
function testIssueWhenPausedRevert() public {
559+
bytes32 fakeDoc = "0x1234";
560+
561+
vm.expectRevert(abi.encodeWithSelector(PausableUpgradeable.EnforcedPause.selector));
562+
563+
vm.prank(issuer);
564+
documentStore.issue(recipients[0], fakeDoc, false);
565+
}
566+
567+
function testRevokeWhenPausedRevert() public {
568+
bytes32 revokeDoc = documents()[0];
569+
570+
vm.expectRevert(abi.encodeWithSelector(PausableUpgradeable.EnforcedPause.selector));
571+
572+
vm.prank(revoker);
573+
documentStore.revoke(revokeDoc);
574+
}
575+
576+
function testNonlockedDocTransferFromWhenPausedRevert() public {
577+
bytes32 transferNonLockedDoc = documents()[0];
578+
579+
vm.expectRevert(abi.encodeWithSelector(PausableUpgradeable.EnforcedPause.selector));
580+
581+
vm.prank(recipients[0]);
582+
documentStore.transferFrom(recipients[0], recipients[1], uint256(transferNonLockedDoc));
583+
}
584+
585+
function testLockedDocTransferFromWhenPausedRevert() public {
586+
bytes32 transferLockedDoc = documents()[1];
587+
588+
vm.expectRevert(abi.encodeWithSelector(PausableUpgradeable.EnforcedPause.selector));
589+
590+
vm.prank(recipients[0]);
591+
documentStore.transferFrom(recipients[1], recipients[0], uint256(transferLockedDoc));
592+
}
593+
594+
function testSetBaseURIWhenPausedRevert() public {
595+
vm.expectRevert(abi.encodeWithSelector(PausableUpgradeable.EnforcedPause.selector));
596+
597+
vm.prank(owner);
598+
documentStore.setBaseURI("https://example.com/");
599+
}
600+
}

0 commit comments

Comments
 (0)