feat: contract when minting can set verifier (#164)

* feat: add token verifier token data and requirement

* feat: add verifier argument on mint function

* test: fix current foundry tests

* test: fix current hardhat tests

* test: add test for non token verifier with verifier role

* fix: mint signature on hardhat tests

* refactor: single mint function

* fix: overloaded mint calls on javascript side
This commit is contained in:
Felipe Mendes 2023-03-16 11:23:50 -03:00 committed by GitHub
parent d30fcc35cd
commit ce71790c17
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 132 additions and 102 deletions

View File

@ -13,6 +13,7 @@ import "./util/FleekStrings.sol";
import "./IERCX.sol";
error MustBeTokenOwner(uint256 tokenId);
error MustBeTokenVerifier(uint256 tokenId);
error ThereIsNoTokenMinted();
contract FleekERC721 is
@ -43,8 +44,11 @@ contract FleekERC721 is
address indexed owner
);
event MetadataUpdate(uint256 indexed _tokenId, string key, address value, address indexed triggeredBy);
uint256 private _appIds;
mapping(uint256 => Token) private _apps;
mapping(uint256 => address) private _tokenVerifier;
/**
* @dev Initializes the contract by setting a `name` and a `symbol` to the token collection.
@ -60,6 +64,14 @@ contract FleekERC721 is
__FleekPausable_init();
}
/**
* @dev Checks if caller is the verifier of the token.
*/
modifier requireTokenVerifier(uint256 tokenId) {
if (_tokenVerifier[tokenId] != msg.sender) revert MustBeTokenVerifier(tokenId);
_;
}
/**
* @dev Mints a token and returns a tokenId.
*
@ -81,7 +93,9 @@ contract FleekERC721 is
string memory commitHash,
string memory gitRepository,
string memory logo,
uint24 color
uint24 color,
bool accessPointAutoApproval,
address verifier
) public payable requirePayment(Billing.Mint) returns (uint256) {
uint256 tokenId = _appIds;
_mint(to, tokenId);
@ -99,6 +113,7 @@ contract FleekERC721 is
// 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;
app.builds[0] = Build(commitHash, gitRepository);
emit NewMint(
tokenId,
name,
@ -112,6 +127,10 @@ contract FleekERC721 is
msg.sender,
to
);
_tokenVerifier[tokenId] = verifier;
_setAccessPointAutoApproval(tokenId, accessPointAutoApproval);
return tokenId;
}
@ -380,30 +399,44 @@ contract FleekERC721 is
}
}
/**
* @dev Sets an address as verifier of a token.
* The verifier must have `CollectionRoles.Verifier` role.
*
* May emit a {MetadataUpdate} event.
*
* Requirements:
*
* - the tokenId must be minted and valid.
* - the sender must be the owner of the token.
* - the verifier must have `CollectionRoles.Verifier` role.
*
*/
function setTokenVerifier(uint256 tokenId, address verifier) public requireTokenOwner(tokenId) {
if (!hasCollectionRole(CollectionRoles.Verifier, verifier))
revert MustHaveCollectionRole(uint8(CollectionRoles.Verifier));
_requireMinted(tokenId);
_tokenVerifier[tokenId] = verifier;
emit MetadataUpdate(tokenId, "verifier", verifier, msg.sender);
}
/**
* @dev Returns the verifier of a token.
*
* Requirements:
*
* - the tokenId must be minted and valid.
*
*/
function getTokenVerifier(uint256 tokenId) public view returns (address) {
_requireMinted(tokenId);
return _tokenVerifier[tokenId];
}
/*//////////////////////////////////////////////////////////////
ACCESS POINTS
//////////////////////////////////////////////////////////////*/
/**
* @dev Mints with access auto approval setting
*/
function mint(
address to,
string memory name,
string memory description,
string memory externalURL,
string memory ENS,
string memory commitHash,
string memory gitRepository,
string memory logo,
uint24 color,
bool accessPointAutoApproval
) public payable returns (uint256) {
uint256 tokenId = mint(to, name, description, externalURL, ENS, commitHash, gitRepository, logo, color);
_setAccessPointAutoApproval(tokenId, accessPointAutoApproval);
return tokenId;
}
/**
* @dev Add a new AccessPoint register for an app token.
* The AP name should be a DNS or ENS url and it should be unique.
@ -494,7 +527,7 @@ contract FleekERC721 is
function setAccessPointContentVerify(
string memory apName,
bool verified
) public requireCollectionRole(CollectionRoles.Verifier) {
) public requireCollectionRole(CollectionRoles.Verifier) requireTokenVerifier(_getAccessPointTokenId(apName)) {
_setAccessPointContentVerify(apName, verified);
}
@ -512,7 +545,7 @@ contract FleekERC721 is
function setAccessPointNameVerify(
string memory apName,
bool verified
) public requireCollectionRole(CollectionRoles.Verifier) {
) public requireCollectionRole(CollectionRoles.Verifier) requireTokenVerifier(_getAccessPointTokenId(apName)) {
_setAccessPointNameVerify(apName, verified);
}

View File

@ -51,21 +51,6 @@ interface IERCX {
mapping(uint256 => Build) builds; // Mapping to build details for each build number
}
/**
* @dev Mints a token and returns a tokenId.
*/
function mint(
address to,
string memory name,
string memory description,
string memory externalURL,
string memory ENS,
string memory commitHash,
string memory gitRepository,
string memory logo,
uint24 color
) external payable returns (uint256);
/**
* @dev Sets a minted token's external URL.
*/

