payout ready, but needs fixes

This commit is contained in:
emochka2007 2024-05-16 01:34:49 +03:00
parent 0a74e430b8
commit 958bf670ae
8 changed files with 140 additions and 33 deletions

View File

@ -34,7 +34,7 @@ export class GetTransactionCount {}
export class GetTransactionDto extends ConfirmTransactionDto {} export class GetTransactionDto extends ConfirmTransactionDto {}
export class DepositMultiSigDto { export class DepositContractDto {
@IsString() @IsString()
@ApiProperty() @ApiProperty()
contractAddress: string; contractAddress: string;

View File

@ -1,4 +1,5 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
// 0x74f11486DB0FCAA2dCDE0aEB477e1F37fCAa510A
pragma solidity ^0.8.19; pragma solidity ^0.8.19;
// The wallet owners can // The wallet owners can
@ -18,6 +19,9 @@ contract MultiSigWallet {
event ConfirmTransaction(address indexed owner, uint indexed txIndex); event ConfirmTransaction(address indexed owner, uint indexed txIndex);
event RevokeConfirmation(address indexed owner, uint indexed txIndex); event RevokeConfirmation(address indexed owner, uint indexed txIndex);
event ExecuteTransaction(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; address[] public owners;
@ -111,20 +115,63 @@ contract MultiSigWallet {
emit ConfirmTransaction(msg.sender, _txIndex); emit ConfirmTransaction(msg.sender, _txIndex);
} }
function executeTransaction( function executeTransaction(uint _txIndex)
uint _txIndex public
) public onlyOwner txExists(_txIndex) notExecuted(_txIndex) { onlyOwner
txExists(_txIndex)
notExecuted(_txIndex)
{
Transaction storage transaction = transactions[_txIndex]; Transaction storage transaction = transactions[_txIndex];
require( require(
transaction.numConfirmations >= numConfirmationsRequired, transaction.numConfirmations >= numConfirmationsRequired,
'cannot execute tx' "cannot execute tx"
); );
(bool success, bytes memory returnData) = transaction.to.call{value: transaction.value}(transaction.data);
if (success) {
transaction.executed = true; transaction.executed = true;
(bool success, ) = transaction.to.call{value: transaction.value}(
transaction.data
);
require(success, 'tx failed');
emit ExecuteTransaction(msg.sender, _txIndex); 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( function revokeConfirmation(

View File

@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
// 0x2F9442900d067a3D37A1C2aE99462E055e32c741
pragma solidity ^0.8.7; pragma solidity ^0.8.7;
import {AggregatorV3Interface} from '@chainlink/contracts/src/v0.8/shared/interfaces/AggregatorV3Interface.sol'; import {AggregatorV3Interface} from '@chainlink/contracts/src/v0.8/shared/interfaces/AggregatorV3Interface.sol';
@ -8,7 +8,8 @@ contract Salaries {
AggregatorV3Interface internal dataFeed; AggregatorV3Interface internal dataFeed;
address public multisigWallet; address public multisigWallet;
mapping(address => uint) public salaries; mapping(address => uint) public salaries;
event Payout(address indexed employee, uint salaryInETH);
event PayoutFailed(address indexed employee, uint salaryInETH, string reason);
//0xF0d50568e3A7e8259E16663972b11910F89BD8e7 //0xF0d50568e3A7e8259E16663972b11910F89BD8e7
constructor(address _multisigWallet, address _priceFeedAddress) { constructor(address _multisigWallet, address _priceFeedAddress) {
multisigWallet = _multisigWallet; multisigWallet = _multisigWallet;
@ -42,7 +43,7 @@ contract Salaries {
salaries[employee] = salaryInUSDT; salaries[employee] = salaryInUSDT;
} }
function payoutInETH(address employee) external onlyMultisig { function payoutInETH(address payable employee) external onlyMultisig {
uint salaryInUSDT = salaries[employee]; uint salaryInUSDT = salaries[employee];
require(salaryInUSDT > 0, 'No salary set'); require(salaryInUSDT > 0, 'No salary set');
@ -58,8 +59,16 @@ contract Salaries {
'Insufficient contract balance' 'Insufficient contract balance'
); );
salaries[employee] = 0; // Reset salary after payment (bool success, ) = employee.call{value: salaryInETH}("");
payable(employee).transfer(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 // Fallback to receive ETH

View File

@ -3,7 +3,7 @@ import { ApiOkResponse, ApiTags } from '@nestjs/swagger';
import { MultiSigWalletService } from 'src/hardhat/modules/multi-sig/multi-sig.service'; import { MultiSigWalletService } from 'src/hardhat/modules/multi-sig/multi-sig.service';
import { import {
ConfirmTransactionDto, ConfirmTransactionDto,
DepositMultiSigDto, DepositContractDto,
ExecuteTransactionDto, ExecuteTransactionDto,
GetTransactionDto, GetTransactionDto,
RevokeConfirmationDto, RevokeConfirmationDto,
@ -59,7 +59,7 @@ export class MultiSigInteractController {
} }
@Post('deposit') @Post('deposit')
async deposit(@Body() dto: DepositMultiSigDto) { async deposit(@Body() dto: DepositContractDto) {
return this.multiSigWalletService.deposit(dto); return this.multiSigWalletService.deposit(dto);
} }
} }

View File

@ -1,22 +1,16 @@
import { TransactionReceipt, ethers } from 'ethers'; import { TransactionReceipt, ethers, parseEther } from 'ethers';
import { ConfigService } from '@nestjs/config'; import { ConfigService } from '@nestjs/config';
import * as hre from 'hardhat'; import * as hre from 'hardhat';
import { BaseContractService } from '../base-contract.service'; import { BaseContractService } from '../base-contract.service';
import { MultiSigWalletDto } from './multi-sig.dto'; import { MultiSigWalletDto } from './multi-sig.dto';
import { import {
ConfirmTransactionDto, ConfirmTransactionDto,
DepositMultiSigDto, DepositContractDto,
ExecuteTransactionDto, ExecuteTransactionDto,
GetTransactionDto, GetTransactionDto,
RevokeConfirmationDto, RevokeConfirmationDto,
SubmitTransactionDto, SubmitTransactionDto,
} from 'src/contract-interact/dto/multi-sig.dto'; } 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'; import { parseLogs } from 'src/contract-interact/ethers.helpers';
export class MultiSigWalletService extends BaseContractService { export class MultiSigWalletService extends BaseContractService {
@ -102,6 +96,7 @@ export class MultiSigWalletService extends BaseContractService {
const tx = await contract.executeTransaction(index); const tx = await contract.executeTransaction(index);
const txResponse: TransactionReceipt = await tx.wait(); const txResponse: TransactionReceipt = await tx.wait();
console.log('=>(multi-sig.service.ts:99) txResponse', txResponse.logs);
const eventParse = parseLogs(txResponse, contract); const eventParse = parseLogs(txResponse, contract);
return { return {
txHash: txResponse.hash, txHash: txResponse.hash,
@ -145,8 +140,9 @@ export class MultiSigWalletService extends BaseContractService {
return tx; return tx;
} }
async deposit(dto: DepositMultiSigDto) { async deposit(dto: DepositContractDto) {
const { contractAddress, value } = dto; const { contractAddress, value } = dto;
const convertValue = parseEther(value);
const signer = await this.providerService.getSigner(); const signer = await this.providerService.getSigner();
const { abi } = await hre.artifacts.readArtifact('MultiSigWallet'); const { abi } = await hre.artifacts.readArtifact('MultiSigWallet');
@ -154,7 +150,7 @@ export class MultiSigWalletService extends BaseContractService {
const tx = await signer.sendTransaction({ const tx = await signer.sendTransaction({
to: contractAddress, to: contractAddress,
value: BigInt(value), value: convertValue,
}); });
const txResponse: TransactionReceipt = await tx.wait(); const txResponse: TransactionReceipt = await tx.wait();

View File

@ -1,11 +1,13 @@
import { Body, Controller, Get, Param, Post } from '@nestjs/common'; import { Body, Controller, Get, Param, Post } from '@nestjs/common';
import { SalariesService } from './salaries.service'; import { SalariesService } from './salaries.service';
import { import {
CreatePayoutDto,
GetEmployeeSalariesDto, GetEmployeeSalariesDto,
SalariesDeployDto, SalariesDeployDto,
SetSalaryDto, SetSalaryDto,
} from './salaries.dto'; } from './salaries.dto';
import { ApiTags } from '@nestjs/swagger'; import { ApiTags } from '@nestjs/swagger';
import { DepositContractDto } from '../../../contract-interact/dto/multi-sig.dto';
@ApiTags('salaries') @ApiTags('salaries')
@Controller('salaries') @Controller('salaries')
export class SalariesController { export class SalariesController {
@ -30,4 +32,14 @@ export class SalariesController {
async getSalary(@Body() dto: GetEmployeeSalariesDto) { async getSalary(@Body() dto: GetEmployeeSalariesDto) {
return this.salariesService.getSalary(dto); 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);
}
} }

View File

@ -21,8 +21,7 @@ export class SetSalaryDto {
@IsNumber() @IsNumber()
salary: number; salary: number;
} }
export class GeneralEmpoyeeSalaryDto {
export class GetEmployeeSalariesDto {
@ApiProperty() @ApiProperty()
@IsString() @IsString()
contractAddress: string; contractAddress: string;
@ -30,3 +29,9 @@ export class GetEmployeeSalariesDto {
@IsString() @IsString()
employeeAddress: string; employeeAddress: string;
} }
export class GetEmployeeSalariesDto extends GeneralEmpoyeeSalaryDto {}
export class CreatePayoutDto extends GeneralEmpoyeeSalaryDto {
@IsString()
multiSigWallet: string;
}

View File

@ -1,7 +1,8 @@
import { Injectable } from '@nestjs/common'; import { Injectable } from '@nestjs/common';
import { BaseContractService } from '../base-contract.service'; import { BaseContractService } from '../base-contract.service';
import { ethers } from 'ethers'; import { ethers, parseEther, TransactionReceipt } from 'ethers';
import { import {
CreatePayoutDto,
GetEmployeeSalariesDto, GetEmployeeSalariesDto,
SalariesDeployDto, SalariesDeployDto,
SetSalaryDto, SetSalaryDto,
@ -9,6 +10,7 @@ import {
import * as hre from 'hardhat'; import * as hre from 'hardhat';
import { MultiSigWalletService } from '../multi-sig/multi-sig.service'; import { MultiSigWalletService } from '../multi-sig/multi-sig.service';
import { ProviderService } from '../../../provider/provider.service'; import { ProviderService } from '../../../provider/provider.service';
import { DepositContractDto } from '../../../contract-interact/dto/multi-sig.dto';
@Injectable() @Injectable()
export class SalariesService extends BaseContractService { export class SalariesService extends BaseContractService {
@ -69,7 +71,43 @@ export class SalariesService extends BaseContractService {
const contract = new ethers.Contract(contractAddress, abi, signer); const contract = new ethers.Contract(contractAddress, abi, signer);
const answer: string = await contract.getSalary(employeeAddress); const answer: BigInt = await contract.getSalary(employeeAddress);
return answer; 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;
} }
} }