Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: pausable transferable doc #160

Merged
merged 2 commits into from
Mar 27, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
29 changes: 25 additions & 4 deletions src/base/BaseTransferableDocumentStore.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
pragma solidity >=0.8.23 <0.9.0;

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

import "../base/DocumentStoreAccessControl.sol";
Expand All @@ -13,6 +14,7 @@ import "../interfaces/IERC5192.sol";
abstract contract BaseTransferableDocumentStore is
DocumentStoreAccessControl,
ERC721Upgradeable,
PausableUpgradeable,
IERC5192,
ITransferableDocumentStoreErrors,
ITransferableDocumentStore
Expand All @@ -26,8 +28,7 @@ abstract contract BaseTransferableDocumentStore is
mapping(uint256 => bool) locked;
}

// keccak256(abi.encode(uint256(keccak256("openattestation.storage.TransferableDocumentStore")) - 1)) &
// ~bytes32(uint256(0xff))
// keccak256(abi.encode(uint256(keccak256("openattestation.storage.TransferableDocumentStore")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant _DocumentStoreStorageSlot =
0xe00db8ee8fc09b2a809fe10830715c77ed23b7661f93309e127fb70c1f33ef00;

Expand Down Expand Up @@ -124,10 +125,26 @@ abstract contract BaseTransferableDocumentStore is
return _isLocked(tokenId);
}

function setBaseURI(string memory baseURI) public onlyRole(DEFAULT_ADMIN_ROLE) {
function setBaseURI(string memory baseURI) public whenNotPaused onlyRole(DEFAULT_ADMIN_ROLE) {
_getStorage().baseURI = baseURI;
}

/**
* @dev Pauses all token transfers.
* @notice Requires the caller to be admin.
*/
function pause() external onlyRole(DEFAULT_ADMIN_ROLE) {
_pause();
}

/**
* @dev Unpauses all token transfers.
* @notice Requires the caller to be admin.
*/
function unpause() external onlyRole(DEFAULT_ADMIN_ROLE) {
_unpause();
}

function _isRevoked(uint256 tokenId) internal view returns (bool) {
return _getStorage().revoked[tokenId];
}
Expand All @@ -136,7 +153,11 @@ abstract contract BaseTransferableDocumentStore is
return _getStorage().locked[tokenId];
}

function _update(address to, uint256 tokenId, address auth) internal virtual override returns (address) {
function _update(
address to,
uint256 tokenId,
address auth
) internal virtual override whenNotPaused returns (address) {
address from = super._update(to, tokenId, auth);
if (_isLocked(tokenId) && (from != address(0) && to != address(0))) {
revert DocumentLocked(bytes32(tokenId));
Expand Down
73 changes: 73 additions & 0 deletions test/DeployUtils.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity >=0.8.23 <0.9.0;

import { DocumentStoreUpgradeable } from "../src/upgradeables/DocumentStoreUpgradeable.sol";
import { TransferableDocumentStoreUpgradeable } from "../src/upgradeables/TransferableDocumentStoreUpgradeable.sol";
import { CommonTest } from "./CommonTest.t.sol";
import { DeployUtils } from "../src/libraries/DeployUtils.sol";

contract DeployUtils_Test is CommonTest {
string public initialName = "Test DocumentStore";
string public initialSymbol = "TEST";

function testDeployDocumentStoreUpgradeableParameters() public {
(DocumentStoreUpgradeable dsProxy, DocumentStoreUpgradeable documentStore) = _runDeployDocumentStoreUpgradeable();

assertEq(dsProxy.name(), initialName);
assertTrue(dsProxy.hasRole(documentStore.DEFAULT_ADMIN_ROLE(), owner));
}

function testDeployDocumentStorageUpgradeableImplementation() public {
(DocumentStoreUpgradeable dsProxy, DocumentStoreUpgradeable documentStore) = _runDeployDocumentStoreUpgradeable();

bytes32 proxyImpl = vm.load(address(dsProxy), 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc);

assertEq(address(documentStore), address(uint160(uint256(proxyImpl))));
}

function testDeployTransferableDocumentStoreUpgradeableParameters() public {
(
TransferableDocumentStoreUpgradeable dsProxy,
TransferableDocumentStoreUpgradeable documentStore
) = _runDeployTransferableDocumentStoreUpgradeable();

assertEq(dsProxy.name(), initialName);
assertEq(dsProxy.symbol(), initialSymbol);
assertTrue(dsProxy.hasRole(documentStore.DEFAULT_ADMIN_ROLE(), owner));
}

function testDeployTransferableDocumentStorageUpgradeableImplementation() public {
(
TransferableDocumentStoreUpgradeable dsProxy,
TransferableDocumentStoreUpgradeable documentStore
) = _runDeployTransferableDocumentStoreUpgradeable();

bytes32 proxyImpl = vm.load(address(dsProxy), 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc);

assertEq(address(documentStore), address(uint160(uint256(proxyImpl))));
}

function _runDeployDocumentStoreUpgradeable()
internal
returns (DocumentStoreUpgradeable dsProxy, DocumentStoreUpgradeable ds)
{
(address proxyAddr, address dsAddr) = DeployUtils.deployDocumentStoreUpgradeable(initialName, owner);

dsProxy = DocumentStoreUpgradeable(proxyAddr);
ds = DocumentStoreUpgradeable(dsAddr);
}

function _runDeployTransferableDocumentStoreUpgradeable()
internal
returns (TransferableDocumentStoreUpgradeable dsProxy, TransferableDocumentStoreUpgradeable ds)
{
(address proxyAddr, address dsAddr) = DeployUtils.deployTransferableDocumentStoreUpgradeable(
initialName,
initialSymbol,
owner
);

dsProxy = TransferableDocumentStoreUpgradeable(proxyAddr);
ds = TransferableDocumentStoreUpgradeable(dsAddr);
}
}
6 changes: 4 additions & 2 deletions test/DocumentStore.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,10 @@ contract DocumentStore_init_Test is DocumentStoreCommonTest {
assert(documentStore.hasRole(documentStore.REVOKER_ROLE(), owner));
}

function testFailZeroOwner() public {
documentStore = new DocumentStore(storeName, vm.addr(0));
function testZeroOwnerRevert() public {
vm.expectRevert(abi.encodeWithSelector(DocumentStoreAccessControl.ZeroOwner.selector));

documentStore = new DocumentStore(storeName, address(0));
}
}

Expand Down
106 changes: 104 additions & 2 deletions test/TransferableDocumentStore.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,10 @@ contract TransferableDocumentStore_init_Test is TranferableDocumentStoreCommonTe
assert(documentStore.hasRole(documentStore.REVOKER_ROLE(), owner));
}

function testFailZeroOwner() public {
documentStore = new TransferableDocumentStore(storeName, storeSymbol, vm.addr(0));
function testZeroOwnerRevert() public {
vm.expectRevert(abi.encodeWithSelector(DocumentStoreAccessControl.ZeroOwner.selector));

documentStore = new TransferableDocumentStore(storeName, storeSymbol, address(0));
}
}

Expand Down Expand Up @@ -496,3 +498,103 @@ contract TransferableDocumentStore_supportsInterface_Test is TranferableDocument
assertTrue(documentStore.supportsInterface(type(IAccessControl).interfaceId));
}
}

