From ed2f6b9eca73b240ceb73dcd7b33f939f6030a11 Mon Sep 17 00:00:00 2001 From: emochka2007 Date: Fri, 10 May 2024 00:17:03 +0300 Subject: [PATCH] multisig ready, deploy and interact controller --- chain-api/package-lock.json | 43 ++++++++++-- chain-api/package.json | 4 +- .../contract-factory.controller.ts | 7 +- .../contract-factory.module.ts | 2 +- .../contract-factory.service.ts | 18 +++-- .../contract-interact.controller.ts | 49 ------------- .../contract-interact.module.ts | 7 +- .../contract-interact/dto/multi-sig.dto.ts | 18 +++++ .../multi-sig-interact.controller.ts | 20 ++++++ chain-api/src/hardhat/contracts/Agreement.sol | 0 .../src/hardhat/contracts/MultiSigWallet.sol | 24 +++---- chain-api/src/hardhat/contracts/Salaries.sol | 58 +++++++++++++--- .../src/hardhat/module/hardhat.module.ts | 10 --- .../hardhat/modules/base-contract.service.ts | 12 ++++ .../src/hardhat/modules/dto/multi-sig.dto.ts | 11 +++ .../src/hardhat/modules/hardhat.module.ts | 15 ++++ .../src/hardhat/modules/hardhat.service.ts | 4 ++ .../modules/multi-sig/multi-sig.module.ts | 15 ++++ .../modules/multi-sig/multi-sig.service.ts | 68 +++++++++++++++++++ .../salary.service.ts} | 29 ++++---- chain-api/src/main.ts | 3 + chain-api/src/provider/provider.module.ts | 10 +++ chain-api/src/provider/provider.service.ts | 38 +++++++++++ 23 files changed, 353 insertions(+), 112 deletions(-) delete mode 100644 chain-api/src/contract-interact/contract-interact.controller.ts create mode 100644 chain-api/src/contract-interact/dto/multi-sig.dto.ts create mode 100644 chain-api/src/contract-interact/multi-sig-interact.controller.ts create mode 100644 chain-api/src/hardhat/contracts/Agreement.sol delete mode 100644 chain-api/src/hardhat/module/hardhat.module.ts create mode 100644 chain-api/src/hardhat/modules/base-contract.service.ts create mode 100644 chain-api/src/hardhat/modules/dto/multi-sig.dto.ts create mode 100644 chain-api/src/hardhat/modules/hardhat.module.ts create mode 100644 chain-api/src/hardhat/modules/hardhat.service.ts create mode 100644 chain-api/src/hardhat/modules/multi-sig/multi-sig.module.ts create mode 100644 chain-api/src/hardhat/modules/multi-sig/multi-sig.service.ts rename chain-api/src/hardhat/{module/hardhat.service.ts => modules/salary.service.ts} (56%) create mode 100644 chain-api/src/provider/provider.module.ts create mode 100644 chain-api/src/provider/provider.service.ts diff --git a/chain-api/package-lock.json b/chain-api/package-lock.json index 235743e..6c62f3e 100644 --- a/chain-api/package-lock.json +++ b/chain-api/package-lock.json @@ -17,6 +17,8 @@ "@nestjs/platform-express": "^10.0.0", "@nestjs/swagger": "^7.3.1", "@nomicfoundation/hardhat-toolbox": "^5.0.0", + "class-transformer": "^0.5.1", + "class-validator": "^0.14.1", "dotenv": "^16.4.5", "reflect-metadata": "^0.1.13", "rxjs": "^7.8.1" @@ -28,7 +30,7 @@ "@nomicfoundation/hardhat-ethers": "^3.0.5", "@types/express": "^4.17.17", "@types/jest": "^29.5.2", - "@types/node": "^20.3.1", + "@types/node": "^20.12.11", "@types/supertest": "^2.0.12", "@typescript-eslint/eslint-plugin": "^6.0.0", "@typescript-eslint/parser": "^6.0.0", @@ -5138,9 +5140,9 @@ "peer": true }, "node_modules/@types/node": { - "version": "20.12.10", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.10.tgz", - "integrity": "sha512-Eem5pH9pmWBHoGAT8Dr5fdc5rYA+4NAovdM4EktRPVAAiJhmWWfQrA0cFhAbOsQdSfIHjAud6YdkbL69+zSKjw==", + "version": "20.12.11", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.11.tgz", + "integrity": "sha512-vDg9PZ/zi+Nqp6boSOT7plNuthRugEKixDv5sFTIpkE89MmNtEArAShI4mxuX2+UrLEe9pxC1vm2cjm9YlWbJw==", "dependencies": { "undici-types": "~5.26.4" } @@ -5235,6 +5237,11 @@ "@types/superagent": "*" } }, + "node_modules/@types/validator": { + "version": "13.11.9", + "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.11.9.tgz", + "integrity": "sha512-FCTsikRozryfayPuiI46QzH3fnrOoctTjvOYZkho9BTFLCOZ2rgZJHMOVgCOfttjPJcgOx52EpkY0CMfy87MIw==" + }, "node_modules/@types/yargs": { "version": "17.0.32", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz", @@ -6714,6 +6721,21 @@ "integrity": "sha512-a3KdPAANPbNE4ZUv9h6LckSl9zLsYOP4MBmhIPkRaeyybt+r4UghLvq+xw/YwUcC1gqylCkL4rdVs3Lwupjm4Q==", "dev": true }, + "node_modules/class-transformer": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.5.1.tgz", + "integrity": "sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==" + }, + "node_modules/class-validator": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/class-validator/-/class-validator-0.14.1.tgz", + "integrity": "sha512-2VEG9JICxIqTpoK1eMzZqaV+u/EiwEJkMGzTrZf6sU/fwsnOITVgYJ8yojSy6CaXtO9V0Cc6ZQZ8h8m4UBuLwQ==", + "dependencies": { + "@types/validator": "^13.11.8", + "libphonenumber-js": "^1.10.53", + "validator": "^13.9.0" + } + }, "node_modules/clean-stack": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", @@ -11544,6 +11566,11 @@ "node": ">= 0.8.0" } }, + "node_modules/libphonenumber-js": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.11.1.tgz", + "integrity": "sha512-Wze1LPwcnzvcKGcRHFGFECTaLzxOtujwpf924difr5zniyYv1C2PiW0419qDR7m8lKDxsImu5mwxFuXhXpjmvw==" + }, "node_modules/lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", @@ -16173,6 +16200,14 @@ "spdx-expression-parse": "^3.0.0" } }, + "node_modules/validator": { + "version": "13.12.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.12.0.tgz", + "integrity": "sha512-c1Q0mCiPlgdTVVVIJIrBuxNicYE+t/7oKeI9MWLj3fh/uq2Pxh/3eeWbVZ4OcGW1TUf53At0njHw5SMdA3tmMg==", + "engines": { + "node": ">= 0.10" + } + }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", diff --git a/chain-api/package.json b/chain-api/package.json index f0e7323..56aa3ff 100644 --- a/chain-api/package.json +++ b/chain-api/package.json @@ -28,6 +28,8 @@ "@nestjs/platform-express": "^10.0.0", "@nestjs/swagger": "^7.3.1", "@nomicfoundation/hardhat-toolbox": "^5.0.0", + "class-transformer": "^0.5.1", + "class-validator": "^0.14.1", "dotenv": "^16.4.5", "reflect-metadata": "^0.1.13", "rxjs": "^7.8.1" @@ -39,7 +41,7 @@ "@nomicfoundation/hardhat-ethers": "^3.0.5", "@types/express": "^4.17.17", "@types/jest": "^29.5.2", - "@types/node": "^20.3.1", + "@types/node": "^20.12.11", "@types/supertest": "^2.0.12", "@typescript-eslint/eslint-plugin": "^6.0.0", "@typescript-eslint/parser": "^6.0.0", diff --git a/chain-api/src/contract-factory/contract-factory.controller.ts b/chain-api/src/contract-factory/contract-factory.controller.ts index b537d0a..c8a5db5 100644 --- a/chain-api/src/contract-factory/contract-factory.controller.ts +++ b/chain-api/src/contract-factory/contract-factory.controller.ts @@ -11,6 +11,7 @@ import { ContractFactoryService } from './contract-factory.service'; import { CreateContractFactoryDto } from './dto/create-contract-factory.dto'; import { UpdateContractFactoryDto } from './dto/update-contract-factory.dto'; import { ApiTags } from '@nestjs/swagger'; +import { MultiSigWalletDto } from 'src/hardhat/modules/dto/multi-sig.dto'; @ApiTags('contract-factory') @Controller('contract-factory') export class ContractFactoryController { @@ -18,8 +19,8 @@ export class ContractFactoryController { private readonly contractFactoryService: ContractFactoryService, ) {} - @Post('') - create(@Body() createContractFactoryDto: CreateContractFactoryDto) { - return this.contractFactoryService.create(createContractFactoryDto); + @Post('multi-sig') + create(@Body() createContractFactoryDto: MultiSigWalletDto) { + return this.contractFactoryService.createMultiSig(createContractFactoryDto); } } diff --git a/chain-api/src/contract-factory/contract-factory.module.ts b/chain-api/src/contract-factory/contract-factory.module.ts index 9688aba..a671464 100644 --- a/chain-api/src/contract-factory/contract-factory.module.ts +++ b/chain-api/src/contract-factory/contract-factory.module.ts @@ -1,4 +1,4 @@ -import { HardhatModule } from '../hardhat/module/hardhat.module'; +import { HardhatModule } from '../hardhat/modules/hardhat.module'; import { Module } from '@nestjs/common'; import { ContractFactoryService } from './contract-factory.service'; import { ContractFactoryController } from './contract-factory.controller'; diff --git a/chain-api/src/contract-factory/contract-factory.service.ts b/chain-api/src/contract-factory/contract-factory.service.ts index 07cb6a0..cb4487a 100644 --- a/chain-api/src/contract-factory/contract-factory.service.ts +++ b/chain-api/src/contract-factory/contract-factory.service.ts @@ -1,11 +1,21 @@ -import { HardhatService } from '../hardhat/module/hardhat.service'; +import { HardhatService } from '../hardhat/modules/hardhat.service'; import { Injectable } from '@nestjs/common'; import { CreateContractFactoryDto } from './dto/create-contract-factory.dto'; +import { SalariesService } from 'src/hardhat/modules/salary.service'; +import { MultiSigWalletService } from 'src/hardhat/modules/multi-sig/multi-sig.service'; +import { MultiSigWalletDto } from 'src/hardhat/modules/dto/multi-sig.dto'; @Injectable() export class ContractFactoryService { - constructor(private readonly hhService: HardhatService) {} - async create(createContractFactoryDto: CreateContractFactoryDto) { - return await this.hhService.deploySalaryContract(); + constructor( + private readonly salaryService: SalariesService, + private readonly multiSigService: MultiSigWalletService, + ) {} + async createSalary(createContractFactoryDto: CreateContractFactoryDto) { + return await this.salaryService.deploy(); + } + + async createMultiSig(dto: MultiSigWalletDto) { + return await this.multiSigService.deploy(dto); } } diff --git a/chain-api/src/contract-interact/contract-interact.controller.ts b/chain-api/src/contract-interact/contract-interact.controller.ts deleted file mode 100644 index f42ca09..0000000 --- a/chain-api/src/contract-interact/contract-interact.controller.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { - Controller, - Get, - Post, - Body, - Patch, - Param, - Delete, -} from '@nestjs/common'; -import { ContractInteractService } from './contract-interact.service'; -import { CreateContractInteractDto } from './dto/create-contract-interact.dto'; -import { UpdateContractInteractDto } from './dto/update-contract-interact.dto'; -import { ApiTags } from '@nestjs/swagger'; - -@ApiTags('contract-interact') -@Controller('contract-interact') -export class ContractInteractController { - constructor( - private readonly contractInteractService: ContractInteractService, - ) {} - - @Post() - create(@Body() createContractInteractDto: CreateContractInteractDto) { - return this.contractInteractService.create(createContractInteractDto); - } - - @Get() - findAll() { - return this.contractInteractService.findAll(); - } - - @Get(':id') - findOne(@Param('id') id: string) { - return this.contractInteractService.findOne(+id); - } - - @Patch(':id') - update( - @Param('id') id: string, - @Body() updateContractInteractDto: UpdateContractInteractDto, - ) { - return this.contractInteractService.update(+id, updateContractInteractDto); - } - - @Delete(':id') - remove(@Param('id') id: string) { - return this.contractInteractService.remove(+id); - } -} diff --git a/chain-api/src/contract-interact/contract-interact.module.ts b/chain-api/src/contract-interact/contract-interact.module.ts index 56e3a19..2063f80 100644 --- a/chain-api/src/contract-interact/contract-interact.module.ts +++ b/chain-api/src/contract-interact/contract-interact.module.ts @@ -1,9 +1,12 @@ import { Module } from '@nestjs/common'; import { ContractInteractService } from './contract-interact.service'; -import { ContractInteractController } from './contract-interact.controller'; + +import { HardhatModule } from 'src/hardhat/modules/hardhat.module'; +import { MultiSigInteractController } from './multi-sig-interact.controller'; @Module({ - controllers: [ContractInteractController], + imports: [HardhatModule], + controllers: [MultiSigInteractController], providers: [ContractInteractService], }) export class ContractInteractModule {} diff --git a/chain-api/src/contract-interact/dto/multi-sig.dto.ts b/chain-api/src/contract-interact/dto/multi-sig.dto.ts new file mode 100644 index 0000000..290ae54 --- /dev/null +++ b/chain-api/src/contract-interact/dto/multi-sig.dto.ts @@ -0,0 +1,18 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsOptional, IsString } from 'class-validator'; + +export class SubmitTransactionDto { + @IsString() + @ApiProperty() + contractAddress: string; + @ApiProperty() + @IsString() + destination: string; + @IsString() + @ApiProperty() + value: string; + @IsOptional() + @IsString() + // @ApiProperty() + data: string; +} diff --git a/chain-api/src/contract-interact/multi-sig-interact.controller.ts b/chain-api/src/contract-interact/multi-sig-interact.controller.ts new file mode 100644 index 0000000..e396448 --- /dev/null +++ b/chain-api/src/contract-interact/multi-sig-interact.controller.ts @@ -0,0 +1,20 @@ +import { Body, Controller, Get, Param, Post } from '@nestjs/common'; +import { ApiOkResponse, ApiTags } from '@nestjs/swagger'; +import { MultiSigWalletService } from 'src/hardhat/modules/multi-sig/multi-sig.service'; +import { SubmitTransactionDto } from './dto/multi-sig.dto'; +@ApiTags('multi-sig-interact') +@Controller() +export class MultiSigInteractController { + constructor(private readonly multiSigWalletService: MultiSigWalletService) {} + + @Get('owners/:address') + async getOwners(@Param('address') address: string) { + return this.multiSigWalletService.getOwners(address); + } + + @ApiOkResponse() + @Post('submit-transaction') + async submitTransaction(@Body() dto: SubmitTransactionDto) { + return this.multiSigWalletService.submitTransaction(dto); + } +} diff --git a/chain-api/src/hardhat/contracts/Agreement.sol b/chain-api/src/hardhat/contracts/Agreement.sol new file mode 100644 index 0000000..e69de29 diff --git a/chain-api/src/hardhat/contracts/MultiSigWallet.sol b/chain-api/src/hardhat/contracts/MultiSigWallet.sol index 98b0431..eaae1e4 100644 --- a/chain-api/src/hardhat/contracts/MultiSigWallet.sol +++ b/chain-api/src/hardhat/contracts/MultiSigWallet.sol @@ -8,7 +8,7 @@ pragma solidity ^0.8.19; contract MultiSigWallet { event Deposit(address indexed sender, uint amount, uint balance); event SubmitTransaction( - address indexed owener, + address indexed owner, uint indexed txIndex, address indexed to, uint value, @@ -38,36 +38,36 @@ contract MultiSigWallet { Transaction[] public transactions; modifier onlyOwner() { - require(isOwner[msg.sender], "not owner"); + require(isOwner[msg.sender], 'not owner'); _; } modifier txExists(uint _txIndex) { - require(_txIndex < transactions.length, "tx does not exist"); + require(_txIndex < transactions.length, 'tx does not exist'); _; } modifier notConfirmed(uint _txIndex) { - require(!isConfirmed[_txIndex][msg.sender], "tx already confirmed"); + require(!isConfirmed[_txIndex][msg.sender], 'tx already confirmed'); _; } modifier notExecuted(uint _txIndex) { - require(!transactions[_txIndex].executed, "tx already confirmed"); + require(!transactions[_txIndex].executed, 'tx already confirmed'); _; } constructor(address[] memory _owners, uint _numConfirmationsRequired) { - require(_owners.length > 0, "owners required"); + require(_owners.length > 0, 'owners required'); require( _numConfirmationsRequired > 0 && _numConfirmationsRequired <= _owners.length, - "invalid number of required confirmations" + 'invalid number of required confirmations' ); for (uint i = 0; i < _owners.length; i++) { address owner = _owners[i]; - require(owner != address(0), "invalid owner"); - require(!isOwner[owner], "owner not unique"); + require(owner != address(0), 'invalid owner'); + require(!isOwner[owner], 'owner not unique'); isOwner[owner] = true; owners.push(owner); } @@ -117,13 +117,13 @@ contract MultiSigWallet { Transaction storage transaction = transactions[_txIndex]; require( transaction.numConfirmations >= numConfirmationsRequired, - "cannot execute tx" + 'cannot execute tx' ); transaction.executed = true; (bool success, ) = transaction.to.call{value: transaction.value}( transaction.data ); - require(success, "tx failed"); + require(success, 'tx failed'); emit ExecuteTransaction(msg.sender, _txIndex); } @@ -131,7 +131,7 @@ contract MultiSigWallet { uint _txIndex ) public onlyOwner txExists(_txIndex) notExecuted(_txIndex) { Transaction storage transaction = transactions[_txIndex]; - require(isConfirmed[_txIndex][msg.sender], "tx not confirmed"); + require(isConfirmed[_txIndex][msg.sender], 'tx not confirmed'); transaction.numConfirmations -= 1; isConfirmed[_txIndex][msg.sender] = false; diff --git a/chain-api/src/hardhat/contracts/Salaries.sol b/chain-api/src/hardhat/contracts/Salaries.sol index 5e5d7b7..3c343c8 100644 --- a/chain-api/src/hardhat/contracts/Salaries.sol +++ b/chain-api/src/hardhat/contracts/Salaries.sol @@ -1,26 +1,62 @@ // SPDX-License-Identifier: MIT + pragma solidity ^0.8.7; import {AggregatorV3Interface} from '@chainlink/contracts/src/v0.8/shared/interfaces/AggregatorV3Interface.sol'; contract Salaries { AggregatorV3Interface internal dataFeed; + address public multisigWallet; + mapping(address => uint) public salaries; - constructor() { - dataFeed = AggregatorV3Interface( - 0xF0d50568e3A7e8259E16663972b11910F89BD8e7 - ); + constructor(address _multisigWallet, address _priceFeedAddress) { + multisigWallet = _multisigWallet; + dataFeed = AggregatorV3Interface(_priceFeedAddress); } - function getChainlinkDataFeedLatestAnswer() public view returns (int) { - // prettier-ignore + modifier onlyMultisig() { + require(msg.sender == multisigWallet, 'Unauthorized'); + _; + } + + function getLatestUSDTPriceInETH() public view returns (int) { ( - /* uint80 roundID */, - int answer, - /*uint startedAt*/, - /*uint timeStamp*/, - /*uint80 answeredInRound*/ + , + /* uint80 roundID */ int answer /* uint startedAt */ /* uint timeStamp */ /* uint80 answeredInRound */, + , + , + ) = dataFeed.latestRoundData(); return answer; } + + function setSalary( + address employee, + uint salaryInUSDT + ) external onlyMultisig { + salaries[employee] = salaryInUSDT; + } + + function payoutInETH(address employee) external onlyMultisig { + uint salaryInUSDT = salaries[employee]; + require(salaryInUSDT > 0, 'No salary set'); + + int ethToUSDT = getLatestUSDTPriceInETH(); + require(ethToUSDT > 0, 'Invalid price data'); + + // Convert salary from USDT to ETH based on the latest price + uint salaryInETH = uint(salaryInUSDT * 1e18) / uint(ethToUSDT); + + // Check sufficient balance + require( + address(this).balance >= salaryInETH, + 'Insufficient contract balance' + ); + + salaries[employee] = 0; // Reset salary after payment + payable(employee).transfer(salaryInETH); + } + + // Fallback to receive ETH + receive() external payable {} } diff --git a/chain-api/src/hardhat/module/hardhat.module.ts b/chain-api/src/hardhat/module/hardhat.module.ts deleted file mode 100644 index a683e25..0000000 --- a/chain-api/src/hardhat/module/hardhat.module.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { Module } from '@nestjs/common'; -import { HardhatService } from './hardhat.service'; - -@Module({ - imports: [], - controllers: [], - providers: [HardhatService], - exports: [HardhatService], -}) -export class HardhatModule {} diff --git a/chain-api/src/hardhat/modules/base-contract.service.ts b/chain-api/src/hardhat/modules/base-contract.service.ts new file mode 100644 index 0000000..5a9209f --- /dev/null +++ b/chain-api/src/hardhat/modules/base-contract.service.ts @@ -0,0 +1,12 @@ +import { Injectable } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import { ProviderService } from 'src/provider/provider.service'; + +@Injectable() +export abstract class BaseContractService { + constructor( + public readonly configService: ConfigService, + public readonly providerService: ProviderService, + ) {} + abstract deploy(dto: object): Promise; +} diff --git a/chain-api/src/hardhat/modules/dto/multi-sig.dto.ts b/chain-api/src/hardhat/modules/dto/multi-sig.dto.ts new file mode 100644 index 0000000..e50d15f --- /dev/null +++ b/chain-api/src/hardhat/modules/dto/multi-sig.dto.ts @@ -0,0 +1,11 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsArray, IsNumber } from 'class-validator'; + +export class MultiSigWalletDto { + @IsArray() + @ApiProperty() + owners: string[]; + @IsNumber() + @ApiProperty() + confirmations: number; +} diff --git a/chain-api/src/hardhat/modules/hardhat.module.ts b/chain-api/src/hardhat/modules/hardhat.module.ts new file mode 100644 index 0000000..164093f --- /dev/null +++ b/chain-api/src/hardhat/modules/hardhat.module.ts @@ -0,0 +1,15 @@ +import { Module } from '@nestjs/common'; +import { HardhatService } from './hardhat.service'; +import { ProviderModule } from 'src/provider/provider.module'; +import { MultiSigWalletService } from './multi-sig/multi-sig.service'; +import { SalariesService } from './salary.service'; +import { BaseContractService } from './base-contract.service'; +import { MultiSigModule } from './multi-sig/multi-sig.module'; + +@Module({ + imports: [ProviderModule, MultiSigModule], + controllers: [], + providers: [HardhatService, SalariesService], + exports: [HardhatService, SalariesService, MultiSigModule], +}) +export class HardhatModule {} diff --git a/chain-api/src/hardhat/modules/hardhat.service.ts b/chain-api/src/hardhat/modules/hardhat.service.ts new file mode 100644 index 0000000..55c2582 --- /dev/null +++ b/chain-api/src/hardhat/modules/hardhat.service.ts @@ -0,0 +1,4 @@ +import { Injectable } from '@nestjs/common'; + +@Injectable() +export class HardhatService {} diff --git a/chain-api/src/hardhat/modules/multi-sig/multi-sig.module.ts b/chain-api/src/hardhat/modules/multi-sig/multi-sig.module.ts new file mode 100644 index 0000000..0b197fd --- /dev/null +++ b/chain-api/src/hardhat/modules/multi-sig/multi-sig.module.ts @@ -0,0 +1,15 @@ +import { Module } from '@nestjs/common'; + +import { ProviderModule } from 'src/provider/provider.module'; + +import { BaseContractService } from '../base-contract.service'; +import { ProviderService } from 'src/provider/provider.service'; +import { MultiSigWalletService } from './multi-sig.service'; + +@Module({ + imports: [ProviderModule], + controllers: [], + providers: [MultiSigWalletService], + exports: [MultiSigWalletService], +}) +export class MultiSigModule {} diff --git a/chain-api/src/hardhat/modules/multi-sig/multi-sig.service.ts b/chain-api/src/hardhat/modules/multi-sig/multi-sig.service.ts new file mode 100644 index 0000000..b3e7a2e --- /dev/null +++ b/chain-api/src/hardhat/modules/multi-sig/multi-sig.service.ts @@ -0,0 +1,68 @@ +import { MultiSigWallet } from '../../typechain-types/contracts/MultiSigWallet'; +import { Injectable } from '@nestjs/common'; +import { ethers } from 'ethers'; +import { ConfigService } from '@nestjs/config'; +import * as hre from 'hardhat'; +import { BaseContractService } from '../base-contract.service'; +import { MultiSigWalletDto } from '../dto/multi-sig.dto'; +import { SubmitTransactionDto } from 'src/contract-interact/dto/multi-sig.dto'; + +export class MultiSigWalletService extends BaseContractService { + async deploy(dto: MultiSigWalletDto) { + const { abi, bytecode } = + await hre.artifacts.readArtifact('MultiSigWallet'); + + const signer = await this.providerService.getSigner(); + + const salaryContract = new ethers.ContractFactory(abi, bytecode, signer); + + const myContract = await salaryContract.deploy( + dto.owners, + dto.confirmations, + ); + await myContract.waitForDeployment(); + + console.log( + '🚀 ~ HardhatService ~ deploySalaryContract ~ myContract:', + myContract, + ); + const address = myContract.getAddress(); + console.log('🚀 ~ SalariesService ~ deploy ~ address:', address); + } + + async getOwners(address: string) { + const { abi } = await hre.artifacts.readArtifact('MultiSigWallet'); + const multiSigContract = new ethers.Contract(address, abi); + + const signer = await this.providerService.getSigner(); + + const contract = new ethers.Contract(address, abi, signer); + + const owners = await contract.getOwners(); + + return owners; + } + + async submitTransaction(dto: SubmitTransactionDto) { + const { destination, value, data, contractAddress } = dto; + const { abi } = await hre.artifacts.readArtifact('MultiSigWallet'); + const multiSigContract = new ethers.Contract(contractAddress, abi); + + const signer = await this.providerService.getSigner(); + + const contract = new ethers.Contract(contractAddress, abi, signer); + console.log( + '🚀 ~ MultiSigWalletService ~ submitTransaction ~ contract:', + contract.interface, + ); + + const tx = await contract.submitTransaction( + destination, + value, + new TextEncoder().encode(data), + ); + console.log('🚀 ~ MultiSigWalletService ~ submitTransaction ~ tx:', tx); + + return tx; + } +} diff --git a/chain-api/src/hardhat/module/hardhat.service.ts b/chain-api/src/hardhat/modules/salary.service.ts similarity index 56% rename from chain-api/src/hardhat/module/hardhat.service.ts rename to chain-api/src/hardhat/modules/salary.service.ts index ad31ed6..ebded0c 100644 --- a/chain-api/src/hardhat/module/hardhat.service.ts +++ b/chain-api/src/hardhat/modules/salary.service.ts @@ -1,25 +1,19 @@ -// const hre = require('hardhat'); -import * as hre from 'hardhat'; import { Injectable } from '@nestjs/common'; import { ethers } from 'ethers'; import { ConfigService } from '@nestjs/config'; +import * as hre from 'hardhat'; +import { BaseContractService } from './base-contract.service'; + @Injectable() -export class HardhatService { - constructor(private readonly configService: ConfigService) {} - async deploySalaryContract() { - const provider = new ethers.JsonRpcProvider( - 'https://polygon-amoy.g.alchemy.com/v2/pEtFFy_Qr_NrM1vMnlzSXmYXkozVNzLy', - 80002, - ); +export class SalariesService extends BaseContractService { + getSalaries() {} + + async deploy() { + const provider = await this.providerService.getProvider(); const salary = await hre.artifacts.readArtifact('Salaries'); const abi = salary.abi; - console.log('🚀 ~ HardhatService ~ deploySalaryContract ~ abi:', abi); const bytecode = salary.deployedBytecode; - console.log( - '🚀 ~ HardhatService ~ deploySalaryContract ~ bytecode:', - bytecode, - ); const signer = new ethers.Wallet( this.configService.getOrThrow('POLYGON_PK'), provider, @@ -31,12 +25,17 @@ export class HardhatService { signer, ); - const myContract = await salaryContract.deploy(); + const myContract = await salaryContract.deploy( + 'multisig address', + this.configService.getOrThrow('CHAINLINK_AGGREGATOR_V3'), + ); await myContract.waitForDeployment(); console.log( '🚀 ~ HardhatService ~ deploySalaryContract ~ myContract:', myContract, ); + const address = myContract.getAddress(); + console.log('🚀 ~ SalariesService ~ deploy ~ address:', address); } } diff --git a/chain-api/src/main.ts b/chain-api/src/main.ts index 52240b8..9ccc209 100644 --- a/chain-api/src/main.ts +++ b/chain-api/src/main.ts @@ -1,6 +1,7 @@ import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger'; +import { ValidationPipe } from '@nestjs/common'; async function bootstrap() { const app = await NestFactory.create(AppModule); @@ -11,7 +12,9 @@ async function bootstrap() { .build(); const document = SwaggerModule.createDocument(app, config); SwaggerModule.setup('api', app, document); + app.useGlobalPipes(new ValidationPipe()); await app.listen(3000); + console.log('Swagger avaliable at http://localhost:3000/api'); } bootstrap(); diff --git a/chain-api/src/provider/provider.module.ts b/chain-api/src/provider/provider.module.ts new file mode 100644 index 0000000..6b753b4 --- /dev/null +++ b/chain-api/src/provider/provider.module.ts @@ -0,0 +1,10 @@ +import { Module } from '@nestjs/common'; +import { ProviderService } from './provider.service'; + +@Module({ + imports: [], + controllers: [], + providers: [ProviderService], + exports: [ProviderService], +}) +export class ProviderModule {} diff --git a/chain-api/src/provider/provider.service.ts b/chain-api/src/provider/provider.service.ts new file mode 100644 index 0000000..711574c --- /dev/null +++ b/chain-api/src/provider/provider.service.ts @@ -0,0 +1,38 @@ +import { Injectable } from '@nestjs/common'; +import { ethers } from 'ethers'; +import { ConfigService } from '@nestjs/config'; +@Injectable() +export class ProviderService { + public provider: ethers.JsonRpcProvider; + public networkId: number; + private nodeUrl: string; + constructor(private readonly configService: ConfigService) { + this.networkId = parseInt( + this.configService.getOrThrow('POLYGON_NETWORK_ID'), + ); + this.nodeUrl = this.configService.getOrThrow('POLYGON_NODE'); + } + + async getProvider() { + if (this.provider) { + return this.provider; + } + const polygonProvider = new ethers.JsonRpcProvider( + this.nodeUrl, + this.networkId, + ); + this.provider = polygonProvider; + return this.provider; + } + + async getSigner() { + if (!this.provider) { + await this.getProvider(); + } + const signer = new ethers.Wallet( + this.configService.getOrThrow('POLYGON_PK'), + this.provider, + ); + return signer; + } +}