文件 1 的 14:AlternativeTokenHelper.sol
pragma solidity 0.8.2;
import "../security/Security.sol";
import "../utils/IPancakeRouter01.sol";
abstract contract AlternativeTokenHelper is Security {
SwapRouter public swapRouter;
event SetRouter(address indexed newSwapRouter);
function setRouter(address router) onlyOwner external {
swapRouter = SwapRouter(router);
emit SetRouter(router);
}
function evaluateAlternativeAmount(uint mainAmount, address mainToken, address alternativeToken) internal view returns (uint) {
address[] memory path = new address[](2);
path[0] = mainToken;
path[1] = alternativeToken;
return swapRouter.getAmountsOut(mainAmount, path)[0];
}
}
文件 2 的 14:CompanyVault.sol
pragma solidity 0.8.2;
import "../security/Security.sol";
import "../utils/IERC20.sol";
abstract contract CompanyVault is Security {
SecurityDTOs.ChangeAlterToken public changeAlterToken;
IERC20 internal _IERC20token;
IERC20 internal _AlternativeIERC20token;
mapping(address => uint) private feeBalance;
bool private _alternativeTokenEnabled;
uint private _timestampExpirationDelay;
event ChangeAlterToken(address indexed newAlterToken);
event ChangeTimestampDelay(uint timestampExpirationDelay);
constructor (address mainToken) {
_IERC20token = IERC20(mainToken);
_timestampExpirationDelay = 2 * 60 * 60;
}
function setTimestampExpirationDelay(uint timestampExpirationDelay) external onlyOwner {
_timestampExpirationDelay = timestampExpirationDelay;
emit ChangeTimestampDelay(timestampExpirationDelay);
}
function changeAlternativeTokenStart(address alternativeToken) external onlyOwner {
require(address(getMainIERC20Token()) != alternativeToken, "CompanyVault: main token and alter token can't be the same");
uint votingCode = startVoting("CHANGE_ALTER_TOKEN");
changeAlterToken = SecurityDTOs.ChangeAlterToken(
alternativeToken,
block.timestamp,
votingCode
);
}
function acquireNewAlternativeToken() external onlyOwner {
pass(changeAlterToken.votingCode);
_AlternativeIERC20token = IERC20(changeAlterToken.newAlterToken);
emit ChangeAlterToken(changeAlterToken.newAlterToken);
}
function getTimestampExpirationDelay() public view returns (uint) {
return _timestampExpirationDelay;
}
function enableAlternativeToken(bool enable) external onlyOwner {
_alternativeTokenEnabled = enable;
}
function isAlternativeTokenEnabled() public view returns (bool) {
return _alternativeTokenEnabled;
}
function getMainIERC20Token() public view returns (IERC20) {
return _IERC20token;
}
function getAlternativeIERC20Token() public view returns (IERC20) {
return _AlternativeIERC20token;
}
function getCompanyFeeBalance(address token) public view returns (uint) {
return feeBalance[token];
}
function increaseFee(uint amount, address token) internal {
feeBalance[token] += amount;
}
function decreaseFee(uint amount, address token) internal {
feeBalance[token] -= amount;
}
}
文件 3 的 14:Context.sol
pragma solidity 0.8.2;
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
}
文件 4 的 14:CustomDTOs.sol
pragma solidity 0.8.2;
library CustomDTOs {
struct CustomBet {
uint id;
string eventId;
bool hidden;
uint lockTime;
uint expirationTime;
string targetValue;
bool targetSide;
uint coefficient;
string finalValue;
bool targetSideWon;
}
struct CustomMatchingInfo {
mapping(uint => JoinCustomBetClient) leftSide;
uint leftLength;
uint leftLastId;
mapping(uint => JoinCustomBetClient) rightSide;
uint rightLength;
uint rightLastId;
uint leftFree;
uint rightFree;
uint leftLocked;
uint rightLocked;
}
struct JoinCustomBetClientList {
mapping(uint => JoinCustomBetClientRef) joinListRefs;
uint length;
}
struct JoinCustomBetClientRef {
bool side;
uint id;
}
struct JoinCustomBetClient {
uint id;
address client;
uint freeAmount;
uint lockedAmount;
bool targetSide;
uint joinRefId;
}
struct CreateCustomRequest {
string eventId;
bool hidden;
uint lockTime;
uint expirationTime;
string targetValue;
bool targetSide;
uint coefficient;
}
struct JoinCustomRequest {
bool side;
uint amount;
}
}
文件 5 的 14:CustomProcessor.sol
pragma solidity 0.8.2;
import "./CustomDTOs.sol";
abstract contract CustomProcessor {
function processRefundingCustomBet(CustomDTOs.CustomMatchingInfo storage info, CustomDTOs.JoinCustomBetClientList storage clientList) internal returns (uint) {
uint resultAmount;
for (uint i = 0; i < clientList.length; ++i) {
CustomDTOs.JoinCustomBetClient storage joinClient = extractCustomJoinBetClientByRef(info, clientList.joinListRefs[i]);
resultAmount += joinClient.freeAmount;
resultAmount += joinClient.lockedAmount;
joinClient.freeAmount = 0;
joinClient.lockedAmount = 0;
}
return resultAmount;
}
function takePrize(CustomDTOs.CustomBet storage bet, CustomDTOs.CustomMatchingInfo storage info, CustomDTOs.JoinCustomBetClientList storage clientList) internal returns (uint, uint) {
uint resultAmount;
uint freeAmount;
for (uint i = 0; i < clientList.length; ++i) {
CustomDTOs.JoinCustomBetClient storage joinClient = extractCustomJoinBetClientByRef(info, clientList.joinListRefs[i]);
if (joinClient.targetSide) {
if (bet.targetSideWon) {
resultAmount += applyCoefficient(joinClient.lockedAmount, bet.coefficient, true);
joinClient.lockedAmount = 0;
}
} else {
if (!bet.targetSideWon) {
resultAmount += applyCoefficient(joinClient.lockedAmount, bet.coefficient, false);
joinClient.lockedAmount = 0;
}
}
freeAmount += joinClient.freeAmount;
joinClient.freeAmount = 0;
}
return (resultAmount, freeAmount);
}
function evaluatePrize(CustomDTOs.CustomBet storage bet, CustomDTOs.CustomMatchingInfo storage info, CustomDTOs.JoinCustomBetClientList storage clientList) internal view returns (uint) {
uint resultAmount;
for (uint i = 0; i < clientList.length; ++i) {
CustomDTOs.JoinCustomBetClient storage joinClient = extractCustomJoinBetClientByRef(info, clientList.joinListRefs[i]);
if (joinClient.targetSide) {
if (bet.targetSideWon) {
resultAmount += applyCoefficient(joinClient.lockedAmount, bet.coefficient, true);
}
} else {
if (!bet.targetSideWon) {
resultAmount += applyCoefficient(joinClient.lockedAmount, bet.coefficient, false);
}
}
resultAmount += joinClient.freeAmount;
}
return resultAmount;
}
function cancelCustomBet(CustomDTOs.CustomMatchingInfo storage info, CustomDTOs.JoinCustomBetClient storage joinClient) internal returns (uint) {
uint freeAmount = joinClient.freeAmount;
if (joinClient.targetSide) {
info.leftFree -= freeAmount;
} else {
info.rightFree -= freeAmount;
}
joinClient.freeAmount = 0;
return freeAmount;
}
function joinCustomBet(CustomDTOs.CustomBet storage bet, CustomDTOs.CustomMatchingInfo storage info, CustomDTOs.JoinCustomBetClient memory joinCustomRequestBet) internal returns (CustomDTOs.JoinCustomBetClient storage, uint) {
if (joinCustomRequestBet.targetSide) {
processLeft(info, joinCustomRequestBet, bet.coefficient);
return (info.leftSide[info.leftLength - 1], info.leftLength - 1);
} else {
processRight(info, joinCustomRequestBet, bet.coefficient);
return (info.rightSide[info.rightLength - 1], info.rightLength - 1);
}
}
function processLeft(CustomDTOs.CustomMatchingInfo storage info, CustomDTOs.JoinCustomBetClient memory joinRequest, uint coefficient) private {
joinRequest.id = info.leftLength;
info.leftSide[info.leftLength++] = joinRequest;
CustomDTOs.JoinCustomBetClient storage joinRequestStored = info.leftSide[info.leftLength - 1];
if (info.leftFree > 0) {
} else {
info.rightLastId = mapToOtherSide(info.rightSide, info, info.rightLastId, joinRequestStored, coefficient, true);
}
info.leftFree += joinRequestStored.freeAmount;
info.leftLocked += joinRequestStored.lockedAmount;
info.rightFree -= applyPureCoefficientMapping(joinRequestStored.lockedAmount, coefficient, true);
info.rightLocked += applyPureCoefficientMapping(joinRequestStored.lockedAmount, coefficient, true);
}
function processRight(CustomDTOs.CustomMatchingInfo storage info, CustomDTOs.JoinCustomBetClient memory joinRequest, uint coefficient) private {
joinRequest.id = info.rightLength;
info.rightSide[info.rightLength++] = joinRequest;
CustomDTOs.JoinCustomBetClient storage joinRequestStored = info.rightSide[info.rightLength - 1];
if (info.rightFree > 0) {
} else {
info.leftLastId = mapToOtherSide(info.leftSide, info, info.leftLastId, joinRequestStored, coefficient, false);
}
info.rightFree += joinRequestStored.freeAmount;
info.rightLocked += joinRequestStored.lockedAmount;
info.leftFree -= applyPureCoefficientMapping(joinRequestStored.lockedAmount, coefficient, false);
info.leftLocked += applyPureCoefficientMapping(joinRequestStored.lockedAmount, coefficient, false);
}
function mapToOtherSide(mapping(uint => CustomDTOs.JoinCustomBetClient) storage otherSide,
CustomDTOs.CustomMatchingInfo storage info,
uint otherLastId, CustomDTOs.JoinCustomBetClient storage joinRequest,
uint coefficient, bool direct) private returns (uint) {
if (otherSide[otherLastId].client == address(0)) {
return otherLastId;
}
if (otherSide[otherLastId].freeAmount == 0) {
return mapToOtherSide(otherSide, info, otherLastId + 1, joinRequest, coefficient, direct);
}
uint freeAmountWithCoefficient = applyPureCoefficientMapping(joinRequest.freeAmount, coefficient, direct);
if (otherSide[otherLastId].freeAmount >= freeAmountWithCoefficient) {
otherSide[otherLastId].freeAmount -= freeAmountWithCoefficient;
otherSide[otherLastId].lockedAmount += freeAmountWithCoefficient;
joinRequest.lockedAmount += joinRequest.freeAmount;
joinRequest.freeAmount = 0;
return otherLastId;
}
uint otherFreeAmount = applyPureCoefficientMapping(otherSide[otherLastId].freeAmount, coefficient, !direct);
joinRequest.lockedAmount += otherFreeAmount;
joinRequest.freeAmount -= otherFreeAmount;
otherSide[otherLastId].lockedAmount += otherSide[otherLastId].freeAmount;
otherSide[otherLastId].freeAmount = 0;
return mapToOtherSide(otherSide, info, otherLastId + 1, joinRequest, coefficient, direct);
}
function extractCustomJoinBetClientByRef(CustomDTOs.CustomMatchingInfo storage info, CustomDTOs.JoinCustomBetClientRef storage ref) internal view returns (CustomDTOs.JoinCustomBetClient storage) {
if (ref.side) {
return info.leftSide[ref.id];
} else {
return info.rightSide[ref.id];
}
}
uint private constant coefficientDecimals = 10 ** 9;
function applyPureCoefficientMapping(uint amount, uint coefficient, bool direct) private pure returns (uint) {
if (amount == 0) {
return 0;
}
return applyCoefficient(amount, coefficient, direct) - amount;
}
function applyCoefficient(uint amount, uint coefficient, bool direct) private pure returns (uint) {
if (amount == 0) {
return 0;
}
if (direct) {
return (amount * coefficient) / coefficientDecimals;
} else {
return (amount * ((coefficientDecimals ** 2) / (coefficient - coefficientDecimals) + coefficientDecimals)) / coefficientDecimals;
}
}
}
文件 6 的 14:FeeConfiguration.sol
pragma solidity 0.8.2;
import "../security/Security.sol";
abstract contract FeeConfiguration is Security {
uint private _companyFee;
uint private _alternativeFee;
event CompanyFeeChanged(uint previousCompanyFee, uint newCompanyFee);
event CompanyAlterFeeChanged(uint previousAlternativeFee, uint newAlternativeFee);
function setCompanyFee(uint companyFee) external onlyOwner {
require(companyFee <= 2 * 10 ** 5);
emit CompanyFeeChanged(_companyFee, companyFee);
_companyFee = companyFee;
}
function setAlternativeFeeFee(uint alternativeFee) external onlyOwner {
require(alternativeFee <= 2 * 10 ** 5);
emit CompanyAlterFeeChanged(_alternativeFee, alternativeFee);
_alternativeFee = alternativeFee;
}
function getCompanyFee() external view returns (uint) {
return _companyFee;
}
function getAlternativeFee() external view returns (uint) {
return _alternativeFee;
}
function applyCompanyFee(uint amount) internal view returns (uint) {
return (amount * _companyFee) / 10 ** 6;
}
function applyAlternativeFee(uint amount) internal view returns (uint) {
return (amount * _alternativeFee) / 10 ** 6;
}
}
文件 7 的 14:IERC20.sol
pragma solidity 0.8.2;
interface IERC20 {
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address recipient, uint256 amount) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 amount) external returns (bool);
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
}
文件 8 的 14:IPancakeRouter01.sol
pragma solidity 0.8.2;
interface SwapRouter {
function getAmountsOut(uint amountIn, address[] calldata path) external view returns (uint[] memory amounts);
}
文件 9 的 14:Ownable.sol
pragma solidity 0.8.2;
import "../utils/Context.sol";
import "./SecurityDTOs.sol";
abstract contract Ownable is Context {
mapping(address => bool) public owners;
address private _company;
uint public totalOwners;
event CompanyTransferred(address indexed previousCompany, address indexed newCompany);
event AddOwner(address indexed newOwner);
event RemoveOwner(address indexed ownerToRemove);
modifier onlyOwner() {
require(owners[_msgSender()], "Security: caller is not the owner");
_;
}
function removeOwner(address ownerToRemove) internal {
require(owners[ownerToRemove], "Security: now owner");
owners[ownerToRemove] = false;
totalOwners--;
emit RemoveOwner(ownerToRemove);
}
function addOwner(address newOwner) internal {
require(newOwner != address(0), "Security: new owner is the zero address");
require(!owners[newOwner], "Security: already owner");
owners[newOwner] = true;
totalOwners++;
emit AddOwner(newOwner);
}
function company() public view virtual returns (address) {
return _company;
}
modifier onlyCompany() {
require(company() == _msgSender(), "Security: caller is not the company");
_;
}
function transferCompany(address newCompany) internal {
require(newCompany != address(0), "Security: new company is the zero address");
emit CompanyTransferred(_company, newCompany);
_company = newCompany;
}
}
文件 10 的 14:P2PCustomBetProvider.sol
pragma solidity 0.8.2;
import "../../processing/TokenProcessing.sol";
import "./CustomProcessor.sol";
contract P2PCustomBetProvider is TokenProcessing, CustomProcessor {
mapping(uint => CustomDTOs.CustomBet) private customBets;
mapping(uint => CustomDTOs.CustomMatchingInfo) private matchingInfo;
mapping(address => mapping(uint => CustomDTOs.JoinCustomBetClientList)) private clientInfo;
mapping(address => uint[]) private clientBets;
mapping(address => uint) public clientBetsLength;
uint public customBetIdCounter;
constructor(address mainToken, address[] memory owners) TokenProcessing(mainToken) {
for (uint i = 0; i < owners.length; i++) {
addOwner(owners[i]);
}
}
function getClientBets(address client, uint offset, uint size) external view returns (uint[] memory) {
uint resultSize = size;
for (uint i = offset; i < offset + size; i++) {
if (clientBets[client].length <= i) {
resultSize = i - offset;
break;
}
}
uint[] memory result = new uint[](resultSize);
for (uint i = offset; i < offset + resultSize; i++) {
result[i - offset] = clientBets[client][i];
}
return result;
}
function getCustomBet(uint betId) external view returns (CustomDTOs.CustomBet memory, uint, uint, uint, uint) {
CustomDTOs.CustomMatchingInfo storage info = matchingInfo[betId];
return (customBets[betId], info.leftFree, info.leftLocked, info.rightFree, info.rightLocked);
}
function getCustomClientJoins(address client, uint betId) external view returns (CustomDTOs.JoinCustomBetClient[] memory) {
CustomDTOs.JoinCustomBetClient[] memory clientList = new CustomDTOs.JoinCustomBetClient[](clientInfo[client][betId].length);
for (uint i = 0; i < clientInfo[client][betId].length; i++) {
clientList[i] = extractCustomJoinBetClientByRef(matchingInfo[betId], clientInfo[client][betId].joinListRefs[i]);
}
return clientList;
}
function closeCustomBet(uint betId, string calldata finalValue, bool targetSideWon) external onlyCompany {
require(keccak256(abi.encodePacked(finalValue)) != keccak256(abi.encodePacked("")), "P2PCustomBetProvider: close error - custom bet can't be closed with empty value");
CustomDTOs.CustomBet storage customBet = customBets[betId];
require(customBet.expirationTime < block.timestamp, "P2PCustomBetProvider: close error - expiration error");
require(customBet.expirationTime + getTimestampExpirationDelay() > block.timestamp, "P2PCustomBetProvider: close error - expiration error with delay");
require(keccak256(abi.encodePacked(customBet.finalValue)) == keccak256(abi.encodePacked("")), "P2PCustomBetProvider: close error - bet already closed");
customBet.finalValue = finalValue;
customBet.targetSideWon = targetSideWon;
emit CustomBetClosed(
betId,
finalValue,
targetSideWon
);
}
function refundCustomBet(uint betId, address client) external {
CustomDTOs.CustomBet storage customBet = customBets[betId];
require(keccak256(abi.encodePacked(customBet.finalValue)) == keccak256(abi.encodePacked("")), "P2PCustomBetProvider: refund - custom haven't to be open");
require(customBet.expirationTime + getTimestampExpirationDelay() < block.timestamp, "P2PCustomBetProvider: refund - expiration error");
uint mainTokenToRefund = processRefundingCustomBet(matchingInfo[betId], clientInfo[client][betId]);
require(mainTokenToRefund > 0, "P2PCustomBetProvider: refund - nothing");
withdrawalMainToken(client, mainTokenToRefund);
emit CustomBetRefunded(
betId,
client,
mainTokenToRefund
);
}
function takeCustomPrize(uint betId, address client, bool useAlterFee) external {
CustomDTOs.CustomBet storage customBet = customBets[betId];
require(keccak256(abi.encodePacked(customBet.finalValue)) != keccak256(abi.encodePacked("")), "P2PCustomBetProvider: take prize - custom bet wasn't closed");
(uint wonAmount, uint freeAmount) = takePrize(customBet, matchingInfo[betId], clientInfo[client][betId]);
require(wonAmount + freeAmount > 0, "P2PCustomBetProvider: take prize - nothing");
uint wonAmountAfterFee;
if (wonAmount > 0) {
wonAmountAfterFee = takeFeeFromAmount(msg.sender, wonAmount, useAlterFee);
} else {
wonAmountAfterFee = 0;
}
withdrawalMainToken(client, wonAmountAfterFee + freeAmount);
emit CustomPrizeTaken(
betId,
client,
wonAmountAfterFee + freeAmount,
useAlterFee
);
}
function getCustomWonAmount(uint betId, address client) public view returns (uint) {
CustomDTOs.CustomBet storage customBet = customBets[betId];
if (keccak256(abi.encodePacked(customBet.targetValue)) == keccak256(abi.encodePacked(""))) {
return 0;
}
return evaluatePrize(customBet, matchingInfo[betId], clientInfo[client][betId]);
}
function createCustomBet(CustomDTOs.CreateCustomRequest calldata createRequest, CustomDTOs.JoinCustomRequest calldata joinRequest) external returns (uint) {
require(createRequest.lockTime >= block.timestamp + 60 * 3, "P2PCustomBetProvider: create - lock time test");
require(createRequest.expirationTime >= createRequest.lockTime + 60 * 3, "P2PCustomBetProvider: create - expirationTime time");
uint betId = customBetIdCounter++;
customBets[betId] = CustomDTOs.CustomBet(
betId,
createRequest.eventId,
createRequest.hidden,
createRequest.lockTime,
createRequest.expirationTime,
createRequest.targetValue,
createRequest.targetSide,
createRequest.coefficient,
"",
false
);
emit CustomBetCreated(
betId,
createRequest.eventId,
createRequest.hidden,
createRequest.lockTime,
createRequest.expirationTime,
createRequest.targetValue,
createRequest.targetSide,
createRequest.coefficient,
msg.sender
);
joinCustomBet(betId, joinRequest);
return betId;
}
function cancelCustomJoin(uint betId, uint joinIdRef) external {
CustomDTOs.JoinCustomBetClient storage clientJoin = extractCustomJoinBetClientByRef(matchingInfo[betId], clientInfo[msg.sender][betId].joinListRefs[joinIdRef]);
require(clientJoin.freeAmount != 0, "P2PCustomBetProvider: cancel - free amount empty");
require(clientJoin.client == msg.sender, "P2PCustomBetProvider: cancel - not owner");
uint mainTokenToRefund = cancelCustomBet(matchingInfo[betId], clientJoin);
withdrawalMainToken(clientJoin.client, mainTokenToRefund);
emit CustomBetCancelled(
betId,
clientJoin.client,
joinIdRef,
mainTokenToRefund
);
}
function joinCustomBet(uint betId, CustomDTOs.JoinCustomRequest calldata joinRequest) public {
require(customBets[betId].lockTime >= block.timestamp, "P2PCustomBetProvider: cancel - lock time");
clientBets[msg.sender].push(betId);
clientBetsLength[msg.sender]++;
CustomDTOs.CustomBet storage bet = customBets[betId];
CustomDTOs.JoinCustomBetClientList storage clientBetList = clientInfo[msg.sender][betId];
DepositedValue memory depositedValue = deposit(msg.sender, joinRequest.amount);
CustomDTOs.JoinCustomBetClient memory joinBetClient = CustomDTOs.JoinCustomBetClient(
0,
msg.sender,
depositedValue.mainAmount,
0,
joinRequest.side,
clientBetList.length
);
(CustomDTOs.JoinCustomBetClient storage storedJoinBetClient, uint sidePointer) = joinCustomBet(bet, matchingInfo[betId], joinBetClient);
clientBetList.joinListRefs[clientBetList.length++] = CustomDTOs.JoinCustomBetClientRef(joinBetClient.targetSide, sidePointer);
emit CustomBetJoined(
joinRequest.side,
joinRequest.amount,
msg.sender,
betId,
storedJoinBetClient.id,
clientBetList.length - 1
);
}
event CustomBetCreated(
uint id,
string eventId,
bool hidden,
uint lockTime,
uint expirationTime,
string targetValue,
bool targetSide,
uint coefficient,
address indexed creator
);
event CustomBetJoined(
bool side,
uint mainAmount,
address indexed client,
uint betId,
uint joinId,
uint joinIdRef
);
event CustomBetCancelled(
uint betId,
address indexed client,
uint joinIdRef,
uint mainTokenRefunded
);
event CustomBetClosed(
uint betId,
string finalValue,
bool targetSideWon
);
event CustomBetRefunded(
uint betId,
address indexed client,
uint mainTokenRefunded
);
event CustomPrizeTaken(
uint betId,
address indexed client,
uint amount,
bool useAlterFee
);
}
文件 11 的 14:Security.sol
pragma solidity 0.8.2;
import "./Voting.sol";
abstract contract Security is Voting {
SecurityDTOs.AddOwner public addOwnerVoting;
SecurityDTOs.TransferCompany public transferCompanyVoting;
SecurityDTOs.RemoveOwner public removeOwnerVoting;
SecurityDTOs.TakeFee public takeFeeVoting;
function ownerAddStart(address newOwner) external onlyOwner {
require(!owners[newOwner], "Security: already owner");
uint votingCode = startVoting("ADD_OWNER");
addOwnerVoting = SecurityDTOs.AddOwner(
newOwner,
block.timestamp,
votingCode
);
}
function acquireNewOwner() external onlyOwner {
pass(addOwnerVoting.votingCode);
addOwner(addOwnerVoting.newOwner);
}
function transferCompanyStart(address newCompany) public virtual onlyOwner {
require(newCompany != address(0), "Security: new company is the zero address");
uint votingCode = startVoting("TRANSFER_COMPANY");
transferCompanyVoting = SecurityDTOs.TransferCompany(
newCompany,
block.timestamp,
votingCode
);
}
function acquireTransferCompany() external onlyOwner {
pass(transferCompanyVoting.votingCode);
transferCompany(transferCompanyVoting.newCompanyAddress);
}
function ownerToRemoveStart(address ownerToRemove) external onlyOwner {
require(owners[ownerToRemove], "Security: is not owner");
uint votingCode = startVoting("REMOVE_OWNER");
removeOwnerVoting = SecurityDTOs.RemoveOwner(
ownerToRemove,
block.timestamp,
votingCode
);
}
function acquireOwnerToRemove() external onlyOwner {
pass(removeOwnerVoting.votingCode);
removeOwner(removeOwnerVoting.ownerToRemove);
}
}
文件 12 的 14:SecurityDTOs.sol
pragma solidity 0.8.2;
library SecurityDTOs {
struct ChangeAlterToken {
address newAlterToken;
uint createdDate;
uint votingCode;
}
struct AddOwner {
address newOwner;
uint createdDate;
uint votingCode;
}
struct RemoveOwner {
address ownerToRemove;
uint createdDate;
uint votingCode;
}
struct TransferCompany {
address newCompanyAddress;
uint createdDate;
uint votingCode;
}
struct TakeFee {
uint amount;
address targetAddress;
bool isAlternative;
uint createdDate;
uint votingCode;
}
struct VotingInfo {
address initiator;
uint currentNumberOfVotesPositive;
uint currentNumberOfVotesNegative;
uint startedDate;
string votingCode;
}
}
文件 13 的 14:TokenProcessing.sol
pragma solidity 0.8.2;
import "../security/Ownable.sol";
import "./CompanyVault.sol";
import "./AlternativeTokenHelper.sol";
import "./FeeConfiguration.sol";
abstract contract TokenProcessing is CompanyVault, AlternativeTokenHelper, FeeConfiguration {
event FeeTaken(uint amount, address indexed targetAddress, bool isAlternative);
struct DepositedValue {
uint mainAmount;
}
constructor(address mainToken) CompanyVault(mainToken) {}
function deposit(address sender, uint amount) internal returns (DepositedValue memory) {
require(amount > 0, "TokenProcessing - deposit zero amount");
depositToken(getMainIERC20Token(), sender, amount);
return DepositedValue(amount);
}
function withdrawalMainToken(address recipient, uint amount) internal {
require(recipient == _msgSender(), "Caller is not recipient");
bool result = getMainIERC20Token().transfer(recipient, amount);
require(result, "TokenProcessing: withdrawal token failed");
}
function takeFeeFromAmount(address winner, uint amount, bool useAlternativeFee) internal returns (uint) {
if (useAlternativeFee) {
require(isAlternativeTokenEnabled(), "TokenProcessing: alternative token disabled");
uint alternativeFeePart = applyAlternativeFee(amount);
uint feeInAlternativeToken = evaluateAlternativeAmount(alternativeFeePart, address(getMainIERC20Token()), address(getAlternativeIERC20Token()));
depositToken(getAlternativeIERC20Token(), winner, feeInAlternativeToken);
increaseFee(feeInAlternativeToken, address(getAlternativeIERC20Token()));
return amount;
} else {
uint feePart = applyCompanyFee(amount);
increaseFee(feePart, address(getMainIERC20Token()));
return amount - feePart;
}
}
function depositToken(IERC20 token, address sender, uint amount) internal {
require(token.allowance(sender, address(this)) >= amount, "TokenProcessing: depositMainToken, not enough funds to deposit token");
bool result = token.transferFrom(sender, address(this), amount);
require(result, "TokenProcessing: depositMainToken, transfer from failed");
}
function takeFeeStart(uint amount, address targetAddress, bool isAlternative) external onlyOwner {
if (isAlternative) {
require(amount <= getCompanyFeeBalance(address(getAlternativeIERC20Token())), "CompanyVault: take fee amount exeeds alter token balance");
} else {
require(amount <= getCompanyFeeBalance(address(getMainIERC20Token())), "CompanyVault: take fee amount exeeds token balance");
}
uint votingCode = startVoting("TAKE_FEE");
takeFeeVoting = SecurityDTOs.TakeFee(
amount,
targetAddress,
isAlternative,
block.timestamp,
votingCode
);
}
function acquireTakeFee() external onlyOwner {
pass(takeFeeVoting.votingCode);
IERC20 token;
if (takeFeeVoting.isAlternative) {
token = getAlternativeIERC20Token();
decreaseFee(takeFeeVoting.amount, address(getAlternativeIERC20Token()));
} else {
token = getMainIERC20Token();
decreaseFee(takeFeeVoting.amount, address(getMainIERC20Token()));
}
bool result = token.transfer(takeFeeVoting.targetAddress, takeFeeVoting.amount);
require(result, "TokenProcessing: take fee transfer failed");
emit FeeTaken(takeFeeVoting.amount, takeFeeVoting.targetAddress, takeFeeVoting.isAlternative);
}
}
文件 14 的 14:Voting.sol
pragma solidity 0.8.2;
import "./Ownable.sol";
abstract contract Voting is Ownable {
event VotingStarted(string code, uint votingNumber, address indexed initiator);
event VotingResult(string code, uint votingNumber, bool passed);
bool public votingActive;
SecurityDTOs.VotingInfo public votingInfo;
uint private votingNumber;
mapping(uint => mapping(address => bool)) public voted;
function startVoting(string memory votingCode) internal returns (uint) {
require(!votingActive, "Voting: there is active voting already");
require(totalOwners >= 3, "Voting: not enough owners for starting new vote");
votingInfo = SecurityDTOs.VotingInfo(
_msgSender(),
0,
0,
block.timestamp,
votingCode
);
votingActive = true;
votingNumber++;
votePositive();
emit VotingStarted(
votingCode,
votingNumber,
_msgSender()
);
return votingNumber;
}
function pass(uint toCheckVotingNumber) internal {
require(votingActive, "Voting: there is no active voting");
require(toCheckVotingNumber == votingNumber, "Voting: old voting found");
require(votingInfo.currentNumberOfVotesPositive > totalOwners / 2, "Voting: not enough positive votes");
require(votingInfo.startedDate + 60 * 60 * 72 < block.timestamp || votingInfo.currentNumberOfVotesPositive == totalOwners, "Voting: 72 hours have not yet passed");
votingActive = false;
emit VotingResult(
votingInfo.votingCode,
votingNumber,
true
);
}
function close() external onlyOwner {
require(votingActive, "Voting: there is no active voting");
require(votingInfo.startedDate + 144 * 60 * 60 < block.timestamp || votingInfo.currentNumberOfVotesNegative > totalOwners / 2, "Voting: condition close error");
votingActive = false;
emit VotingResult(
votingInfo.votingCode,
votingNumber,
false
);
}
function votePositive() public onlyOwner {
vote();
votingInfo.currentNumberOfVotesPositive++;
}
function voteNegative() external onlyOwner {
vote();
votingInfo.currentNumberOfVotesNegative++;
}
function vote() private {
require(votingActive, "Voting: there is no active voting");
require(!voted[votingNumber][_msgSender()], "Voting: already voted");
voted[votingNumber][_msgSender()] = true;
}
}
{
"compilationTarget": {
"contracts/p2pbet/bets/custom/P2PCustomBetProvider.sol": "P2PCustomBetProvider"
},
"evmVersion": "istanbul",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 1000
},
"remappings": []
}
[{"inputs":[{"internalType":"address","name":"mainToken","type":"address"},{"internalType":"address[]","name":"owners","type":"address[]"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"AddOwner","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"newAlterToken","type":"address"}],"name":"ChangeAlterToken","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"timestampExpirationDelay","type":"uint256"}],"name":"ChangeTimestampDelay","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"previousAlternativeFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newAlternativeFee","type":"uint256"}],"name":"CompanyAlterFeeChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"previousCompanyFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newCompanyFee","type":"uint256"}],"name":"CompanyFeeChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousCompany","type":"address"},{"indexed":true,"internalType":"address","name":"newCompany","type":"address"}],"name":"CompanyTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"betId","type":"uint256"},{"indexed":true,"internalType":"address","name":"client","type":"address"},{"indexed":false,"internalType":"uint256","name":"joinIdRef","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"mainTokenRefunded","type":"uint256"}],"name":"CustomBetCancelled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"betId","type":"uint256"},{"indexed":false,"internalType":"string","name":"finalValue","type":"string"},{"indexed":false,"internalType":"bool","name":"targetSideWon","type":"bool"}],"name":"CustomBetClosed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"string","name":"eventId","type":"string"},{"indexed":false,"internalType":"bool","name":"hidden","type":"bool"},{"indexed":false,"internalType":"uint256","name":"lockTime","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"expirationTime","type":"uint256"},{"indexed":false,"internalType":"string","name":"targetValue","type":"string"},{"indexed":false,"internalType":"bool","name":"targetSide","type":"bool"},{"indexed":false,"internalType":"uint256","name":"coefficient","type":"uint256"},{"indexed":true,"internalType":"address","name":"creator","type":"address"}],"name":"CustomBetCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"side","type":"bool"},{"indexed":false,"internalType":"uint256","name":"mainAmount","type":"uint256"},{"indexed":true,"internalType":"address","name":"client","type":"address"},{"indexed":false,"internalType":"uint256","name":"betId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"joinId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"joinIdRef","type":"uint256"}],"name":"CustomBetJoined","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"betId","type":"uint256"},{"indexed":true,"internalType":"address","name":"client","type":"address"},{"indexed":false,"internalType":"uint256","name":"mainTokenRefunded","type":"uint256"}],"name":"CustomBetRefunded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"betId","type":"uint256"},{"indexed":true,"internalType":"address","name":"client","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"bool","name":"useAlterFee","type":"bool"}],"name":"CustomPrizeTaken","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":true,"internalType":"address","name":"targetAddress","type":"address"},{"indexed":false,"internalType":"bool","name":"isAlternative","type":"bool"}],"name":"FeeTaken","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"ownerToRemove","type":"address"}],"name":"RemoveOwner","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"newSwapRouter","type":"address"}],"name":"SetRouter","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"code","type":"string"},{"indexed":false,"internalType":"uint256","name":"votingNumber","type":"uint256"},{"indexed":false,"internalType":"bool","name":"passed","type":"bool"}],"name":"VotingResult","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"code","type":"string"},{"indexed":false,"internalType":"uint256","name":"votingNumber","type":"uint256"},{"indexed":true,"internalType":"address","name":"initiator","type":"address"}],"name":"VotingStarted","type":"event"},{"inputs":[],"name":"acquireNewAlternativeToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"acquireNewOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"acquireOwnerToRemove","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"acquireTakeFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"acquireTransferCompany","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"addOwnerVoting","outputs":[{"internalType":"address","name":"newOwner","type":"address"},{"internalType":"uint256","name":"createdDate","type":"uint256"},{"internalType":"uint256","name":"votingCode","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"betId","type":"uint256"},{"internalType":"uint256","name":"joinIdRef","type":"uint256"}],"name":"cancelCustomJoin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"changeAlterToken","outputs":[{"internalType":"address","name":"newAlterToken","type":"address"},{"internalType":"uint256","name":"createdDate","type":"uint256"},{"internalType":"uint256","name":"votingCode","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"alternativeToken","type":"address"}],"name":"changeAlternativeTokenStart","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"clientBetsLength","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"close","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"betId","type":"uint256"},{"internalType":"string","name":"finalValue","type":"string"},{"internalType":"bool","name":"targetSideWon","type":"bool"}],"name":"closeCustomBet","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"company","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"string","name":"eventId","type":"string"},{"internalType":"bool","name":"hidden","type":"bool"},{"internalType":"uint256","name":"lockTime","type":"uint256"},{"internalType":"uint256","name":"expirationTime","type":"uint256"},{"internalType":"string","name":"targetValue","type":"string"},{"internalType":"bool","name":"targetSide","type":"bool"},{"internalType":"uint256","name":"coefficient","type":"uint256"}],"internalType":"struct CustomDTOs.CreateCustomRequest","name":"createRequest","type":"tuple"},{"components":[{"internalType":"bool","name":"side","type":"bool"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct CustomDTOs.JoinCustomRequest","name":"joinRequest","type":"tuple"}],"name":"createCustomBet","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"customBetIdCounter","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"enable","type":"bool"}],"name":"enableAlternativeToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getAlternativeFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getAlternativeIERC20Token","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"client","type":"address"},{"internalType":"uint256","name":"offset","type":"uint256"},{"internalType":"uint256","name":"size","type":"uint256"}],"name":"getClientBets","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCompanyFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"getCompanyFeeBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"betId","type":"uint256"}],"name":"getCustomBet","outputs":[{"components":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"string","name":"eventId","type":"string"},{"internalType":"bool","name":"hidden","type":"bool"},{"internalType":"uint256","name":"lockTime","type":"uint256"},{"internalType":"uint256","name":"expirationTime","type":"uint256"},{"internalType":"string","name":"targetValue","type":"string"},{"internalType":"bool","name":"targetSide","type":"bool"},{"internalType":"uint256","name":"coefficient","type":"uint256"},{"internalType":"string","name":"finalValue","type":"string"},{"internalType":"bool","name":"targetSideWon","type":"bool"}],"internalType":"struct CustomDTOs.CustomBet","name":"","type":"tuple"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"client","type":"address"},{"internalType":"uint256","name":"betId","type":"uint256"}],"name":"getCustomClientJoins","outputs":[{"components":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"address","name":"client","type":"address"},{"internalType":"uint256","name":"freeAmount","type":"uint256"},{"internalType":"uint256","name":"lockedAmount","type":"uint256"},{"internalType":"bool","name":"targetSide","type":"bool"},{"internalType":"uint256","name":"joinRefId","type":"uint256"}],"internalType":"struct CustomDTOs.JoinCustomBetClient[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"betId","type":"uint256"},{"internalType":"address","name":"client","type":"address"}],"name":"getCustomWonAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getMainIERC20Token","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTimestampExpirationDelay","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isAlternativeTokenEnabled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"betId","type":"uint256"},{"components":[{"internalType":"bool","name":"side","type":"bool"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct CustomDTOs.JoinCustomRequest","name":"joinRequest","type":"tuple"}],"name":"joinCustomBet","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"ownerAddStart","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"ownerToRemove","type":"address"}],"name":"ownerToRemoveStart","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"owners","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"betId","type":"uint256"},{"internalType":"address","name":"client","type":"address"}],"name":"refundCustomBet","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"removeOwnerVoting","outputs":[{"internalType":"address","name":"ownerToRemove","type":"address"},{"internalType":"uint256","name":"createdDate","type":"uint256"},{"internalType":"uint256","name":"votingCode","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"alternativeFee","type":"uint256"}],"name":"setAlternativeFeeFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"companyFee","type":"uint256"}],"name":"setCompanyFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"router","type":"address"}],"name":"setRouter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"timestampExpirationDelay","type":"uint256"}],"name":"setTimestampExpirationDelay","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"swapRouter","outputs":[{"internalType":"contract SwapRouter","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"betId","type":"uint256"},{"internalType":"address","name":"client","type":"address"},{"internalType":"bool","name":"useAlterFee","type":"bool"}],"name":"takeCustomPrize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"targetAddress","type":"address"},{"internalType":"bool","name":"isAlternative","type":"bool"}],"name":"takeFeeStart","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"takeFeeVoting","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"targetAddress","type":"address"},{"internalType":"bool","name":"isAlternative","type":"bool"},{"internalType":"uint256","name":"createdDate","type":"uint256"},{"internalType":"uint256","name":"votingCode","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalOwners","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newCompany","type":"address"}],"name":"transferCompanyStart","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"transferCompanyVoting","outputs":[{"internalType":"address","name":"newCompanyAddress","type":"address"},{"internalType":"uint256","name":"createdDate","type":"uint256"},{"internalType":"uint256","name":"votingCode","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"voteNegative","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"votePositive","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"voted","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"votingActive","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"votingInfo","outputs":[{"internalType":"address","name":"initiator","type":"address"},{"internalType":"uint256","name":"currentNumberOfVotesPositive","type":"uint256"},{"internalType":"uint256","name":"currentNumberOfVotesNegative","type":"uint256"},{"internalType":"uint256","name":"startedDate","type":"uint256"},{"internalType":"string","name":"votingCode","type":"string"}],"stateMutability":"view","type":"function"}]