View File

@ -80,6 +80,7 @@ const DEFAULT_MINTS = {
const params = DEFAULT_MINTS.fleek;
const mintTo = '0x7ED735b7095C05d78dF169F991f2b7f1A1F1A049';
const verifier = '0x7ED735b7095C05d78dF169F991f2b7f1A1F1A049';
(async () => {
const contract = await getContract('FleekERC721');
@ -90,10 +91,10 @@ const mintTo = '0x7ED735b7095C05d78dF169F991f2b7f1A1F1A049';
params.push(await getSVGBase64(svgPath));
console.log('SVG length: ', params[params.length - 1].length);
params.push(await getSVGColor(svgPath));
params.push(false);
params.push(verifier);
const transaction = await contract[
'mint(address,string,string,string,string,string,string,string,uint24)'
](...params);
const transaction = await contract.mint(...params);
console.log('Response: ', transaction);
})();

View File

@ -34,8 +34,10 @@ contract Test_FleekERC721_AccessControl is Test_FleekERC721_Base, Test_FleekERC7
// Mint to tokenOwner to set tokenOwner
mintDefault(tokenOwner);
// Set tokenController to minted token
vm.prank(tokenOwner);
vm.startPrank(tokenOwner);
CuT.grantTokenRole(tokenId, FleekAccessControl.TokenRoles.Controller, tokenController);
CuT.setTokenVerifier(tokenId, collectionVerifier);
vm.stopPrank();
}
function test_setUp() public {

View File

@ -177,6 +177,15 @@ contract Test_FleekERC721_AccessPoint is Test_FleekERC721_Base, APConstants {
CuT.grantCollectionRole(FleekAccessControl.CollectionRoles.Verifier, randomAddress);
vm.startPrank(randomAddress);
expectRevertWithMustBeTokenVerifier(tokenId);
CuT.setAccessPointNameVerify(accessPointName, true);
expectRevertWithMustBeTokenVerifier(tokenId);
CuT.setAccessPointContentVerify(accessPointName, true);
vm.stopPrank();
CuT.setTokenVerifier(tokenId, randomAddress);
vm.startPrank(randomAddress);
CuT.setAccessPointNameVerify(accessPointName, true);
CuT.setAccessPointContentVerify(accessPointName, true);

View File

@ -179,6 +179,15 @@ contract Test_FleekERC721_AccessPoint is Test_FleekERC721_Base, APConstants {
CuT.grantCollectionRole(FleekAccessControl.CollectionRoles.Verifier, randomAddress);
vm.startPrank(randomAddress);
expectRevertWithMustBeTokenVerifier(tokenId);
CuT.setAccessPointNameVerify(accessPointName, true);
expectRevertWithMustBeTokenVerifier(tokenId);
CuT.setAccessPointContentVerify(accessPointName, true);
vm.stopPrank();
CuT.setTokenVerifier(tokenId, randomAddress);
vm.startPrank(randomAddress);
CuT.setAccessPointNameVerify(accessPointName, true);
CuT.setAccessPointContentVerify(accessPointName, true);

View File

@ -7,6 +7,10 @@ import {Strings} from "@openzeppelin/contracts/utils/Strings.sol";
contract APConstants is Test {
using Strings for address;
function expectRevertWithMustBeTokenVerifier(uint256 tokenId) public {
vm.expectRevert(abi.encodeWithSelector(MustBeTokenVerifier.selector, tokenId));
}
function assertAccessPointJSON(
string memory accessPointName,
string memory _tokenId,

View File

@ -56,7 +56,8 @@ contract Test_FleekERC721_Billing is Test_FleekERC721_Base, Test_FleekERC721_Bil
TestConstants.APP_GIT_REPOSITORY,
TestConstants.LOGO_0,
TestConstants.APP_COLOR,
TestConstants.APP_ACCESS_POINT_AUTO_APPROVAL_SETTINGS
TestConstants.APP_ACCESS_POINT_AUTO_APPROVAL_SETTINGS,
deployer
);
assertEq(CuT.ownerOf(tokenId), deployer);
assertEq(address(CuT).balance, mintPrice);
@ -76,7 +77,8 @@ contract Test_FleekERC721_Billing is Test_FleekERC721_Base, Test_FleekERC721_Bil
TestConstants.APP_GIT_REPOSITORY,
TestConstants.LOGO_0,
TestConstants.APP_COLOR,
TestConstants.APP_ACCESS_POINT_AUTO_APPROVAL_SETTINGS
TestConstants.APP_ACCESS_POINT_AUTO_APPROVAL_SETTINGS,
deployer
);
assertEq(address(CuT).balance, 0);
}
@ -98,7 +100,8 @@ contract Test_FleekERC721_Billing is Test_FleekERC721_Base, Test_FleekERC721_Bil
TestConstants.APP_GIT_REPOSITORY,
TestConstants.LOGO_0,
TestConstants.APP_COLOR,
TestConstants.APP_ACCESS_POINT_AUTO_APPROVAL_SETTINGS
TestConstants.APP_ACCESS_POINT_AUTO_APPROVAL_SETTINGS,
deployer
);
assertEq(CuT.ownerOf(tokenId), deployer);
assertEq(address(CuT).balance, value);

View File

@ -36,7 +36,8 @@ contract Test_FleekERC721_Mint is Test_FleekERC721_Base {
"https://github.com/a-different/repository",
TestConstants.LOGO_1,
0x654321,
false
false,
deployer
);
assertEq(firstMint, 0);
@ -54,7 +55,8 @@ contract Test_FleekERC721_Mint is Test_FleekERC721_Base {
"https://github.com/a-different/repository",
TestConstants.LOGO_1,
0x654321,
true
true,
deployer
);
assertEq(mint, 0);
@ -91,7 +93,8 @@ contract Test_FleekERC721_Mint is Test_FleekERC721_Base {
gitRepository,
logo,
color,
autoApprovalAp
autoApprovalAp,
deployer
);
assertEq(tokenId, 0);
assertEq(CuT.ownerOf(tokenId), to);

View File

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

View File

@ -1,12 +1,7 @@
import { loadFixture } from '@nomicfoundation/hardhat-network-helpers';
import { ethers } from 'hardhat';
import { expect } from 'chai';
import {
Fixtures,
TestConstants,
Errors,
OverloadedFunctions,
} from './helpers';
import { Fixtures, TestConstants, Errors } from './helpers';
const { Billing, MintParams } = TestConstants;
@ -17,7 +12,7 @@ describe('FleekERC721.Billing', () => {
const mint = (value?: any) => {
const { contract, owner } = fixture;
return contract[OverloadedFunctions.Mint.Default](
return contract.mint(
owner.address,
MintParams.name,
MintParams.description,
@ -27,6 +22,8 @@ describe('FleekERC721.Billing', () => {
MintParams.gitRepository,
MintParams.logo,
MintParams.color,
MintParams.accessPointAutoApprovalSettings,
owner.address,
{ value }
);
};

View File

@ -1,11 +1,6 @@
import { loadFixture } from '@nomicfoundation/hardhat-network-helpers';
import { expect } from 'chai';
import {
TestConstants,
Fixtures,
Errors,
OverloadedFunctions,
} from './helpers';
import { TestConstants, Fixtures, Errors } from './helpers';
const { CollectionRoles } = TestConstants;
@ -182,9 +177,9 @@ describe('FleekERC721.CollectionRoles', () => {
});
it('should not be able to verify access point if not verifier', async () => {
const { contract, otherAccount } = fixture;
const { contract, otherAccount, owner } = fixture;
await contract[OverloadedFunctions.Mint.Default](
await contract.mint(
otherAccount.address,
TestConstants.MintParams.name,
TestConstants.MintParams.description,
@ -193,7 +188,9 @@ describe('FleekERC721.CollectionRoles', () => {
TestConstants.MintParams.commitHash,
TestConstants.MintParams.gitRepository,
TestConstants.MintParams.logo,
TestConstants.MintParams.color
TestConstants.MintParams.color,
TestConstants.MintParams.accessPointAutoApprovalSettings,
owner.address
);
await contract.addAccessPoint(0, 'random.com');

View File

@ -1,17 +1,12 @@
import { loadFixture } from '@nomicfoundation/hardhat-network-helpers';
import { expect } from 'chai';
import {
TestConstants,
Fixtures,
Errors,
OverloadedFunctions,
} from './helpers';
import { TestConstants, Fixtures, Errors } from './helpers';
describe('FleekERC721.GetLastTokenId', () => {
let fixture: Awaited<ReturnType<typeof Fixtures.default>>;
const mint = async () => {
const response = await fixture.contract[OverloadedFunctions.Mint.Default](
const response = await fixture.contract.mint(
fixture.owner.address,
TestConstants.MintParams.name,
TestConstants.MintParams.description,
@ -20,7 +15,9 @@ describe('FleekERC721.GetLastTokenId', () => {
TestConstants.MintParams.commitHash,
TestConstants.MintParams.gitRepository,
TestConstants.MintParams.logo,
TestConstants.MintParams.color
TestConstants.MintParams.color,
false,
fixture.owner.address
);
return response;

View File

@ -1,10 +1,7 @@
import { ethers, upgrades } from 'hardhat';
import { TestConstants } from './constants';
import { OverloadedFunctions } from './overloaded-functions';
export abstract class Fixtures {
static async paused() {}
static async default() {
// Contracts are deployed using the first signer/account by default
const [owner, otherAccount] = await ethers.getSigners();
@ -36,9 +33,7 @@ export abstract class Fixtures {
static async withMint() {
const fromDefault = await Fixtures.default();
const response = await fromDefault.contract[
OverloadedFunctions.Mint.Default
](
const response = await fromDefault.contract.mint(
fromDefault.owner.address,
TestConstants.MintParams.name,
TestConstants.MintParams.description,
@ -47,7 +42,9 @@ export abstract class Fixtures {
TestConstants.MintParams.commitHash,
TestConstants.MintParams.gitRepository,
TestConstants.MintParams.logo,
TestConstants.MintParams.color
TestConstants.MintParams.color,
TestConstants.MintParams.accessPointAutoApprovalSettings,
fromDefault.owner.address
);
const tokenId = response.value.toNumber();

View File

@ -3,4 +3,3 @@ export * from './fixture';
export * from './utils';
export * from './errors';
export * from './events';
export * from './overloaded-functions';

View File

@ -1,8 +0,0 @@
export const OverloadedFunctions = Object.freeze({
Mint: {
Default:
'mint(address,string,string,string,string,string,string,string,uint24)',
WithAPAutoApproval:
'mint(address,string,string,string,string,string,string,string,uint24,bool)',
},
});

View File

@ -1,6 +1,6 @@
import { loadFixture } from '@nomicfoundation/hardhat-network-helpers';
import { expect } from 'chai';
import { TestConstants, Fixtures, OverloadedFunctions } from './helpers';
import { TestConstants, Fixtures } from './helpers';
import { ethers } from 'hardhat';
const { MintParams } = TestConstants;
@ -9,7 +9,7 @@ describe('FleekERC721.Minting', () => {
it('should be able to mint a new token', async () => {
const { owner, contract } = await loadFixture(Fixtures.default);
const response = await contract[OverloadedFunctions.Mint.Default](
const response = await contract.mint(
owner.address,
MintParams.name,
MintParams.description,
@ -18,7 +18,9 @@ describe('FleekERC721.Minting', () => {
MintParams.commitHash,
MintParams.gitRepository,
MintParams.logo,
MintParams.color
MintParams.color,
MintParams.accessPointAutoApprovalSettings,
owner.address
);
expect(response.value).to.be.instanceOf(ethers.BigNumber);
@ -30,7 +32,7 @@ describe('FleekERC721.Minting', () => {
Fixtures.default
);
const response = await contract[OverloadedFunctions.Mint.Default](
const response = await contract.mint(
owner.address,
MintParams.name,
MintParams.description,
@ -39,7 +41,9 @@ describe('FleekERC721.Minting', () => {
MintParams.commitHash,
MintParams.gitRepository,
MintParams.logo,
MintParams.color
MintParams.color,
MintParams.accessPointAutoApprovalSettings,
owner.address
);
const tokenId = response.value.toNumber();

View File

@ -1,11 +1,6 @@
import { loadFixture } from '@nomicfoundation/hardhat-network-helpers';
import { expect } from 'chai';
import {
TestConstants,
Fixtures,
Errors,
OverloadedFunctions,
} from './helpers';
import { TestConstants, Fixtures, Errors } from './helpers';
const { MintParams, CollectionRoles, TokenRoles } = TestConstants;
@ -15,7 +10,7 @@ describe('FleekERC721.Pausable', () => {
const mint = () => {
const { owner, contract } = fixture;
return contract[OverloadedFunctions.Mint.Default](
return contract.mint(
owner.address,
MintParams.name,
MintParams.description,
@ -24,7 +19,9 @@ describe('FleekERC721.Pausable', () => {
MintParams.commitHash,
MintParams.gitRepository,
MintParams.logo,
MintParams.color
MintParams.color,
false,
owner.address
);
};