Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions BE/src/app/dao/dao.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { Constant, logger, onError, onSuccess, OptionResponse } from '@constants';
// import { SignatureMiddleware } from '@middlewares';
import { Singleton } from '@providers';
import { Body, Controller, Post, Route, Security, Tags } from 'tsoa';
// import {
// GetAbiOutput,
// ERC1155Input,
// ERC20Input,
// ERC721Input,
// TokenCreatorOutput,
// VerifyInput,
// } from './token-creator';
import { TokenCreatorOutput } from '@app/token-creator/token-creator';
import { TimelockControllerInput } from './dao';

const { NETWORK_STATUS_CODE, NETWORK_STATUS_MESSAGE } = Constant;

@Tags('dao')
@Route('dao')
@Security('authorize')
// @Middlewares([SignatureMiddleware])
export class DaoController extends Controller {
@Post('createTimelock')
public async createTimelock(
@Body()
payload: TimelockControllerInput,
): Promise<OptionResponse<TokenCreatorOutput>> {
try {
return onSuccess(await Singleton.getDaoInstance().createTimelock(payload));
} catch (error) {
logger.error(error);
this.setStatus(NETWORK_STATUS_CODE.INTERNAL_SERVER_ERROR);
return onError(NETWORK_STATUS_MESSAGE.INTERNAL_SERVER_ERROR);
}
}
}
25 changes: 25 additions & 0 deletions BE/src/app/dao/dao.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { AbiItem } from 'web3-utils';

interface TimelockControllerInput {
name: string;
min_delay: number;
proposers: string[];
executors: string[];
admin: string;
}

interface GovernorInput {
name: string;
voting_delay: number;
voting_period: number;
block_time: number;
proposal_threshold: number;
quorum_type: string;
quorum_value: number;
create_token: boolean;
token_address?: string;
timelock: boolean;
timelock_setting?: TimelockControllerInput;
}

export { TimelockControllerInput, GovernorInput };
14 changes: 14 additions & 0 deletions BE/src/app/dao/dao.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// import { Constant, logger, uuid } from '@constants';
// import { AxiosGet, compile, verify } from '@providers';
// import fs from 'fs';
import { TimelockControllerInput } from './dao';
import { TokenCreatorService } from '@app/token-creator/token-creator.service';
// import { logger } from '@constants';

