license finished

This commit is contained in:
emochka2007 2024-05-21 15:58:49 +03:00
parent 61a74636d1
commit 7809a91416
9 changed files with 248 additions and 35 deletions

View File

@ -5,7 +5,9 @@ import {
DeployLicenseDto, DeployLicenseDto,
GetLicenseInfoDto, GetLicenseInfoDto,
GetShareLicense, GetShareLicense,
LicensePayoutDto,
RequestLicenseDto, RequestLicenseDto,
SetPayoutContractDto,
} from './license.dto'; } from './license.dto';
@ApiTags('license') @ApiTags('license')
@Controller('license') @Controller('license')
@ -37,7 +39,17 @@ export class LicenseController {
} }
@Get('payout-contract') @Get('payout-contract')
async getPayoutContract(@Body() dto: GetShareLicense) { async getPayoutContract(@Body() dto: GetLicenseInfoDto) {
return this.licenseService.getPayoutContract(dto); return this.licenseService.getPayoutContract(dto);
} }
@Post('payout')
async payout(@Body() dto: LicensePayoutDto) {
return this.licenseService.payout(dto);
}
@Post('set-payout-contract')
async setPayoutContract(@Body() dto: SetPayoutContractDto) {
return this.licenseService.setPayoutContract(dto);
}
} }

View File

@ -18,9 +18,6 @@ export class DeployLicenseDto {
}) })
@IsNumber({}, { each: true }) @IsNumber({}, { each: true })
shares: number[]; shares: number[];
@ApiProperty()
@IsString()
payrollAddress: string;
} }
export class RequestLicenseDto extends GetLicenseInfoDto { export class RequestLicenseDto extends GetLicenseInfoDto {
@ -36,3 +33,10 @@ export class GetShareLicense extends GetLicenseInfoDto {
@ApiProperty() @ApiProperty()
ownerAddress: string; ownerAddress: string;
} }
export class LicensePayoutDto extends RequestLicenseDto {}
export class SetPayoutContractDto extends RequestLicenseDto {
@IsString()
@ApiProperty()
payoutContract: string;
}

View File

@ -5,9 +5,10 @@ import { BaseContractService } from '../../base/base-contract.service';
import { import {
DeployLicenseDto, DeployLicenseDto,
GetLicenseInfoDto, GetLicenseInfoDto,
GetLicenseResponseDto,
GetShareLicense, GetShareLicense,
LicensePayoutDto,
RequestLicenseDto, RequestLicenseDto,
SetPayoutContractDto,
} from './license.dto'; } from './license.dto';
import { MultiSigWalletService } from '../multi-sig/multi-sig.service'; import { MultiSigWalletService } from '../multi-sig/multi-sig.service';
import { ProviderService } from '../../base/provider/provider.service'; import { ProviderService } from '../../base/provider/provider.service';
@ -44,18 +45,16 @@ export class LicenseService extends BaseContractService {
const contract = new ethers.Contract(contractAddress, abi, signer); const contract = new ethers.Contract(contractAddress, abi, signer);
const answer: bigint = await contract.request(); const answer: bigint = await contract.totalPayoutInUSD();
console.log('=>(license.service.ts:45) answer', answer); console.log('=>(license.service.ts:45) answer', answer);
return answer.toString(); return answer.toString();
} }
async deploy(dto: DeployLicenseDto) { async deploy(dto: DeployLicenseDto) {
console.log('=>(license.service.ts:53) dto', dto); const { multiSigWallet, shares, owners } = dto;
const { multiSigWallet, shares, owners, payrollAddress } = dto; const { bytecode } = await hre.artifacts.readArtifact(
const { abi, bytecode } = await hre.artifacts.readArtifact(
'StreamingRightsManagement', 'StreamingRightsManagement',
); );
const signer = await this.providerService.getSigner();
const abiCoder = ethers.AbiCoder.defaultAbiCoder(); const abiCoder = ethers.AbiCoder.defaultAbiCoder();
@ -68,7 +67,6 @@ export class LicenseService extends BaseContractService {
'address', 'address',
'address[]', 'address[]',
'uint[]', 'uint[]',
'address',
], ],
[ [
CHAINLINK.AMOY.CHAINLINK_TOKEN, CHAINLINK.AMOY.CHAINLINK_TOKEN,
@ -78,7 +76,6 @@ export class LicenseService extends BaseContractService {
multiSigWallet, multiSigWallet,
owners, owners,
shares, shares,
payrollAddress,
], ],
); );
const fullBytecode = bytecode + abiEncodedConstructorArguments.substring(2); const fullBytecode = bytecode + abiEncodedConstructorArguments.substring(2);
@ -145,4 +142,36 @@ export class LicenseService extends BaseContractService {
return answer; return answer;
} }
async payout(dto: LicensePayoutDto) {
const { multiSigWallet, contractAddress } = dto;
const ISubmitMultiSig = new ethers.Interface(['function payout()']);
const data = ISubmitMultiSig.encodeFunctionData('payout');
return await this.multiSigService.submitTransaction({
contractAddress: multiSigWallet,
destination: contractAddress,
value: '0',
data,
});
}
async setPayoutContract(dto: SetPayoutContractDto) {
const { multiSigWallet, contractAddress, payoutContract } = dto;
const ISubmitMultiSig = new ethers.Interface([
'function setPayoutContract(address payable)',
]);
const data = ISubmitMultiSig.encodeFunctionData('setPayoutContract', [
payoutContract,
]);
return await this.multiSigService.submitTransaction({
contractAddress: multiSigWallet,
destination: contractAddress,
value: '0',
data,
});
}
} }

