feat: verifications and mongo write operations (#253)

* integration

* refactor: rename the mintId parameter, add serverless offline.

* docs: update documentation readme.

* feat: add the create call for handling new mint info.

* feat: add build record db, write the signed transaction call to the contract, query builds in the mint handler.

* feat: new prisma schema.

* feat: add logic to the build handler and update mint handler to pass the verified field.

* feat: update token record in the build handler, add start command to package.json

* feat: add dotenv to read the private key variable from the main .env file.

* feat: add domain and ipfs hash to mint and setTokenBuild functions. Alter the structs accordingly.

* test: update mint parameters with ipfsHash for foundry and hardhat tests.

* feat: add build record entity to subgraph.

* feat: add domain field, update schema, remove tokenId from build handler.

* test: fix missing parameters.

* fix: build info test parameters missing.

* test: final test fix for the foundry metadataupdate params.

* refactor: tsconfig changes and pr feedback.
This commit is contained in:
Shredder 2023-05-09 21:25:19 +03:30 committed by GitHub
parent 393c4a316d
commit 70f2df4210
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
35 changed files with 3268 additions and 106 deletions

View File

@ -39,6 +39,7 @@ contract FleekERC721 is
string ENS,
string commitHash,
string gitRepository,
string ipfsHash,
string logo,
uint24 color,
bool accessPointAutoApproval,
@ -108,6 +109,7 @@ contract FleekERC721 is
string calldata ens,
string memory commitHash,
string memory gitRepository,
string memory ipfsHash,
string memory logo,
uint24 color,
bool accessPointAutoApproval,
@ -131,7 +133,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);
app.builds[0] = Build(commitHash, gitRepository, ipfsHash, externalURL);
emit NewMint(
tokenId,
@ -141,6 +143,7 @@ contract FleekERC721 is
ens,
commitHash,
gitRepository,
ipfsHash,
logo,
color,
accessPointAutoApproval,
@ -396,11 +399,14 @@ contract FleekERC721 is
function setTokenBuild(
uint256 tokenId,
string memory _commitHash,
string memory _gitRepository
string memory _gitRepository,
string memory _ipfsHash,
string memory _domain
) public virtual requireTokenRole(tokenId, TokenRoles.Controller) {
_requireMinted(tokenId);
_apps[tokenId].builds[++_apps[tokenId].currentBuild] = Build(_commitHash, _gitRepository);
emit MetadataUpdate(tokenId, "build", [_commitHash, _gitRepository], msg.sender);
_apps[tokenId].builds[++_apps[tokenId].currentBuild] = Build(_commitHash, _gitRepository, _ipfsHash, _domain);
// Note from Nima: should we update the externalURL field with each new domain?
emit MetadataUpdate(tokenId, "build", [_commitHash, _gitRepository, _ipfsHash, _domain], msg.sender);
}
/**

View File

@ -24,7 +24,7 @@ interface IERCX {
*/
event MetadataUpdate(uint256 indexed _tokenId, string key, string value, address indexed triggeredBy);
event MetadataUpdate(uint256 indexed _tokenId, string key, uint24 value, address indexed triggeredBy);
event MetadataUpdate(uint256 indexed _tokenId, string key, string[2] value, address indexed triggeredBy);
event MetadataUpdate(uint256 indexed _tokenId, string key, string[4] value, address indexed triggeredBy);
event MetadataUpdate(uint256 indexed _tokenId, string key, bool value, address indexed triggeredBy);
/**
@ -33,6 +33,8 @@ interface IERCX {
struct Build {
string commitHash;
string gitRepository;
string ipfsHash;
string domain;
}
/**
@ -84,7 +86,13 @@ interface IERCX {
/**
* @dev Sets a minted token's build.
*/
function setTokenBuild(uint256 tokenId, string memory commitHash, string memory gitRepository) external;
function setTokenBuild(
uint256 tokenId,
string memory commitHash,
string memory gitRepository,
string memory ipfsHash,
string memory domain
) external;
/**
* @dev Returns the token metadata for a given tokenId.

View File

@ -369,29 +369,31 @@ contract Test_FleekERC721_AccessControl is Test_FleekERC721_Base, Test_FleekERC7
function test_setTokenBuild() public {
string memory commitHash = "commitHash";
string memory gitRepository = "gitRepository";
string memory ipfsHash = "ipfsHash";
string memory domain = "domain";
// ColletionOwner
vm.prank(collectionOwner);
expectRevertWithTokenRole(tokenId, FleekAccessControl.TokenRoles.Controller);
CuT.setTokenBuild(tokenId, commitHash, gitRepository);
CuT.setTokenBuild(tokenId, commitHash, gitRepository, ipfsHash, domain);
// CollectionVerifier
vm.prank(collectionVerifier);
expectRevertWithTokenRole(tokenId, FleekAccessControl.TokenRoles.Controller);
CuT.setTokenBuild(tokenId, commitHash, gitRepository);
CuT.setTokenBuild(tokenId, commitHash, gitRepository, ipfsHash, domain);
// TokenOwner
vm.prank(tokenOwner);
CuT.setTokenBuild(tokenId, commitHash, gitRepository);
CuT.setTokenBuild(tokenId, commitHash, gitRepository, ipfsHash, domain);
// TokenController
vm.prank(tokenController);
CuT.setTokenBuild(tokenId, commitHash, gitRepository);
CuT.setTokenBuild(tokenId, commitHash, gitRepository, ipfsHash, domain);
// AnyAddress
vm.prank(anyAddress);
expectRevertWithTokenRole(tokenId, FleekAccessControl.TokenRoles.Controller);
CuT.setTokenBuild(tokenId, commitHash, gitRepository);
CuT.setTokenBuild(tokenId, commitHash, gitRepository, ipfsHash, domain);
}
function test_burn() public {

View File

@ -54,6 +54,7 @@ contract Test_FleekERC721_Billing is Test_FleekERC721_Base, Test_FleekERC721_Bil
TestConstants.APP_ENS,
TestConstants.APP_COMMIT_HASH,
TestConstants.APP_GIT_REPOSITORY,
TestConstants.APP_IPFS_HASH,
TestConstants.LOGO_0,
TestConstants.APP_COLOR,
TestConstants.APP_ACCESS_POINT_AUTO_APPROVAL_SETTINGS,
@ -75,6 +76,7 @@ contract Test_FleekERC721_Billing is Test_FleekERC721_Base, Test_FleekERC721_Bil
TestConstants.APP_ENS,
TestConstants.APP_COMMIT_HASH,
TestConstants.APP_GIT_REPOSITORY,
TestConstants.APP_IPFS_HASH,
TestConstants.LOGO_0,
TestConstants.APP_COLOR,
TestConstants.APP_ACCESS_POINT_AUTO_APPROVAL_SETTINGS,
@ -98,6 +100,7 @@ contract Test_FleekERC721_Billing is Test_FleekERC721_Base, Test_FleekERC721_Bil
TestConstants.APP_ENS,
TestConstants.APP_COMMIT_HASH,
TestConstants.APP_GIT_REPOSITORY,
TestConstants.APP_IPFS_HASH,
TestConstants.LOGO_0,
TestConstants.APP_COLOR,
TestConstants.APP_ACCESS_POINT_AUTO_APPROVAL_SETTINGS,

View File

@ -15,6 +15,8 @@ library TestConstants {
string public constant APP_GIT_REPOSITORY = "https://github.com/fleekxyz/non-fungible-apps";
string public constant APP_IPFS_HASH = "mtwirsqawjuoloq2gvtyug2tc3jbf5htm2zeo4rsknfiv3fdp46a";
uint24 public constant APP_COLOR = 0x123456;
bool public constant APP_ACCESS_POINT_AUTO_APPROVAL_SETTINGS = true;

View File

@ -39,6 +39,8 @@ contract Test_FleekERC721_GetToken is Test_FleekERC721_Base {
string memory newENS,
string memory newCommitHash,
string memory newRepository,
string memory newIpfsHash,
string memory newDomain,
string memory newLogo,
uint24 newColor
) public {
@ -47,7 +49,7 @@ contract Test_FleekERC721_GetToken is Test_FleekERC721_Base {
CuT.setTokenExternalURL(tokenId, newExternalURL);
transferENS(newENS, deployer);
CuT.setTokenENS(tokenId, newENS);
CuT.setTokenBuild(tokenId, newCommitHash, newRepository);
CuT.setTokenBuild(tokenId, newCommitHash, newRepository, newIpfsHash, newDomain);
CuT.setTokenLogoAndColor(tokenId, newLogo, newColor);
(

View File

@ -36,6 +36,7 @@ contract Test_FleekERC721_Mint is Test_FleekERC721_Base {
"fleek.eth",
"94e8ba38568aea4fb277a37a4c472d94a6ce880a",
"https://github.com/a-different/repository",
"mtwirsqawjuoloq2gvtyug2tc3jbf5htm2zeo4rsknfiv3fdp46a",
TestConstants.LOGO_1,
0x654321,
false,
@ -56,6 +57,7 @@ contract Test_FleekERC721_Mint is Test_FleekERC721_Base {
"fleek.eth",
"94e8ba38568aea4fb277a37a4c472d94a6ce880a",
"https://github.com/a-different/repository",
"mtwirsqawjuoloq2gvtyug2tc3jbf5htm2zeo4rsknfiv3fdp46a",
TestConstants.LOGO_1,
0x654321,
true,
@ -81,6 +83,7 @@ contract Test_FleekERC721_Mint is Test_FleekERC721_Base {
string memory ens,
string memory commitHash,
string memory gitRepository,
string memory ipfsHash,
string memory logo,
uint24 color,
bool autoApprovalAp
@ -95,6 +98,7 @@ contract Test_FleekERC721_Mint is Test_FleekERC721_Base {
ens,
commitHash,
gitRepository,
ipfsHash,
logo,
color,
autoApprovalAp,
@ -115,6 +119,7 @@ contract Test_FleekERC721_Mint is Test_FleekERC721_Base {
TestConstants.APP_ENS,
TestConstants.APP_COMMIT_HASH,
TestConstants.APP_GIT_REPOSITORY,
TestConstants.APP_IPFS_HASH,
TestConstants.LOGO_0,
TestConstants.APP_COLOR,
false,
@ -131,6 +136,7 @@ contract Test_FleekERC721_Mint is Test_FleekERC721_Base {
"",
TestConstants.APP_COMMIT_HASH,
TestConstants.APP_GIT_REPOSITORY,
TestConstants.APP_IPFS_HASH,
TestConstants.LOGO_0,
TestConstants.APP_COLOR,
false,

View File

@ -81,6 +81,7 @@ abstract contract Test_FleekERC721_Base is Test, Test_FleekERC721_Assertions {
TestConstants.APP_ENS,
TestConstants.APP_COMMIT_HASH,
TestConstants.APP_GIT_REPOSITORY,
TestConstants.APP_IPFS_HASH,
TestConstants.LOGO_0,
TestConstants.APP_COLOR,
false, // Auto Approval Is OFF

View File

@ -6,7 +6,7 @@ import "./TestBase.sol";
contract Test_FleekERC721_TokenURIAssertions is Test {
event MetadataUpdate(uint256 indexed _tokenId, string key, string value, address indexed triggeredBy);
event MetadataUpdate(uint256 indexed _tokenId, string key, string[2] value, address indexed triggeredBy);
event MetadataUpdate(uint256 indexed _tokenId, string key, string[4] value, address indexed triggeredBy);
event MetadataUpdate(uint256 indexed _tokenId, string key, uint24 value, address indexed triggeredBy);
function expectMetadataUpdate(
@ -22,7 +22,7 @@ contract Test_FleekERC721_TokenURIAssertions is Test {
function expectMetadataUpdate(
uint256 _tokenId,
string memory key,
string[2] memory value,
string[4] memory value,
address triggeredBy
) public {
vm.expectEmit(true, true, true, true);
@ -57,7 +57,13 @@ contract Test_FleekERC721_TokenURI is Test_FleekERC721_Base, Test_FleekERC721_To
CuT.setTokenExternalURL(tokenId, "https://new-url.com");
transferENS("new-ens.eth", deployer);
CuT.setTokenENS(tokenId, "new-ens.eth");
CuT.setTokenBuild(tokenId, "ce1a3fc141e29f8e1d00a654e156c4982d7711bf", "https://github.com/other/repo");
CuT.setTokenBuild(
tokenId,
"ce1a3fc141e29f8e1d00a654e156c4982d7711bf",
"https://github.com/other/repo",
"ipfsHash",
"domain"
);
CuT.setTokenLogoAndColor(tokenId, TestConstants.LOGO_1, 0x654321);
CuT.setTokenVerified(tokenId, true);
@ -96,10 +102,16 @@ contract Test_FleekERC721_TokenURI is Test_FleekERC721_Base, Test_FleekERC721_To
expectMetadataUpdate(
tokenId,
"build",
["ce1a3fc141e29f8e1d00a654e156c4982d7711bf", "https://github.com/other/repo"],
["ce1a3fc141e29f8e1d00a654e156c4982d7711bf", "https://github.com/other/repo", "ipfshash", "domain"],
deployer
);
CuT.setTokenBuild(tokenId, "ce1a3fc141e29f8e1d00a654e156c4982d7711bf", "https://github.com/other/repo");
CuT.setTokenBuild(
tokenId,
"ce1a3fc141e29f8e1d00a654e156c4982d7711bf",
"https://github.com/other/repo",
"ipfshash",
"domain"
);
expectMetadataUpdate(tokenId, "logo", TestConstants.LOGO_1, deployer);
CuT.setTokenLogo(tokenId, TestConstants.LOGO_1);

View File

@ -21,6 +21,7 @@ describe('FleekERC721.Billing', () => {
MintParams.ens,
MintParams.commitHash,
MintParams.gitRepository,
MintParams.ipfsHash,
MintParams.logo,
MintParams.color,
MintParams.accessPointAutoApprovalSettings,

View File

@ -187,6 +187,7 @@ describe('FleekERC721.CollectionRoles', () => {
TestConstants.MintParams.ens,
TestConstants.MintParams.commitHash,
TestConstants.MintParams.gitRepository,
TestConstants.MintParams.ipfsHash,
TestConstants.MintParams.logo,
TestConstants.MintParams.color,
TestConstants.MintParams.accessPointAutoApprovalSettings,

View File

@ -22,6 +22,7 @@ describe('FleekERC721.ENS', () => {
'app.eth',
MintParams.commitHash,
MintParams.gitRepository,
MintParams.ipfsHash,
MintParams.logo,
MintParams.color,
MintParams.accessPointAutoApprovalSettings,
@ -43,6 +44,7 @@ describe('FleekERC721.ENS', () => {
'app.eth',
MintParams.commitHash,
MintParams.gitRepository,
MintParams.ipfsHash,
MintParams.logo,
MintParams.color,
MintParams.accessPointAutoApprovalSettings,

View File

@ -14,6 +14,7 @@ describe('FleekERC721.GetLastTokenId', () => {
TestConstants.MintParams.ens,
TestConstants.MintParams.commitHash,
TestConstants.MintParams.gitRepository,
TestConstants.MintParams.ipfsHash,
TestConstants.MintParams.logo,
TestConstants.MintParams.color,
false,

View File

@ -23,6 +23,7 @@ export const TestConstants = Object.freeze({
externalUrl: 'https://fleek.co',
commitHash: 'b72e47171746b6a9e29b801af9cb655ecf4d665c',
gitRepository: 'https://github.com/fleekxyz/non-fungible-apps',
ipfsHash: 'mtwirsqawjuoloq2gvtyug2tc3jbf5htm2zeo4rsknfiv3fdp46a',
logo: 'data:image/svg+xml;base64,PHN2ZyBmaWxsPSJub25lIiBoZWlnaHQ9IjI1MDAiIHdpZHRoPSIyMTgzIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMjQgMTQxLjUzMTk5OTk5OTk5OTk4Ij48cGF0aCBkPSJNMTAuMzgzIDEyNi44OTRMMCAwbDEyNCAuMjU1LTEwLjk3OSAxMjYuNjM5LTUwLjU1MyAxNC42Mzh6IiBmaWxsPSIjZTM0ZjI2Ii8+PHBhdGggZD0iTTYyLjQ2OCAxMjkuMjc3VjEyLjA4NWw1MS4wNjQuMTctOS4xMDYgMTA0Ljg1MXoiIGZpbGw9IiNlZjY1MmEiLz48cGF0aCBkPSJNOTkuNDkgNDEuMzYybDEuNDQ2LTE1LjQ5SDIyLjM4M2w0LjM0IDQ3LjQ5aDU0LjIxM0w3OC44MSA5My42MTdsLTE3LjM2MiA0LjY4LTE3LjYxNy01LjEwNi0uOTM2LTEyLjA4NUgyNy4zMTlsMi4xMjggMjQuNjgxIDMyIDguOTM2IDMyLjI1NS04LjkzNiA0LjM0LTQ4LjE3SDQxLjEwN0wzOS40OSA0MS4zNjJ6IiBmaWxsPSIjZmZmIi8+PC9zdmc+',
color: 0xe34f26,
accessPointAutoApprovalSettings: false,

View File

@ -2,6 +2,6 @@ export const Events = Object.freeze({
MetadataUpdate: {
string: 'MetadataUpdate(uint256,string,string,address)',
uint24: 'MetadataUpdate(uint256,string,uint24,address)',
stringTuple: 'MetadataUpdate(uint256,string,string[2],address)',
stringArray4: 'MetadataUpdate(uint256,string,string[4],address)',
},
});

View File

@ -44,6 +44,7 @@ export abstract class Fixtures {
TestConstants.MintParams.ens,
TestConstants.MintParams.commitHash,
TestConstants.MintParams.gitRepository,
TestConstants.MintParams.ipfsHash,
TestConstants.MintParams.logo,
TestConstants.MintParams.color,
TestConstants.MintParams.accessPointAutoApprovalSettings,

View File

@ -17,6 +17,7 @@ describe('FleekERC721.Minting', () => {
MintParams.ens,
MintParams.commitHash,
MintParams.gitRepository,
MintParams.ipfsHash,
MintParams.logo,
MintParams.color,
MintParams.accessPointAutoApprovalSettings,
@ -40,6 +41,7 @@ describe('FleekERC721.Minting', () => {
MintParams.ens,
MintParams.commitHash,
MintParams.gitRepository,
MintParams.ipfsHash,
MintParams.logo,
MintParams.color,
MintParams.accessPointAutoApprovalSettings,
@ -66,6 +68,7 @@ describe('FleekERC721.Minting', () => {
MintParams.ens,
MintParams.commitHash,
MintParams.gitRepository,
MintParams.ipfsHash,
MintParams.logo,
MintParams.color,
MintParams.accessPointAutoApprovalSettings,
@ -87,6 +90,7 @@ describe('FleekERC721.Minting', () => {
'',
MintParams.commitHash,
MintParams.gitRepository,
MintParams.ipfsHash,
MintParams.logo,
MintParams.color,
MintParams.accessPointAutoApprovalSettings,

View File

@ -18,6 +18,7 @@ describe('FleekERC721.Pausable', () => {
MintParams.ens,
MintParams.commitHash,
MintParams.gitRepository,
MintParams.ipfsHash,
MintParams.logo,
MintParams.color,
false,

View File

@ -51,12 +51,20 @@ describe('FleekERC721.UpdateProperties', () => {
it('should emit event for build change', async () => {
const { contract, tokenId, owner } = fixture;
await expect(contract.setTokenBuild(tokenId, 'commitHash', 'gitRepository'))
.to.emit(contract, Events.MetadataUpdate.stringTuple)
await expect(
contract.setTokenBuild(
tokenId,
'commitHash',
'gitRepository',
'ipfsHash',
'domain'
)
)
.to.emit(contract, Events.MetadataUpdate.stringArray4)
.withArgs(
tokenId,
'build',
['commitHash', 'gitRepository'],
['commitHash', 'gitRepository', 'ipfsHash', 'domain'],
owner.address
);
});

View File

@ -99,6 +99,7 @@ describe('Deploy', () => {
TestConstants.MintParams.ens,
TestConstants.MintParams.commitHash,
TestConstants.MintParams.gitRepository,
TestConstants.MintParams.ipfsHash,
TestConstants.MintParams.logo,
TestConstants.MintParams.color,
TestConstants.MintParams.accessPointAutoApprovalSettings,

View File

@ -33,6 +33,7 @@
"pinst": "^3.0.0",
"prettier": "^2.8.4",
"prettier-plugin-solidity": "^1.0.0",
"serverless-offline": "^12.0.4",
"typescript": "^4.9.5"
}
}

View File

@ -37,6 +37,22 @@ To deploy to development environment:
To deploy to production environment:
`yarn sls deploy --stage prd`
### Running MongoDB
The first step to run MongoDB is making sure the service is installed on the machine locally. You can check the [official MongoDB website](https://www.mongodb.com/docs/manual/installation/#mongodb-installation-tutorials) for more information on the installation process.
To process database transactions such as `create` calls, Prisma needs the MongoDB instance to be running as a replica set. Run the commands below to start a replica set with `mongod` and `mongosh`:
```
// You should replace the dbpath with the actual path on your machine and assign a name to your replica set. (Default path on linux is: /var/lib/mongodb)
// Do not close the terminal tab after running mongod.
$ sudo mongod --port 27017 --dbpath /path/to/db --replSet replicaName --bind_ip localhost,127.0.0.1
// Start a mongosh session and run the replica set initiation command in the mongo shell.
$ mongosh
> rs.initiate()
```
Make sure you copy the connection string that is presented in the `Connecting to` field when the mongosh service starts to run. We need the connection string to access the replica set. Rename the `.env.example` file to `.env` and replace the connection string placeholder in the file with the one you copied.
### Prisma configuration

View File

@ -7,7 +7,8 @@
"build": "yarn tsc",
"invoke:build": "yarn build && serverless invoke local --function submitBuildInfo",
"prisma:generate": "npx prisma generate",
"prisma:pull": "npx prisma db pull"
"prisma:pull": "npx prisma db pull --force",
"start": "serverless offline"
},
"author": "fleek",
"license": "MIT",
@ -30,12 +31,13 @@
"@middy/http-json-body-parser": "^4.2.7",
"@middy/http-response-serializer": "^4.2.8",
"@prisma/client": "^4.13.0",
"aws-sdk": "^2.1342.0",
"prisma": "^4.13.0",
"uuid": "^9.0.0",
"@types/node": "^18.15.11",
"aws-sdk": "^2.1342.0",
"dotenv": "^16.0.3",
"prisma": "^4.13.0",
"ts-node": "^10.9.1",
"typescript": "^5.0.4",
"uuid": "^9.0.0",
"web3": "^1.9.0"
}
}

View File

@ -7,10 +7,21 @@ datasource db {
url = env("DATABASE_URL")
}
model tokens {
id String @id @default(auto()) @map("_id") @db.ObjectId
commit_hash String
github_url String
owner String
tokenId Int
model builds {
id String @id @default(auto()) @map("_id") @db.ObjectId
commitHash String
domain String
githubRepository String
ipfsHash String
}
model tokens {
id String @id @default(auto()) @map("_id") @db.ObjectId
commitHash String
domain String
githubRepository String
ipfsHash String
owner String
tokenId Int
verified Boolean
}

View File

@ -1,30 +1,91 @@
import { APIGatewayProxyResult, APIGatewayEvent } from 'aws-lambda';
import { formatJSONResponse } from '@libs/api-gateway';
const querystring = require('querystring');
import { v4 } from 'uuid';
import { nfaContract } from '@libs/nfa-contract';
import { prisma } from '@libs/prisma';
import { account, nfaContract } from '@libs/nfa-contract';
export const submitBuildInfo = async (
event: APIGatewayEvent
): Promise<APIGatewayProxyResult> => {
try {
const eventData = querystring.parse(event.body);
if (event.body === null) {
return formatJSONResponse({
status: 422,
message: 'Required parameters were not passed.',
});
}
const data = JSON.parse(event.body);
const id = v4();
const buildInfo = {
buildId: id,
createdAt: new Date().toISOString(),
submittedData: eventData,
githubRepository: data.githubRepository,
commitHash: data.commitHash,
ipfsHash: data.ipfsHash,
domain: data.domain,
};
// place holder call
nfaContract.methods
.setTokenBuild(1, 'hash', 'repo')
.call((err: string | undefined, res: any) => {
if (err) throw new Error(err);
console.log('result');
console.log(res);
// Add build record to the database, if it's not already added
const buildRecord = await prisma.builds.findMany({
where: {
commitHash: buildInfo.commitHash,
githubRepository: buildInfo.githubRepository,
ipfsHash: buildInfo.ipfsHash,
domain: buildInfo.domain,
},
});
if (buildRecord.length == 0) {
await prisma.builds.create({
data: {
githubRepository: buildInfo.githubRepository,
commitHash: buildInfo.commitHash,
ipfsHash: buildInfo.ipfsHash,
domain: buildInfo.domain,
},
});
}
const mintRecord = await prisma.tokens.findMany({
where: {
ipfsHash: buildInfo.ipfsHash,
domain: buildInfo.domain,
commitHash: buildInfo.commitHash,
githubRepository: buildInfo.githubRepository,
verified: false,
},
});
if (mintRecord.length > 0) {
// Trigger verification
// Mark the token as verified in the contract
// call the `setTokenVerified` method
await nfaContract.methods
.setTokenVerified(mintRecord[0].tokenId, true)
.send({
from: account.address,
gas: '1000000',
})
.catch(console.error);
// Update the database record in the tokens collection
await prisma.tokens.updateMany({
where: {
ipfsHash: buildInfo.ipfsHash,
domain: buildInfo.domain,
commitHash: buildInfo.commitHash,
githubRepository: buildInfo.githubRepository,
verified: false,
},
data: {
verified: true,
},
});
}
return formatJSONResponse({
buildInfo,

View File

@ -6,31 +6,185 @@ import {
import { formatJSONResponse } from '@libs/api-gateway';
import { v4 } from 'uuid';
import { initPrisma, prisma } from '@libs/prisma';
import { account, nfaContract, web3 } from '@libs/nfa-contract';
export const submitMintInfo = async (
event: APIGatewayEvent
///context: APIGatewayEventRequestContext
): Promise<APIGatewayProxyResult> => {
try {
if (event.body === null) {
return formatJSONResponse({
status: 422,
message: 'Required parameters were not passed.',
});
}
const id = v4();
/**if (!verifyAlchemySig(event.headers.xalchemywork)) {
throw new Error('Invalid sig');
}**/
if (event.body == undefined) {
throw new Error('Undefined data');
}
const eventBody = JSON.parse(event.body);
const topics = eventBody.event.data.block.logs[1].slice(1, 3);
const hexCalldata = eventBody.event.data.block.logs[1].data;
const decodedLogs = web3.eth.abi.decodeLog(
[
{
indexed: true,
internalType: 'uint256',
name: 'tokenId',
type: 'uint256',
},
{
indexed: false,
internalType: 'string',
name: 'name',
type: 'string',
},
{
indexed: false,
internalType: 'string',
name: 'description',
type: 'string',
},
{
indexed: false,
internalType: 'string',
name: 'externalURL',
type: 'string',
},
{
indexed: false,
internalType: 'string',
name: 'ENS',
type: 'string',
},
{
indexed: false,
internalType: 'string',
name: 'commitHash',
type: 'string',
},
{
indexed: false,
internalType: 'string',
name: 'gitRepository',
type: 'string',
},
{
indexed: false,
internalType: 'string',
name: 'ipfsHash',
type: 'string',
},
{
indexed: false,
internalType: 'string',
name: 'logo',
type: 'string',
},
{
indexed: false,
internalType: 'uint24',
name: 'color',
type: 'uint24',
},
{
indexed: false,
internalType: 'bool',
name: 'accessPointAutoApproval',
type: 'bool',
},
{
indexed: true,
internalType: 'address',
name: 'minter',
type: 'address',
},
{
indexed: true,
internalType: 'address',
name: 'owner',
type: 'address',
},
{
indexed: false,
internalType: 'address',
name: 'verifier',
type: 'address',
},
],
hexCalldata,
topics
);
const mintInfo = {
buildId: id,
mintId: id,
createdAt: new Date().toISOString(),
body: JSON.parse(event.body),
tokenId: decodedLogs.tokenId,
githubRepository: decodedLogs.gitRepository,
commit_hash: decodedLogs.commitHash,
owner: decodedLogs.owner,
ipfsHash: decodedLogs.ipfsHash,
domain: decodedLogs.externalURL,
};
// check if we have it in mongo
// if so, trigger verification call
// if not, add to mongo
initPrisma();
// Check if there is any build associated with the repository, commit hash, tokenId, and ipfsHash
const build = await prisma.builds.findMany({
where: {
githubRepository: mintInfo.githubRepository,
commitHash: mintInfo.commit_hash,
ipfsHash: mintInfo.ipfsHash,
domain: mintInfo.domain,
},
});
let verified = false;
if (build.length > 0) {
// Mark the token as verified in the contract
try {
// call the `setTokenVerified` method
await nfaContract.methods
.setTokenVerified(mintInfo.tokenId, true)
.send({
from: account.address,
gas: '1000000',
});
verified = true;
} catch (error) {
// catch transaction error
console.error(error);
}
}
// Add the record to the database
const token = await prisma.tokens.findMany({
where: {
tokenId: Number(mintInfo.tokenId),
},
});
if (token.length == 0) {
await prisma.tokens.create({
data: {
tokenId: Number(mintInfo.tokenId),
githubRepository: mintInfo.githubRepository,
commitHash: mintInfo.commit_hash,
owner: mintInfo.owner,
ipfsHash: mintInfo.ipfsHash,
verified: verified,
domain: mintInfo.domain,
},
});
}
return formatJSONResponse({
mintInfo,

View File

@ -1,9 +1,18 @@
import Web3 from 'web3';
import * as abiFile from '../../../contracts/deployments/goerli/FleekERC721.json';
import * as dotenv from 'dotenv';
dotenv.config();
if (process.env.PRIVATE_KEY === undefined) {
throw Error('Private key environment variable not set.');
}
const contract_address = abiFile.address;
const abi = abiFile.abi as any;
const web3 = new Web3('https://rpc.goerli.mudit.blog');
export const abi = abiFile.abi as any;
export const web3 = new Web3('https://rpc.goerli.mudit.blog');
export const nfaContract = new web3.eth.Contract(abi, contract_address);
export const account = web3.eth.accounts.privateKeyToAccount(
process.env.PRIVATE_KEY
);

View File

@ -10,7 +10,9 @@ export async function initPrisma() {
initPrisma()
.catch(async (e) => {
console.error(e);
})
.finally(async () => {
await prisma.$disconnect();
process.exit(1);
})
.finally(() => {
prisma.$disconnect();
});

View File

@ -11,7 +11,6 @@
"outDir": "dist",
"target": "es2016",
"module": "commonjs",
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"skipLibCheck": true,

View File

@ -1588,7 +1588,7 @@
resolved "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.191.tgz"
integrity sha512-BdZ5BCCvho3EIXw6wUCXHe7rS53AIDPLE+JzwgT+OsJk53oBfbSmZZ7CX4VaRoN78N+TJpFi9QPlfIVNmJYWxQ==
"@types/node@*", "@types/node@^18.15.5":
"@types/node@*":
version "18.15.5"
resolved "https://registry.npmjs.org/@types/node/-/node-18.15.5.tgz"
integrity sha512-Ark2WDjjZO7GmvsyFFf81MXuGTA/d6oP38anyxWOL6EREyBKAxKoFHwBhaZxCfLRLpO8JgVXwqOwSwa7jRcjew==
@ -1598,6 +1598,11 @@
resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.55.tgz#c329cbd434c42164f846b909bd6f85b5537f6240"
integrity sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==
"@types/node@^18.15.11":
version "18.16.1"
resolved "https://registry.yarnpkg.com/@types/node/-/node-18.16.1.tgz#5db121e9c5352925bb1f1b892c4ae620e3526799"
integrity sha512-DZxSZWXxFfOlx7k7Rv4LAyiMroaxa3Ly/7OOzZO8cBNho0YzAi4qlbrx8W27JGqG57IgR/6J7r+nOJWw6kcvZA==
"@types/pbkdf2@^3.0.0":
version "3.1.0"
resolved "https://registry.yarnpkg.com/@types/pbkdf2/-/pbkdf2-3.1.0.tgz#039a0e9b67da0cdc4ee5dab865caa6b267bb66b1"
@ -2767,7 +2772,7 @@ dotenv-expand@^9.0.0:
dotenv@^16.0.3:
version "16.0.3"
resolved "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz"
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.0.3.tgz#115aec42bac5053db3c456db30cc243a5a836a07"
integrity sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==
duration@^0.2.2:
@ -6262,10 +6267,10 @@ typedarray-to-buffer@^3.1.5:
dependencies:
is-typedarray "^1.0.0"
typescript@^5.0.2:
version "5.0.2"
resolved "https://registry.npmjs.org/typescript/-/typescript-5.0.2.tgz"
integrity sha512-wVORMBGO/FAs/++blGNeAVdbNKtIh1rbBL2EyQ1+J9lClJ93KiiKe8PmFIVdXhHcyv44SL9oglmfeSsndo0jRw==
typescript@^5.0.4:
version "5.0.4"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.0.4.tgz#b217fd20119bd61a94d4011274e0ab369058da3b"
integrity sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==
ultron@~1.1.0:
version "1.1.1"

View File

@ -27,6 +27,7 @@ type NewMint @entity(immutable: true) {
ENS: String!
commitHash: String! # string
gitRepository: String! # string
ipfsHash: String!
logo: String!
color: Int!
accessPointAutoApproval: Boolean!
@ -44,7 +45,7 @@ type MetadataUpdate @entity(immutable: true) {
key: String!
stringValue: String
uint24Value: Int
doubleStringValue: [String!]!
multipleStringValue: [String!]!
booleanValue: Boolean
byAddress: Bytes!
blockNumber: BigInt!
@ -76,12 +77,20 @@ type Token @entity {
owner: Owner!
mintedBy: Bytes!
controllers: [Controller!]
gitRepository: GitRepository!
commitHash: String!
accessPoints: [AccessPoint!] @derivedFrom(field: "token")
verifier: Verifier # Address
verified: Boolean!
createdAt: BigInt!
builds: [Build!]!
}
type Build @entity {
id: Bytes! # Token ID
gitRepository: GitRepository!
commitHash: String!
ipfsHash: String!
domain: String!
token: Token! @derivedFrom(field: "builds")
}
# Owner entity for collection, access points, and tokens
@ -106,7 +115,7 @@ type Verifier @entity {
type GitRepository @entity {
id: String! # transaction hash of the first transaction this repository appeared in
tokens: [Token!] @derivedFrom(field: "gitRepository")
builds: [Build!] @derivedFrom(field: "gitRepository")
}
type AccessPoint @entity {

View File

@ -13,6 +13,7 @@ import {
GitRepository as GitRepositoryEntity,
MetadataUpdate,
Token,
Build,
} from '../generated/schema';
export function handleMetadataUpdateWithStringValue(
@ -61,7 +62,7 @@ export function handleMetadataUpdateWithStringValue(
}
}
export function handleMetadataUpdateWithDoubleStringValue(
export function handleMetadataUpdateWithMultipleStringValues(
event: MetadataUpdateEvent3
): void {
/**
@ -73,30 +74,29 @@ export function handleMetadataUpdateWithDoubleStringValue(
entity.key = event.params.key;
entity.tokenId = event.params._tokenId;
entity.doubleStringValue = event.params.value;
entity.multipleStringValue = event.params.value;
entity.blockNumber = event.block.number;
entity.blockTimestamp = event.block.timestamp;
entity.transactionHash = event.transaction.hash;
entity.save();
// UPDATE TOKEN
const token = Token.load(
// CREATE BUILD
const build = new Build(
Bytes.fromByteArray(Bytes.fromBigInt(event.params._tokenId))
);
if (token) {
if (event.params.key == 'build') {
let gitRepositoryEntity = GitRepositoryEntity.load(event.params.value[1]);
if (!gitRepositoryEntity) {
// Create a new gitRepository entity
gitRepositoryEntity = new GitRepositoryEntity(event.params.value[1]);
}
token.commitHash = event.params.value[0];
token.gitRepository = event.params.value[1];
token.save();
gitRepositoryEntity.save();
if (event.params.key == 'build') {
let gitRepositoryEntity = GitRepositoryEntity.load(event.params.value[1]);
if (!gitRepositoryEntity) {
// Create a new gitRepository entity
gitRepositoryEntity = new GitRepositoryEntity(event.params.value[1]);
}
build.commitHash = event.params.value[0];
build.gitRepository = event.params.value[1];
build.ipfsHash = event.params.value[2];
build.domain = event.params.value[3];
build.save();
gitRepositoryEntity.save();
}
}

View File

@ -23,6 +23,7 @@ export function handleNewMint(event: NewMintEvent): void {
const externalURL = event.params.externalURL;
const ENS = event.params.ENS;
const gitRepository = event.params.gitRepository;
const ipfsHash = event.params.ipfsHash;
const commitHash = event.params.commitHash;
const logo = event.params.logo;
const color = event.params.color;
@ -38,6 +39,7 @@ export function handleNewMint(event: NewMintEvent): void {
newMintEntity.ENS = ENS;
newMintEntity.commitHash = commitHash;
newMintEntity.gitRepository = gitRepository;
newMintEntity.ipfsHash = ipfsHash;
newMintEntity.logo = logo;
newMintEntity.color = color;
newMintEntity.accessPointAutoApproval = accessPointAutoApproval;
@ -68,8 +70,6 @@ export function handleNewMint(event: NewMintEvent): void {
token.description = description;
token.externalURL = externalURL;
token.ENS = ENS;
token.gitRepository = gitRepository;
token.commitHash = commitHash;
token.logo = logo;
token.color = color;
token.accessPointAutoApproval = accessPointAutoApproval;

View File

@ -32,7 +32,7 @@ dataSources:
- ChangeAccessPointAutoApproval
abis:
- name: FleekNFA
file: ../contracts/deployments/goerli/FleekERC721.json
file: ../contracts/artifacts/contracts/FleekERC721.sol/FleekERC721.json
eventHandlers:
- event: Approval(indexed address,indexed address,indexed uint256)
handler: handleApproval
@ -41,13 +41,13 @@ dataSources:
# Token Events
- event: MetadataUpdate(indexed uint256,string,string,indexed address)
handler: handleMetadataUpdateWithStringValue
- event: MetadataUpdate(indexed uint256,string,string[2],indexed address)
handler: handleMetadataUpdateWithDoubleStringValue
- event: MetadataUpdate(indexed uint256,string,string[4],indexed address)
handler: handleMetadataUpdateWithMultipleStringValues
- event: MetadataUpdate(indexed uint256,string,uint24,indexed address)
handler: handleMetadataUpdateWithIntValue
- event: MetadataUpdate(indexed uint256,string,bool,indexed address)
handler: handleMetadataUpdateWithBooleanValue
- event: NewMint(indexed uint256,string,string,string,string,string,string,string,uint24,bool,indexed address,indexed address,address)
- event: NewMint(indexed uint256,string,string,string,string,string,string,string,string,uint24,bool,indexed address,indexed address,address)
handler: handleNewMint
- event: Transfer(indexed address,indexed address,indexed uint256)
handler: handleTransfer

2866
yarn.lock

File diff suppressed because it is too large Load Diff