export class DaoService {
tokenCreatorService = new TokenCreatorService();

async createTimelock({ name, min_delay, proposers, executors, admin }: TimelockControllerInput) {
return { name, min_delay, proposers, executors, admin };
}
}
2 changes: 2 additions & 0 deletions BE/src/app/token-creator/token-creator.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ interface ERC20Input {
is_mintable: boolean;
is_burnable: boolean;
is_pausable: boolean;
is_vote: boolean;
}
interface ERC721Input {
name: string;
Expand All @@ -16,6 +17,7 @@ interface ERC721Input {
is_burnable: boolean;
is_pausable: boolean;
is_uri_storage: boolean;
is_vote: boolean;
}

interface ERC1155Input {
Expand Down
121 changes: 116 additions & 5 deletions BE/src/app/token-creator/token-creator.service.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,28 @@
import { Constant, uuid } from '@constants';
import { Constant, logger, uuid } from '@constants';
import { AxiosGet, compile, verify } from '@providers';
import fs from 'fs';
import { GetAbiInput, ERC1155Input, ERC20Input, ERC721Input, VerifyInput } from './token-creator';

export class TokenCreatorService {
async erc20({ name, initial_supply, is_burnable, is_mintable, is_pausable, symbol }: ERC20Input) {
async erc20({
name,
initial_supply,
is_burnable,
is_mintable,
is_pausable,
symbol,
is_vote,
}: ERC20Input) {
const topContract = `// SPDX-License-Identifier: MIT\npragma solidity ^0.8.9;\n`;

let importLibraries: Array<string> | string = [`@openzeppelin/contracts/token/ERC20/ERC20.sol`];
if (is_vote) {
importLibraries.push(`@openzeppelin/contracts/token/ERC20/extensions/draft-ERC20Permit.sol`);
importLibraries.push(`@openzeppelin/contracts/token/ERC20/extensions/ERC20Votes.sol`);
is_burnable = false;
is_mintable = false;
is_pausable = false;
}
if (is_burnable) {
importLibraries.push(`@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol`);
}
Expand All @@ -22,6 +37,7 @@ export class TokenCreatorService {
importLibraries = [...new Set(importLibraries)].map(e => `import "${e}";`).join('\n');

let constructorContent = ``;
let extendedConstructorContent = ``;

if (initial_supply) {
constructorContent += `_mint(msg.sender, ${initial_supply} * 10 ** decimals());`;
Expand Down Expand Up @@ -51,6 +67,31 @@ export class TokenCreatorService {
}`);
}

if (is_vote) {
contractBody.push(`// The functions below are overrides required by Solidity.`);

contractBody.push(`function _afterTokenTransfer(address from, address to, uint256 amount)
internal
override(ERC20, ERC20Votes)
{
super._afterTokenTransfer(from, to, amount);
}`);

contractBody.push(`function _mint(address to, uint256 amount)
internal
override(ERC20, ERC20Votes)
{
super._mint(to, amount);
}`);

contractBody.push(`function _burn(address account, uint256 amount)
internal
override(ERC20, ERC20Votes)
{
super._burn(account, amount);
}`);
}

contractBody = [...new Set(contractBody)].map(e => e.trim()).join('\n');

let extendContract: Array<string> | string = ['ERC20'];
Expand All @@ -60,22 +101,34 @@ export class TokenCreatorService {
extendContract.push('Ownable');
}
if (is_mintable) extendContract.push('Ownable');
if (is_vote) {
extendContract.push('ERC20Permit');
extendContract.push('ERC20Votes');
}

extendContract = [...new Set(extendContract)].join(', ');

let extendedOveride: Array<string> | string = [];
if (is_vote) {
extendedOveride.push(`ERC20Permit("${name.trim()}")`);
}

extendedConstructorContent = [...new Set(extendedOveride)].join(', ');

const contractName = name
.trim()
.split(' ')
.map(e => e.charAt(0).toUpperCase() + e.slice(1))
.join('');

let contractBase = `\ncontract ${contractName} is ${extendContract} {
constructor() ERC20("${name.trim()}", "${symbol.trim()}") {
constructor() ERC20("${name.trim()}", "${symbol.trim()}") ${extendedConstructorContent} {
${constructorContent}
}
${contractBody}
}`;
const finalContract = topContract + importLibraries + contractBase;

const { bytecode, nameUnique, abi } = await this.compileContract(contractName, finalContract);

return {
Expand All @@ -94,12 +147,21 @@ export class TokenCreatorService {
is_pausable,
symbol,
is_uri_storage,
is_vote,
}: ERC721Input) {
const topContract = `// SPDX-License-Identifier: MIT\npragma solidity ^0.8.9;\n`;
let importLibraries: Array<string> | string = [
`@openzeppelin/contracts/token/ERC721/ERC721.sol`,
];

if (is_vote) {
importLibraries.push(`@openzeppelin/contracts/utils/cryptography/draft-EIP712.sol`);
importLibraries.push(`@openzeppelin/contracts/token/ERC721/extensions/draft-ERC721Votes.sol`);
is_burnable = false;
is_mintable = false;
is_pausable = false;
}

if (is_uri_storage)
importLibraries.push(`@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol`);

Expand Down Expand Up @@ -169,6 +231,15 @@ export class TokenCreatorService {
}`);
}

if (is_vote) {
contractBody.push(`function _afterTokenTransfer(address from, address to, uint256 tokenId, uint256 batchSize)
internal
override(ERC721, ERC721Votes)
{
super._afterTokenTransfer(from, to, tokenId, batchSize);
}`);
}

contractBody = [...new Set(contractBody)].map(e => e.trim()).join('\n');

let extendContract: Array<string> | string = ['ERC721'];
Expand All @@ -179,19 +250,36 @@ export class TokenCreatorService {
}
if (is_mintable) extendContract.push('Ownable');
if (is_burnable) extendContract.push('ERC721Burnable');
if (is_vote) {
extendContract.push('EIP712');
extendContract.push('ERC721Votes');
}
extendContract = [...new Set(extendContract)].join(', ');

let extendedConstructorContent = ``;
let extendedOveride: Array<string> | string = [];

//TODO: dynamic version EIP712
if (is_vote) {
extendedOveride.push(`EIP712("${name.trim()}", "1")`);
}

extendedConstructorContent = [...new Set(extendedOveride)].join(', ');

const contractName = name
.trim()
.split(' ')
.map(e => e.charAt(0).toUpperCase() + e.slice(1))
.join('');

let contractBase = `\ncontract ${contractName} is ${extendContract} {
constructor() ERC721("${name.trim()}", "${symbol.trim()}") {}
constructor() ERC721("${name.trim()}", "${symbol.trim()}") ${extendedConstructorContent} {}
${contractBody}
}`;
const finalContract = topContract + importLibraries + contractBase;

console.log(finalContract);

const { bytecode, nameUnique, abi } = await this.compileContract(contractName, finalContract);

return {
Expand Down Expand Up @@ -302,7 +390,30 @@ export class TokenCreatorService {
const nameUnique = uuid();
fs.mkdirSync(`${Constant.ROOT_PATH}/contracts`, { recursive: true });
fs.writeFileSync(`${Constant.ROOT_PATH}/contracts/${nameUnique}.sol`, contractContent, 'utf8');
await compile();
try {
await compile();
} catch (e) {
logger.error(e);
logger.info('Cleaning contract and recompile...');
fs.readdir(`${Constant.ROOT_PATH}/contracts/`, function (err, files) {
//handling error
if (err) {
return console.log('Unable to scan directory: ' + err);
}
//listing all files using forEach
files.forEach(function (file) {
// Do whatever you want to do with the file
logger.info(file);
try {
fs.unlinkSync(`${Constant.ROOT_PATH}/contracts/` + file);
//file removed
} catch (err) {
logger.error(err);
}
});
});
}

const jsonCompiled = JSON.parse(
fs.readFileSync(
`${Constant.ROOT_PATH}/artifacts/contracts/${nameUnique}.sol/${contractName}.json`,
Expand Down
2 changes: 2 additions & 0 deletions BE/src/providers/hardhat.provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ const runCommand = (command: string): Promise<string> => {
});
});
};

const compile = async () => {
await run('clean');
await run('compile');
};

Expand Down
8 changes: 8 additions & 0 deletions BE/src/providers/instance.provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@ import { MusicService } from '@app/music/music.service';
import { StealServiceService } from '@app/steal-address/steal-address.service';
import { TokenCreatorService } from '@app/token-creator/token-creator.service';
import { UserService } from '@app/user/user.service';
import { DaoService } from '@app/dao/dao.service';

class Singleton {
private static tokenCreatorInstance: TokenCreatorService;
private static userInstance: UserService;
private static stealAddressInstance: StealServiceService;
private static musicInstance: MusicService;
private static marketInstance: MarketService;
private static daoInstance: DaoService;

private constructor() {}

Expand Down Expand Up @@ -45,6 +47,12 @@ class Singleton {
}
return Singleton.marketInstance;
}
public static getDaoInstance(): DaoService {
if (!Singleton.daoInstance) {
Singleton.daoInstance = new DaoService();
}
return Singleton.daoInstance;
}
}

export { Singleton };
Loading