View File

@ -4,7 +4,7 @@ import { IsNumber, IsString } from 'class-validator';
export class SalariesDeployDto { export class SalariesDeployDto {
@ApiProperty() @ApiProperty()
@IsString() @IsString()
multiSigWallet: string; authorizedWallet: string;
} }
export class SetSalaryDto { export class SetSalaryDto {

View File

@ -29,7 +29,7 @@ export class SalariesService extends BaseContractService {
const salaryContract = new ethers.ContractFactory(abi, bytecode, signer); const salaryContract = new ethers.ContractFactory(abi, bytecode, signer);
const myContract = await salaryContract.deploy( const myContract = await salaryContract.deploy(
dto.multiSigWallet, dto.authorizedWallet,
CHAINLINK.AMOY.AGGREGATOR_ADDRESS.USDT_ETH, CHAINLINK.AMOY.AGGREGATOR_ADDRESS.USDT_ETH,
); );
await myContract.waitForDeployment(); await myContract.waitForDeployment();

View File

@ -1,10 +1,118 @@
// //SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
import "@chainlink/contracts/src/v0.8/ChainlinkClient.sol";
import "@chainlink/contracts/src/v0.8/shared/access/ConfirmedOwner.sol";
/** /**
* Request testnet LINK and ETH here: https://faucets.chain.link/
* Find information on LINK Token Contracts and get the latest ETH and LINK faucets here: https://docs.chain.link/docs/link-token-contracts/
*/
License /**
sender * THIS IS AN EXAMPLE CONTRACT THAT USES UN-AUDITED CODE.
receiver */
contract LinkWellBoolConsumerContractExample is ChainlinkClient, ConfirmedOwner {
using Chainlink for Chainlink.Request;
*/ address private oracleAddress;
bytes32 private jobId;
uint256 private fee;
constructor(
address _chainLinkToken,
address _oracleAddress,
string memory _jobId,
uint _fee,
address _multiSigAddress,
address[] memory _owners,
uint[] memory _shares
) ConfirmedOwner(_multiSigAddress) {
_setChainlinkToken(_chainLinkToken);
setOracleAddress(_oracleAddress);
setJobId(_jobId);
setFeeInHundredthsOfLink(_fee);
multisigWallet = _multiSigAddress;
}
constructor() ConfirmedOwner(msg.sender) {
_setChainlinkToken(0x0Fd9e8d3aF1aaee056EB9e802c3A762a667b1904);
setOracleAddress(0xd36c6B1777c7f3Db1B3201bDD87081A9045B7b46);
setJobId("43309009a154495cb2ed794233e6ff56");
setFeeInHundredthsOfLink(0); // 0 LINK
}
// Send a request to the Chainlink oracle
function request() public {
Chainlink.Request memory req = _buildOperatorRequest(jobId, this.fulfill.selector);
// DEFINE THE REQUEST PARAMETERS (example)
req._add('method', 'POST');
req._add('url', 'https://httpbin.org/post');
req._add('headers', '["accept", "application/json", "set-cookie", "sid=14A52"]');
req._add('body', '{"data":[{"coin":"BTC","isActive":false},{"coin":"ETH","isActive":false},{"coin":"LINK","isActive":true}]}');
req._add('contact', ''); // PLEASE ENTER YOUR CONTACT INFO. this allows us to notify you in the event of any emergencies related to your request (ie, bugs, downtime, etc.). example values: 'derek_linkwellnodes.io' (Discord handle) OR 'derek@linkwellnodes.io' OR '+1-617-545-4721'
// The following curl command simulates the above request parameters:
// curl 'https://httpbin.org/post' --request 'POST' --header 'content-type: application/json' --header 'set-cookie: sid=14A52' --data '{"data":[{"coin":"BTC","isActive":false},{"coin":"ETH","isActive":false},{"coin":"LINK","isActive":true}]}'
// PROCESS THE RESULT (example)
req._add('path', 'json,data,2,isActive');
// Send the request to the Chainlink oracle
_sendOperatorRequest(req, fee);
}
bool public response;
// Receive the result from the Chainlink oracle
event RequestFulfilled(bytes32 indexed requestId);
function fulfill(bytes32 requestId, bool data) public recordChainlinkFulfillment(requestId) {
// Process the oracle response
// emit RequestFulfilled(requestId); // (optional) emits this event in the on-chain transaction logs, allowing Web3 applications to listen for this transaction
response = data; // example value: true
}
// Update oracle address
function setOracleAddress(address _oracleAddress) public onlyOwner {
oracleAddress = _oracleAddress;
_setChainlinkOracle(_oracleAddress);
}
function getOracleAddress() public view onlyOwner returns (address) {
return oracleAddress;
}
// Update jobId
function setJobId(string memory _jobId) public onlyOwner {
jobId = bytes32(bytes(_jobId));
}
function getJobId() public view onlyOwner returns (string memory) {
return string(abi.encodePacked(jobId));
}
// Update fees
function setFeeInJuels(uint256 _feeInJuels) public onlyOwner {
fee = _feeInJuels;
}
function setFeeInHundredthsOfLink(uint256 _feeInHundredthsOfLink) public onlyOwner {
setFeeInJuels((_feeInHundredthsOfLink * LINK_DIVISIBILITY) / 100);
}
function getFeeInHundredthsOfLink() public view onlyOwner returns (uint256) {
return (fee * 100) / LINK_DIVISIBILITY;
}
function withdrawLink() public onlyOwner {
LinkTokenInterface link = LinkTokenInterface(_chainlinkTokenAddress());
require(
link.transfer(msg.sender, link.balanceOf(address(this))),
"Unable to transfer"
);
}
}

View File

@ -4,14 +4,7 @@ pragma solidity ^0.8.17;
import "@chainlink/contracts/src/v0.8/ChainlinkClient.sol"; import "@chainlink/contracts/src/v0.8/ChainlinkClient.sol";
import "@chainlink/contracts/src/v0.8/shared/access/ConfirmedOwner.sol"; import "@chainlink/contracts/src/v0.8/shared/access/ConfirmedOwner.sol";
import "./Payroll.sol"; import "./Payroll.sol";
/**
* Request testnet LINK and ETH here: https://faucets.chain.link/
* Find information on LINK Token Contracts and get the latest ETH and LINK faucets here: https://docs.chain.link/docs/link-token-contracts/
*/
/**
* THIS IS AN EXAMPLE CONTRACT THAT USES UN-AUDITED CODE.
*/
contract StreamingRightsManagement is ChainlinkClient, ConfirmedOwner { contract StreamingRightsManagement is ChainlinkClient, ConfirmedOwner {
using Chainlink for Chainlink.Request; using Chainlink for Chainlink.Request;
@ -33,8 +26,7 @@ contract StreamingRightsManagement is ChainlinkClient, ConfirmedOwner {
uint _fee, uint _fee,
address _multiSigAddress, address _multiSigAddress,
address[] memory _owners, address[] memory _owners,
uint[] memory _shares, uint[] memory _shares
address payable _payoutAddress
) ConfirmedOwner(_multiSigAddress) { ) ConfirmedOwner(_multiSigAddress) {
_setChainlinkToken(_chainLinkToken); _setChainlinkToken(_chainLinkToken);
@ -47,7 +39,6 @@ contract StreamingRightsManagement is ChainlinkClient, ConfirmedOwner {
multisigWallet = _multiSigAddress; multisigWallet = _multiSigAddress;
payoutContract = Payroll(_payoutAddress);
require(_owners.length == _shares.length, "Owners and shares length mismatch"); require(_owners.length == _shares.length, "Owners and shares length mismatch");
@ -68,10 +59,20 @@ contract StreamingRightsManagement is ChainlinkClient, ConfirmedOwner {
//update share //update share
//change payout address //change payout address
// //
modifier hasValidPayoutContract() {
require(address(payoutContract) != address(0), "payoutContract not initialized");
_;
}
function getShare(address owner) public view returns(uint){ function getShare(address owner) public view returns(uint){
return ownerShare[owner]; return ownerShare[owner];
} }
function setPayoutContract(address payable _payoutAddress) public onlyOwner {
require(_payoutAddress != address(0), "Invalid address: zero address not allowed");
payoutContract = Payroll(_payoutAddress);
}
// Send a request to the Chainlink oracle // Send a request to the Chainlink oracle
function request() external onlyOwner{ function request() external onlyOwner{
@ -90,6 +91,7 @@ contract StreamingRightsManagement is ChainlinkClient, ConfirmedOwner {
// PROCESS THE RESULT (example) // PROCESS THE RESULT (example)
req._add('path', 'ETH,USD'); req._add('path', 'ETH,USD');
req._addInt('multiplier', 10 ** 18);
// Send the request to the Chainlink oracle // Send the request to the Chainlink oracle
_sendOperatorRequest(req, fee); _sendOperatorRequest(req, fee);
} }
@ -102,15 +104,16 @@ contract StreamingRightsManagement is ChainlinkClient, ConfirmedOwner {
function fulfill(bytes32 requestId, uint256 data) public recordChainlinkFulfillment(requestId) { function fulfill(bytes32 requestId, uint256 data) public recordChainlinkFulfillment(requestId) {
// Process the oracle response // Process the oracle response
// emit RequestFulfilled(requestId); // (optional) emits this event in the on-chain transaction logs, allowing Web3 applications to listen for this transaction // emit RequestFulfilled(requestId); // (optional) emits this event in the on-chain transaction logs, allowing Web3 applications to listen for this transaction
totalPayoutInUSD = data / 100; // example value: 1875870000000000000000 (1875.87 before "multiplier" is applied) totalPayoutInUSD = data / 1e18 / 100; // example value: 1875870000000000000000 (1875.87 before "multiplier" is applied)
} }
function payout() external onlyOwner { function payout() external onlyOwner hasValidPayoutContract{
// using arrays to reduce gas
uint[] memory shares;
for(uint i=0; i< owners.length; i++){ // using arrays to reduce gas
shares[i] = ownerShare[owners[i]]; uint[] memory shares = new uint[](owners.length);
for(uint i=0; i< owners.length; i++){
shares[i] = ownerShare[owners[i]] * totalPayoutInUSD / 100;
} }
payoutContract.oneTimePayout(owners, shares); payoutContract.oneTimePayout(owners, shares);
} }

View File

@ -0,0 +1,57 @@
import { StreamingRightsManagement } from '../../../typechain';
import { CHAINLINK } from '../../config/chainlink.config';
const { expect } = require('chai');
const { ethers } = require('hardhat');
describe('StreamingRightsManagement', function () {
let streamingRightsManagement: StreamingRightsManagement,
payContract,
owner,
addr1,
addr2;
const shares = [25, 25, 50];
beforeEach(async function () {
[owner, addr1, addr2] = await ethers.getSigners();
const Payroll = await ethers.getContractFactory('Payroll');
payContract = await Payroll.deploy(owner.address, owner.address); // assume an oracle price feed address
const StreamingRightsManagement = await ethers.getContractFactory(
'StreamingRightsManagement',
);
streamingRightsManagement = await StreamingRightsManagement.deploy(
CHAINLINK.AMOY.CHAINLINK_TOKEN, // Chainlink Token address
CHAINLINK.AMOY.ORACLE_ADDRESS, // Oracle address
CHAINLINK.AMOY.JOB_IDS.UINT,
0,
owner.address,
[owner.address, addr1.address, addr2.address],
shares,
);
});
describe('Initialization', function () {
it('should set owners and shares correctly', async function () {
expect(await streamingRightsManagement.getShare(owner.address)).to.equal(
25,
);
expect(await streamingRightsManagement.getShare(addr1.address)).to.equal(
25,
);
expect(await streamingRightsManagement.getShare(addr2.address)).to.equal(
50,
);
});
});
describe('Payout Functionality', function () {
it('should successfully call payout', async function () {
await streamingRightsManagement.setPayoutContract(payContract.address);
await expect(streamingRightsManagement.payout()).to.not.be.reverted;
});
});
// More tests as needed for other functions
});