feat: access point auto approval settings for tokens (#121)

* feat: add accessPointAutoApprovalSettings field to mint, app struct, and expose a function for changing the field later + an event.

* feat: add checks for the autoapproval settings on function addAccessPoint.

* feat: add setApprovalForAccessPoint function and ChangeAccessPointApprovalStatus event.

* test: add new constant variables to the hardhat tests and update mint tests.

* feat: update removeAccessPoint function to check the status and also update getAccessPointJSON to include status.

* test: add two access point test files and fix errors and mismatches in them with the auto approval set up

* feat: remove the access point mapping in the App struct and wherever it was used.

* chore: update foundry tests to match the new interface of the contract.

* test: add new tests for the approval settings

* chore: update foundry tests to match new interface.

* test: update foundry tests and the settings for auto approvals

* feat: keep history of removed APs. Update tests.

* fix: make changes to the contract and tests to fix the tests.

* chore: apply changes Zoruka requested.

* fix: change name of setAutoApprovalSettings function in foundry tests.

* perf: revert back to enums, update hardhat and foundry tests.

* fix: apply requested changes by janison.

* fix: error in hardhat test.

* fix: mint params of a foundry test.

* fix: merge errors.

* fix: revert back to tokenOwner for setAutoApproval functions.

* chore: remove comment for accessPointAutoApproval
This commit is contained in:
Shredder 2023-02-23 12:29:53 +03:30 committed by GitHub
parent 770ab78668
commit cfea9a90ea
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 602 additions and 61 deletions

View File

@ -27,6 +27,13 @@ contract FleekERC721 is Initializable, ERC721Upgradeable, FleekAccessControl, Fl
event NewAccessPoint(string apName, uint256 indexed tokenId, address indexed owner);
event RemoveAccessPoint(string apName, uint256 indexed tokenId, address indexed owner);
event ChangeAccessPointAutoApproval(
uint256 indexed token,
bool indexed settings,
address indexed triggeredBy
);
event ChangeAccessPointScore(string apName, uint256 indexed tokenId, uint256 score, address indexed triggeredBy);
event ChangeAccessPointNameVerify(
@ -41,6 +48,13 @@ contract FleekERC721 is Initializable, ERC721Upgradeable, FleekAccessControl, Fl
bool indexed verified,
address indexed triggeredBy
);
event ChangeAccessPointStatus(
string apName,
uint256 tokenId,
AccessPointCreationStatus status,
address indexed triggeredBy
);
/**
* The properties are stored as string to keep consistency with
* other token contracts, we might consider changing for bytes32
@ -55,6 +69,7 @@ contract FleekERC721 is Initializable, ERC721Upgradeable, FleekAccessControl, Fl
mapping(uint256 => Build) builds; // Mapping to build details for each build number
string logo;
uint24 color; // Color of the nft
bool accessPointAutoApproval; // AP Auto Approval
}
/**
@ -65,6 +80,16 @@ contract FleekERC721 is Initializable, ERC721Upgradeable, FleekAccessControl, Fl
string gitRepository;
}
/**
* Creation status enums for access points
*/
enum AccessPointCreationStatus {
DRAFT,
APPROVED,
REJECTED,
REMOVED
}
/**
* The stored data for each AccessPoint.
*/
@ -74,6 +99,7 @@ contract FleekERC721 is Initializable, ERC721Upgradeable, FleekAccessControl, Fl
bool contentVerified;
bool nameVerified;
address owner;
AccessPointCreationStatus status;
}
Counters.Counter private _appIds;
@ -117,7 +143,8 @@ contract FleekERC721 is Initializable, ERC721Upgradeable, FleekAccessControl, Fl
string memory commitHash,
string memory gitRepository,
string memory logo,
uint24 color
uint24 color,
bool accessPointAutoApproval
) public payable requireCollectionRole(CollectionRoles.Owner) returns (uint256) {
uint256 tokenId = _appIds.current();
_mint(to, tokenId);
@ -130,6 +157,7 @@ contract FleekERC721 is Initializable, ERC721Upgradeable, FleekAccessControl, Fl
app.ENS = ENS;
app.logo = logo;
app.color = color;
app.accessPointAutoApproval = accessPointAutoApproval;
// The mint interaction is considered to be the first build of the site. Updates from now on all increment the currentBuild by one and update the mapping.
app.currentBuild = 0;
@ -225,6 +253,26 @@ contract FleekERC721 is Initializable, ERC721Upgradeable, FleekAccessControl, Fl
return "data:application/json;base64,";
}
/**
* @dev Updates the `accessPointAutoApproval` settings on minted `tokenId`.
*
* May emit a {ChangeAccessPointAutoApproval} event.
*
* Requirements:
*
* - the tokenId must be minted and valid.
* - the sender must have the `tokenController` role.
*
*/
function setAccessPointAutoApproval(
uint256 tokenId,
bool _apAutoApproval
) public virtual requireTokenOwner(tokenId) {
_requireMinted(tokenId);
_apps[tokenId].accessPointAutoApproval = _apAutoApproval;
emit ChangeAccessPointAutoApproval(tokenId, _apAutoApproval, msg.sender);
}
/**
* @dev Updates the `externalURL` metadata field of a minted `tokenId`.
*
@ -380,9 +428,56 @@ contract FleekERC721 is Initializable, ERC721Upgradeable, FleekAccessControl, Fl
_requireMinted(tokenId);
require(_accessPoints[apName].owner == address(0), "FleekERC721: AP already exists");
_accessPoints[apName] = AccessPoint(tokenId, 0, false, false, msg.sender);
emit NewAccessPoint(apName, tokenId, msg.sender);
if (_apps[tokenId].accessPointAutoApproval) {
// Auto Approval is on.
_accessPoints[apName] = AccessPoint(tokenId, 0, false, false, msg.sender, AccessPointCreationStatus.APPROVED);
emit ChangeAccessPointStatus(apName, tokenId, AccessPointCreationStatus.APPROVED, msg.sender);
} else {
// Auto Approval is off. Should wait for approval.
_accessPoints[apName] = AccessPoint(tokenId, 0, false, false, msg.sender, AccessPointCreationStatus.DRAFT);
emit ChangeAccessPointStatus(apName, tokenId, AccessPointCreationStatus.DRAFT, msg.sender);
}
}
/**
* @dev Set approval settings for an access point.
* It will add the access point to the token's AP list, if `approved` is true.
*
* May emit a {ChangeAccessPointApprovalStatus} event.
*
* Requirements:
*
* - the tokenId must exist and be the same as the tokenId that is set for the AP.
* - the AP must exist.
* - must be called by a token controller.
*/
function setApprovalForAccessPoint(
uint256 tokenId,
string memory apName,
bool approved
) public requireTokenOwner(tokenId) {
AccessPoint storage accessPoint = _accessPoints[apName];
require(
accessPoint.tokenId == tokenId,
"FleekERC721: the passed tokenId is not the same as the access point's tokenId."
);
require(
accessPoint.status == AccessPointCreationStatus.DRAFT,
"FleekERC721: the access point creation status has been set before."
);
if (approved) {
// Approval
accessPoint.status = AccessPointCreationStatus.APPROVED;
emit ChangeAccessPointStatus(apName, tokenId, AccessPointCreationStatus.APPROVED, msg.sender);
} else {
// Not Approved
accessPoint.status = AccessPointCreationStatus.REJECTED;
emit ChangeAccessPointStatus(apName, tokenId, AccessPointCreationStatus.REJECTED, msg.sender);
}
}
/**
@ -400,9 +495,9 @@ contract FleekERC721 is Initializable, ERC721Upgradeable, FleekAccessControl, Fl
*/
function removeAccessPoint(string memory apName) public whenNotPaused requireAP(apName) {
require(msg.sender == _accessPoints[apName].owner, "FleekERC721: must be AP owner");
_accessPoints[apName].status = AccessPointCreationStatus.REMOVED;
uint256 tokenId = _accessPoints[apName].tokenId;
delete _accessPoints[apName];
emit ChangeAccessPointStatus(apName, tokenId, AccessPointCreationStatus.REMOVED, msg.sender);
emit RemoveAccessPoint(apName, tokenId, msg.sender);
}