contract TransferableDocumentStore_Pause_Test is TranferableDocumentStoreCommonTest {
function testPause() public {
vm.prank(owner);
documentStore.pause();

assertTrue(documentStore.paused(), "Document store should be paused");
}

function testUnpause() public {
vm.prank(owner);
documentStore.pause();
vm.prank(owner);
documentStore.unpause();

assertFalse(documentStore.paused(), "Document store should not be paused");
}

function testPauseAsNonAdminRevert() public {
address nonAdmin = vm.addr(69);

vm.expectRevert(
abi.encodeWithSelector(
IAccessControl.AccessControlUnauthorizedAccount.selector,
nonAdmin,
documentStore.DEFAULT_ADMIN_ROLE()
)
);

vm.prank(nonAdmin);
documentStore.pause();
}

function testUnpauseAsNonAdminRevert() public {
address nonAdmin = vm.addr(69);

vm.expectRevert(
abi.encodeWithSelector(
IAccessControl.AccessControlUnauthorizedAccount.selector,
nonAdmin,
documentStore.DEFAULT_ADMIN_ROLE()
)
);

vm.prank(nonAdmin);
documentStore.unpause();
}
}

contract TransferableDocumentStore_Pausable_Test is TransferableDocumentStore_Initializer {
function setUp() public override {
super.setUp();

vm.prank(owner);
documentStore.pause();
}

function testIssueWhenPausedRevert() public {
bytes32 fakeDoc = "0x1234";

vm.expectRevert(abi.encodeWithSelector(PausableUpgradeable.EnforcedPause.selector));

vm.prank(issuer);
documentStore.issue(recipients[0], fakeDoc, false);
}

function testRevokeWhenPausedRevert() public {
bytes32 revokeDoc = documents()[0];

vm.expectRevert(abi.encodeWithSelector(PausableUpgradeable.EnforcedPause.selector));

vm.prank(revoker);
documentStore.revoke(revokeDoc);
}

function testNonlockedDocTransferFromWhenPausedRevert() public {
bytes32 transferNonLockedDoc = documents()[0];

vm.expectRevert(abi.encodeWithSelector(PausableUpgradeable.EnforcedPause.selector));

vm.prank(recipients[0]);
documentStore.transferFrom(recipients[0], recipients[1], uint256(transferNonLockedDoc));
}

function testLockedDocTransferFromWhenPausedRevert() public {
bytes32 transferLockedDoc = documents()[1];

vm.expectRevert(abi.encodeWithSelector(PausableUpgradeable.EnforcedPause.selector));

vm.prank(recipients[0]);
documentStore.transferFrom(recipients[1], recipients[0], uint256(transferLockedDoc));
}

function testSetBaseURIWhenPausedRevert() public {
vm.expectRevert(abi.encodeWithSelector(PausableUpgradeable.EnforcedPause.selector));

vm.prank(owner);
documentStore.setBaseURI("https://example.com/");
}
}
Loading