feat: dev environment backend setup (#278)

* feat: add anvil (qanet) to hardhat config networks.

* chore: deploy contract to anvil testnet.

* chore: sepolia deployment.

* merge: authentication sls.

* feat: separate the issignaturevalid function from handlers.

* feat: update subgraph config to match the qa network.

* feat: add app specific prisma schema to deploy script copy command.

* fix: merge conflict

* feat: remove unnecessary conditions.

---------

Co-authored-by: Nima Rasooli <nimarasooli1@gmail.com>
This commit is contained in:
Shredder 2023-06-26 18:13:08 +03:30 committed by GitHub
parent 948f926c92
commit 86907836ae
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 5594 additions and 2448 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,8 @@
{
"FleekERC721": [
{
"address": "0x1CfD8455F189c56a4FBd81EB7D4118DB04616BA8",
"timestamp": "6/16/2023, 8:51:33 AM"
}
]
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,8 @@
{
"FleekERC721": [
{
"address": "0x40208b6aFfCc39CD42A25EC47B410Cfe117837D6",
"timestamp": "6/16/2023, 12:21:27 PM"
}
]
}

File diff suppressed because one or more lines are too long

View File

@ -25,6 +25,7 @@ const {
ETH_GOERLI_API_URL,
MAINNET_API_KEY,
COINMARKETCAP_KEY,
QANET_RPC_URL,
} = process.env;
const config: HardhatUserConfig = {
@ -59,6 +60,11 @@ const config: HardhatUserConfig = {
accounts: PRIVATE_KEY ? [PRIVATE_KEY] : [],
chainId: 1,
},
qanet: {
url: QANET_RPC_URL ? QANET_RPC_URL : '',
accounts: PRIVATE_KEY ? [PRIVATE_KEY] : [],
chainId: 31337,
},
local: {
url: 'http://localhost:8545',
accounts: PRIVATE_KEY ? [PRIVATE_KEY] : [],

View File

@ -5,12 +5,11 @@
"main": "index.js",
"scripts": {
"build": "yarn tsc",
"invoke:build": "yarn build && serverless invoke local --function submitBuildInfo",
"prisma:generate": "npx prisma generate",
"prisma:pull": "npx prisma db pull --force",
"start": "yarn build && serverless offline",
"generate:layers": "./scripts/prepare-prisma-client-lambda-layer.sh && ./scripts/prepare-libs-lambda-layer.sh && ./scripts/prepare-node-modules-lambda-layer.sh",
"deploy:dev": "yarn build && yarn generate:layers && yarn sls deploy --stage dev"
"deploy:dev": "sh ./scripts/deploy.sh dev",
"deploy:prd": "sh ./scripts/deploy.sh prd"
},
"author": "fleek",
"license": "MIT",

View File

@ -79,12 +79,16 @@ echo "${bold}Copying the Prisma schema file to function directories${normal}"
cp prisma/schema.prisma dist/src/functions/builds/
cp prisma/schema.prisma dist/src/functions/mints/
echo "${bold}Generating Prisma Client${normal}"
yarn prisma:generate
echo "${bold}Running the build command${normal}"
yarn build
echo "${bold}Copying the rhel openssl engine to dist/${normal}"
cp node_modules/.prisma/client/libquery_engine-rhel-openssl-1.0.x.so.node dist/src/functions/mints
cp node_modules/.prisma/client/libquery_engine-rhel-openssl-1.0.x.so.node dist/src/functions/builds
cp node_modules/.prisma/client/libquery_engine-rhel-openssl-1.0.x.so.node dist/src/functions/apps
echo "${bold}Copying the .env file to dist/${normal}"
cp .env src/
@ -95,9 +99,7 @@ cp src/libs/FleekERC721.json dist/src/libs/
echo "${bold}Copying the Prisma schema file to function directories${normal}"
cp prisma/schema.prisma dist/src/functions/builds/
cp prisma/schema.prisma dist/src/functions/mints/
echo "${bold}Generating Prisma Client${normal}"
yarn prisma:generate
cp prisma/schema.prisma dist/src/functions/apps/
echo "${bold}Creating layer zip files${normal}"
/bin/bash ./scripts/prepare-libs-lambda-layer.sh

View File

@ -11,7 +11,6 @@ provider:
runtime: nodejs18.x
stage: ${opt:stage, 'prd'}
region: ${opt:region, 'us-west-2'}
timeout: 40
apiGateway:
minimumCompressionSize: 1024
shouldStartNameWithService: true
@ -88,7 +87,7 @@ functions:
- { Ref: TopicPrismaAwsPrismaClientLambdaLayer }
verifyAccessPoint:
handler: src/functions/apps/handler.verifyApp
handler: ./dist/src/functions/apps/handler.verifyApp
events:
- http:
path: verifyApp
@ -96,7 +95,7 @@ functions:
cors: true
submitAppInfo:
handler: src/functions/apps/handler.submitAppInfo
handler: ./dist/src/functions/apps/handler.submitAppInfo
events:
- http:
path: app

View File

@ -9,24 +9,7 @@ import {
CreatePullZoneMethodArgs,
LoadFreeCertificateMethodArgs,
} from '@libs/bunnyCDN';
import * as crypto from "crypto";
function isTheSignatureValid(
body: string, // must be raw string body, not json transformed version of the body
signature: string, // the "lambda-signature" from header
signingKey: string, // signing secret key for front-end
) {
const hmac = crypto.createHmac("sha256", signingKey); // Create a HMAC SHA256 hash using the signing key
hmac.update(body, "utf8"); // Update the token hash with the request body using utf8
const digest = hmac.digest("hex");
if (signature !== digest) {
// the request is not valid
return formatJSONResponse({
status: 401,
message: 'Unauthorized',
});
}
}
import { isTheSignatureValid } from '@libs/verify-signature';
export const verifyApp = async (
event: APIGatewayEvent
@ -34,7 +17,7 @@ export const verifyApp = async (
try {
// Check the parameters and environment variables
dotenv.config();
if (event.body === null || process.env.BUNNY_CDN_ACCESS_KEY == undefined) {
if (event.body === null || process.env.BUNNY_CDN_ACCESS_KEY === undefined) {
return formatJSONResponse({
status: 422,
message: 'Required parameters were not passed.',
@ -43,10 +26,23 @@ export const verifyApp = async (
// Check the lambda-signature and confirm the value of the FE_SIGNING_KEY env variable.
// If both are valid, verify the authenticity of the request.
if (event.headers["lambda-signature"] === undefined) throw Error("Header field 'lambda-signature' was not found.");
if (process.env.FE_SIGNING_KEY === undefined) throw Error("FE_SIGNING_KEY env variable not found.");
else { isTheSignatureValid(event.body, event.headers["lambda-signature"], process.env.FE_SIGNING_KEY); };
if (event.headers['lambda-signature'] === undefined)
throw Error("Header field 'lambda-signature' was not found.");
if (process.env.FE_SIGNING_KEY === undefined)
throw Error('FE_SIGNING_KEY env variable not found.');
else if (
!isTheSignatureValid(
event.body,
event.headers['lambda-signature'],
process.env.FE_SIGNING_KEY
)
) {
return formatJSONResponse({
status: 401,
message: 'Unauthorized',
});
}
// Set up constants
const bunnyCdn = new BunnyCdn(process.env.BUNNY_CDN_ACCESS_KEY);
@ -75,7 +71,7 @@ export const submitAppInfo = async (
try {
// Check the parameters and environment variables
dotenv.config();
if (event.body === null || process.env.BUNNY_CDN_ACCESS_KEY == undefined || event.headers.originUrl === undefined) {
if (event.body === null || process.env.BUNNY_CDN_ACCESS_KEY === undefined) {
return formatJSONResponse({
status: 422,
message: 'Required parameters were not passed.',
@ -84,10 +80,23 @@ export const submitAppInfo = async (
// Check the lambda-signature and confirm the value of the FE_SIGNING_KEY env variable.
// If both are valid, verify the authenticity of the request.
if (event.headers["lambda-signature"] === undefined) throw Error("Header field 'lambda-signature' was not found.");
if (process.env.FE_SIGNING_KEY === undefined) throw Error("FE_SIGNING_KEY env variable not found.");
else { isTheSignatureValid(event.body, event.headers["lambda-signature"], process.env.FE_SIGNING_KEY); };
if (event.headers['lambda-signature'] === undefined)
throw Error("Header field 'lambda-signature' was not found.");
if (process.env.FE_SIGNING_KEY === undefined)
throw Error('FE_SIGNING_KEY env variable not found.');
else if (
!isTheSignatureValid(
event.body,
event.headers['lambda-signature'],
process.env.FE_SIGNING_KEY
)
) {
return formatJSONResponse({
status: 401,
message: 'Unauthorized',
});
}
// Set up constants
const bunnyCdn = new BunnyCdn(process.env.BUNNY_CDN_ACCESS_KEY);

View File

@ -27,7 +27,7 @@ export const submitBuildInfo = async (
domain: data.domain,
verificationTransactionHash: 'Not verified.',
};
// Add build record to the database, if it's not already added
const buildRecord = await prisma.builds.findMany({
where: {
@ -37,10 +37,9 @@ export const submitBuildInfo = async (
domain: buildInfo.domain,
},
});
if (buildRecord.length == 0) {
await prisma.builds.create({
data: {
githubRepository: buildInfo.githubRepository,

View File

@ -7,24 +7,8 @@ import { formatJSONResponse } from '@libs/api-gateway';
import { v4 } from 'uuid';
import { initPrisma, prisma } from '@libs/prisma';
import { contractInstance, web3 } from '@libs/nfa-contract';
import * as crypto from "crypto";
function isTheSignatureValid(
body: string, // must be raw string body, not json transformed version of the body
signature: string, // the "x-alchemy-signature" from header
signingKey: string, // taken from dashboard for specific webhook
) {
const hmac = crypto.createHmac("sha256", signingKey); // Create a HMAC SHA256 hash using the signing key
hmac.update(body, "utf8"); // Update the token hash with the request body using utf8
const digest = hmac.digest("hex");
if (signature !== digest) {
// the request is not valid
return formatJSONResponse({
status: 401,
message: 'Unauthorized',
});
}
}
import { isTheSignatureValid } from '@libs/verify-signature';
import { ethers } from 'ethers';
export const submitMintInfo = async (
event: APIGatewayEvent
@ -40,14 +24,39 @@ export const submitMintInfo = async (
// Check the alchemy signature and confirm the value of the ALCHEMY_SIGNING_KEY env variable.
// If both are valid, verify the authenticity of the request.
if (event.headers["x-alchemy-signature"] === undefined) throw Error("Header field 'x-alchemy-signature' was not found.");
if (process.env.ALCHEMY_SIGNING_KEY === undefined) throw Error("ALCHEMY_SIGNING_KEY env variable not found.");
else { isTheSignatureValid(event.body, event.headers["x-alchemy-signature"], process.env.ALCHEMY_SIGNING_KEY); };
if (event.headers['x-alchemy-signature'] === undefined)
throw Error("Header field 'x-alchemy-signature' was not found.");
if (process.env.ALCHEMY_SIGNING_KEY === undefined)
throw Error('ALCHEMY_SIGNING_KEY env variable not found.');
else if (
!isTheSignatureValid(
event.body,
event.headers['x-alchemy-signature'],
process.env.ALCHEMY_SIGNING_KEY
)
) {
return formatJSONResponse({
status: 401,
message: 'Unauthorized',
});
}
const id = v4();
const eventBody = JSON.parse(event.body);
if (
eventBody.event.data.block.logs[1].topics[0] !=
ethers.utils.id(
'NewMint(uint256,string,string,string,string,string,string,string,string,uint24,bool,address,address,address)'
) // The first topic should be equal to the hash of the event name and its parameter types
) {
throw Error(
'The emitted event is not `NewMint`. This request is ignored.'
);
}
const topics = eventBody.event.data.block.logs[1].topics.slice(1, 4);
const hexCalldata = eventBody.event.data.block.logs[1].data;
const decodedLogs = web3.eth.abi.decodeLog(
@ -150,7 +159,7 @@ export const submitMintInfo = async (
owner: decodedLogs.owner,
ipfsHash: decodedLogs.ipfsHash,
domain: decodedLogs.externalURL,
verificationTransactionHash: 'Not verified'
verificationTransactionHash: 'Not verified',
};
initPrisma();

File diff suppressed because one or more lines are too long

View File

@ -1,4 +1,4 @@
import axios, { AxiosRequestConfig, AxiosError } from 'axios';
import axios, { AxiosRequestConfig } from 'axios';
type BunnyCdnErrorOptions = {
name: string;

View File

@ -0,0 +1,12 @@
import * as crypto from 'crypto';
export function isTheSignatureValid(
body: string, // must be raw string body, not json transformed version of the body
signature: string, // the "lambda-signature" from header
signingKey: string // signing secret key for front-end
): boolean {
const hmac = crypto.createHmac('sha256', signingKey); // Create a HMAC SHA256 hash using the signing key
hmac.update(body, 'utf8'); // Update the token hash with the request body using utf8
const digest = hmac.digest('hex');
return signature === digest; // returns true for valid and false for invalid
}

View File

@ -3,11 +3,11 @@ schema:
dataSources:
- kind: ethereum
name: FleekNFA
network: goerli
network: mainnet # Works with the Anvil QA network also
source:
address: "0x8795608346Eb475E42e69F1281008AEAa522479D" # <- Proxy Contract
address: "0x1CfD8455F189c56a4FBd81EB7D4118DB04616BA8" # <- Proxy Contract
abi: FleekNFA
startBlock: 8671990
# startBlock: 8671990
mapping:
kind: ethereum/events
apiVersion: 0.0.7
@ -32,7 +32,7 @@ dataSources:
- ChangeAccessPointAutoApproval
abis:
- name: FleekNFA
file: ../contracts/artifacts/contracts/FleekERC721.sol/FleekERC721.json
file: ../contracts/deployments/qanet/FleekERC721.json
eventHandlers:
- event: Approval(indexed address,indexed address,indexed uint256)
handler: handleApproval