View File

@ -41,6 +41,7 @@ library FleekStrings {
'"owner":"', uint160(owner).toHexString(20), '",',
'"external_url":"', app.externalURL, '",',
'"image":"', FleekSVG.generateBase64(app.name, app.ENS, app.logo, app.color.toColorString()), '",',
'"access_point_auto_approval":',app.accessPointAutoApproval.toString(),',',
'"attributes": [',
'{"trait_type": "ENS", "value":"', app.ENS,'"},',
'{"trait_type": "Commit Hash", "value":"', app.builds[app.currentBuild].commitHash,'"},',
@ -63,7 +64,8 @@ library FleekStrings {
'"score":', ap.score.toString(), ",",
'"nameVerified":', ap.nameVerified.toString(), ",",
'"contentVerified":', ap.contentVerified.toString(), ",",
'"owner":"', uint160(ap.owner).toHexString(20), '"',
'"owner":"', uint160(ap.owner).toHexString(20), '",',
'"status":',uint(ap.status).toString(),
"}"
));
}

View File

@ -2,28 +2,16 @@
pragma solidity ^0.8.17;
import "./TestBase.sol";
import "../TestBase.sol";
import {Strings} from "@openzeppelin/contracts/utils/Strings.sol";
import {FleekAccessControl} from "contracts/FleekAccessControl.sol";
import "../../../../contracts/FleekERC721.sol";
import './ApBase.sol';
contract Test_FleekERC721_AccessPoint is Test_FleekERC721_Base {
contract Test_FleekERC721_AccessPoint is Test_FleekERC721_Base, APConstants {
using Strings for address;
uint256 internal tokenId;
function assertAccessPointJSON(
string memory accessPointName,
string memory _tokenId,
string memory score,
string memory nameVerified,
string memory contentVerified,
address owner
) internal {
string memory current = CuT.getAccessPointJSON(accessPointName);
// prettier-ignore
string memory expectedJSON = string(abi.encodePacked('{"tokenId":', _tokenId, ',"score":', score, ',"nameVerified":', nameVerified, ',"contentVerified":', contentVerified, ',"owner":"', owner.toHexString(), '"}'));
assertEq(current, expectedJSON);
}
function setUp() public {
baseSetUp();
tokenId = mintDefault(deployer);
@ -33,7 +21,7 @@ contract Test_FleekERC721_AccessPoint is Test_FleekERC721_Base {
string memory accessPointName = "accesspoint.com";
CuT.addAccessPoint(tokenId, accessPointName);
assertAccessPointJSON(accessPointName, "0", "0", "false", "false", deployer);
APConstants.assertAccessPointJSON(accessPointName, "0", "0", "false", "false", deployer, "0", CuT.getAccessPointJSON(accessPointName));
}
function test_removeAccessPoint() public {
@ -41,24 +29,14 @@ contract Test_FleekERC721_AccessPoint is Test_FleekERC721_Base {
CuT.addAccessPoint(tokenId, accessPointName);
CuT.removeAccessPoint(accessPointName);
expectRevertWithInvalidAP();
CuT.getAccessPointJSON(accessPointName);
APConstants.assertAccessPointJSON(accessPointName, "0", "0", "false", "false", deployer, "3", CuT.getAccessPointJSON(accessPointName));
}
function test_cannotRemoveNonexistentAccessPoint() public {
function test_cannotRemoveNonExistentAccessPoint() public {
expectRevertWithInvalidAP();
CuT.removeAccessPoint("accesspoint.com");
}
function test_cannotTwiceRemoveAccessPoint() public {
string memory accessPointName = "accesspoint.com";
CuT.addAccessPoint(tokenId, accessPointName);
CuT.removeAccessPoint(accessPointName);
expectRevertWithInvalidAP();
CuT.removeAccessPoint(accessPointName);
}
function test_isAccessPointNameVerified() public {
string memory accessPointName = "accesspoint.com";
CuT.addAccessPoint(tokenId, accessPointName);
@ -69,20 +47,20 @@ contract Test_FleekERC721_AccessPoint is Test_FleekERC721_Base {
function test_increaseAccessPointScore() public {
string memory accessPointName = "accesspoint.com";
CuT.addAccessPoint(tokenId, accessPointName);
assertAccessPointJSON(accessPointName, "0", "0", "false", "false", deployer);
APConstants.assertAccessPointJSON(accessPointName, "0", "0", "false", "false", deployer, "0", CuT.getAccessPointJSON(accessPointName));
CuT.increaseAccessPointScore(accessPointName);
assertAccessPointJSON(accessPointName, "0", "1", "false", "false", deployer);
APConstants.assertAccessPointJSON(accessPointName, "0", "1", "false", "false", deployer, "0", CuT.getAccessPointJSON(accessPointName));
CuT.increaseAccessPointScore(accessPointName);
assertAccessPointJSON(accessPointName, "0", "2", "false", "false", deployer);
APConstants.assertAccessPointJSON(accessPointName, "0", "2", "false", "false", deployer, "0", CuT.getAccessPointJSON(accessPointName));
}
function test_cannotDecreaseAccessPointScoreToMinusOne() public {
string memory accessPointName = "accesspoint.com";
CuT.addAccessPoint(tokenId, accessPointName);
assertAccessPointJSON(accessPointName, "0", "0", "false", "false", deployer);
APConstants.assertAccessPointJSON(accessPointName, "0", "0", "false", "false", deployer, "0", CuT.getAccessPointJSON(accessPointName));
expectRevertWithMinimalScore();
CuT.decreaseAccessPointScore(accessPointName);
}
@ -91,11 +69,11 @@ contract Test_FleekERC721_AccessPoint is Test_FleekERC721_Base {
string memory accessPointName = "accesspoint.com";
CuT.addAccessPoint(tokenId, accessPointName);
assertAccessPointJSON(accessPointName, "0", "0", "false", "false", deployer);
APConstants.assertAccessPointJSON(accessPointName, "0", "0", "false", "false", deployer, "0", CuT.getAccessPointJSON(accessPointName));
CuT.increaseAccessPointScore(accessPointName);
assertAccessPointJSON(accessPointName, "0", "1", "false", "false", deployer);
APConstants.assertAccessPointJSON(accessPointName, "0", "1", "false", "false", deployer, "0", CuT.getAccessPointJSON(accessPointName));
CuT.decreaseAccessPointScore(accessPointName);
assertAccessPointJSON(accessPointName, "0", "0", "false", "false", deployer);
APConstants.assertAccessPointJSON(accessPointName, "0", "0", "false", "false", deployer, "0", CuT.getAccessPointJSON(accessPointName));
}
function test_cannotAddAccessPointToNonexistentToken() public {
@ -122,6 +100,6 @@ contract Test_FleekERC721_AccessPoint is Test_FleekERC721_Base {
CuT.setAccessPointContentVerify(accessPointName, true);
vm.stopPrank();
assertAccessPointJSON(accessPointName, "0", "0", "true", "true", deployer);
APConstants.assertAccessPointJSON(accessPointName, "0", "0", "true", "true", deployer, "0", CuT.getAccessPointJSON(accessPointName));
}
}

View File

@ -0,0 +1,107 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
import "../TestBase.sol";
import {Strings} from "@openzeppelin/contracts/utils/Strings.sol";
import {FleekAccessControl} from "contracts/FleekAccessControl.sol";
import "../../../../contracts/FleekERC721.sol";
import "./ApBase.sol";
contract Test_FleekERC721_AccessPoint is Test_FleekERC721_Base, APConstants {
using Strings for address;
uint256 internal tokenId;
function setUp() public {
baseSetUp();
tokenId = mintDefault(deployer);
CuT.setAccessPointAutoApproval(0, true);
}
function test_getAccessPointJSON() public {
string memory accessPointName = "accesspoint.com";
CuT.addAccessPoint(tokenId, accessPointName);
APConstants.assertAccessPointJSON(accessPointName, "0", "0", "false", "false", deployer, "1", CuT.getAccessPointJSON(accessPointName));
}
function test_removeAccessPoint() public {
string memory accessPointName = "accesspoint.com";
CuT.addAccessPoint(tokenId, accessPointName);
CuT.removeAccessPoint(accessPointName);
APConstants.assertAccessPointJSON(accessPointName, "0", "0", "false", "false", deployer, "3", CuT.getAccessPointJSON(accessPointName));
}
function test_cannotRemoveNonExistentAccessPoint() public {
expectRevertWithInvalidAP();
CuT.removeAccessPoint("accesspoint.com");
}
function test_isAccessPointNameVerified() public {
string memory accessPointName = "accesspoint.com";
CuT.addAccessPoint(tokenId, accessPointName);
assertFalse(CuT.isAccessPointNameVerified(accessPointName));
CuT.setAccessPointNameVerify(accessPointName, true);
assertEq(CuT.isAccessPointNameVerified(accessPointName), true);
}
function test_increaseAccessPointScore() public {
string memory accessPointName = "accesspoint.com";
CuT.addAccessPoint(tokenId, accessPointName);
APConstants.assertAccessPointJSON(accessPointName, "0", "0", "false", "false", deployer, "1", CuT.getAccessPointJSON(accessPointName));
CuT.increaseAccessPointScore(accessPointName);
APConstants.assertAccessPointJSON(accessPointName, "0", "1", "false", "false", deployer, "1", CuT.getAccessPointJSON(accessPointName));
CuT.increaseAccessPointScore(accessPointName);
APConstants.assertAccessPointJSON(accessPointName, "0", "2", "false", "false", deployer, "1", CuT.getAccessPointJSON(accessPointName));
}
function test_cannotDecreaseAccessPointScoreToMinusOne() public {
string memory accessPointName = "accesspoint.com";
CuT.addAccessPoint(tokenId, accessPointName);
APConstants.assertAccessPointJSON(accessPointName, "0", "0", "false", "false", deployer, "1", CuT.getAccessPointJSON(accessPointName));
expectRevertWithMinimalScore();
CuT.decreaseAccessPointScore(accessPointName);
}
function test_decreaseAccessPointScore() public {
string memory accessPointName = "accesspoint.com";
CuT.addAccessPoint(tokenId, accessPointName);
APConstants.assertAccessPointJSON(accessPointName, "0", "0", "false", "false", deployer, "1", CuT.getAccessPointJSON(accessPointName));
CuT.increaseAccessPointScore(accessPointName);
APConstants.assertAccessPointJSON(accessPointName, "0", "1", "false", "false", deployer, "1", CuT.getAccessPointJSON(accessPointName));
CuT.decreaseAccessPointScore(accessPointName);
APConstants.assertAccessPointJSON(accessPointName, "0", "0", "false", "false", deployer, "1", CuT.getAccessPointJSON(accessPointName));
}
function test_cannotAddAccessPointToNonExistentToken() public {
expectRevertWithInvalidTokenId();
CuT.addAccessPoint(1, "accesspoint.com");
}
function test_setAccessPointVerifiesWithCorrectRole() public {
string memory accessPointName = "accesspoint.com";
address randomAddress = address(12);
CuT.addAccessPoint(tokenId, accessPointName);
vm.startPrank(randomAddress);
expectRevertWithTokenRole(tokenId, FleekAccessControl.TokenRoles.Controller);
CuT.setAccessPointNameVerify(accessPointName, true);
expectRevertWithTokenRole(tokenId, FleekAccessControl.TokenRoles.Controller);
CuT.setAccessPointContentVerify(accessPointName, true);
vm.stopPrank();
CuT.grantTokenRole(tokenId, FleekAccessControl.TokenRoles.Controller, randomAddress);
vm.startPrank(randomAddress);
CuT.setAccessPointNameVerify(accessPointName, true);
CuT.setAccessPointContentVerify(accessPointName, true);
vm.stopPrank();
APConstants.assertAccessPointJSON(accessPointName, "0", "0", "true", "true", deployer, "1", CuT.getAccessPointJSON(accessPointName));
}
}

View File

@ -0,0 +1,24 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
import "../TestBase.sol";
import {Strings} from "@openzeppelin/contracts/utils/Strings.sol";
contract APConstants is Test {
using Strings for address;
function assertAccessPointJSON(
string memory accessPointName,
string memory _tokenId,
string memory score,
string memory nameVerified,
string memory contentVerified,
address owner,
string memory status,
string memory current // the json result from getAccessPointJSON
) public {
// prettier-ignore
string memory expectedJSON = string(abi.encodePacked('{"tokenId":', _tokenId, ',"score":', score, ',"nameVerified":', nameVerified, ',"contentVerified":', contentVerified, ',"owner":"', owner.toHexString(), '","status":', status,'}'));
assertEq(current, expectedJSON);
}
}

View File

@ -35,13 +35,31 @@ contract Test_FleekERC721_Mint is Test_FleekERC721_Base {
"94e8ba38568aea4fb277a37a4c472d94a6ce880a",
"https://github.com/a-different/repository",
TestConstants.LOGO_1,
0x654321
0x654321,
false
);
assertEq(firstMint, 0);
assertEq(secondMint, 1);
}
function test_mintWithAutoApprovalAPsOn() public {
uint256 mint = CuT.mint(
address(12),
"Different App Name",
"This is a different description for another app.",
"https://fleek.xyz",
"fleek.eth",
"94e8ba38568aea4fb277a37a4c472d94a6ce880a",
"https://github.com/a-different/repository",
TestConstants.LOGO_1,
0x654321,
true
);
assertEq(mint, 0);
}
function test_balanceOfDeployerAfterAndBeforeMinting() public {
assertEq(CuT.balanceOf(deployer), 0);
@ -59,10 +77,22 @@ contract Test_FleekERC721_Mint is Test_FleekERC721_Base {
string memory commitHash,
string memory gitRepository,
string memory logo,
uint24 color
uint24 color,
bool autoApprovalAp
) public {
vm.assume(to != address(0));
uint256 tokenId = CuT.mint(to, appName, description, externalURL, ens, commitHash, gitRepository, logo, color);
uint256 tokenId = CuT.mint(
to,
appName,
description,
externalURL,
ens,
commitHash,
gitRepository,
logo,
color,
autoApprovalAp
);
assertEq(tokenId, 0);
assertEq(CuT.ownerOf(tokenId), to);

View File

@ -60,7 +60,8 @@ abstract contract Test_FleekERC721_Base is Test, Test_FleekERC721_Assertions {
TestConstants.APP_COMMIT_HASH,
TestConstants.APP_GIT_REPOSITORY,
TestConstants.LOGO_0,
TestConstants.APP_COLOR
TestConstants.APP_COLOR,
false // Auto Approval Is OFF
);
return mint;

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,255 @@
import { loadFixture } from '@nomicfoundation/hardhat-network-helpers';
import { expect } from 'chai';
import { before } from 'mocha';
import { TestConstants, Fixtures } from '../helpers';
const { AccessPointStatus } = TestConstants;
describe('FleekERC721.AccessPoints.AutoApprovalOff', () => {
let fixture: Awaited<ReturnType<typeof Fixtures.withMint>>;
beforeEach(async () => {
fixture = await loadFixture(Fixtures.withMint);
});
it('should add an AP with draft status', async () => {
const { contract, owner, tokenId } = fixture;
await expect(contract.addAccessPoint(tokenId, 'accesspoint.com'))
.to.emit(contract, 'NewAccessPoint')
.withArgs('accesspoint.com', tokenId, owner.address);
let ap = await contract.getAccessPointJSON('accesspoint.com');
const parsedAp = JSON.parse(ap);
expect(parsedAp).to.eql({
tokenId,
score: 0,
owner: owner.address.toLowerCase(),
contentVerified: false,
nameVerified: false,
status: AccessPointStatus.DRAFT,
});
});
it('should return a AP json object', async () => {
const { contract, owner, tokenId } = fixture;
await contract.addAccessPoint(tokenId, 'accesspoint.com');
const ap = await contract.getAccessPointJSON('accesspoint.com');
const parsedAp = JSON.parse(ap);
expect(parsedAp).to.eql({
tokenId,
score: 0,
owner: owner.address.toLowerCase(),
contentVerified: false,
nameVerified: false,
status: AccessPointStatus.DRAFT,
});
});
it('should revert if AP does not exist', async () => {
const { contract } = fixture;
await expect(
contract.getAccessPointJSON('accesspoint.com')
).to.be.revertedWith('FleekERC721: invalid AP');
});
it('should increase the AP score', async () => {
const { contract, owner, tokenId } = fixture;
await contract.addAccessPoint(tokenId, 'accesspoint.com');
await contract.increaseAccessPointScore('accesspoint.com');
const ap = await contract.getAccessPointJSON('accesspoint.com');
const parsedAp = JSON.parse(ap);
expect(parsedAp).to.eql({
tokenId,
score: 1,
owner: owner.address.toLowerCase(),
contentVerified: false,
nameVerified: false,
status: AccessPointStatus.DRAFT,
});
});
it('should decrease the AP score', async () => {
const { contract, owner, tokenId } = fixture;
await contract.addAccessPoint(tokenId, 'accesspoint.com');
await contract.increaseAccessPointScore('accesspoint.com');
await contract.increaseAccessPointScore('accesspoint.com');
await contract.decreaseAccessPointScore('accesspoint.com');
const ap = await contract.getAccessPointJSON('accesspoint.com');
const parsedAp = JSON.parse(ap);
expect(parsedAp).to.eql({
tokenId,
score: 1,
owner: owner.address.toLowerCase(),
contentVerified: false,
nameVerified: false,
status: AccessPointStatus.DRAFT,
});
});
it('should allow anyone to change AP score', async () => {
const { contract, otherAccount, tokenId } = fixture;
await contract.addAccessPoint(tokenId, 'accesspoint.com');
await contract.increaseAccessPointScore('accesspoint.com');
await contract
.connect(otherAccount)
.increaseAccessPointScore('accesspoint.com');
});
it('should remove an AP', async () => {
const { contract, owner, tokenId } = fixture;
await contract.addAccessPoint(tokenId, 'accesspoint.com');
await expect(contract.removeAccessPoint('accesspoint.com'))
.to.emit(contract, 'RemoveAccessPoint')
.withArgs('accesspoint.com', tokenId, owner.address);
});
it('should allow only AP owner to remove it', async () => {
const { contract, otherAccount, tokenId } = fixture;
await contract.addAccessPoint(tokenId, 'accesspoint.com');
await expect(
contract.connect(otherAccount).removeAccessPoint('accesspoint.com')
).to.be.revertedWith('FleekERC721: must be AP owner');
});
it('should not be allowed to add the same AP more than once', async () => {
const { contract, tokenId } = fixture;
await contract.addAccessPoint(tokenId, 'accesspoint.com');
await expect(
contract.addAccessPoint(tokenId, 'accesspoint.com')
).to.be.revertedWith('FleekERC721: AP already exists');
});
it('should change "contentVerified" to true', async () => {
const { contract, tokenId } = fixture;
await contract.addAccessPoint(tokenId, 'accesspoint.com');
await contract.setAccessPointContentVerify('accesspoint.com', true);
const ap = await contract.getAccessPointJSON('accesspoint.com');
const parsedAp = JSON.parse(ap);
expect(parsedAp.contentVerified).to.be.true;
});
it('should change "contentVerified" to false', async () => {
const { contract, tokenId } = fixture;
await contract.addAccessPoint(tokenId, 'accesspoint.com');
const beforeAp = await contract.getAccessPointJSON('accesspoint.com');
const beforeParsedAp = JSON.parse(beforeAp);
expect(beforeParsedAp.contentVerified).to.be.false;
await contract.setAccessPointContentVerify('accesspoint.com', true);
await contract.setAccessPointContentVerify('accesspoint.com', false);
const ap = await contract.getAccessPointJSON('accesspoint.com');
const parsedAp = JSON.parse(ap);
expect(parsedAp.contentVerified).to.be.false;
});
it('should change "nameVerified" to true', async () => {
const { contract, tokenId } = fixture;
await contract.addAccessPoint(tokenId, 'accesspoint.com');
await contract.setAccessPointNameVerify('accesspoint.com', true);
const ap = await contract.getAccessPointJSON('accesspoint.com');
const parsedAp = JSON.parse(ap);
expect(parsedAp.nameVerified).to.be.true;
});
it('should change "nameVerified" to false', async () => {
const { contract, tokenId } = fixture;
await contract.addAccessPoint(tokenId, 'accesspoint.com');
const beforeAp = await contract.getAccessPointJSON('accesspoint.com');
const beforeParsedAp = JSON.parse(beforeAp);
expect(beforeParsedAp.nameVerified).to.be.false;
await contract.setAccessPointNameVerify('accesspoint.com', true);
await contract.setAccessPointNameVerify('accesspoint.com', false);
const ap = await contract.getAccessPointJSON('accesspoint.com');
const parsedAp = JSON.parse(ap);
expect(parsedAp.nameVerified).to.be.false;
});
it('should token owner be able to change the auto approval settings to on', async () => {
const { contract, tokenId, owner } = fixture;
await contract
.connect(owner)
.setAccessPointAutoApproval(tokenId, true);
await contract.addAccessPoint(tokenId, 'accesspoint.com');
const beforeAp = await contract.getAccessPointJSON('accesspoint.com');
const beforeParsedAp = JSON.parse(beforeAp);
expect(beforeParsedAp.status).to.be.eql(AccessPointStatus.APPROVED); //APPROVED STATUS
});
it('should token owner be able to approve a draft ap', async () => {
const { contract, tokenId, owner } = fixture;
await contract.addAccessPoint(tokenId, 'accesspoint.com');
const beforeAp = await contract.getAccessPointJSON('accesspoint.com');
const beforeParsedAp = JSON.parse(beforeAp);
expect(beforeParsedAp.status).to.be.eql(AccessPointStatus.DRAFT); //DRAFT STATUS
await contract
.connect(owner)
.setApprovalForAccessPoint(tokenId, 'accesspoint.com', true);
const afterAp = await contract.getAccessPointJSON('accesspoint.com');
const afterParsedAp = JSON.parse(afterAp);
expect(afterParsedAp.status).to.be.eql(AccessPointStatus.APPROVED); //APPROVED STATUS
});
it('should token owner be able to reject a draft ap', async () => {
const { contract, tokenId, owner } = fixture;
await contract.addAccessPoint(tokenId, 'accesspoint.com');
const beforeAp = await contract.getAccessPointJSON('accesspoint.com');
const beforeParsedAp = JSON.parse(beforeAp);
expect(beforeParsedAp.status).to.be.eql(AccessPointStatus.DRAFT); //DRAFT STATUS
await contract
.connect(owner)
.setApprovalForAccessPoint(tokenId, 'accesspoint.com', false);
const afterAp = await contract.getAccessPointJSON('accesspoint.com');
const afterParsedAp = JSON.parse(afterAp);
expect(afterParsedAp.status).to.be.eql(AccessPointStatus.REJECTED); //REJECTED STATUS
});
});

View File

@ -1,27 +1,34 @@
import { loadFixture } from '@nomicfoundation/hardhat-network-helpers';
import { expect } from 'chai';
import { Fixtures } from './helpers';
import { before } from 'mocha';
import { TestConstants, Fixtures } from '../helpers';
const { AccessPointStatus } = TestConstants;
describe('AccessPoints', () => {
describe('FleekERC721.AccessPoints.AutoApprovalOn', () => {
let fixture: Awaited<ReturnType<typeof Fixtures.withMint>>;
const DefaultAP = 'accesspoint.com';
beforeEach(async () => {
fixture = await loadFixture(Fixtures.withMint);
fixture.contract.setAccessPointAutoApproval(fixture.tokenId, true);
fixture.contract.addAccessPoint(fixture.tokenId, DefaultAP);
});
it('should add an AP', async () => {
it('should add an AP with approved status', async () => {
const { contract, owner, tokenId } = fixture;
await expect(contract.addAccessPoint(tokenId, 'random.com'))
.to.emit(contract, 'NewAccessPoint')
.withArgs('random.com', tokenId, owner.address);
.to.emit(contract, 'ChangeAccessPointStatus')
.withArgs(
'random.com',
tokenId,
AccessPointStatus.APPROVED,
owner.address
);
});
it('should return a AP json object', async () => {
const { contract, owner, tokenId } = fixture;
const ap = await contract.getAccessPointJSON(DefaultAP);
const parsedAp = JSON.parse(ap);
@ -31,6 +38,7 @@ describe('AccessPoints', () => {
owner: owner.address.toLowerCase(),
contentVerified: false,
nameVerified: false,
status: AccessPointStatus.APPROVED,
});
});
@ -56,6 +64,7 @@ describe('AccessPoints', () => {
owner: owner.address.toLowerCase(),
contentVerified: false,
nameVerified: false,
status: AccessPointStatus.APPROVED,
});
});
@ -75,6 +84,7 @@ describe('AccessPoints', () => {
owner: owner.address.toLowerCase(),
contentVerified: false,
nameVerified: false,
status: AccessPointStatus.APPROVED,
});
});
@ -91,6 +101,18 @@ describe('AccessPoints', () => {
await expect(contract.removeAccessPoint(DefaultAP))
.to.emit(contract, 'RemoveAccessPoint')
.withArgs(DefaultAP, tokenId, owner.address);
const ap = await contract.getAccessPointJSON(DefaultAP);
const parsedAp = JSON.parse(ap);
expect(parsedAp).to.eql({
tokenId,
score: 0,
owner: owner.address.toLowerCase(),
contentVerified: false,
nameVerified: false,
status: AccessPointStatus.REMOVED,
});
});
it('should allow only AP owner to remove it', async () => {
@ -162,4 +184,17 @@ describe('AccessPoints', () => {
expect(parsedAp.nameVerified).to.be.false;
});
it('should token owner be able to change the auto approval settings to off', async () => {
const { contract, tokenId } = fixture;
await contract.setAccessPointAutoApproval(tokenId, false);
await contract.addAccessPoint(tokenId, 'random.com');
const beforeAp = await contract.getAccessPointJSON('random.com');
const beforeParsedAp = JSON.parse(beforeAp);
expect(beforeParsedAp.status).to.be.eql(AccessPointStatus.DRAFT); //DRAFT STATUS
});
});

View File

@ -18,7 +18,8 @@ describe('FleekERC721.GetLastTokenId', () => {
TestConstants.MintParams.commitHash,
TestConstants.MintParams.gitRepository,
TestConstants.MintParams.logo,
TestConstants.MintParams.color
TestConstants.MintParams.color,
false
);
return response;

View File

@ -5,6 +5,12 @@ export const TestConstants = Object.freeze({
TokenRoles: {
Controller: 0,
},
AccessPointStatus: {
DRAFT: 0,
APPROVED: 1,
REJECTED: 2,
REMOVED: 3,
},
MintParams: {
name: 'Fleek Test App',
description: 'Fleek Test App Description',
@ -14,6 +20,7 @@ export const TestConstants = Object.freeze({
gitRepository: 'https://github.com/fleekxyz/non-fungible-apps',
logo: 'data:image/svg+xml;base64,PHN2ZyBmaWxsPSJub25lIiBoZWlnaHQ9IjI1MDAiIHdpZHRoPSIyMTgzIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMjQgMTQxLjUzMTk5OTk5OTk5OTk4Ij48cGF0aCBkPSJNMTAuMzgzIDEyNi44OTRMMCAwbDEyNCAuMjU1LTEwLjk3OSAxMjYuNjM5LTUwLjU1MyAxNC42Mzh6IiBmaWxsPSIjZTM0ZjI2Ii8+PHBhdGggZD0iTTYyLjQ2OCAxMjkuMjc3VjEyLjA4NWw1MS4wNjQuMTctOS4xMDYgMTA0Ljg1MXoiIGZpbGw9IiNlZjY1MmEiLz48cGF0aCBkPSJNOTkuNDkgNDEuMzYybDEuNDQ2LTE1LjQ5SDIyLjM4M2w0LjM0IDQ3LjQ5aDU0LjIxM0w3OC44MSA5My42MTdsLTE3LjM2MiA0LjY4LTE3LjYxNy01LjEwNi0uOTM2LTEyLjA4NUgyNy4zMTlsMi4xMjggMjQuNjgxIDMyIDguOTM2IDMyLjI1NS04LjkzNiA0LjM0LTQ4LjE3SDQxLjEwN0wzOS40OSA0MS4zNjJ6IiBmaWxsPSIjZmZmIi8+PC9zdmc+',
color: 0xe34f26,
accessPointAutoApprovalSettings: false,
},
CollectionParams: {
name: 'FleekERC721',

View File

@ -42,7 +42,8 @@ export abstract class Fixtures {
TestConstants.MintParams.commitHash,
TestConstants.MintParams.gitRepository,
TestConstants.MintParams.logo,
TestConstants.MintParams.color
TestConstants.MintParams.color,
TestConstants.MintParams.accessPointAutoApprovalSettings
);
const tokenId = response.value.toNumber();

View File

@ -18,7 +18,8 @@ describe('FleekERC721.Minting', () => {
MintParams.commitHash,
MintParams.gitRepository,
MintParams.logo,
MintParams.color
MintParams.color,
MintParams.accessPointAutoApprovalSettings
);
expect(response.value).to.be.instanceOf(ethers.BigNumber);
@ -40,7 +41,8 @@ describe('FleekERC721.Minting', () => {
MintParams.commitHash,
MintParams.gitRepository,
MintParams.logo,
MintParams.color
MintParams.color,
MintParams.accessPointAutoApprovalSettings
)
)
.to.be.revertedWithCustomError(contract, Errors.MustHaveCollectionRole)
@ -61,7 +63,8 @@ describe('FleekERC721.Minting', () => {
MintParams.commitHash,
MintParams.gitRepository,
MintParams.logo,
MintParams.color
MintParams.color,
MintParams.accessPointAutoApprovalSettings
);
const tokenId = response.value.toNumber();

View File

@ -19,7 +19,8 @@ describe('FleekERC721.Pausable', () => {
MintParams.commitHash,
MintParams.gitRepository,
MintParams.logo,
MintParams.color
MintParams.color,
false
);
};

View File

@ -21,6 +21,7 @@ describe('FleekERC721.TokenURI', () => {
description: TestConstants.MintParams.description,
image: TestConstants.ResultantImage.Default,
external_url: TestConstants.MintParams.externalUrl,
access_point_auto_approval: false,
attributes: [
{
trait_type: 'ENS',