diff --git a/chain-api/src/contract-interact/dto/multi-sig.dto.ts b/chain-api/src/contract-interact/dto/multi-sig.dto.ts index d124f50..6ef37e0 100644 --- a/chain-api/src/contract-interact/dto/multi-sig.dto.ts +++ b/chain-api/src/contract-interact/dto/multi-sig.dto.ts @@ -34,7 +34,7 @@ export class GetTransactionCount {} export class GetTransactionDto extends ConfirmTransactionDto {} -export class DepositMultiSigDto { +export class DepositContractDto { @IsString() @ApiProperty() contractAddress: string; diff --git a/chain-api/src/hardhat/contracts/MultiSigWallet.sol b/chain-api/src/hardhat/contracts/MultiSigWallet.sol index eaae1e4..0ffba22 100644 --- a/chain-api/src/hardhat/contracts/MultiSigWallet.sol +++ b/chain-api/src/hardhat/contracts/MultiSigWallet.sol @@ -1,4 +1,5 @@ // SPDX-License-Identifier: MIT +// 0x74f11486DB0FCAA2dCDE0aEB477e1F37fCAa510A pragma solidity ^0.8.19; // The wallet owners can @@ -18,6 +19,9 @@ contract MultiSigWallet { event ConfirmTransaction(address indexed owner, uint indexed txIndex); event RevokeConfirmation(address indexed owner, uint indexed txIndex); event ExecuteTransaction(address indexed owner, uint indexed txIndex); + event ExecuteTransactionFailed(address indexed owner, uint indexed txIndex, string reason); + event Payout(address indexed employee, uint salaryInETH); + event PayoutFailed(address indexed employee, uint salaryInETH, string reason); address[] public owners; @@ -111,20 +115,63 @@ contract MultiSigWallet { emit ConfirmTransaction(msg.sender, _txIndex); } - function executeTransaction( - uint _txIndex - ) public onlyOwner txExists(_txIndex) notExecuted(_txIndex) { + function executeTransaction(uint _txIndex) + public + onlyOwner + txExists(_txIndex) + notExecuted(_txIndex) + { 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'); - emit ExecuteTransaction(msg.sender, _txIndex); + + + (bool success, bytes memory returnData) = transaction.to.call{value: transaction.value}(transaction.data); + if (success) { + transaction.executed = true; + emit ExecuteTransaction(msg.sender, _txIndex); + if (returnData.length > 0) { + emitEventFromReturnData(returnData); + } + } else { + // Get the revert reason and emit it + if (returnData.length > 0) { + // The call reverted with a message + assembly { + let returndata_size := mload(returnData) + revert(add(32, returnData), returndata_size) + } + } else { + // The call reverted without a message + emit ExecuteTransactionFailed(msg.sender, _txIndex, "Transaction failed without a reason"); + } + } + } + + function emitEventFromReturnData(bytes memory returnData) internal { + // Decode the selector from returnData + bytes4 selector; + assembly { + selector := mload(add(returnData, 32)) + } + + // Match the selector to the known events + if (selector == Payout.selector) { + (address employee, uint salaryInETH) = abi.decode(slice(returnData, 4, returnData.length), (address, uint)); + emit Payout(employee, salaryInETH); + } else if (selector == PayoutFailed.selector) { + (address employee, uint salaryInETH, string memory reason) = abi.decode(slice(returnData, 4, returnData.length), (address, uint, string)); + emit PayoutFailed(employee, salaryInETH, reason); + } + } + function slice(bytes memory data, uint start, uint length) internal pure returns (bytes memory) { + bytes memory result = new bytes(length); + for (uint i = 0; i < length; i++) { + result[i] = data[start + i]; + } + return result; } function revokeConfirmation( diff --git a/chain-api/src/hardhat/contracts/Salaries.sol b/chain-api/src/hardhat/contracts/Salaries.sol index e7b9c0c..0c2d3ed 100644 --- a/chain-api/src/hardhat/contracts/Salaries.sol +++ b/chain-api/src/hardhat/contracts/Salaries.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT - +// 0x2F9442900d067a3D37A1C2aE99462E055e32c741 pragma solidity ^0.8.7; import {AggregatorV3Interface} from '@chainlink/contracts/src/v0.8/shared/interfaces/AggregatorV3Interface.sol'; @@ -8,7 +8,8 @@ contract Salaries { AggregatorV3Interface internal dataFeed; address public multisigWallet; mapping(address => uint) public salaries; - + event Payout(address indexed employee, uint salaryInETH); + event PayoutFailed(address indexed employee, uint salaryInETH, string reason); //0xF0d50568e3A7e8259E16663972b11910F89BD8e7 constructor(address _multisigWallet, address _priceFeedAddress) { multisigWallet = _multisigWallet; @@ -42,7 +43,7 @@ contract Salaries { salaries[employee] = salaryInUSDT; } - function payoutInETH(address employee) external onlyMultisig { + function payoutInETH(address payable employee) external onlyMultisig { uint salaryInUSDT = salaries[employee]; require(salaryInUSDT > 0, 'No salary set'); @@ -58,8 +59,16 @@ contract Salaries { 'Insufficient contract balance' ); - salaries[employee] = 0; // Reset salary after payment - payable(employee).transfer(salaryInETH); + (bool success, ) = employee.call{value: salaryInETH}(""); + if (success) { + emit Payout(employee, salaryInETH); + } else { + emit PayoutFailed(employee, salaryInETH, "Transfer failed"); + } + } + + function dummy() public pure returns (uint){ + return 1337; } // Fallback to receive ETH diff --git a/chain-api/src/hardhat/modules/multi-sig/multi-sig-interact.controller.ts b/chain-api/src/hardhat/modules/multi-sig/multi-sig-interact.controller.ts index f434be2..b4722ab 100644 --- a/chain-api/src/hardhat/modules/multi-sig/multi-sig-interact.controller.ts +++ b/chain-api/src/hardhat/modules/multi-sig/multi-sig-interact.controller.ts @@ -3,7 +3,7 @@ import { ApiOkResponse, ApiTags } from '@nestjs/swagger'; import { MultiSigWalletService } from 'src/hardhat/modules/multi-sig/multi-sig.service'; import { ConfirmTransactionDto, - DepositMultiSigDto, + DepositContractDto, ExecuteTransactionDto, GetTransactionDto, RevokeConfirmationDto, @@ -59,7 +59,7 @@ export class MultiSigInteractController { } @Post('deposit') - async deposit(@Body() dto: DepositMultiSigDto) { + async deposit(@Body() dto: DepositContractDto) { return this.multiSigWalletService.deposit(dto); } } 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 index 02bcfe3..cc54946 100644 --- a/chain-api/src/hardhat/modules/multi-sig/multi-sig.service.ts +++ b/chain-api/src/hardhat/modules/multi-sig/multi-sig.service.ts @@ -1,22 +1,16 @@ -import { TransactionReceipt, ethers } from 'ethers'; +import { TransactionReceipt, ethers, parseEther } from 'ethers'; import { ConfigService } from '@nestjs/config'; import * as hre from 'hardhat'; import { BaseContractService } from '../base-contract.service'; import { MultiSigWalletDto } from './multi-sig.dto'; import { ConfirmTransactionDto, - DepositMultiSigDto, + DepositContractDto, ExecuteTransactionDto, GetTransactionDto, RevokeConfirmationDto, SubmitTransactionDto, } from 'src/contract-interact/dto/multi-sig.dto'; -import { - ConfirmTransactionLogs, - DepositLogs, - ExecuteTransactionLogs, - SubmitTransactionLogs, -} from 'src/hardhat/modules/dto/ethers.dto'; import { parseLogs } from 'src/contract-interact/ethers.helpers'; export class MultiSigWalletService extends BaseContractService { @@ -102,6 +96,7 @@ export class MultiSigWalletService extends BaseContractService { const tx = await contract.executeTransaction(index); const txResponse: TransactionReceipt = await tx.wait(); + console.log('=>(multi-sig.service.ts:99) txResponse', txResponse.logs); const eventParse = parseLogs(txResponse, contract); return { txHash: txResponse.hash, @@ -145,8 +140,9 @@ export class MultiSigWalletService extends BaseContractService { return tx; } - async deposit(dto: DepositMultiSigDto) { + async deposit(dto: DepositContractDto) { const { contractAddress, value } = dto; + const convertValue = parseEther(value); const signer = await this.providerService.getSigner(); const { abi } = await hre.artifacts.readArtifact('MultiSigWallet'); @@ -154,7 +150,7 @@ export class MultiSigWalletService extends BaseContractService { const tx = await signer.sendTransaction({ to: contractAddress, - value: BigInt(value), + value: convertValue, }); const txResponse: TransactionReceipt = await tx.wait(); diff --git a/chain-api/src/hardhat/modules/salaries/salaries-interact.controller.ts b/chain-api/src/hardhat/modules/salaries/salaries-interact.controller.ts index 2c89238..0646fc5 100644 --- a/chain-api/src/hardhat/modules/salaries/salaries-interact.controller.ts +++ b/chain-api/src/hardhat/modules/salaries/salaries-interact.controller.ts @@ -1,11 +1,13 @@ import { Body, Controller, Get, Param, Post } from '@nestjs/common'; import { SalariesService } from './salaries.service'; import { + CreatePayoutDto, GetEmployeeSalariesDto, SalariesDeployDto, SetSalaryDto, } from './salaries.dto'; import { ApiTags } from '@nestjs/swagger'; +import { DepositContractDto } from '../../../contract-interact/dto/multi-sig.dto'; @ApiTags('salaries') @Controller('salaries') export class SalariesController { @@ -30,4 +32,14 @@ export class SalariesController { async getSalary(@Body() dto: GetEmployeeSalariesDto) { return this.salariesService.getSalary(dto); } + + @Post('payout') + async createPayout(@Body() dto: CreatePayoutDto) { + return this.salariesService.createPayout(dto); + } + + @Post('deposit') + async deposit(@Body() dto: DepositContractDto) { + return this.salariesService.deposit(dto); + } } diff --git a/chain-api/src/hardhat/modules/salaries/salaries.dto.ts b/chain-api/src/hardhat/modules/salaries/salaries.dto.ts index 4875918..322ae38 100644 --- a/chain-api/src/hardhat/modules/salaries/salaries.dto.ts +++ b/chain-api/src/hardhat/modules/salaries/salaries.dto.ts @@ -21,8 +21,7 @@ export class SetSalaryDto { @IsNumber() salary: number; } - -export class GetEmployeeSalariesDto { +export class GeneralEmpoyeeSalaryDto { @ApiProperty() @IsString() contractAddress: string; @@ -30,3 +29,9 @@ export class GetEmployeeSalariesDto { @IsString() employeeAddress: string; } +export class GetEmployeeSalariesDto extends GeneralEmpoyeeSalaryDto {} + +export class CreatePayoutDto extends GeneralEmpoyeeSalaryDto { + @IsString() + multiSigWallet: string; +} diff --git a/chain-api/src/hardhat/modules/salaries/salaries.service.ts b/chain-api/src/hardhat/modules/salaries/salaries.service.ts index 43af1b7..8bef96e 100644 --- a/chain-api/src/hardhat/modules/salaries/salaries.service.ts +++ b/chain-api/src/hardhat/modules/salaries/salaries.service.ts @@ -1,7 +1,8 @@ import { Injectable } from '@nestjs/common'; import { BaseContractService } from '../base-contract.service'; -import { ethers } from 'ethers'; +import { ethers, parseEther, TransactionReceipt } from 'ethers'; import { + CreatePayoutDto, GetEmployeeSalariesDto, SalariesDeployDto, SetSalaryDto, @@ -9,6 +10,7 @@ import { import * as hre from 'hardhat'; import { MultiSigWalletService } from '../multi-sig/multi-sig.service'; import { ProviderService } from '../../../provider/provider.service'; +import { DepositContractDto } from '../../../contract-interact/dto/multi-sig.dto'; @Injectable() export class SalariesService extends BaseContractService { @@ -69,7 +71,43 @@ export class SalariesService extends BaseContractService { const contract = new ethers.Contract(contractAddress, abi, signer); - const answer: string = await contract.getSalary(employeeAddress); - return answer; + const answer: BigInt = await contract.getSalary(employeeAddress); + return { + salaryInUsd: answer.toString(), + }; + } + + async createPayout(dto: CreatePayoutDto) { + const { employeeAddress, contractAddress, multiSigWallet } = dto; + console.log('=>(salaries.service.ts:82) employeeAddress', employeeAddress); + const ISubmitMultiSig = new ethers.Interface([ + 'function payoutInETH(address employee)', + ]); + const data = ISubmitMultiSig.encodeFunctionData('payoutInETH', [ + employeeAddress, + ]); + + return await this.multiSigWalletService.submitTransaction({ + contractAddress: multiSigWallet, + destination: contractAddress, + value: '0', + data, + }); + } + + async deposit(dto: DepositContractDto) { + const { contractAddress, value } = dto; + const signer = await this.providerService.getSigner(); + + const convertValue = parseEther(value); + + const tx = await signer.sendTransaction({ + to: contractAddress, + value: convertValue, + }); + + const txResponse: TransactionReceipt = await tx.wait(); + + return txResponse; } }