From 40405c7559051ec9efa8df743801735601c49537 Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Thu, 4 May 2023 03:17:49 -0700 Subject: [PATCH 001/120] add predictoor template --- contracts/templates/PredictoorTemplate.sol | 1147 ++++++++++++++++++++ scripts/deploy-contracts.js | 47 +- 2 files changed, 1176 insertions(+), 18 deletions(-) create mode 100644 contracts/templates/PredictoorTemplate.sol diff --git a/contracts/templates/PredictoorTemplate.sol b/contracts/templates/PredictoorTemplate.sol new file mode 100644 index 000000000..391d12b5d --- /dev/null +++ b/contracts/templates/PredictoorTemplate.sol @@ -0,0 +1,1147 @@ +pragma solidity 0.8.12; +// Copyright BigchainDB GmbH and Ocean Protocol contributors +// SPDX-License-Identifier: (Apache-2.0 AND CC-BY-4.0) +// Code is Apache-2.0 and docs are CC-BY-4.0 + +import "../interfaces/IERC721Template.sol"; +import "../interfaces/IERC20Template.sol"; +import "../interfaces/IFactoryRouter.sol"; +import "../interfaces/IFixedRateExchange.sol"; +import "../interfaces/IDispenser.sol"; +import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; +import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol"; +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import "@openzeppelin/contracts/utils/math/SafeMath.sol"; +import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; +import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; +import "../utils/ERC20Roles.sol"; + +/** + * @title DatatokenTemplate + * + * @dev ERC20TemplateEnterprise is an ERC20 compliant token template + * Used by the factory contract as a bytecode reference to + * deploy new Datatokens. + * IMPORTANT CHANGES: + * - buyFromFreAndOrder function: one call to buy a DT from the minting capable FRE, startOrder and burn the DT + * - buyFromDispenserAndOrder function: one call to fetch a DT from the Dispenser, startOrder and burn the DT + * - creation of pools is not allowed + */ +contract ERC20TemplatePredictoor is + ERC20("test", "testSymbol"), + ERC20Roles, + ERC20Burnable, + ReentrancyGuard +{ + using SafeMath for uint256; + using SafeERC20 for IERC20; + string private _name; + string private _symbol; + uint256 private _cap; + uint8 private constant _decimals = 18; + bool private initialized = false; + address private _erc721Address; + address private paymentCollector; + address private publishMarketFeeAddress; + address private publishMarketFeeToken; + uint256 private publishMarketFeeAmount; + + uint256 public constant BASE = 1e18; + + // -------------------------- PREDICTOOR -------------------------- + struct Prediction { + bool predval; + uint256 stake; + address predictoor; + bool paid; + } + struct Subscription { + address user; + uint256 expires; + } + mapping(uint256 => mapping(address => Prediction)) predobjs; // id to prediction object + mapping(uint256 => uint256) predcounter; // block num to id counter + mapping(uint256 => uint256) agg_predvals_numer; + mapping(uint256 => uint256) agg_predvals_denom; + mapping(uint256 => bool) truevals; + mapping(uint256 => bool) truval_submitted; + mapping(uint256 => uint256) subscription_revenue_at_block; //income registred + mapping(address => Subscription) subscriptions; // valid subscription per user + uint256 blocks_per_epoch; + uint256 blocks_per_subscription; + uint256 truval_submit_timeout = 3; + address stake_token; + bool paused = false; + // -------------------------- PREDICTOOR -------------------------- + + // EIP 2612 SUPPORT + bytes32 public DOMAIN_SEPARATOR; + // keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"); + bytes32 public constant PERMIT_TYPEHASH = + 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9; + + mapping(address => uint256) public nonces; + address public router; + + struct fixedRate { + address contractAddress; + bytes32 id; + } + fixedRate[] fixedRateExchanges; + address[] dispensers; + + struct providerFee { + address providerFeeAddress; + address providerFeeToken; // address of the token + uint256 providerFeeAmount; // amount to be transfered to provider + uint8 v; // v of provider signed message + bytes32 r; // r of provider signed message + bytes32 s; // s of provider signed message + uint256 validUntil; //validity expresses in unix timestamp + bytes providerData; //data encoded by provider + } + + struct consumeMarketFee { + address consumeMarketFeeAddress; + address consumeMarketFeeToken; // address of the token marketplace wants to add fee on top + uint256 consumeMarketFeeAmount; // amount to be transfered to marketFeeCollector + } + + event OrderStarted( + address indexed consumer, + address payer, + uint256 amount, + uint256 serviceIndex, + uint256 timestamp, + address indexed publishMarketAddress, + uint256 blockNumber + ); + + event OrderReused( + bytes32 orderTxId, + address caller, + uint256 timestamp, + uint256 number + ); + + event OrderExecuted( + address indexed providerAddress, + address indexed consumerAddress, + bytes32 orderTxId, + bytes providerData, + bytes providerSignature, + bytes consumerData, + bytes consumerSignature, + uint256 timestamp, + uint256 blockNumber + ); + + // emited for every order + event PublishMarketFee( + address indexed PublishMarketFeeAddress, + address indexed PublishMarketFeeToken, + uint256 PublishMarketFeeAmount + ); + + // emited for every order + event ConsumeMarketFee( + address indexed consumeMarketFeeAddress, + address indexed consumeMarketFeeToken, + uint256 consumeMarketFeeAmount + ); + + event PublishMarketFeeChanged( + address caller, + address PublishMarketFeeAddress, + address PublishMarketFeeToken, + uint256 PublishMarketFeeAmount + ); + + event MinterProposed(address currentMinter, address newMinter); + + event MinterApproved(address currentMinter, address newMinter); + + event NewFixedRate( + bytes32 exchangeId, + address indexed owner, + address exchangeContract, + address indexed baseToken + ); + event NewDispenser(address dispenserContract); + + event NewPaymentCollector( + address indexed caller, + address indexed _newPaymentCollector, + uint256 timestamp, + uint256 blockNumber + ); + + modifier onlyNotInitialized() { + require( + !initialized, + "ERC20Template: token instance already initialized" + ); + _; + } + modifier onlyNFTOwner() { + require( + msg.sender == IERC721Template(_erc721Address).ownerOf(1), + "ERC20Template: not NFTOwner" + ); + _; + } + + modifier onlyPublishingMarketFeeAddress() { + require( + msg.sender == publishMarketFeeAddress, + "ERC20Template: not publishMarketFeeAddress" + ); + _; + } + + modifier onlyERC20Deployer() { + require( + IERC721Template(_erc721Address) + .getPermissions(msg.sender) + .deployERC20 || + IERC721Template(_erc721Address).ownerOf(1) == msg.sender, + "ERC20Template: NOT DEPLOYER ROLE" + ); + _; + } + + /** + * @dev initialize + * Called prior contract initialization (e.g creating new Datatoken instance) + * Calls private _initialize function. Only if contract is not initialized. + * @param strings_ refers to an array of strings + * [0] = name token + * [1] = symbol + * @param addresses_ refers to an array of addresses passed by user + * [0] = minter account who can mint datatokens (can have multiple minters) + * [1] = paymentCollector initial paymentCollector for this DT + * [2] = publishing Market Address + * [3] = publishing Market Fee Token + * [4] = predictoor stake token + * @param factoryAddresses_ refers to an array of addresses passed by the factory + * [0] = erc721Address + * [1] = router address + * + * @param uints_ refers to an array of uints + * [0] = cap_ the total ERC20 cap + * [1] = publishing Market Fee Amount + * [2] = s_per_block, + * [3] = s_per_epoch, + * [4] = s_per_subscription, + * @param bytes_ refers to an array of bytes + * Currently not used, usefull for future templates + */ + function initialize( + string[] calldata strings_, + address[] calldata addresses_, + address[] calldata factoryAddresses_, + uint256[] calldata uints_, + bytes[] calldata bytes_ + ) external onlyNotInitialized returns (bool) { + return + _initialize( + strings_, + addresses_, + factoryAddresses_, + uints_, + bytes_ + ); + } + + /** + * @dev _initialize + * Private function called on contract initialization. + * @param strings_ refers to an array of strings + * [0] = name token + * [1] = symbol + * @param addresses_ refers to an array of addresses passed by user + * [0] = minter account who can mint datatokens (can have multiple minters) + * [1] = paymentCollector initial paymentCollector for this DT + * [2] = publishing Market Address + * [3] = publishing Market Fee Token + * [4] = predictoor stake token + * @param factoryAddresses_ refers to an array of addresses passed by the factory + * [0] = erc721Address + * [1] = router address + * + * @param uints_ refers to an array of uints + * [0] = cap_ the total ERC20 cap + * [1] = publishing Market Fee + * [2] = s_per_block, + * [3] = s_per_epoch, + * [4] = s_per_subscription, + * param bytes_ refers to an array of bytes + * Currently not used, usefull for future templates + */ + function _initialize( + string[] memory strings_, + address[] memory addresses_, + address[] memory factoryAddresses_, + uint256[] memory uints_, + bytes[] memory + ) private returns (bool) { + address erc721Address = factoryAddresses_[0]; + router = factoryAddresses_[1]; + require( + erc721Address != address(0), + "ERC20Template: Invalid minter, zero address" + ); + + require( + router != address(0), + "ERC20Template: Invalid router, zero address" + ); + + require(uints_[0] != 0, "DatatokenTemplate: Invalid cap value"); + _cap = uints_[0]; + _name = strings_[0]; + _symbol = strings_[1]; + _erc721Address = erc721Address; + + initialized = true; + // add a default minter, similar to what happens with manager in the 721 contract + _addMinter(addresses_[0]); + // set payment collector to this contract, so we can get the $$$ + _setPaymentCollector(address(this)); + emit NewPaymentCollector( + msg.sender, + addresses_[1], + block.timestamp, + block.number + ); + + publishMarketFeeAddress = addresses_[2]; + publishMarketFeeToken = addresses_[3]; + publishMarketFeeAmount = uints_[1]; + emit PublishMarketFeeChanged( + msg.sender, + publishMarketFeeAddress, + publishMarketFeeToken, + publishMarketFeeAmount + ); + uint256 chainId; + assembly { + chainId := chainid() + } + DOMAIN_SEPARATOR = keccak256( + abi.encode( + keccak256( + "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" + ), + keccak256(bytes(_name)), + keccak256(bytes("1")), // version, could be any other value + chainId, + address(this) + ) + ); + + stake_token = addresses_[4]; + + require(uints_[4] % uints_[2] == 0, "must be divisible"); + require(uints_[3] % uints_[2] == 0, "must be divisible"); + + blocks_per_epoch = uints_[3] / uints_[2]; + blocks_per_subscription = uints_[4] / uints_[2]; + return initialized; + } + + /** + * @dev createFixedRate + * Creates a new FixedRateExchange setup. + * @param fixedPriceAddress fixedPriceAddress + * @param addresses array of addresses [baseToken,owner,marketFeeCollector] + * @param uints array of uints [baseTokenDecimals,datatokenDecimals, fixedRate, marketFee, withMint] + * @return exchangeId + */ + function createFixedRate( + address fixedPriceAddress, + address[] memory addresses, + uint256[] memory uints + ) external onlyERC20Deployer nonReentrant returns (bytes32 exchangeId) { + require( + stake_token == addresses[0], + "Cannot create FRE with baseToken!=stake_token" + ); + //force FRE allowedSwapper to this contract address. no one else can swap because we need to record the income + addresses[3] = address(this); + if (uints[4] > 0) _addMinter(fixedPriceAddress); + exchangeId = IFactoryRouter(router).deployFixedRate( + fixedPriceAddress, + addresses, + uints + ); + emit NewFixedRate( + exchangeId, + addresses[1], + fixedPriceAddress, + addresses[0] + ); + fixedRateExchanges.push(fixedRate(fixedPriceAddress, exchangeId)); + } + + /** + * @dev createDispenser + * Creates a new Dispenser + * @param _dispenser dispenser contract address + * @param maxTokens - max tokens to dispense + * @param maxBalance - max balance of requester. + * @param withMint - with MinterRole + * @param allowedSwapper allowed swappers + */ + function createDispenser( + address _dispenser, + uint256 maxTokens, + uint256 maxBalance, + bool withMint, + address allowedSwapper + ) external onlyERC20Deployer nonReentrant { + // add dispenser contract as minter if withMint == true + if (withMint) _addMinter(_dispenser); + dispensers.push(_dispenser); + emit NewDispenser(_dispenser); + IFactoryRouter(router).deployDispenser( + _dispenser, + address(this), + maxTokens, + maxBalance, + msg.sender, + allowedSwapper + ); + } + + /** + * @dev mint + * Only the minter address can call it. + * msg.value should be higher than zero and gt or eq minting fee + * @param account refers to an address that token is going to be minted to. + * @param value refers to amount of tokens that is going to be minted. + */ + function mint(address account, uint256 value) external { + require(permissions[msg.sender].minter, "ERC20Template: NOT MINTER"); + require( + totalSupply().add(value) <= _cap, + "DatatokenTemplate: cap exceeded" + ); + _mint(account, value); + } + + /** + * @dev startOrder + * called by payer or consumer prior ordering a service consume on a marketplace. + * Requires previous approval of consumeFeeToken and publishMarketFeeToken + * @param consumer is the consumer address (payer could be different address) + * @param serviceIndex service index in the metadata + * @param _providerFee provider fee + * @param _consumeMarketFee consume market fee + */ + function startOrder( + address consumer, + uint256 serviceIndex, + providerFee calldata _providerFee, + consumeMarketFee calldata _consumeMarketFee + ) public { + uint256 amount = 1e18; // we always pay 1 DT. No more, no less + require( + balanceOf(msg.sender) >= amount, + "Not enough datatokens to start Order" + ); + emit OrderStarted( + consumer, + msg.sender, + amount, + serviceIndex, + block.timestamp, + publishMarketFeeAddress, + block.number + ); + // publishMarketFee + // Requires approval for the publishMarketFeeToken of publishMarketFeeAmount + // skip fee if amount == 0 or feeToken == 0x0 address or feeAddress == 0x0 address + if ( + publishMarketFeeAmount > 0 && + publishMarketFeeToken != address(0) && + publishMarketFeeAddress != address(0) + ) { + _pullUnderlying( + publishMarketFeeToken, + msg.sender, + publishMarketFeeAddress, + publishMarketFeeAmount + ); + emit PublishMarketFee( + publishMarketFeeAddress, + publishMarketFeeToken, + publishMarketFeeAmount + ); + } + + // consumeMarketFee + // Requires approval for the FeeToken + // skip fee if amount == 0 or feeToken == 0x0 address or feeAddress == 0x0 address + if ( + _consumeMarketFee.consumeMarketFeeAmount > 0 && + _consumeMarketFee.consumeMarketFeeToken != address(0) && + _consumeMarketFee.consumeMarketFeeAddress != address(0) + ) { + _pullUnderlying( + _consumeMarketFee.consumeMarketFeeToken, + msg.sender, + _consumeMarketFee.consumeMarketFeeAddress, + _consumeMarketFee.consumeMarketFeeAmount + ); + emit ConsumeMarketFee( + _consumeMarketFee.consumeMarketFeeAddress, + _consumeMarketFee.consumeMarketFeeToken, + _consumeMarketFee.consumeMarketFeeAmount + ); + } + Subscription memory sub = Subscription( + consumer, + block.number + blocks_per_subscription + ); + subscriptions[consumer] = sub; + + burn(amount); + } + + /** + * @dev addMinter + * Only ERC20Deployer (at 721 level) can update. + * There can be multiple minters + * @param _minter new minter address + */ + + function addMinter(address _minter) external onlyERC20Deployer { + _addMinter(_minter); + } + + /** + * @dev removeMinter + * Only ERC20Deployer (at 721 level) can update. + * There can be multiple minters + * @param _minter minter address to remove + */ + + function removeMinter(address _minter) external onlyERC20Deployer { + _removeMinter(_minter); + } + + /** + * @dev addPaymentManager (can set who's going to collect fee when consuming orders) + * Only ERC20Deployer (at 721 level) can update. + * There can be multiple paymentCollectors + * @param _paymentManager new minter address + */ + + function addPaymentManager( + address _paymentManager + ) external onlyERC20Deployer { + _addPaymentManager(_paymentManager); + } + + /** + * @dev removePaymentManager + * Only ERC20Deployer (at 721 level) can update. + * There can be multiple paymentManagers + * @param _paymentManager _paymentManager address to remove + */ + + function removePaymentManager( + address _paymentManager + ) external onlyERC20Deployer { + _removePaymentManager(_paymentManager); + } + + /** + * @dev setData + * Only ERC20Deployer (at 721 level) can call it. + * This function allows to store data with a preset key (keccak256(ERC20Address)) into NFT 725 Store + * @param _value data to be set with this key + */ + + function setData(bytes calldata _value) external onlyERC20Deployer { + bytes32 key = keccak256(abi.encodePacked(address(this))); + IERC721Template(_erc721Address).setDataERC20(key, _value); + } + + /** + * @dev cleanPermissions() + * Only NFT Owner (at 721 level) can call it. + * This function allows to remove all minters, feeManagers and reset the paymentCollector + * + */ + + function cleanPermissions() external onlyNFTOwner { + _internalCleanPermissions(); + } + + /** + * @dev cleanFrom721() + * OnlyNFT(721) Contract can call it. + * This function allows to remove all minters, feeManagers and reset the paymentCollector + * This function is used when transferring an NFT to a new owner, + * so that permissions at ERC20level (minter,feeManager,paymentCollector) can be reset. + * + */ + function cleanFrom721() external { + require( + msg.sender == _erc721Address, + "ERC20Template: NOT 721 Contract" + ); + _internalCleanPermissions(); + } + + function _internalCleanPermissions() internal { + uint256 totalLen = fixedRateExchanges.length + dispensers.length; + uint256 curentLen = 0; + address[] memory previousMinters = new address[](totalLen); + // loop though fixedrates, empty and preserve the minter rols if exists + uint256 i; + for (i = 0; i < fixedRateExchanges.length; i++) { + IFixedRateExchange fre = IFixedRateExchange( + fixedRateExchanges[i].contractAddress + ); + ( + , + , + , + , + , + , + , + , + , + uint256 dtBalance, + uint256 btBalance, + bool withMint + ) = fre.getExchange(fixedRateExchanges[i].id); + if (btBalance > 0) + fre.collectBT(fixedRateExchanges[i].id, btBalance); + if (dtBalance > 0) + fre.collectDT(fixedRateExchanges[i].id, dtBalance); + // add it to the list of minters + if ( + isMinter(fixedRateExchanges[i].contractAddress) && + withMint == true + ) { + previousMinters[curentLen] = fixedRateExchanges[i] + .contractAddress; + curentLen++; + } + } + // loop though dispenser and preserve the minter rols if exists + for (i = 0; i < dispensers.length; i++) { + IDispenser(dispensers[i]).ownerWithdraw(address(this)); + if (isMinter(dispensers[i])) { + previousMinters[curentLen] = dispensers[i]; + curentLen++; + } + } + // clear all permisions + _cleanPermissions(); + // set collector to 0 + paymentCollector = address(0); + // add existing minter roles for fixedrate & dispensers + for (i = 0; i < curentLen; i++) { + _addMinter(previousMinters[i]); + } + } + + /** + * @dev setPaymentCollector + * Only feeManager can call it + * This function allows to set a newPaymentCollector (receives DT when consuming) + If not set the paymentCollector is the NFT Owner + * @param _newPaymentCollector new fee collector + */ + + function setPaymentCollector(address _newPaymentCollector) external { + // does nothing for this template, paymentCollector is always address(this) + } + + /** + * @dev _setPaymentCollector + * @param _newPaymentCollector new fee collector + */ + + function _setPaymentCollector(address _newPaymentCollector) internal { + paymentCollector = _newPaymentCollector; + } + + /** + * @dev getPublishingMarketFee + * Get publishingMarket Fee + * This function allows to get the current fee set by the publishing market + */ + function getPublishingMarketFee() + external + view + returns (address, address, uint256) + { + return ( + publishMarketFeeAddress, + publishMarketFeeToken, + publishMarketFeeAmount + ); + } + + /** + * @dev setPublishingMarketFee + * Only publishMarketFeeAddress can call it + * This function allows to set the fee required by the publisherMarket + * @param _publishMarketFeeAddress new _publishMarketFeeAddress + * @param _publishMarketFeeToken new _publishMarketFeeToken + * @param _publishMarketFeeAmount new fee amount + */ + function setPublishingMarketFee( + address _publishMarketFeeAddress, + address _publishMarketFeeToken, + uint256 _publishMarketFeeAmount + ) external onlyPublishingMarketFeeAddress { + require( + _publishMarketFeeAddress != address(0), + "Invalid _publishMarketFeeAddress address" + ); + require( + _publishMarketFeeToken != address(0), + "Invalid _publishMarketFeeToken address" + ); + publishMarketFeeAddress = _publishMarketFeeAddress; + publishMarketFeeToken = _publishMarketFeeToken; + publishMarketFeeAmount = _publishMarketFeeAmount; + emit PublishMarketFeeChanged( + msg.sender, + _publishMarketFeeAddress, + _publishMarketFeeToken, + _publishMarketFeeAmount + ); + } + + /** + * @dev getId + * Return template id in case we need different ABIs. + * If you construct your own template, please make sure to change the hardcoded value + */ + function getId() public pure returns (uint8) { + return 3; + } + + /** + * @dev name + * It returns the token name. + * @return Datatoken name. + */ + function name() public view override returns (string memory) { + return _name; + } + + /** + * @dev symbol + * It returns the token symbol. + * @return Datatoken symbol. + */ + function symbol() public view override returns (string memory) { + return _symbol; + } + + /** + * @dev getERC721Address + * It returns the parent ERC721 + * @return ERC721 address. + */ + function getERC721Address() public view returns (address) { + return _erc721Address; + } + + /** + * @dev decimals + * It returns the token decimals. + * how many supported decimal points + * @return Datatoken decimals. + */ + function decimals() public pure override returns (uint8) { + return _decimals; + } + + /** + * @dev cap + * it returns the capital. + * @return Datatoken cap. + */ + function cap() external view returns (uint256) { + return _cap; + } + + /** + * @dev isInitialized + * It checks whether the contract is initialized. + * @return true if the contract is initialized. + */ + + function isInitialized() external view returns (bool) { + return initialized; + } + + /** + * @dev getPaymentCollector + * It returns the current paymentCollector + * @return paymentCollector address + */ + + function getPaymentCollector() public view returns (address) { + return address(this); + } + + /** + * @dev fallback function + * this is a default fallback function in which receives + * the collected ether. + */ + fallback() external payable {} + + /** + * @dev receive function + * this is a default receive function in which receives + * the collected ether. + */ + receive() external payable {} + + /** + * @dev withdrawETH + * transfers all the accumlated ether the collector account + */ + function withdrawETH() external payable { + payable(getPaymentCollector()).transfer(address(this).balance); + } + + struct OrderParams { + address consumer; + uint256 serviceIndex; + providerFee _providerFee; + consumeMarketFee _consumeMarketFee; + } + struct FreParams { + address exchangeContract; + bytes32 exchangeId; + uint256 maxBaseTokenAmount; + uint256 swapMarketFee; + address marketFeeAddress; + } + + /** + * @dev buyFromFre + * Buys 1 DT from the FRE + */ + function buyFromFre(FreParams calldata _freParams) internal { + // get exchange info + IFixedRateExchange fre = IFixedRateExchange( + _freParams.exchangeContract + ); + (, address datatoken, , address baseToken, , , , , , , , ) = fre + .getExchange(_freParams.exchangeId); + require( + datatoken == address(this), + "This FixedRate is not providing this DT" + ); + // get token amounts needed + (uint256 baseTokenAmount, , , ) = fre.calcBaseInGivenOutDT( + _freParams.exchangeId, + 1e18, // we always take 1 DT + _freParams.swapMarketFee + ); + require( + baseTokenAmount <= _freParams.maxBaseTokenAmount, + "FixedRateExchange: Too many base tokens" + ); + + //transfer baseToken to us first + _pullUnderlying(baseToken, msg.sender, address(this), baseTokenAmount); + //approve FRE to spend baseTokens + IERC20(baseToken).safeIncreaseAllowance( + _freParams.exchangeContract, + baseTokenAmount + ); + //buy DT + fre.buyDT( + _freParams.exchangeId, + 1e18, // we always take 1 dt + baseTokenAmount, + _freParams.marketFeeAddress, + _freParams.swapMarketFee + ); + require( + balanceOf(address(this)) >= 1e18, + "Unable to buy DT from FixedRate" + ); + // collect the basetoken from fixedrate and sent it + (, , , , , , , , , , uint256 btBalance, ) = fre.getExchange( + _freParams.exchangeId + ); + if (btBalance > 0) { + //record income + add_revenue(block.number, btBalance); + fre.collectBT(_freParams.exchangeId, btBalance); + } + } + + /** + * @dev buyFromFreAndOrder + * Buys 1 DT from the FRE and then startsOrder, while burning that DT + */ + function buyFromFreAndOrder( + OrderParams calldata _orderParams, + FreParams calldata _freParams + ) external nonReentrant { + //first buy 1.0 DT + buyFromFre(_freParams); + //we need the following because startOrder expects msg.sender to have dt + _transfer(address(this), msg.sender, 1e18); + //startOrder and burn it + startOrder( + _orderParams.consumer, + _orderParams.serviceIndex, + _orderParams._providerFee, + _orderParams._consumeMarketFee + ); + } + + /** + * @dev buyFromDispenserAndOrder + * Gets DT from dispenser and then startsOrder, while burning that DT + */ + function buyFromDispenserAndOrder( + OrderParams calldata _orderParams, + address dispenserContract + ) external nonReentrant { + uint256 amount = 1e18; + //get DT + IDispenser(dispenserContract).dispense( + address(this), + amount, + msg.sender + ); + require( + balanceOf(address(msg.sender)) >= amount, + "Unable to get DT from Dispenser" + ); + //startOrder and burn it + startOrder( + _orderParams.consumer, + _orderParams.serviceIndex, + _orderParams._providerFee, + _orderParams._consumeMarketFee + ); + } + + /** + * @dev isERC20Deployer + * returns true if address has deployERC20 role + */ + function isERC20Deployer(address user) public view returns (bool) { + return ( + IERC721Template(_erc721Address).getPermissions(user).deployERC20 + ); + } + + /** + * @dev getFixedRates + * Returns the list of fixedRateExchanges created for this datatoken + */ + function getFixedRates() public view returns (fixedRate[] memory) { + return (fixedRateExchanges); + } + + /** + * @dev getDispensers + * Returns the list of dispensers created for this datatoken + */ + function getDispensers() public view returns (address[] memory) { + return (dispensers); + } + + function _pullUnderlying( + address erc20, + address from, + address to, + uint256 amount + ) internal { + uint256 balanceBefore = IERC20(erc20).balanceOf(to); + IERC20(erc20).safeTransferFrom(from, to, amount); + require( + IERC20(erc20).balanceOf(to) >= balanceBefore.add(amount), + "Transfer amount is too low" + ); + } + + // ------------ PREDICTOOR ------------ + function is_valid_subscription(address user) public view returns (bool) { + return subscriptions[user].expires <= block.number ? false : true; + } + + function epoch() public view returns (uint256) { + return block.number / blocks_per_epoch; + } + + function rail_blocknum_to_slot( + uint256 blocknum + ) public view returns (uint256) { + return (blocknum / blocks_per_epoch) * blocks_per_epoch; + } + + function blocknum_is_on_a_slot( + uint256 blocknum + ) public view returns (bool) { + // a slot == beginning/end of an epoch + return blocknum == rail_blocknum_to_slot(blocknum); + } + + function soonest_block_to_predict() public view returns (uint256) { + uint256 slotted_blocknum = rail_blocknum_to_slot(block.number); + + uint256 _blocknum; + if (slotted_blocknum == block.number) { + _blocknum = slotted_blocknum + blocks_per_epoch; + } else { + _blocknum = slotted_blocknum + 2 * blocks_per_epoch; + } + require(blocknum_is_on_a_slot(_blocknum), "blocknum must be on a slot"); + return _blocknum; + } + + function submitted_predval( + uint256 blocknum, + address predictoor + ) public view returns (bool) { + require(blocknum_is_on_a_slot(blocknum), "blocknum must be on a slot"); + return predobjs[blocknum][predictoor].predictoor != address(0); + } + + function get_agg_predval( + uint256 blocknum + ) public view returns (uint256, uint256) { + require(blocknum_is_on_a_slot(blocknum), "blocknum must be on a slot"); + require(is_valid_subscription(msg.sender), "Not valid subscription"); + return (agg_predvals_numer[blocknum], agg_predvals_denom[blocknum]); + } + + function get_subscription_revenue_at_block( + uint256 blocknum + ) public view returns (uint256) { + require(blocknum_is_on_a_slot(blocknum), "blocknum must be on a slot"); + return (subscription_revenue_at_block[blocknum]); + } + + function get_prediction( + uint256 blocknum, + address predictoor + ) public view returns (Prediction memory prediction) { + require(blocknum_is_on_a_slot(blocknum), "blocknum must be on a slot"); + if (msg.sender != predictoor) { + require(blocknum > soonest_block_to_predict(), "too early to view"); + } + //allow predictoors to see their own submissions + require( + is_valid_subscription(msg.sender) || msg.sender == predictoor, + "Not valid subscription" + ); + prediction = predobjs[blocknum][predictoor]; + } + + // ----------------------- MUTATING FUNCTIONS ----------------------- + + function submit_predval( + bool predval, + uint256 stake, + uint256 blocknum + ) external { + require(blocknum_is_on_a_slot(blocknum), "blocknum must be on a slot"); + require(blocknum > soonest_block_to_predict(), "too late to submit"); + require(!submitted_predval(blocknum, msg.sender), "already submitted"); + require(paused == false, "paused"); + + Prediction memory predobj = Prediction( + predval, + stake, + msg.sender, + false + ); + + predobjs[blocknum][msg.sender] = predobj; + + // safe transfer stake + IERC20(stake_token).safeTransferFrom(msg.sender, address(this), stake); + + // update agg_predvals + agg_predvals_numer[blocknum] += stake * (predval ? 1 : 0); + agg_predvals_denom[blocknum] += stake; + } + + function payout( + uint256 blocknum, + address predictoor_addr + ) external nonReentrant { + require(blocknum_is_on_a_slot(blocknum), "blocknum must be on a slot"); + Prediction memory predobj = get_prediction(blocknum, predictoor_addr); + require(predobj.paid == false, "already paid"); + + // if OPF hasn't submitted trueval in truval_submit_timeout days + // refund stake to predictoor and cancel round + if ( + block.number > + blocknum + blocks_per_epoch * truval_submit_timeout && + !truval_submitted[blocknum] + ) { + IERC20(stake_token).safeTransfer(predobj.predictoor, predobj.stake); + predobj.paid = true; + return; + } + + require(truval_submitted[blocknum], "trueval not submitted"); + require(truevals[blocknum] == predobj.predval, "wrong prediction"); + + uint256 swe = truevals[blocknum] + ? agg_predvals_numer[blocknum] + : agg_predvals_denom[blocknum] - agg_predvals_numer[blocknum]; + uint256 payout_amt = (predobj.stake * + agg_predvals_denom[blocknum] * + get_subscription_revenue_at_block(blocknum)) / swe; + + IERC20(stake_token).safeTransferFrom( + address(this), + predobj.predictoor, + payout_amt + ); + predobj.paid = true; + } + + // ----------------------- ADMIN FUNCTIONS ----------------------- + function pause_predictions() external onlyERC20Deployer { + paused = !paused; + } + + function submit_trueval( + uint256 blocknum, + bool trueval + ) external onlyERC20Deployer { + // TODO, is onlyERC20Deployer the right modifier? + require(blocknum_is_on_a_slot(blocknum), "blocknum must be on a slot"); + require(blocknum < soonest_block_to_predict(), "too early to submit"); + truevals[blocknum] = trueval; + truval_submitted[blocknum] = true; + } + + function add_revenue(uint256 blocknum, uint256 amount) internal { + blocknum = rail_blocknum_to_slot(blocknum); + // for loop and add revenue for blocks_per_epoch blocks + for (uint256 i = 0; i < blocks_per_subscription; i++) { + subscription_revenue_at_block[blocknum + blocks_per_epoch] += + amount / + blocks_per_subscription; + } + } +} diff --git a/scripts/deploy-contracts.js b/scripts/deploy-contracts.js index 437472ee9..32726d40b 100644 --- a/scripts/deploy-contracts.js +++ b/scripts/deploy-contracts.js @@ -401,17 +401,6 @@ async function main() { console.log("\tnpx hardhat verify --network " + networkName + " " + addresses.FixedPrice + " " + router.address) } if (sleepAmount > 0) await sleep(sleepAmount) - if (logging) console.info("Deploying StakingContract"); - const SSContract = await ethers.getContractFactory("SideStaking", owner); - let ssPool - if (options) ssPool = await SSContract.connect(owner).deploy(router.address, options); - else ssPool = await SSContract.connect(owner).deploy(router.address); - await ssPool.deployTransaction.wait(); - addresses.Staking = ssPool.address; - if (show_verify) { - console.log("\tRun the following to verify on etherscan"); - console.log("\tnpx hardhat verify --network " + networkName + " " + addresses.Staking + " " + router.address) - } addresses.ERC20Template = {}; if (sleepAmount > 0) await sleep(sleepAmount) if (logging) console.info("Deploying ERC20 Template"); @@ -438,6 +427,21 @@ async function main() { console.log("\tRun the following to verify on etherscan"); console.log("\tnpx hardhat verify --network " + networkName + " " + templateERC20Enterprise.address) } + + if (logging) console.info("Deploying ERC20 Enterprise Template"); + const ERC20PredictoorTemplate = await ethers.getContractFactory( + "ERC20TemplatePredictoor", + owner + ); + let templateERC20TemplatePredictoor + if (options) templateERC20TemplatePredictoor = await ERC20PredictoorTemplate.connect(owner).deploy(options); + else templateERC20TemplatePredictoor = await ERC20PredictoorTemplate.connect(owner).deploy(); + await templateERC20TemplatePredictoor.deployTransaction.wait(); + if (show_verify) { + console.log("\tRun the following to verify on etherscan"); + console.log("\tnpx hardhat verify --network " + networkName + " " + templateERC20TemplatePredictoor.address) + } + addresses.ERC721Template = {}; if (sleepAmount > 0) await sleep(sleepAmount) if (logging) console.info("Deploying ERC721 Template"); @@ -533,6 +537,20 @@ async function main() { addresses.ERC20Template[currentTokenCount.toString()] = templateERC20Enterprise.address; + if (logging) console.info("Adding ERC20TemplatePredictoor to ERC721Factory"); + if (options) templateadd = await factoryERC721.connect(owner).addTokenTemplate(templateERC20TemplatePredictoor.address, options); + else templateadd = await factoryERC721.connect(owner).addTokenTemplate(templateERC20TemplatePredictoor.address); + await templateadd.wait(); + if (sleepAmount > 0) await sleep(sleepAmount) + if (options) currentTokenCount = await factoryERC721.getCurrentTemplateCount(options); + else currentTokenCount = await factoryERC721.getCurrentTemplateCount(options); + + if (options) tokenTemplate = await factoryERC721.getTokenTemplate(currentTokenCount, options); + else tokenTemplate = await factoryERC721.getTokenTemplate(currentTokenCount); + + addresses.ERC20Template[currentTokenCount.toString()] = + templateERC20TemplatePredictoor.address; + // SET REQUIRED ADDRESS if (sleepAmount > 0) await sleep(sleepAmount) if (logging) console.info("Adding factoryERC721.address(" + factoryERC721.address + ") to router"); @@ -559,13 +577,6 @@ async function main() { if (sleepAmount > 0) await sleep(sleepAmount) - if (logging) console.info("Adding ssPool.address(" + ssPool.address + ") to router"); - let ssAddTx - if (options) ssAddTx = await router.connect(owner).addSSContract(ssPool.address, options); - else ssAddTx = await router.connect(owner).addSSContract(ssPool.address); - await ssAddTx.wait(); - if (sleepAmount > 0) await sleep(sleepAmount) - // add additional tokens for (const token of additionalApprovedTokens) { if (logging) console.info("Adding " + token + " as approved token"); From e4d4264a9c28f43172f246ab21554ef5823b5fcd Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Thu, 4 May 2023 03:45:29 -0700 Subject: [PATCH 002/120] sync ERC20TemplatePredictoor --- ...mplate.sol => ERC20TemplatePredictoor.sol} | 77 +++++++++++++------ 1 file changed, 53 insertions(+), 24 deletions(-) rename contracts/templates/{PredictoorTemplate.sol => ERC20TemplatePredictoor.sol} (95%) diff --git a/contracts/templates/PredictoorTemplate.sol b/contracts/templates/ERC20TemplatePredictoor.sol similarity index 95% rename from contracts/templates/PredictoorTemplate.sol rename to contracts/templates/ERC20TemplatePredictoor.sol index 391d12b5d..8653bc5d0 100644 --- a/contracts/templates/PredictoorTemplate.sol +++ b/contracts/templates/ERC20TemplatePredictoor.sol @@ -69,7 +69,7 @@ contract ERC20TemplatePredictoor is mapping(address => Subscription) subscriptions; // valid subscription per user uint256 blocks_per_epoch; uint256 blocks_per_subscription; - uint256 truval_submit_timeout = 3; + uint256 truval_submit_timeout_block = 3; address stake_token; bool paused = false; // -------------------------- PREDICTOOR -------------------------- @@ -210,6 +210,14 @@ contract ERC20TemplatePredictoor is _; } + modifier blocknumOnSlot(uint256 num) { + require( + blocknum_is_on_a_slot(num), + "Predictoor: blocknum must be on a slot" + ); + _; + } + /** * @dev initialize * Called prior contract initialization (e.g creating new Datatoken instance) @@ -341,12 +349,7 @@ contract ERC20TemplatePredictoor is ); stake_token = addresses_[4]; - - require(uints_[4] % uints_[2] == 0, "must be divisible"); - require(uints_[3] % uints_[2] == 0, "must be divisible"); - - blocks_per_epoch = uints_[3] / uints_[2]; - blocks_per_subscription = uints_[4] / uints_[2]; + _update_seconds(uints_[2], uints_[3], uints_[4], uints_[5]); return initialized; } @@ -1009,38 +1012,38 @@ contract ERC20TemplatePredictoor is } else { _blocknum = slotted_blocknum + 2 * blocks_per_epoch; } - require(blocknum_is_on_a_slot(_blocknum), "blocknum must be on a slot"); return _blocknum; } function submitted_predval( uint256 blocknum, address predictoor - ) public view returns (bool) { - require(blocknum_is_on_a_slot(blocknum), "blocknum must be on a slot"); + ) public view blocknumOnSlot(blocknum) returns (bool) { return predobjs[blocknum][predictoor].predictoor != address(0); } function get_agg_predval( uint256 blocknum - ) public view returns (uint256, uint256) { - require(blocknum_is_on_a_slot(blocknum), "blocknum must be on a slot"); + ) public view blocknumOnSlot(blocknum) returns (uint256, uint256) { require(is_valid_subscription(msg.sender), "Not valid subscription"); return (agg_predvals_numer[blocknum], agg_predvals_denom[blocknum]); } function get_subscription_revenue_at_block( uint256 blocknum - ) public view returns (uint256) { - require(blocknum_is_on_a_slot(blocknum), "blocknum must be on a slot"); + ) public view blocknumOnSlot(blocknum) returns (uint256) { return (subscription_revenue_at_block[blocknum]); } function get_prediction( uint256 blocknum, address predictoor - ) public view returns (Prediction memory prediction) { - require(blocknum_is_on_a_slot(blocknum), "blocknum must be on a slot"); + ) + public + view + blocknumOnSlot(blocknum) + returns (Prediction memory prediction) + { if (msg.sender != predictoor) { require(blocknum > soonest_block_to_predict(), "too early to view"); } @@ -1058,8 +1061,7 @@ contract ERC20TemplatePredictoor is bool predval, uint256 stake, uint256 blocknum - ) external { - require(blocknum_is_on_a_slot(blocknum), "blocknum must be on a slot"); + ) external blocknumOnSlot(blocknum) { require(blocknum > soonest_block_to_predict(), "too late to submit"); require(!submitted_predval(blocknum, msg.sender), "already submitted"); require(paused == false, "paused"); @@ -1084,16 +1086,14 @@ contract ERC20TemplatePredictoor is function payout( uint256 blocknum, address predictoor_addr - ) external nonReentrant { - require(blocknum_is_on_a_slot(blocknum), "blocknum must be on a slot"); + ) external blocknumOnSlot(blocknum) nonReentrant { Prediction memory predobj = get_prediction(blocknum, predictoor_addr); require(predobj.paid == false, "already paid"); // if OPF hasn't submitted trueval in truval_submit_timeout days // refund stake to predictoor and cancel round if ( - block.number > - blocknum + blocks_per_epoch * truval_submit_timeout && + block.number > blocknum + truval_submit_timeout_block && !truval_submitted[blocknum] ) { IERC20(stake_token).safeTransfer(predobj.predictoor, predobj.stake); @@ -1127,14 +1127,43 @@ contract ERC20TemplatePredictoor is function submit_trueval( uint256 blocknum, bool trueval - ) external onlyERC20Deployer { + ) external blocknumOnSlot(blocknum) onlyERC20Deployer { // TODO, is onlyERC20Deployer the right modifier? - require(blocknum_is_on_a_slot(blocknum), "blocknum must be on a slot"); require(blocknum < soonest_block_to_predict(), "too early to submit"); truevals[blocknum] = trueval; truval_submitted[blocknum] = true; } + function update_seconds( + uint256 s_per_block, + uint256 s_per_epoch, + uint256 s_per_subscription, + uint256 _truval_submit_timeout + ) external onlyERC20Deployer { + _update_seconds( + s_per_block, + s_per_epoch, + s_per_subscription, + _truval_submit_timeout + ); + } + + // ----------------------- INTERNAL FUNCTIONS ----------------------- + + function _update_seconds( + uint256 s_per_block, + uint256 s_per_epoch, + uint256 s_per_subscription, + uint256 _truval_submit_timeout + ) internal { + require(s_per_subscription % s_per_block == 0); + require(s_per_epoch % s_per_block == 0); + + blocks_per_epoch = s_per_epoch / s_per_block; + blocks_per_subscription = s_per_subscription / s_per_block; + truval_submit_timeout_block = _truval_submit_timeout / s_per_block; + } + function add_revenue(uint256 blocknum, uint256 amount) internal { blocknum = rail_blocknum_to_slot(blocknum); // for loop and add revenue for blocks_per_epoch blocks From a07c311233bcab5c551e8786627367656f23cb6a Mon Sep 17 00:00:00 2001 From: trizin <25263018+trizin@users.noreply.github.com> Date: Thu, 4 May 2023 20:54:41 +0300 Subject: [PATCH 003/120] Remove unused var --- contracts/templates/ERC20TemplatePredictoor.sol | 1 - 1 file changed, 1 deletion(-) diff --git a/contracts/templates/ERC20TemplatePredictoor.sol b/contracts/templates/ERC20TemplatePredictoor.sol index 8653bc5d0..8be9a5638 100644 --- a/contracts/templates/ERC20TemplatePredictoor.sol +++ b/contracts/templates/ERC20TemplatePredictoor.sol @@ -60,7 +60,6 @@ contract ERC20TemplatePredictoor is uint256 expires; } mapping(uint256 => mapping(address => Prediction)) predobjs; // id to prediction object - mapping(uint256 => uint256) predcounter; // block num to id counter mapping(uint256 => uint256) agg_predvals_numer; mapping(uint256 => uint256) agg_predvals_denom; mapping(uint256 => bool) truevals; From 956c3fd60446823f66944893313599778f2eea80 Mon Sep 17 00:00:00 2001 From: trizin <25263018+trizin@users.noreply.github.com> Date: Thu, 4 May 2023 20:55:22 +0300 Subject: [PATCH 004/120] Add a todo --- contracts/templates/ERC20TemplatePredictoor.sol | 1 + 1 file changed, 1 insertion(+) diff --git a/contracts/templates/ERC20TemplatePredictoor.sol b/contracts/templates/ERC20TemplatePredictoor.sol index 8be9a5638..af209aae0 100644 --- a/contracts/templates/ERC20TemplatePredictoor.sol +++ b/contracts/templates/ERC20TemplatePredictoor.sol @@ -1167,6 +1167,7 @@ contract ERC20TemplatePredictoor is blocknum = rail_blocknum_to_slot(blocknum); // for loop and add revenue for blocks_per_epoch blocks for (uint256 i = 0; i < blocks_per_subscription; i++) { + // TODO FIND A WAY TO ACHIEVE THIS WITHOUT A LOOP subscription_revenue_at_block[blocknum + blocks_per_epoch] += amount / blocks_per_subscription; From 3e68c8025d3e4eaae57c2d8b0d58ea858f485e29 Mon Sep 17 00:00:00 2001 From: trizin <25263018+trizin@users.noreply.github.com> Date: Fri, 5 May 2023 07:43:15 +0000 Subject: [PATCH 005/120] Arranging declarations --- contracts/templates/ERC20TemplatePredictoor.sol | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/contracts/templates/ERC20TemplatePredictoor.sol b/contracts/templates/ERC20TemplatePredictoor.sol index af209aae0..21ea490a6 100644 --- a/contracts/templates/ERC20TemplatePredictoor.sol +++ b/contracts/templates/ERC20TemplatePredictoor.sol @@ -67,9 +67,9 @@ contract ERC20TemplatePredictoor is mapping(uint256 => uint256) subscription_revenue_at_block; //income registred mapping(address => Subscription) subscriptions; // valid subscription per user uint256 blocks_per_epoch; + address stake_token; uint256 blocks_per_subscription; uint256 truval_submit_timeout_block = 3; - address stake_token; bool paused = false; // -------------------------- PREDICTOOR -------------------------- @@ -1158,7 +1158,10 @@ contract ERC20TemplatePredictoor is require(s_per_subscription % s_per_block == 0); require(s_per_epoch % s_per_block == 0); - blocks_per_epoch = s_per_epoch / s_per_block; + if (!initialized) { + blocks_per_epoch = s_per_epoch / s_per_block; // immutaable + } + blocks_per_subscription = s_per_subscription / s_per_block; truval_submit_timeout_block = _truval_submit_timeout / s_per_block; } From c257db85520fadfbf7a4fe8e4216c670bcca230d Mon Sep 17 00:00:00 2001 From: trizin <25263018+trizin@users.noreply.github.com> Date: Fri, 5 May 2023 07:49:48 +0000 Subject: [PATCH 006/120] Add ERC20TemplatePredictoor tests --- .../ERC20TemplatePredictoor.test.js | 1138 +++++++++++++++++ 1 file changed, 1138 insertions(+) create mode 100644 test/unit/datatokens/ERC20TemplatePredictoor.test.js diff --git a/test/unit/datatokens/ERC20TemplatePredictoor.test.js b/test/unit/datatokens/ERC20TemplatePredictoor.test.js new file mode 100644 index 000000000..852e7587b --- /dev/null +++ b/test/unit/datatokens/ERC20TemplatePredictoor.test.js @@ -0,0 +1,1138 @@ +/* eslint-env mocha */ +/* global artifacts, contract, web3, it, beforeEach */ +const hre = require("hardhat"); +const { assert, expect } = require("chai"); +const { expectRevert, expectEvent, BN } = require("@openzeppelin/test-helpers"); +const { getEventFromTx } = require("../../helpers/utils") +const { impersonate } = require("../../helpers/impersonate"); +const constants = require("../../helpers/constants"); +const { web3 } = require("@openzeppelin/test-helpers/src/setup"); +const { keccak256 } = require("@ethersproject/keccak256"); +const ethers = hre.ethers; +const { ecsign, zeroAddress } = require("ethereumjs-util"); + + +const getDomainSeparator = (name, tokenAddress, chainId) => { + return keccak256( + ethers.utils.defaultAbiCoder.encode( + ["bytes32", "bytes32", "bytes32", "uint256", "address"], + [ + keccak256( + ethers.utils.toUtf8Bytes( + "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" + ) + ), + keccak256(ethers.utils.toUtf8Bytes(name)), + keccak256(ethers.utils.toUtf8Bytes("1")), + chainId, + tokenAddress, + ] + ) + ); +}; +const PERMIT_TYPEHASH = keccak256( + ethers.utils.toUtf8Bytes( + "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)" + ) +); + +const getApprovalDigest = async ( + token, + owner, + spender, + value, + nonce, + deadline, + chainId +) => { + const name = await token.name(); + const DOMAIN_SEPARATOR = getDomainSeparator(name, token.address, chainId); + return keccak256( + ethers.utils.solidityPack( + ["bytes1", "bytes1", "bytes32", "bytes32"], + [ + "0x19", + "0x01", + DOMAIN_SEPARATOR, + keccak256( + ethers.utils.defaultAbiCoder.encode( + ["bytes32", "address", "address", "uint256", "uint256", "uint256"], + [PERMIT_TYPEHASH, owner, spender, value, nonce, deadline] + ) + ), + ] + ) + ); +}; +const provider = new ethers.providers.JsonRpcProvider(); + +async function signMessage(message, address) { + let signedMessage = await web3.eth.sign(message, address) + signedMessage = signedMessage.substr(2) // remove 0x + const r = '0x' + signedMessage.slice(0, 64) + const s = '0x' + signedMessage.slice(64, 128) + const v = '0x' + signedMessage.slice(128, 130) + const vDecimal = web3.utils.hexToNumber(v) + return { v, r, s }; + /*const { v, r, s } = ecsign( + Buffer.from(message.slice(2), "hex"), + Buffer.from(privateKey, "hex") + ); + return { v, r, s }; + */ +} + + +describe("ERC20TemplatePredictoor", () => { + let name, + symbol, + owner, + reciever, + metadata, + tokenERC721, + tokenAddress, + data, + flags, + factoryERC721, + factoryERC20, + templateERC721, + templateERC20, + erc20Address, + erc20Token, + erc20AddressWithPublishFee, + erc20TokenWithPublishFee, + publishMarketFeeAddress, + mockErc20, + mockErc20Decimals, + publishMarketFeeToken + + cap = web3.utils.toWei("100000"); + const fakeUSDAmount = cap + + const communityFeeCollector = "0xeE9300b7961e0a01d9f0adb863C7A227A07AaD75"; + const publishMarketFeeAmount = "5" + const addressZero = '0x0000000000000000000000000000000000000000'; + + beforeEach("init contracts for each test", async () => { + const ERC721Template = await ethers.getContractFactory("ERC721Template"); + const ERC20TemplatePredictoor = await ethers.getContractFactory("ERC20TemplatePredictoor"); + const ERC721Factory = await ethers.getContractFactory("ERC721Factory"); + + const Router = await ethers.getContractFactory("FactoryRouter"); + const FixedRateExchange = await ethers.getContractFactory( + "FixedRateExchange" + ); + + const MockErc20 = await ethers.getContractFactory('MockERC20'); + const MockErc20Decimals = await ethers.getContractFactory('MockERC20Decimals'); + + [owner, reciever, user2, user3, user4, user5, user6, opcCollector, marketFeeCollector, publishMarketAccount] = await ethers.getSigners(); + publishMarketFeeAddress = publishMarketAccount.address + data = web3.utils.asciiToHex(constants.blob[0]); + flags = web3.utils.asciiToHex(constants.blob[0]); + + // DEPLOY ROUTER, SETTING OWNER + + + + mockErc20 = await MockErc20.deploy(owner.address, "MockERC20", 'MockERC20'); + mockErc20Decimals = await MockErc20Decimals.deploy("Mock6Digits", 'Mock6Digits', 6); + publishMarketFeeToken = mockErc20Decimals.address + + router = await Router.deploy( + owner.address, + '0x000000000000000000000000000000000000dead', // approved tokens list, unused in this test + '0x000000000000000000000000000000000000dead', // pooltemplate field, unused in this test + opcCollector.address, + [] + ); + + + + fixedRateExchange = await FixedRateExchange.deploy( + router.address + ); + + templateERC20 = await ERC20TemplatePredictoor.deploy(); + + + // SETUP ERC721 Factory with template + templateERC721 = await ERC721Template.deploy(); + factoryERC721 = await ERC721Factory.deploy( + templateERC721.address, + templateERC20.address, + router.address + ); + + // SET REQUIRED ADDRESS + + + await router.addFactory(factoryERC721.address); + + await router.addFixedRateContract(fixedRateExchange.address); // DEPLOY ROUTER, SETTING OWNER + + + + + // by default connect() in ethers goes with the first address (owner in this case) + const tx = await factoryERC721.deployERC721Contract( + "NFT", + "NFTSYMBOL", + 1, + "0x0000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000", + "https://oceanprotocol.com/nft/", + true, + owner.address + ); + const txReceipt = await tx.wait(); + let event = getEventFromTx(txReceipt, 'NFTCreated') + assert(event, "Cannot find NFTCreated event") + tokenAddress = event.args[0]; + tokenERC721 = await ethers.getContractAt("ERC721Template", tokenAddress); + + assert((await tokenERC721.balanceOf(owner.address)) == 1); + + await tokenERC721.addManager(user2.address); + await tokenERC721.connect(user2).addTo725StoreList(user3.address); + await tokenERC721.connect(user2).addToCreateERC20List(user3.address); + await tokenERC721.connect(user2).addToMetadataList(user3.address); + + assert((await tokenERC721.getPermissions(user3.address)).store == true); + assert( + (await tokenERC721.getPermissions(user3.address)).deployERC20 == true + ); + assert( + (await tokenERC721.getPermissions(user3.address)).updateMetadata == true + ); + + + // [user3.address, user6.address, user3.address, addressZero, mockErc20.address], + + const trxERC20 = await tokenERC721.connect(user3).createERC20(1, + ["ERC20DT3", "ERC20DT3Symbol"], + [user3.address, user6.address, user3.address, addressZero, mockErc20.address], + [cap, 0, 24, 288, 24 * 60 * 60, 24 * 60 * 60 * 3], + [] + ); + + const trxReceiptERC20 = await trxERC20.wait(); + event = getEventFromTx(trxReceiptERC20, 'TokenCreated') + assert(event, "Cannot find TokenCreated event") + erc20Address = event.args[0]; + + erc20Token = await ethers.getContractAt("ERC20TemplatePredictoor", erc20Address); + assert((await erc20Token.permissions(user3.address)).minter == true); + + + // create an ERC20 with publish Fee ( 5 USDC, going to publishMarketAddress) + const trxERC20WithPublishFee = await tokenERC721.connect(user3).createERC20(1, + ["ERC20DT3P", "ERC20DT3SymbolP"], + [user3.address, user6.address, publishMarketFeeAddress, publishMarketFeeToken, mockErc20.address], + [cap, web3.utils.toWei(publishMarketFeeAmount), 24, 288, 24 * 60 * 60, 24 * 60 * 60 * 3], + [] + + ); + const trxReceiptERC20WithPublishFee = await trxERC20WithPublishFee.wait(); + event = getEventFromTx(trxReceiptERC20WithPublishFee, 'TokenCreated') + assert(event, "Cannot find TokenCreated event") + erc20AddressWithPublishFee = event.args[0]; + + erc20TokenWithPublishFee = await ethers.getContractAt("ERC20TemplatePredictoor", erc20AddressWithPublishFee); + assert((await erc20TokenWithPublishFee.permissions(user3.address)).minter == true); + + }); + + + it("#isInitialized - should check that the erc20Token contract is initialized", async () => { + expect(await erc20Token.isInitialized()).to.equal(true); + }); + + it("#initialize - should fail to re-initialize the contracts", async () => { + await expectRevert( + erc20Token.initialize( + ["ERC20DT3", "ERC20DT3Symbol"], + [owner.address, marketFeeCollector.address, owner.address, addressZero], + [tokenERC721.address, communityFeeCollector, router.address, erc20Token.address], + [web3.utils.toWei("10"), 0, 24, 300, 24 * 60 * 60], + [] + ), + "ERC20Template: token instance already initialized" + ); + }); + + it("#mint - user3 (minter role) should succeed to mint 1 ERC20Token to user2", async () => { + await erc20Token.connect(user3).mint(user2.address, web3.utils.toWei("1")); + assert( + (await erc20Token.balanceOf(user2.address)) == web3.utils.toWei("1") + ); + }); + + it("#mint - should fail to mint 1 ERC20Token to user2 if NOT MINTER", async () => { + await expectRevert( + erc20Token.connect(user2).mint(user2.address, web3.utils.toWei("1")), + "ERC20Template: NOT MINTER" + ); + }); + + it("#setPaymentCollector - should fail to set new FeeCollector if not NFTOwner", async () => { + await expectRevert( + erc20Token.connect(user2).setPaymentCollector(user2.address), + "ERC20Template: NOT PAYMENT MANAGER or OWNER" + ); + }); + + it("#setPaymentCollector - should succeed to set new paymentCollector if paymentManager", async () => { + await erc20Token.connect(user3).setPaymentCollector(owner.address); + + assert((await erc20Token.getPaymentCollector()) == owner.address, 'PaymentCollector is not owner'); + await erc20Token.connect(user3).setPaymentCollector(user2.address); + assert((await erc20Token.getPaymentCollector()) == user2.address, 'PaymentCollector is not user2'); + }); + + it("#getERC721Address - should succeed to get the parent ERC721 address", async () => { + const address = await erc20Token.connect(user3).getERC721Address(); + assert(address, "Not able to get the parent ERC721 address") + }); + + it("#addMinter - should fail to addMinter if not erc20Deployer (permission to deploy the erc20Contract at 721 level)", async () => { + assert((await erc20Token.permissions(user2.address)).minter == false); + + await expectRevert( + erc20Token.connect(user2).addMinter(user2.address), + "ERC20Template: NOT DEPLOYER ROLE" + ); + + assert((await erc20Token.permissions(user2.address)).minter == false); + }); + + it("#addMinter - should fail to addMinter if it's already minter", async () => { + assert((await erc20Token.permissions(user2.address)).minter == false); + + await erc20Token.connect(user3).addMinter(user2.address); + + assert((await erc20Token.permissions(user2.address)).minter == true); + + await expectRevert( + erc20Token.connect(user3).addMinter(user2.address), + "ERC20Roles: ALREADY A MINTER" + ); + }); + + it("#addMinter - should succeed to addMinter if erc20Deployer (permission to deploy the erc20Contract at 721 level)", async () => { + assert((await erc20Token.permissions(user2.address)).minter == false); + + // owner is already erc20Deployer + await erc20Token.connect(user3).addMinter(user2.address); + + assert((await erc20Token.permissions(user2.address)).minter == true); + }); + + it("#removeMinter - should fail to removeMinter if NOT erc20Deployer", async () => { + await erc20Token.connect(user3).addMinter(user2.address); + assert((await erc20Token.permissions(user2.address)).minter == true); + + await expectRevert( + erc20Token.connect(user2).removeMinter(user2.address), + "ERC20Template: NOT DEPLOYER ROLE" + ); + + assert((await erc20Token.permissions(user2.address)).minter == true); + }); + + it("#removeMinter - should fail to removeMinter even if it's minter", async () => { + await erc20Token.connect(user3).addMinter(user2.address); + + assert((await erc20Token.permissions(user2.address)).minter == true); + + await expectRevert( + erc20Token.connect(user4).removeMinter(user2.address), + "ERC20Template: NOT DEPLOYER ROLE" + ); + + assert((await erc20Token.permissions(user2.address)).minter == true); + }); + + it("#removeMinter - should succeed to removeMinter if erc20Deployer", async () => { + await erc20Token.connect(user3).addMinter(user2.address); + + assert((await erc20Token.permissions(user2.address)).minter == true); + + assert((await tokenERC721.getPermissions(user3.address)).deployERC20 == true) + + await erc20Token.connect(user3).removeMinter(user2.address); + + assert((await erc20Token.permissions(user2.address)).minter == false); + }); + + it("#addPaymentManager - should fail to addPaymentManager if not erc20Deployer (permission to deploy the erc20Contract at 721 level)", async () => { + assert((await erc20Token.permissions(user2.address)).paymentManager == false); + + await expectRevert( + erc20Token.connect(user2).addPaymentManager(user2.address), + "ERC20Template: NOT DEPLOYER ROLE" + ); + + assert((await erc20Token.permissions(user2.address)).paymentManager == false); + }); + + it("#addPaymentManager - should fail to addPaymentManager if it's already feeManager", async () => { + assert((await erc20Token.permissions(user2.address)).paymentManager == false); + + await erc20Token.connect(user3).addPaymentManager(user2.address); + + assert((await erc20Token.permissions(user2.address)).paymentManager == true); + + await expectRevert( + erc20Token.connect(user3).addPaymentManager(user2.address), + "ERC20Roles: ALREADY A FEE MANAGER" + ); + }); + + it("#addPaymentManager - should succeed to addPaymentManager if erc20Deployer (permission to deploy the erc20Contract at 721 level)", async () => { + assert((await erc20Token.permissions(user2.address)).paymentManager == false); + + // owner is already erc20Deployer + await erc20Token.connect(user3).addPaymentManager(user2.address); + + assert((await erc20Token.permissions(user2.address)).paymentManager == true); + }); + + it("#removeFeeManager - should fail to removeFeeManager if NOT erc20Deployer", async () => { + await erc20Token.connect(user3).addPaymentManager(owner.address); + + assert((await erc20Token.permissions(owner.address)).paymentManager == true); + + await expectRevert( + erc20Token.connect(user2).removePaymentManager(owner.address), + "ERC20Template: NOT DEPLOYER ROLE" + ); + + assert((await erc20Token.permissions(owner.address)).paymentManager == true); + }); + + it("#removeFeeManager - should fail to removeFeeManager even if it's feeManager", async () => { + // ERC20 deployer role add himself as manager and user2 + await erc20Token.connect(user3).addPaymentManager(owner.address); + await erc20Token.connect(user3).addPaymentManager(user2.address); + + assert((await erc20Token.permissions(user2.address)).paymentManager == true); + + await expectRevert( + erc20Token.connect(user2).removePaymentManager(owner.address), + "ERC20Template: NOT DEPLOYER ROLE" + ); + + assert((await erc20Token.permissions(owner.address)).paymentManager == true); + }); + + it("#removeFeeManager - should succeed to removeFeeManager if erc20Deployer", async () => { + await erc20Token.connect(user3).addPaymentManager(user2.address); + + assert((await erc20Token.permissions(user2.address)).paymentManager == true); + + await erc20Token.connect(user3).removePaymentManager(user2.address); + + assert((await erc20Token.permissions(user2.address)).paymentManager == false); + }); + + it("#setData - should fail to setData if NOT erc20Deployer", async () => { + const key = web3.utils.keccak256(erc20Token.address); + const value = web3.utils.asciiToHex("SomeData"); + + await expectRevert( + erc20Token.connect(user2).setData(value), + "ERC20Template: NOT DEPLOYER ROLE" + ); + + assert((await tokenERC721.getData(key)) == "0x"); + }); + + it("#setData - should succeed to setData if erc20Deployer", async () => { + const key = web3.utils.keccak256(erc20Token.address); + const value = web3.utils.asciiToHex("SomeData"); + + await erc20Token.connect(user3).setData(value); + + assert((await tokenERC721.getData(key)) == value); + }); + + it("#cleanPermissions - should fail to call cleanPermissions if NOT NFTOwner", async () => { + assert((await erc20Token.permissions(user3.address)).minter == true); + await expectRevert( + erc20Token.connect(user2).cleanPermissions(), + "ERC20Template: not NFTOwner" + ); + + assert((await erc20Token.permissions(user3.address)).minter == true); + }); + + it("#cleanPermissions - should succeed to call cleanPermissions if NFTOwner", async () => { + // user3 is already minter + + assert((await erc20Token.permissions(user3.address)).minter == true); + await erc20Token.connect(user3).addPaymentManager(owner.address); + // we set a new FeeCollector + await erc20Token.connect(owner).setPaymentCollector(user2.address); + assert((await erc20Token.getPaymentCollector()) == user2.address); + // WE add 2 more minters + await erc20Token.connect(user3).addMinter(user2.address); + await erc20Token.connect(user3).addMinter(user4.address); + assert((await erc20Token.permissions(user2.address)).minter == true); + assert((await erc20Token.permissions(user4.address)).minter == true); + + // NFT Owner cleans + await erc20Token.cleanPermissions(); + + // check permission were removed + assert((await erc20Token.permissions(owner.address)).minter == false); + assert((await erc20Token.permissions(owner.address)).paymentManager == false); + assert((await erc20Token.permissions(user2.address)).minter == false); + assert((await erc20Token.permissions(user3.address)).minter == false); + assert((await erc20Token.permissions(user4.address)).minter == false); + // we reassigned feeCollector to address(0) when cleaning permissions, so now getPaymentCollector points to NFT Owner + assert((await erc20Token.getPaymentCollector()) == owner.address); + }); + + it("#startOrder - user should succeed to call startOrder on a ERC20 without publishFee", async () => { + + //MINT SOME DT20 to USER2 so he can start order + await erc20Token.connect(user3).mint(user2.address, web3.utils.toWei("10")); + assert( + (await erc20Token.balanceOf(user2.address)) == web3.utils.toWei("10") + ); + const consumer = user2.address; // could be different user + const dtAmount = web3.utils.toWei("1"); + const serviceIndex = 1; // dummy index + const providerFeeAddress = user5.address; // marketplace fee Collector + const providerFeeAmount = 0; // fee to be collected on top, requires approval + const providerFeeToken = mockErc20.address; // token address for the feeAmount, + const consumeMarketFeeAddress = user5.address; // marketplace fee Collector + const consumeMarketFeeAmount = 0; // fee to be collected on top, requires approval + const consumeMarketFeeToken = mockErc20.address; // token address for the feeAmount, + const providerValidUntil = 0; + //sign provider data + const providerData = JSON.stringify({ "timeout": 0 }) + const message = ethers.utils.solidityKeccak256( + ["bytes", "address", "address", "uint256", "uint256"], + [ + ethers.utils.hexlify(ethers.utils.toUtf8Bytes(providerData)), + providerFeeAddress, + providerFeeToken, + providerFeeAmount, + providerValidUntil + ] + ); + const signedMessage = await signMessage(message, providerFeeAddress); + const tx = await erc20Token + .connect(user2) + .startOrder( + consumer, + serviceIndex, + { + providerFeeAddress: providerFeeAddress, + providerFeeToken: providerFeeToken, + providerFeeAmount: providerFeeAmount, + v: signedMessage.v, + r: signedMessage.r, + s: signedMessage.s, + providerData: ethers.utils.hexlify(ethers.utils.toUtf8Bytes(providerData)), + validUntil: providerValidUntil + }, + { + consumeMarketFeeAddress: consumeMarketFeeAddress, + consumeMarketFeeToken: consumeMarketFeeToken, + consumeMarketFeeAmount: consumeMarketFeeAmount, + } + ); + const txReceipt = await tx.wait(); + let event = getEventFromTx(txReceipt, 'OrderStarted') + assert(event, "Cannot find OrderStarted event") + //make sure that we don't have 'PublishMarketFee') event + event = getEventFromTx(txReceipt, 'PublishMarketFee') + assert.typeOf(event, 'undefined', "PublishMarketFee event found") + //make sure that we have ProviderFee event + event = getEventFromTx(txReceipt, 'ProviderFee') + + assert( + (await erc20Token.balanceOf(user2.address)) == web3.utils.toWei("9"), 'Invalid user balance, DT was not substracted' + ); + + assert( + (await erc20Token.balanceOf(opcCollector.address)) == + web3.utils.toWei("0.03"), 'Invalid OPF balance, we should get 0.03 DTs' + ); + assert( + (await erc20Token.balanceOf(user3.address)) == web3.utils.toWei("0"), 'Invalid consumeFee, we should have DT as fee' + ); + assert( + (await erc20Token.balanceOf(await erc20Token.getPaymentCollector())) == + web3.utils.toWei("0.97"), 'Invalid publisher reward, we should have 0.97 DT' + ); + }); + + + it("#startOrder - user should succeed to call reuseOrder on a ERC20 using a previous txId", async () => { + + //MINT SOME DT20 to USER2 so he can start order + await erc20Token.connect(user3).mint(user2.address, web3.utils.toWei("10")); + assert( + (await erc20Token.balanceOf(user2.address)) == web3.utils.toWei("10") + ); + const consumer = user2.address; // could be different user + const dtAmount = web3.utils.toWei("1"); + const serviceIndex = 1; // dummy index + const providerFeeAddress = user5.address; // marketplace fee Collector + const providerFeeAmount = 0; // fee to be collected on top, requires approval + const providerFeeToken = mockErc20.address; // token address for the feeAmount, in this case DAI + const providerValidUntil = 0; + const consumeMarketFeeAddress = user5.address; // marketplace fee Collector + const consumeMarketFeeAmount = 0; // fee to be collected on top, requires approval + const consumeMarketFeeToken = mockErc20.address; // token address for the feeAmount, + + //sign provider data + const providerData = JSON.stringify({ "timeout": 0 }) + const message = ethers.utils.solidityKeccak256( + ["bytes", "address", "address", "uint256", "uint256"], + [ + ethers.utils.hexlify(ethers.utils.toUtf8Bytes(providerData)), + providerFeeAddress, + providerFeeToken, + providerFeeAmount, + providerValidUntil + ] + ); + const signedMessage = await signMessage(message, providerFeeAddress); + const tx = await erc20Token + .connect(user2) + .startOrder( + consumer, + serviceIndex, + { + providerFeeAddress: providerFeeAddress, + providerFeeToken: providerFeeToken, + providerFeeAmount: providerFeeAmount, + v: signedMessage.v, + r: signedMessage.r, + s: signedMessage.s, + providerData: ethers.utils.hexlify(ethers.utils.toUtf8Bytes(providerData)), + validUntil: providerValidUntil + }, + { + consumeMarketFeeAddress: consumeMarketFeeAddress, + consumeMarketFeeToken: consumeMarketFeeToken, + consumeMarketFeeAmount: consumeMarketFeeAmount, + } + ); + let txReceipt = await tx.wait(); + let event = getEventFromTx(txReceipt, 'OrderStarted') + assert(event, "Cannot find OrderStarted event") + //make sure that we don't have 'PublishMarketFee') event + event = getEventFromTx(txReceipt, 'PublishMarketFee') + assert.typeOf(event, 'undefined', "PublishMarketFee event found") + //make sure that we have ProviderFee event + event = getEventFromTx(txReceipt, 'ProviderFee') + + assert( + (await erc20Token.balanceOf(user2.address)) == web3.utils.toWei("9"), 'Invalid user balance, DT was not substracted' + ); + + assert( + (await erc20Token.balanceOf(opcCollector.address)) == + web3.utils.toWei("0.03"), 'Invalid OPF balance, we should get 0.03 DTs' + ); + assert( + (await erc20Token.balanceOf(user3.address)) == web3.utils.toWei("0"), 'Invalid consumeFee, we should have DT as fee' + ); + assert( + (await erc20Token.balanceOf(await erc20Token.getPaymentCollector())) == + web3.utils.toWei("0.97"), 'Invalid publisher reward, we should have 0.97 DT' + ); + + const reuseTx = await erc20Token + .connect(user2) + .reuseOrder( + txReceipt.transactionHash, + { + providerFeeAddress: providerFeeAddress, + providerFeeToken: providerFeeToken, + providerFeeAmount: providerFeeAmount, + v: signedMessage.v, + r: signedMessage.r, + s: signedMessage.s, + providerData: ethers.utils.hexlify(ethers.utils.toUtf8Bytes(providerData)), + validUntil: providerValidUntil + } + ); + txReceipt = await reuseTx.wait(); + event = getEventFromTx(txReceipt, 'OrderReused') + assert(event, "Cannot find OrderReused event") + //make sure that we have ProviderFee event + event = getEventFromTx(txReceipt, 'ProviderFee') + + }); + + it("#startOrder - user should succeed to call startOrder on a ERC20 without publishFee and provider Fee", async () => { + + //MINT SOME DT20 to USER2 so he can start order + await erc20Token.connect(user3).mint(user2.address, web3.utils.toWei("10")); + assert( + (await erc20Token.balanceOf(user2.address)) == web3.utils.toWei("10") + ); + const consumer = user2.address; // could be different user + const dtAmount = web3.utils.toWei("1"); + const serviceIndex = 1; // dummy index + const providerFeeAddress = user5.address; // marketplace fee Collector + const providerFeeAmount = '1'; // fee to be collected on top, requires approval + const providerFeeToken = mockErc20.address; // token address for the feeAmount, in this case DAI + const providerValidUntil = 0; + const consumeMarketFeeAddress = user5.address; // marketplace fee Collector + const consumeMarketFeeAmount = 0; // fee to be collected on top, requires approval + const consumeMarketFeeToken = mockErc20.address; // token address for the feeAmount, + + // GET SOME consumeFeeToken + const Mock20Contract = await ethers.getContractAt( + "contracts/interfaces/IERC20.sol:IERC20", + mockErc20.address + ); + await Mock20Contract + .connect(owner) + .transfer(user2.address, ethers.utils.parseEther(providerFeeAmount)); + + // we approve the erc20Token contract to pull feeAmount (3 DAI) + + await Mock20Contract + .connect(user2) + .approve(erc20Token.address, web3.utils.toWei(providerFeeAmount)); + + //sign provider data + const providerData = JSON.stringify({ "timeout": 0 }); + const message = ethers.utils.solidityKeccak256( + ["bytes", "address", "address", "uint256", "uint256"], + [ + ethers.utils.hexlify(ethers.utils.toUtf8Bytes(providerData)), + providerFeeAddress, + providerFeeToken, + providerFeeAmount, + providerValidUntil + ] + ); + const signedMessage = await signMessage(message, providerFeeAddress); + const tx = await erc20Token + .connect(user2) + .startOrder( + consumer, + serviceIndex, + { + providerFeeAddress: providerFeeAddress, + providerFeeToken: providerFeeToken, + providerFeeAmount: providerFeeAmount, + v: signedMessage.v, + r: signedMessage.r, + s: signedMessage.s, + providerData: ethers.utils.hexlify(ethers.utils.toUtf8Bytes(providerData)), + validUntil: providerValidUntil + }, + { + consumeMarketFeeAddress: consumeMarketFeeAddress, + consumeMarketFeeToken: consumeMarketFeeToken, + consumeMarketFeeAmount: consumeMarketFeeAmount, + } + ); + const txReceipt = await tx.wait(); + let event = getEventFromTx(txReceipt, 'OrderStarted') + assert(event, "Cannot find OrderStarted event") + //make sure that we don't have 'PublishMarketFee') event + event = getEventFromTx(txReceipt, 'PublishMarketFee') + assert.typeOf(event, 'undefined', "PublishMarketFee event found") + //make sure that we have ProviderFee event + event = getEventFromTx(txReceipt, 'ProviderFee') + assert( + (await erc20Token.balanceOf(user2.address)) == web3.utils.toWei("9"), 'Invalid user balance, DT was not substracted' + ); + + assert( + (await erc20Token.balanceOf(opcCollector.address)) == + web3.utils.toWei("0.03"), 'Invalid OPF balance, we should get 0.03 DTs' + ); + assert( + (await erc20Token.balanceOf(user3.address)) == web3.utils.toWei("0"), 'Invalid consumeFee, we should have DT as fee' + ); + assert( + (await erc20Token.balanceOf(await erc20Token.getPaymentCollector())) == + web3.utils.toWei("0.97"), 'Invalid publisher reward, we should have 0.97 DT' + ); + }); + + + it("#startOrder - user should not succeed to call startOrder on a ERC20 without publishFee and wrong provider Fee", async () => { + + //MINT SOME DT20 to USER2 so he can start order + await erc20Token.connect(user3).mint(user2.address, web3.utils.toWei("10")); + assert( + (await erc20Token.balanceOf(user2.address)) == web3.utils.toWei("10") + ); + const consumer = user2.address; // could be different user + const dtAmount = web3.utils.toWei("1"); + const serviceIndex = 1; // dummy index + const providerFeeAddress = user3.address; // marketplace fee Collector + const providerFeeAmount = '1'; // fee to be collected on top, requires approval + const providerFeeToken = mockErc20.address; // token address for the feeAmount, in this case DAI + const providerValidUntil = 0; + const consumeMarketFeeAddress = user5.address; // marketplace fee Collector + const consumeMarketFeeAmount = 0; // fee to be collected on top, requires approval + const consumeMarketFeeToken = mockErc20.address; // token address for the feeAmount, + // GET SOME consumeFeeToken + const Mock20Contract = await ethers.getContractAt( + "contracts/interfaces/IERC20.sol:IERC20", + mockErc20.address + ); + await Mock20Contract + .connect(owner) + .transfer(user2.address, ethers.utils.parseEther(providerFeeAmount)); + + // we approve the erc20Token contract to pull feeAmount (3 DAI) + + await Mock20Contract + .connect(user2) + .approve(erc20Token.address, web3.utils.toWei(providerFeeAmount)); + + //sign provider data + const providerData = JSON.stringify({ "timeout": 0 }) + const message = ethers.utils.solidityKeccak256( + ["bytes", "address", "address", "uint256", "uint256"], + [ + ethers.utils.hexlify(ethers.utils.toUtf8Bytes(providerData)), + providerFeeAddress, + providerFeeToken, + providerFeeAmount, + providerValidUntil + ] + ); + // providerFeeAddress is user3, but we are signing using user5 private key, so it should fail + const signedMessage = await signMessage(message, user5.address); + + await expectRevert( + erc20Token + .connect(user2) + .startOrder( + consumer, + serviceIndex, + { + providerFeeAddress: providerFeeAddress, + providerFeeToken: providerFeeToken, + providerFeeAmount: providerFeeAmount, + v: signedMessage.v, + r: signedMessage.r, + s: signedMessage.s, + providerData: ethers.utils.hexlify(ethers.utils.toUtf8Bytes(providerData)), + validUntil: providerValidUntil + }, + { + consumeMarketFeeAddress: consumeMarketFeeAddress, + consumeMarketFeeToken: consumeMarketFeeToken, + consumeMarketFeeAmount: consumeMarketFeeAmount, + } + ), + "Invalid provider fee" + ); + + }); + + + + it("#startOrder - user should be able to get getPublishingMarketFee", async () => { + const publishFee = await erc20TokenWithPublishFee + .connect(user2) + .getPublishingMarketFee(); + assert(publishFee[0] = publishMarketFeeAddress) + assert(publishFee[1] = publishMarketFeeToken) + assert(publishFee[2] = web3.utils.toWei(publishMarketFeeAmount)) + + }); + + + ////////// + it("#startOrder - user should succeed to call startOrder on a ERC20 with 5 USDC publishFee, providerFee is ZERO and 5 USDC consumeFee", async () => { + + //MINT SOME DT20 to USER2 so he can start order + await erc20TokenWithPublishFee.connect(user3).mint(user2.address, web3.utils.toWei("10")); + assert( + (await erc20TokenWithPublishFee.balanceOf(user2.address)) == web3.utils.toWei("10") + ); + const publishFee = await erc20TokenWithPublishFee + .connect(user2) + .getPublishingMarketFee(); + const Mock20DecimalContract = await ethers.getContractAt( + "contracts/interfaces/IERC20.sol:IERC20", + publishFee[1] + ); + + const consumer = user2.address; // could be different user + const dtAmount = web3.utils.toWei("1"); + const serviceIndex = 1; // dummy index + const providerFeeAddress = user5.address; // marketplace fee Collector + const providerFeeAmount = 0; // fee to be collected on top, requires approval + const providerFeeToken = mockErc20.address; // token address for the feeAmount, in this case DAI + const consumeMarketFeeAddress = user5.address; // marketplace fee Collector + const consumeMarketFeeAmount = publishFee[2]; // fee to be collected on top, requires approval + const consumeMarketFeeToken = Mock20DecimalContract.address; // token address for the feeAmount, + + // GET SOME consumeFeeToken + await Mock20DecimalContract + .connect(owner) + .transfer(user2.address, publishFee[2].add(consumeMarketFeeAmount)); + + // we approve the erc20Token contract to pull feeAmount + await Mock20DecimalContract + .connect(user2) + .approve(erc20TokenWithPublishFee.address, publishFee[2].add(consumeMarketFeeAmount)); + + //sign provider data + const providerData = JSON.stringify({ "timeout": 0 }) + const providerValidUntil = 0; + const message = ethers.utils.solidityKeccak256( + ["bytes", "address", "address", "uint256", "uint256"], + [ + ethers.utils.hexlify(ethers.utils.toUtf8Bytes(providerData)), + providerFeeAddress, + providerFeeToken, + providerFeeAmount, + providerValidUntil + ] + ); + + const signedMessage = await signMessage(message, providerFeeAddress); + + const tx = await erc20TokenWithPublishFee + .connect(user2) + .startOrder( + consumer, + serviceIndex, + { + providerFeeAddress: providerFeeAddress, + providerFeeToken: providerFeeToken, + providerFeeAmount: providerFeeAmount, + v: signedMessage.v, + r: signedMessage.r, + s: signedMessage.s, + providerData: ethers.utils.hexlify(ethers.utils.toUtf8Bytes(providerData)), + validUntil: providerValidUntil + }, + { + consumeMarketFeeAddress: consumeMarketFeeAddress, + consumeMarketFeeToken: consumeMarketFeeToken, + consumeMarketFeeAmount: consumeMarketFeeAmount, + } + ); + const txReceipt = await tx.wait(); + let event = getEventFromTx(txReceipt, 'OrderStarted') + assert(event, "Cannot find OrderStarted event") + event = getEventFromTx(txReceipt, 'PublishMarketFee') + assert(event, "Cannot find PublishMarketFee event") + event = getEventFromTx(txReceipt, 'ConsumeMarketFee') + assert(event, "Cannot find ConsumeMarketFee event") + //make sure that we have ProviderFee event + event = getEventFromTx(txReceipt, 'ProviderFee') + assert( + (await erc20TokenWithPublishFee.balanceOf(user2.address)) == web3.utils.toWei("9"), 'Invalid user balance, DT was not substracted' + ); + + assert( + (await erc20TokenWithPublishFee.balanceOf(opcCollector.address)) == + web3.utils.toWei("0.03"), 'Invalid OPF balance, we should get 0.03 DTs' + ); + assert( + (await erc20TokenWithPublishFee.balanceOf(user3.address)) == web3.utils.toWei("0"), 'Invalid consumeFee, we should have DT as fee' + ); + assert( + (await erc20TokenWithPublishFee.balanceOf(await erc20TokenWithPublishFee.getPaymentCollector())) == + web3.utils.toWei("0.97"), 'Invalid publisher reward, we should have 0.97 DT' + ); + }); + + it("#startOrder - user should succeed to call startOrder on a ERC20 with 5 USDC publishFee, providerFee is not ZEO", async () => { + + //MINT SOME DT20 to USER2 so he can start order + await erc20TokenWithPublishFee.connect(user3).mint(user2.address, web3.utils.toWei("10")); + assert( + (await erc20TokenWithPublishFee.balanceOf(user2.address)) == web3.utils.toWei("10") + ); + const consumer = user2.address; // could be different user + const dtAmount = web3.utils.toWei("1"); + const serviceIndex = 1; // dummy index + const providerFeeAddress = user5.address; // marketplace fee Collector + const providerFeeAmount = '1'; // fee to be collected on top, requires approval + const providerFeeToken = mockErc20.address; // token address for the feeAmount, in this case DAI + const providerValidUntil = 0; + const consumeMarketFeeAddress = user5.address; // marketplace fee Collector + const consumeMarketFeeAmount = 0; // fee to be collected on top, requires approval + const consumeMarketFeeToken = mockErc20.address; // token address for the feeAmount, + // GET SOME providerFeeToken + const Mock20Contract = await ethers.getContractAt( + "contracts/interfaces/IERC20.sol:IERC20", + providerFeeToken + ); + await Mock20Contract + .connect(owner) + .transfer(user2.address, ethers.utils.parseEther(providerFeeAmount)); + await Mock20Contract + .connect(user2) + .approve(erc20TokenWithPublishFee.address, web3.utils.toWei(providerFeeAmount)); + + const publishFee = await erc20TokenWithPublishFee + .connect(user2) + .getPublishingMarketFee(); + // GET SOME consumeFeeToken + const Mock20DecimalContract = await ethers.getContractAt( + "contracts/interfaces/IERC20.sol:IERC20", + publishFee[1] + ); + await Mock20DecimalContract + .connect(owner) + .transfer(user2.address, publishFee[2]); + + // we approve the erc20Token contract to pull feeAmount + await Mock20DecimalContract + .connect(user2) + .approve(erc20TokenWithPublishFee.address, publishFee[2]); + //sign provider data + const providerData = JSON.stringify({ "timeout": 0 }) + const message = ethers.utils.solidityKeccak256( + ["bytes", "address", "address", "uint256", "uint256"], + [ + ethers.utils.hexlify(ethers.utils.toUtf8Bytes(providerData)), + providerFeeAddress, + providerFeeToken, + providerFeeAmount, + providerValidUntil + ] + ); + const signedMessage = await signMessage(message, providerFeeAddress); + const tx = await erc20TokenWithPublishFee + .connect(user2) + .startOrder( + consumer, + serviceIndex, + { + providerFeeAddress: providerFeeAddress, + providerFeeToken: providerFeeToken, + providerFeeAmount: providerFeeAmount, + v: signedMessage.v, + r: signedMessage.r, + s: signedMessage.s, + providerData: ethers.utils.hexlify(ethers.utils.toUtf8Bytes(providerData)), + validUntil: providerValidUntil + }, + { + consumeMarketFeeAddress: consumeMarketFeeAddress, + consumeMarketFeeToken: consumeMarketFeeToken, + consumeMarketFeeAmount: consumeMarketFeeAmount, + } + ); + const txReceipt = await tx.wait(); + let event = getEventFromTx(txReceipt, 'OrderStarted') + assert(event, "Cannot find OrderStarted event") + event = getEventFromTx(txReceipt, 'PublishMarketFee') + assert(event, "Cannot find PublishMarketFee event") + //make sure that we have ProviderFee event + event = getEventFromTx(txReceipt, 'ProviderFee') + assert( + (await erc20TokenWithPublishFee.balanceOf(user2.address)) == web3.utils.toWei("9"), 'Invalid user balance, DT was not substracted' + ); + + assert( + (await erc20TokenWithPublishFee.balanceOf(opcCollector.address)) == + web3.utils.toWei("0.03"), 'Invalid OPF balance, we should get 0.03 DTs' + ); + assert( + (await erc20TokenWithPublishFee.balanceOf(user3.address)) == web3.utils.toWei("0"), 'Invalid consumeFee, we should have DT as fee' + ); + assert( + (await erc20TokenWithPublishFee.balanceOf(await erc20TokenWithPublishFee.getPaymentCollector())) == + web3.utils.toWei("0.97"), 'Invalid publisher reward, we should have 0.97 DT' + ); + }); + + + it("#setPublishingMarketFee - user should not be able to set new publish fee", async () => { + await expectRevert( + erc20TokenWithPublishFee.connect(user2).setPublishingMarketFee(user2.address, erc20Token.address, web3.utils.toWei('10')), + "ERC20Template: not publishMarketFeeAddress" + ); + const publishFee = await erc20TokenWithPublishFee + .connect(user2) + .getPublishingMarketFee(); + assert(publishFee[0] = publishMarketFeeAddress) + assert(publishFee[1] = publishMarketFeeToken) + assert(publishFee[2] = web3.utils.toWei(publishMarketFeeAmount)) + }); + it("#setPublishingMarketFee - publishMarketAccount should not be able to set new publish fee", async () => { + + await erc20TokenWithPublishFee.connect(publishMarketAccount).setPublishingMarketFee(user2.address, erc20Token.address, web3.utils.toWei('10')) + const publishFee = await erc20TokenWithPublishFee + .connect(user2) + .getPublishingMarketFee(); + assert(publishFee[0] = user2.address) + assert(publishFee[1] = erc20Token.address) + assert(publishFee[2] = web3.utils.toWei('10')) + }); + it("#getId - should return templateId", async () => { + const templateId = 1; + assert((await erc20Token.getId()) == templateId); + }); + it("#burn - user should succeed to burn tokens", async () => { + + //MINT SOME DT20 to USER2 so he can try to burn + await erc20Token.connect(user3).mint(user2.address, web3.utils.toWei("10")); + const burnAmount = web3.utils.toWei("2") + assert( + (await erc20Token.balanceOf(user2.address)) == web3.utils.toWei("10") + , 'Invalid user balance, DT was not minted' + ); + const totalSupply = await erc20Token.totalSupply() + + await erc20Token + .connect(user2) + .burn(burnAmount); + + + assert( + (await erc20Token.balanceOf(user2.address)) == web3.utils.toWei("8"), 'Invalid user balance, DT was not substracted' + ); + const newTotalSupply = await erc20Token.totalSupply() + const expectedSupply = totalSupply.sub(burnAmount) + assert( + (totalSupply.sub(burnAmount).eq(newTotalSupply)) + , 'Invalid total supply' + ); + }); + it("#burnFrom - user3 should succeed to burn some user2's tokens using burnFrom", async () => { + + //MINT SOME DT20 to USER2 so he can try to burn + await erc20Token.connect(user3).mint(user2.address, web3.utils.toWei("10")); + const burnAmount = web3.utils.toWei("2") + assert( + (await erc20Token.balanceOf(user2.address)) == web3.utils.toWei("10") + , 'Invalid user balance, DT was not minted' + ); + const totalSupply = await erc20Token.totalSupply() + //allow user3 to burn + await erc20Token.connect(user2).approve(user3.address, web3.utils.toWei(burnAmount)); + await erc20Token + .connect(user3) + .burnFrom(user2.address, burnAmount); + + + assert( + (await erc20Token.balanceOf(user2.address)) == web3.utils.toWei("8"), 'Invalid user balance, DT were not burned' + ); + const newTotalSupply = await erc20Token.totalSupply() + const expectedSupply = totalSupply.sub(burnAmount) + assert( + (totalSupply.sub(burnAmount).eq(newTotalSupply)) + , 'Invalid total supply' + ); + }); +}); + From 2ea457c10ff2a6f8db8a6c02882e5a7a9fd8e524 Mon Sep 17 00:00:00 2001 From: trizin <25263018+trizin@users.noreply.github.com> Date: Fri, 5 May 2023 07:58:18 +0000 Subject: [PATCH 007/120] Fix tests --- .../datatokens/ERC20TemplatePredictoor.test.js | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/test/unit/datatokens/ERC20TemplatePredictoor.test.js b/test/unit/datatokens/ERC20TemplatePredictoor.test.js index 852e7587b..f6e266fd7 100644 --- a/test/unit/datatokens/ERC20TemplatePredictoor.test.js +++ b/test/unit/datatokens/ERC20TemplatePredictoor.test.js @@ -282,12 +282,12 @@ describe("ERC20TemplatePredictoor", () => { ); }); - it("#setPaymentCollector - should succeed to set new paymentCollector if paymentManager", async () => { + it("#setPaymentCollector - should not modify paymentCollector address", async () => { await erc20Token.connect(user3).setPaymentCollector(owner.address); - - assert((await erc20Token.getPaymentCollector()) == owner.address, 'PaymentCollector is not owner'); + assert((await erc20Token.getPaymentCollector()) == erc20Token.address, 'PaymentCollector is not erc20Token'); await erc20Token.connect(user3).setPaymentCollector(user2.address); - assert((await erc20Token.getPaymentCollector()) == user2.address, 'PaymentCollector is not user2'); + assert((await erc20Token.getPaymentCollector()) == erc20Token.address, 'PaymentCollector is not erc20Token'); + }); it("#getERC721Address - should succeed to get the parent ERC721 address", async () => { @@ -472,9 +472,6 @@ describe("ERC20TemplatePredictoor", () => { assert((await erc20Token.permissions(user3.address)).minter == true); await erc20Token.connect(user3).addPaymentManager(owner.address); - // we set a new FeeCollector - await erc20Token.connect(owner).setPaymentCollector(user2.address); - assert((await erc20Token.getPaymentCollector()) == user2.address); // WE add 2 more minters await erc20Token.connect(user3).addMinter(user2.address); await erc20Token.connect(user3).addMinter(user4.address); @@ -490,8 +487,7 @@ describe("ERC20TemplatePredictoor", () => { assert((await erc20Token.permissions(user2.address)).minter == false); assert((await erc20Token.permissions(user3.address)).minter == false); assert((await erc20Token.permissions(user4.address)).minter == false); - // we reassigned feeCollector to address(0) when cleaning permissions, so now getPaymentCollector points to NFT Owner - assert((await erc20Token.getPaymentCollector()) == owner.address); + assert((await erc20Token.getPaymentCollector()) == erc20Token.address); }); it("#startOrder - user should succeed to call startOrder on a ERC20 without publishFee", async () => { @@ -1078,7 +1074,7 @@ describe("ERC20TemplatePredictoor", () => { assert(publishFee[2] = web3.utils.toWei('10')) }); it("#getId - should return templateId", async () => { - const templateId = 1; + const templateId = 3; assert((await erc20Token.getId()) == templateId); }); it("#burn - user should succeed to burn tokens", async () => { From 5f7b09d4b40296052c91281bf750eaab6f48ff3d Mon Sep 17 00:00:00 2001 From: trizin <25263018+trizin@users.noreply.github.com> Date: Fri, 5 May 2023 08:06:24 +0000 Subject: [PATCH 008/120] Remove redundant test --- test/unit/datatokens/ERC20TemplatePredictoor.test.js | 7 ------- 1 file changed, 7 deletions(-) diff --git a/test/unit/datatokens/ERC20TemplatePredictoor.test.js b/test/unit/datatokens/ERC20TemplatePredictoor.test.js index f6e266fd7..ae6a6a19c 100644 --- a/test/unit/datatokens/ERC20TemplatePredictoor.test.js +++ b/test/unit/datatokens/ERC20TemplatePredictoor.test.js @@ -275,13 +275,6 @@ describe("ERC20TemplatePredictoor", () => { ); }); - it("#setPaymentCollector - should fail to set new FeeCollector if not NFTOwner", async () => { - await expectRevert( - erc20Token.connect(user2).setPaymentCollector(user2.address), - "ERC20Template: NOT PAYMENT MANAGER or OWNER" - ); - }); - it("#setPaymentCollector - should not modify paymentCollector address", async () => { await erc20Token.connect(user3).setPaymentCollector(owner.address); assert((await erc20Token.getPaymentCollector()) == erc20Token.address, 'PaymentCollector is not erc20Token'); From 710e430fee7abd5ca84b9c700793c5cdec8469d7 Mon Sep 17 00:00:00 2001 From: trizin <25263018+trizin@users.noreply.github.com> Date: Fri, 5 May 2023 08:12:36 +0000 Subject: [PATCH 009/120] Remove unused events --- .../templates/ERC20TemplatePredictoor.sol | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/contracts/templates/ERC20TemplatePredictoor.sol b/contracts/templates/ERC20TemplatePredictoor.sol index 21ea490a6..2d9408648 100644 --- a/contracts/templates/ERC20TemplatePredictoor.sol +++ b/contracts/templates/ERC20TemplatePredictoor.sol @@ -116,25 +116,6 @@ contract ERC20TemplatePredictoor is uint256 blockNumber ); - event OrderReused( - bytes32 orderTxId, - address caller, - uint256 timestamp, - uint256 number - ); - - event OrderExecuted( - address indexed providerAddress, - address indexed consumerAddress, - bytes32 orderTxId, - bytes providerData, - bytes providerSignature, - bytes consumerData, - bytes consumerSignature, - uint256 timestamp, - uint256 blockNumber - ); - // emited for every order event PublishMarketFee( address indexed PublishMarketFeeAddress, From b321042f05db2678a7de798dc49d2ae82a1d9fbc Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Fri, 5 May 2023 04:34:00 -0700 Subject: [PATCH 010/120] make stake_token public --- contracts/templates/ERC20TemplatePredictoor.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/templates/ERC20TemplatePredictoor.sol b/contracts/templates/ERC20TemplatePredictoor.sol index 2d9408648..06ee007e0 100644 --- a/contracts/templates/ERC20TemplatePredictoor.sol +++ b/contracts/templates/ERC20TemplatePredictoor.sol @@ -67,7 +67,7 @@ contract ERC20TemplatePredictoor is mapping(uint256 => uint256) subscription_revenue_at_block; //income registred mapping(address => Subscription) subscriptions; // valid subscription per user uint256 blocks_per_epoch; - address stake_token; + address public stake_token; uint256 blocks_per_subscription; uint256 truval_submit_timeout_block = 3; bool paused = false; From b45a83a302cd66d6541c002b476f3e0611a47700 Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Fri, 5 May 2023 04:46:41 -0700 Subject: [PATCH 011/120] more updates --- contracts/templates/ERC20TemplatePredictoor.sol | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/contracts/templates/ERC20TemplatePredictoor.sol b/contracts/templates/ERC20TemplatePredictoor.sol index 06ee007e0..357c54059 100644 --- a/contracts/templates/ERC20TemplatePredictoor.sol +++ b/contracts/templates/ERC20TemplatePredictoor.sol @@ -966,8 +966,11 @@ contract ERC20TemplatePredictoor is return subscriptions[user].expires <= block.number ? false : true; } - function epoch() public view returns (uint256) { - return block.number / blocks_per_epoch; + function epoch(uint256 blocknum) public view returns (uint256) { + return blocknum / blocks_per_epoch; + } + function cur_epoch() public view returns (uint256) { + return epoch(block.number); } function rail_blocknum_to_slot( From 842710169560013501871c53ba5899f0785d935c Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Fri, 5 May 2023 04:54:07 -0700 Subject: [PATCH 012/120] more public vars --- contracts/templates/ERC20TemplatePredictoor.sol | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/contracts/templates/ERC20TemplatePredictoor.sol b/contracts/templates/ERC20TemplatePredictoor.sol index 357c54059..6a9a64e85 100644 --- a/contracts/templates/ERC20TemplatePredictoor.sol +++ b/contracts/templates/ERC20TemplatePredictoor.sol @@ -66,11 +66,11 @@ contract ERC20TemplatePredictoor is mapping(uint256 => bool) truval_submitted; mapping(uint256 => uint256) subscription_revenue_at_block; //income registred mapping(address => Subscription) subscriptions; // valid subscription per user - uint256 blocks_per_epoch; + uint256 public blocks_per_epoch; address public stake_token; - uint256 blocks_per_subscription; - uint256 truval_submit_timeout_block = 3; - bool paused = false; + uint256 public blocks_per_subscription; + uint256 public truval_submit_timeout_block = 3; + bool public paused = false; // -------------------------- PREDICTOOR -------------------------- // EIP 2612 SUPPORT From eeead33e4fca2fed916a61e3ab5be4b8c0853ff5 Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Fri, 5 May 2023 05:19:33 -0700 Subject: [PATCH 013/120] more updates --- Dockerfile | 2 +- contracts/templates/ERC20TemplatePredictoor.sol | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index f4fb2955a..02e51ff4f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -14,5 +14,5 @@ ENV SLEEP_FOR_GANACHE=10 RUN cp hardhat.config.barge.js hardhat.config.js ENV NETWORK=barge ENV NETWORK_RPC_URL=127.0.0.1:8545 -RUN npx hardhat compile +RUN npx hardhat compile --force ENTRYPOINT ["/ocean-contracts/scripts/deploy_docker.sh"] diff --git a/contracts/templates/ERC20TemplatePredictoor.sol b/contracts/templates/ERC20TemplatePredictoor.sol index 6a9a64e85..9bbd36f33 100644 --- a/contracts/templates/ERC20TemplatePredictoor.sol +++ b/contracts/templates/ERC20TemplatePredictoor.sol @@ -820,7 +820,7 @@ contract ERC20TemplatePredictoor is * @dev buyFromFre * Buys 1 DT from the FRE */ - function buyFromFre(FreParams calldata _freParams) internal { + function buyFromFre(FreParams calldata _freParams) public { // get exchange info IFixedRateExchange fre = IFixedRateExchange( _freParams.exchangeContract From 2f04b5067fb8a8472f5e1dbf24adba12d005c388 Mon Sep 17 00:00:00 2001 From: trizin <25263018+trizin@users.noreply.github.com> Date: Fri, 5 May 2023 13:17:24 +0000 Subject: [PATCH 014/120] Remove NewPaymentCollector event and add predictoor events --- .../templates/ERC20TemplatePredictoor.sol | 60 +++++++++++++------ 1 file changed, 42 insertions(+), 18 deletions(-) diff --git a/contracts/templates/ERC20TemplatePredictoor.sol b/contracts/templates/ERC20TemplatePredictoor.sol index 2d9408648..e316e7d4d 100644 --- a/contracts/templates/ERC20TemplatePredictoor.sol +++ b/contracts/templates/ERC20TemplatePredictoor.sol @@ -49,6 +49,24 @@ contract ERC20TemplatePredictoor is uint256 public constant BASE = 1e18; // -------------------------- PREDICTOOR -------------------------- + event PredictionSubmitted( + address indexed predictoor, + uint256 indexed epoch, + uint256 stake + ); + event PredictionPayout( + address indexed predictoor, + uint256 indexed epoch, + uint256 stake, + uint256 payout, + bool prediction, + bool truval + ); + event NewSubscription( + address indexed user, + uint256 expires, + uint256 blocknum + ); struct Prediction { bool predval; uint256 stake; @@ -66,10 +84,10 @@ contract ERC20TemplatePredictoor is mapping(uint256 => bool) truval_submitted; mapping(uint256 => uint256) subscription_revenue_at_block; //income registred mapping(address => Subscription) subscriptions; // valid subscription per user - uint256 blocks_per_epoch; - address stake_token; - uint256 blocks_per_subscription; - uint256 truval_submit_timeout_block = 3; + uint256 public blocks_per_epoch; + address public stake_token; + uint256 public blocks_per_subscription; + uint256 public truval_submit_timeout_block = 3; bool paused = false; // -------------------------- PREDICTOOR -------------------------- @@ -149,13 +167,6 @@ contract ERC20TemplatePredictoor is ); event NewDispenser(address dispenserContract); - event NewPaymentCollector( - address indexed caller, - address indexed _newPaymentCollector, - uint256 timestamp, - uint256 blockNumber - ); - modifier onlyNotInitialized() { require( !initialized, @@ -296,12 +307,6 @@ contract ERC20TemplatePredictoor is _addMinter(addresses_[0]); // set payment collector to this contract, so we can get the $$$ _setPaymentCollector(address(this)); - emit NewPaymentCollector( - msg.sender, - addresses_[1], - block.timestamp, - block.number - ); publishMarketFeeAddress = addresses_[2]; publishMarketFeeToken = addresses_[3]; @@ -1005,7 +1010,7 @@ contract ERC20TemplatePredictoor is function get_agg_predval( uint256 blocknum ) public view blocknumOnSlot(blocknum) returns (uint256, uint256) { - require(is_valid_subscription(msg.sender), "Not valid subscription"); + require(is_valid_subscription(msg.sender), "No subscription"); return (agg_predvals_numer[blocknum], agg_predvals_denom[blocknum]); } @@ -1061,6 +1066,8 @@ contract ERC20TemplatePredictoor is // update agg_predvals agg_predvals_numer[blocknum] += stake * (predval ? 1 : 0); agg_predvals_denom[blocknum] += stake; + + emit PredictionSubmitted(msg.sender, epoch(), stake); } function payout( @@ -1078,6 +1085,14 @@ contract ERC20TemplatePredictoor is ) { IERC20(stake_token).safeTransfer(predobj.predictoor, predobj.stake); predobj.paid = true; + emit PredictionPayout( + predictoor_addr, + epoch(), + predobj.stake, + predobj.stake, + predobj.predval, + truevals[blocknum] + ); return; } @@ -1097,6 +1112,15 @@ contract ERC20TemplatePredictoor is payout_amt ); predobj.paid = true; + + emit PredictionPayout( + predictoor_addr, + epoch(), + predobj.stake, + payout_amt, + predobj.predval, + truevals[blocknum] + ); } // ----------------------- ADMIN FUNCTIONS ----------------------- From 70c1e8ab1e3639c5e9a5172e8539b9cc12cb3ef7 Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Fri, 5 May 2023 06:18:05 -0700 Subject: [PATCH 015/120] fix tests - still WIP --- .../ERC20TemplatePredictoor.test.js | 192 +----------------- 1 file changed, 8 insertions(+), 184 deletions(-) diff --git a/test/unit/datatokens/ERC20TemplatePredictoor.test.js b/test/unit/datatokens/ERC20TemplatePredictoor.test.js index ae6a6a19c..1f684001c 100644 --- a/test/unit/datatokens/ERC20TemplatePredictoor.test.js +++ b/test/unit/datatokens/ERC20TemplatePredictoor.test.js @@ -549,118 +549,18 @@ describe("ERC20TemplatePredictoor", () => { assert( (await erc20Token.balanceOf(opcCollector.address)) == - web3.utils.toWei("0.03"), 'Invalid OPF balance, we should get 0.03 DTs' + web3.utils.toWei("0.0"), 'Invalid OPF balance, we should get 0.03 DTs' ); assert( (await erc20Token.balanceOf(user3.address)) == web3.utils.toWei("0"), 'Invalid consumeFee, we should have DT as fee' ); assert( (await erc20Token.balanceOf(await erc20Token.getPaymentCollector())) == - web3.utils.toWei("0.97"), 'Invalid publisher reward, we should have 0.97 DT' + web3.utils.toWei("0.0"), 'Invalid publisher reward, we should have 0.0 DT' ); }); - it("#startOrder - user should succeed to call reuseOrder on a ERC20 using a previous txId", async () => { - - //MINT SOME DT20 to USER2 so he can start order - await erc20Token.connect(user3).mint(user2.address, web3.utils.toWei("10")); - assert( - (await erc20Token.balanceOf(user2.address)) == web3.utils.toWei("10") - ); - const consumer = user2.address; // could be different user - const dtAmount = web3.utils.toWei("1"); - const serviceIndex = 1; // dummy index - const providerFeeAddress = user5.address; // marketplace fee Collector - const providerFeeAmount = 0; // fee to be collected on top, requires approval - const providerFeeToken = mockErc20.address; // token address for the feeAmount, in this case DAI - const providerValidUntil = 0; - const consumeMarketFeeAddress = user5.address; // marketplace fee Collector - const consumeMarketFeeAmount = 0; // fee to be collected on top, requires approval - const consumeMarketFeeToken = mockErc20.address; // token address for the feeAmount, - - //sign provider data - const providerData = JSON.stringify({ "timeout": 0 }) - const message = ethers.utils.solidityKeccak256( - ["bytes", "address", "address", "uint256", "uint256"], - [ - ethers.utils.hexlify(ethers.utils.toUtf8Bytes(providerData)), - providerFeeAddress, - providerFeeToken, - providerFeeAmount, - providerValidUntil - ] - ); - const signedMessage = await signMessage(message, providerFeeAddress); - const tx = await erc20Token - .connect(user2) - .startOrder( - consumer, - serviceIndex, - { - providerFeeAddress: providerFeeAddress, - providerFeeToken: providerFeeToken, - providerFeeAmount: providerFeeAmount, - v: signedMessage.v, - r: signedMessage.r, - s: signedMessage.s, - providerData: ethers.utils.hexlify(ethers.utils.toUtf8Bytes(providerData)), - validUntil: providerValidUntil - }, - { - consumeMarketFeeAddress: consumeMarketFeeAddress, - consumeMarketFeeToken: consumeMarketFeeToken, - consumeMarketFeeAmount: consumeMarketFeeAmount, - } - ); - let txReceipt = await tx.wait(); - let event = getEventFromTx(txReceipt, 'OrderStarted') - assert(event, "Cannot find OrderStarted event") - //make sure that we don't have 'PublishMarketFee') event - event = getEventFromTx(txReceipt, 'PublishMarketFee') - assert.typeOf(event, 'undefined', "PublishMarketFee event found") - //make sure that we have ProviderFee event - event = getEventFromTx(txReceipt, 'ProviderFee') - - assert( - (await erc20Token.balanceOf(user2.address)) == web3.utils.toWei("9"), 'Invalid user balance, DT was not substracted' - ); - - assert( - (await erc20Token.balanceOf(opcCollector.address)) == - web3.utils.toWei("0.03"), 'Invalid OPF balance, we should get 0.03 DTs' - ); - assert( - (await erc20Token.balanceOf(user3.address)) == web3.utils.toWei("0"), 'Invalid consumeFee, we should have DT as fee' - ); - assert( - (await erc20Token.balanceOf(await erc20Token.getPaymentCollector())) == - web3.utils.toWei("0.97"), 'Invalid publisher reward, we should have 0.97 DT' - ); - - const reuseTx = await erc20Token - .connect(user2) - .reuseOrder( - txReceipt.transactionHash, - { - providerFeeAddress: providerFeeAddress, - providerFeeToken: providerFeeToken, - providerFeeAmount: providerFeeAmount, - v: signedMessage.v, - r: signedMessage.r, - s: signedMessage.s, - providerData: ethers.utils.hexlify(ethers.utils.toUtf8Bytes(providerData)), - validUntil: providerValidUntil - } - ); - txReceipt = await reuseTx.wait(); - event = getEventFromTx(txReceipt, 'OrderReused') - assert(event, "Cannot find OrderReused event") - //make sure that we have ProviderFee event - event = getEventFromTx(txReceipt, 'ProviderFee') - - }); - it("#startOrder - user should succeed to call startOrder on a ERC20 without publishFee and provider Fee", async () => { //MINT SOME DT20 to USER2 so he can start order @@ -742,94 +642,18 @@ describe("ERC20TemplatePredictoor", () => { assert( (await erc20Token.balanceOf(opcCollector.address)) == - web3.utils.toWei("0.03"), 'Invalid OPF balance, we should get 0.03 DTs' + web3.utils.toWei("0.0"), 'Invalid OPF balance, we should get 0.03 DTs' ); assert( (await erc20Token.balanceOf(user3.address)) == web3.utils.toWei("0"), 'Invalid consumeFee, we should have DT as fee' ); assert( (await erc20Token.balanceOf(await erc20Token.getPaymentCollector())) == - web3.utils.toWei("0.97"), 'Invalid publisher reward, we should have 0.97 DT' - ); - }); - - - it("#startOrder - user should not succeed to call startOrder on a ERC20 without publishFee and wrong provider Fee", async () => { - - //MINT SOME DT20 to USER2 so he can start order - await erc20Token.connect(user3).mint(user2.address, web3.utils.toWei("10")); - assert( - (await erc20Token.balanceOf(user2.address)) == web3.utils.toWei("10") - ); - const consumer = user2.address; // could be different user - const dtAmount = web3.utils.toWei("1"); - const serviceIndex = 1; // dummy index - const providerFeeAddress = user3.address; // marketplace fee Collector - const providerFeeAmount = '1'; // fee to be collected on top, requires approval - const providerFeeToken = mockErc20.address; // token address for the feeAmount, in this case DAI - const providerValidUntil = 0; - const consumeMarketFeeAddress = user5.address; // marketplace fee Collector - const consumeMarketFeeAmount = 0; // fee to be collected on top, requires approval - const consumeMarketFeeToken = mockErc20.address; // token address for the feeAmount, - // GET SOME consumeFeeToken - const Mock20Contract = await ethers.getContractAt( - "contracts/interfaces/IERC20.sol:IERC20", - mockErc20.address + web3.utils.toWei("0.0"), 'Invalid publisher reward, we should have 0.97 DT' ); - await Mock20Contract - .connect(owner) - .transfer(user2.address, ethers.utils.parseEther(providerFeeAmount)); - - // we approve the erc20Token contract to pull feeAmount (3 DAI) - - await Mock20Contract - .connect(user2) - .approve(erc20Token.address, web3.utils.toWei(providerFeeAmount)); - - //sign provider data - const providerData = JSON.stringify({ "timeout": 0 }) - const message = ethers.utils.solidityKeccak256( - ["bytes", "address", "address", "uint256", "uint256"], - [ - ethers.utils.hexlify(ethers.utils.toUtf8Bytes(providerData)), - providerFeeAddress, - providerFeeToken, - providerFeeAmount, - providerValidUntil - ] - ); - // providerFeeAddress is user3, but we are signing using user5 private key, so it should fail - const signedMessage = await signMessage(message, user5.address); - - await expectRevert( - erc20Token - .connect(user2) - .startOrder( - consumer, - serviceIndex, - { - providerFeeAddress: providerFeeAddress, - providerFeeToken: providerFeeToken, - providerFeeAmount: providerFeeAmount, - v: signedMessage.v, - r: signedMessage.r, - s: signedMessage.s, - providerData: ethers.utils.hexlify(ethers.utils.toUtf8Bytes(providerData)), - validUntil: providerValidUntil - }, - { - consumeMarketFeeAddress: consumeMarketFeeAddress, - consumeMarketFeeToken: consumeMarketFeeToken, - consumeMarketFeeAmount: consumeMarketFeeAmount, - } - ), - "Invalid provider fee" - ); - }); - it("#startOrder - user should be able to get getPublishingMarketFee", async () => { const publishFee = await erc20TokenWithPublishFee .connect(user2) @@ -929,14 +753,14 @@ describe("ERC20TemplatePredictoor", () => { assert( (await erc20TokenWithPublishFee.balanceOf(opcCollector.address)) == - web3.utils.toWei("0.03"), 'Invalid OPF balance, we should get 0.03 DTs' + web3.utils.toWei("0.0"), 'Invalid OPF balance, we should get 0.03 DTs' ); assert( (await erc20TokenWithPublishFee.balanceOf(user3.address)) == web3.utils.toWei("0"), 'Invalid consumeFee, we should have DT as fee' ); assert( (await erc20TokenWithPublishFee.balanceOf(await erc20TokenWithPublishFee.getPaymentCollector())) == - web3.utils.toWei("0.97"), 'Invalid publisher reward, we should have 0.97 DT' + web3.utils.toWei("0.0"), 'Invalid publisher reward, we should have 0.97 DT' ); }); @@ -1032,14 +856,14 @@ describe("ERC20TemplatePredictoor", () => { assert( (await erc20TokenWithPublishFee.balanceOf(opcCollector.address)) == - web3.utils.toWei("0.03"), 'Invalid OPF balance, we should get 0.03 DTs' + web3.utils.toWei("0.0"), 'Invalid OPF balance, we should get 0.03 DTs' ); assert( (await erc20TokenWithPublishFee.balanceOf(user3.address)) == web3.utils.toWei("0"), 'Invalid consumeFee, we should have DT as fee' ); assert( (await erc20TokenWithPublishFee.balanceOf(await erc20TokenWithPublishFee.getPaymentCollector())) == - web3.utils.toWei("0.97"), 'Invalid publisher reward, we should have 0.97 DT' + web3.utils.toWei("0.0"), 'Invalid publisher reward, we should have 0.97 DT' ); }); From e66ef8cd8a7be70814c7acc89615cc89f0fe3066 Mon Sep 17 00:00:00 2001 From: trizin <25263018+trizin@users.noreply.github.com> Date: Fri, 5 May 2023 13:23:50 +0000 Subject: [PATCH 016/120] Fix --- contracts/templates/ERC20TemplatePredictoor.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/templates/ERC20TemplatePredictoor.sol b/contracts/templates/ERC20TemplatePredictoor.sol index e316e7d4d..f6ff6a491 100644 --- a/contracts/templates/ERC20TemplatePredictoor.sol +++ b/contracts/templates/ERC20TemplatePredictoor.sol @@ -1163,7 +1163,7 @@ contract ERC20TemplatePredictoor is require(s_per_subscription % s_per_block == 0); require(s_per_epoch % s_per_block == 0); - if (!initialized) { + if (blocks_per_epoch == 0) { blocks_per_epoch = s_per_epoch / s_per_block; // immutaable } From 01ccb9170800536bf601eceb65c53fe0e1ac2ad9 Mon Sep 17 00:00:00 2001 From: trizin <25263018+trizin@users.noreply.github.com> Date: Fri, 5 May 2023 13:27:21 +0000 Subject: [PATCH 017/120] Fixes --- contracts/templates/ERC20TemplatePredictoor.sol | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/contracts/templates/ERC20TemplatePredictoor.sol b/contracts/templates/ERC20TemplatePredictoor.sol index 8aa04fa29..c71658c07 100644 --- a/contracts/templates/ERC20TemplatePredictoor.sol +++ b/contracts/templates/ERC20TemplatePredictoor.sol @@ -1071,7 +1071,7 @@ contract ERC20TemplatePredictoor is agg_predvals_numer[blocknum] += stake * (predval ? 1 : 0); agg_predvals_denom[blocknum] += stake; - emit PredictionSubmitted(msg.sender, epoch(), stake); + emit PredictionSubmitted(msg.sender, epoch(blocknum), stake); } function payout( @@ -1091,7 +1091,7 @@ contract ERC20TemplatePredictoor is predobj.paid = true; emit PredictionPayout( predictoor_addr, - epoch(), + epoch(blocknum), predobj.stake, predobj.stake, predobj.predval, @@ -1119,7 +1119,7 @@ contract ERC20TemplatePredictoor is emit PredictionPayout( predictoor_addr, - epoch(), + epoch(blocknum), predobj.stake, payout_amt, predobj.predval, From 759052cbda9d8b94841ede64c8e22f26f6278549 Mon Sep 17 00:00:00 2001 From: trizin <25263018+trizin@users.noreply.github.com> Date: Fri, 5 May 2023 13:30:16 +0000 Subject: [PATCH 018/120] Don't let subscribers see individual predictions --- contracts/templates/ERC20TemplatePredictoor.sol | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/contracts/templates/ERC20TemplatePredictoor.sol b/contracts/templates/ERC20TemplatePredictoor.sol index c71658c07..660a62c4a 100644 --- a/contracts/templates/ERC20TemplatePredictoor.sol +++ b/contracts/templates/ERC20TemplatePredictoor.sol @@ -1033,14 +1033,8 @@ contract ERC20TemplatePredictoor is blocknumOnSlot(blocknum) returns (Prediction memory prediction) { - if (msg.sender != predictoor) { - require(blocknum > soonest_block_to_predict(), "too early to view"); - } //allow predictoors to see their own submissions - require( - is_valid_subscription(msg.sender) || msg.sender == predictoor, - "Not valid subscription" - ); + require(msg.sender == predictoor || blocknum < block.number); prediction = predobjs[blocknum][predictoor]; } From acc62f8e0e61a3e72a1f5ae7a5730b1c197feda5 Mon Sep 17 00:00:00 2001 From: trizin <25263018+trizin@users.noreply.github.com> Date: Fri, 5 May 2023 13:39:13 +0000 Subject: [PATCH 019/120] Add predictoor unit tests --- .../ERC20TemplatePredictoor.test.js | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/test/unit/datatokens/ERC20TemplatePredictoor.test.js b/test/unit/datatokens/ERC20TemplatePredictoor.test.js index 1f684001c..e642d4ac2 100644 --- a/test/unit/datatokens/ERC20TemplatePredictoor.test.js +++ b/test/unit/datatokens/ERC20TemplatePredictoor.test.js @@ -947,5 +947,51 @@ describe("ERC20TemplatePredictoor", () => { , 'Invalid total supply' ); }); + + // PREDICTOOR + it("#blocks_per_epoch - blocks_per_epoch should be set", async () => { + const blocksPerEpoch = await erc20Token.blocks_per_epoch(); + assert(blocksPerEpoch > 0, 'Invalid blocks_per_epoch'); + }); + it("#stake_tokens - stake token should be set", async () => { + const stakeToken = await erc20Token.stake_token(); + assert(stakeToken == mockErc20.address, 'Invalid stake_token'); + }); + it("#blocks_per_subscription - blocks_per_subscription should be set", async () => { + const blocksPerSubscription = await erc20Token.blocks_per_subscription(); + assert(blocksPerSubscription > 0, 'Invalid blocks_per_subscription'); + }); + it("#epoch, cur_epoch - should return currenct epoch", async () => { + const blockNum = await ethers.provider.getBlockNumber(); + const blocksPerEpoch = (await erc20Token.blocks_per_epoch()) + const epoch = parseInt(blockNum / blocksPerEpoch); + assert((await erc20Token.epoch(blockNum))) == epoch; + assert((await erc20Token.cur_epoch())) == epoch; + }); + it("#rail_blocknum_to_slot - should rail blocknum to slot", async () => { + const blockNum = await ethers.provider.getBlockNumber(); + const blocksPerEpoch = (await erc20Token.blocks_per_epoch()) + const slot = parseInt(blockNum / blocksPerEpoch) * blocksPerEpoch; + assert((await erc20Token.rail_blocknum_to_slot(blockNum)) == slot); + }); + it("#soonest_block_to_predict - should return soonest block to predict", async () => { + const soonestBlockToPredict = await erc20Token.soonest_block_to_predict(); + // this should be equal to + // 1 + (currentBlock - 1) / 100 + const blocksPerEpoch = (await erc20Token.blocks_per_epoch()) + const blockNumber = await ethers.provider.getBlockNumber(); + const railed = parseInt(blockNumber / blocksPerEpoch) * blocksPerEpoch + const expected = railed + blocksPerEpoch * (railed == blockNumber ? 1 : 2); + assert(soonestBlockToPredict == expected, 'Invalid soonest block to predict'); + }); + it("#get_agg_predval - without subscription, should revert", async () => { + const blockNumber = await ethers.provider.getBlockNumber() + const blocksPerEpoch = (await erc20Token.blocks_per_epoch()) + const railed = parseInt(blockNumber / blocksPerEpoch) * blocksPerEpoch + await expectRevert( + erc20Token.get_agg_predval(railed), + "No subscription" + ); + }); }); From cae72cb6f2cfe9b2a03d9886c1a8144d91402561 Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Mon, 8 May 2023 02:20:51 -0700 Subject: [PATCH 020/120] force compile --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 02e51ff4f..9f4c0bf4c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,9 +10,9 @@ WORKDIR /ocean-contracts RUN rm package-lock.json RUN rm -rf ./node-modules/ RUN npm i +RUN npx hardhat compile --force ENV SLEEP_FOR_GANACHE=10 RUN cp hardhat.config.barge.js hardhat.config.js ENV NETWORK=barge ENV NETWORK_RPC_URL=127.0.0.1:8545 -RUN npx hardhat compile --force ENTRYPOINT ["/ocean-contracts/scripts/deploy_docker.sh"] From a19921b69e2b66bc4b302e4f1b5dd3036808489b Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Mon, 8 May 2023 02:34:40 -0700 Subject: [PATCH 021/120] more docker tweaks --- Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Dockerfile b/Dockerfile index 9f4c0bf4c..b5d7a9439 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,6 +10,7 @@ WORKDIR /ocean-contracts RUN rm package-lock.json RUN rm -rf ./node-modules/ RUN npm i +RUN npx hardhat clean RUN npx hardhat compile --force ENV SLEEP_FOR_GANACHE=10 RUN cp hardhat.config.barge.js hardhat.config.js From 9a750435edf10238f8331e09a261f4bf9a6a1441 Mon Sep 17 00:00:00 2001 From: trizin <25263018+trizin@users.noreply.github.com> Date: Mon, 8 May 2023 12:21:06 +0000 Subject: [PATCH 022/120] Update tests --- .../datatokens/ERC20TemplatePredictoor.test.js | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/test/unit/datatokens/ERC20TemplatePredictoor.test.js b/test/unit/datatokens/ERC20TemplatePredictoor.test.js index e642d4ac2..a0b9b82a0 100644 --- a/test/unit/datatokens/ERC20TemplatePredictoor.test.js +++ b/test/unit/datatokens/ERC20TemplatePredictoor.test.js @@ -968,11 +968,12 @@ describe("ERC20TemplatePredictoor", () => { assert((await erc20Token.epoch(blockNum))) == epoch; assert((await erc20Token.cur_epoch())) == epoch; }); - it("#rail_blocknum_to_slot - should rail blocknum to slot", async () => { + it("#rail_blocknum_to_slot, blocknum_is_on_a_slot - should rail blocknum to slot", async () => { const blockNum = await ethers.provider.getBlockNumber(); const blocksPerEpoch = (await erc20Token.blocks_per_epoch()) const slot = parseInt(blockNum / blocksPerEpoch) * blocksPerEpoch; assert((await erc20Token.rail_blocknum_to_slot(blockNum)) == slot); + assert((await erc20Token.blocknum_is_on_a_slot(blockNum)) == true); }); it("#soonest_block_to_predict - should return soonest block to predict", async () => { const soonestBlockToPredict = await erc20Token.soonest_block_to_predict(); @@ -993,5 +994,17 @@ describe("ERC20TemplatePredictoor", () => { "No subscription" ); }); + it("#get_agg_predval - without subscription, should revert", async () => { + const blockNumber = await ethers.provider.getBlockNumber() + const blocksPerEpoch = (await erc20Token.blocks_per_epoch()) + const railed = parseInt(blockNumber / blocksPerEpoch) * blocksPerEpoch + await expectRevert( + erc20Token.get_agg_predval(railed), + "No subscription" + ); + }); + it("#is_valid_subscription - without subscription, should return false", async () =>{ + const is_valid_subscription = await erc20Token.is_valid_subscription(erc20Token.address); + assert(is_valid_subscription == false, "Subscription must be invalid"); + }); }); - From 473c90d53dc2c3ce8ed1953a488c6a069988c4d4 Mon Sep 17 00:00:00 2001 From: trizin <25263018+trizin@users.noreply.github.com> Date: Mon, 8 May 2023 12:24:10 +0000 Subject: [PATCH 023/120] Fix add_revenue loop --- contracts/templates/ERC20TemplatePredictoor.sol | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/contracts/templates/ERC20TemplatePredictoor.sol b/contracts/templates/ERC20TemplatePredictoor.sol index 660a62c4a..c74f22d5c 100644 --- a/contracts/templates/ERC20TemplatePredictoor.sol +++ b/contracts/templates/ERC20TemplatePredictoor.sol @@ -1171,12 +1171,16 @@ contract ERC20TemplatePredictoor is function add_revenue(uint256 blocknum, uint256 amount) internal { blocknum = rail_blocknum_to_slot(blocknum); + uint256 num_epochs = blocks_per_subscription / blocks_per_epoch; + uint256 amt_per_epoch = amount / num_epochs; // for loop and add revenue for blocks_per_epoch blocks - for (uint256 i = 0; i < blocks_per_subscription; i++) { + for ( + uint256 i = 0; + i < num_epochs; + i++ + ) { // TODO FIND A WAY TO ACHIEVE THIS WITHOUT A LOOP - subscription_revenue_at_block[blocknum + blocks_per_epoch] += - amount / - blocks_per_subscription; + subscription_revenue_at_block[blocknum + blocks_per_epoch * (i+1)] += amt_per_epoch; } - } + } } } From 0c4f52a75e6419bdad9a4d02179971a333358864 Mon Sep 17 00:00:00 2001 From: trizin <25263018+trizin@users.noreply.github.com> Date: Mon, 8 May 2023 15:35:24 +0300 Subject: [PATCH 024/120] Fix --- contracts/templates/ERC20TemplatePredictoor.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/templates/ERC20TemplatePredictoor.sol b/contracts/templates/ERC20TemplatePredictoor.sol index c74f22d5c..9a3d48856 100644 --- a/contracts/templates/ERC20TemplatePredictoor.sol +++ b/contracts/templates/ERC20TemplatePredictoor.sol @@ -1182,5 +1182,5 @@ contract ERC20TemplatePredictoor is // TODO FIND A WAY TO ACHIEVE THIS WITHOUT A LOOP subscription_revenue_at_block[blocknum + blocks_per_epoch * (i+1)] += amt_per_epoch; } - } } + } } From 466e2487a4ba2bf6c84ad6c90224cda78334aef2 Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Mon, 8 May 2023 07:45:42 -0700 Subject: [PATCH 025/120] move revenue to startOrder --- .../templates/ERC20TemplatePredictoor.sol | 40 ++++--------------- .../ERC20TemplatePredictoor.test.js | 2 +- 2 files changed, 8 insertions(+), 34 deletions(-) diff --git a/contracts/templates/ERC20TemplatePredictoor.sol b/contracts/templates/ERC20TemplatePredictoor.sol index 9a3d48856..9ff140256 100644 --- a/contracts/templates/ERC20TemplatePredictoor.sol +++ b/contracts/templates/ERC20TemplatePredictoor.sol @@ -45,7 +45,7 @@ contract ERC20TemplatePredictoor is address private publishMarketFeeAddress; address private publishMarketFeeToken; uint256 private publishMarketFeeAmount; - + uint256 private rate =0; uint256 public constant BASE = 1e18; // -------------------------- PREDICTOOR -------------------------- @@ -351,6 +351,7 @@ contract ERC20TemplatePredictoor is address[] memory addresses, uint256[] memory uints ) external onlyERC20Deployer nonReentrant returns (bytes32 exchangeId) { + require(fixedRateExchanges.length==0, "Fixed rate already present"); require( stake_token == addresses[0], "Cannot create FRE with baseToken!=stake_token" @@ -370,37 +371,10 @@ contract ERC20TemplatePredictoor is addresses[0] ); fixedRateExchanges.push(fixedRate(fixedPriceAddress, exchangeId)); + rate = uints[2]; //set rate to be added in add_revenue } - /** - * @dev createDispenser - * Creates a new Dispenser - * @param _dispenser dispenser contract address - * @param maxTokens - max tokens to dispense - * @param maxBalance - max balance of requester. - * @param withMint - with MinterRole - * @param allowedSwapper allowed swappers - */ - function createDispenser( - address _dispenser, - uint256 maxTokens, - uint256 maxBalance, - bool withMint, - address allowedSwapper - ) external onlyERC20Deployer nonReentrant { - // add dispenser contract as minter if withMint == true - if (withMint) _addMinter(_dispenser); - dispensers.push(_dispenser); - emit NewDispenser(_dispenser); - IFactoryRouter(router).deployDispenser( - _dispenser, - address(this), - maxTokens, - maxBalance, - msg.sender, - allowedSwapper - ); - } + /** * @dev mint @@ -493,7 +467,9 @@ contract ERC20TemplatePredictoor is block.number + blocks_per_subscription ); subscriptions[consumer] = sub; - + //record income + add_revenue(block.number, rate); + burn(amount); } @@ -871,8 +847,6 @@ contract ERC20TemplatePredictoor is _freParams.exchangeId ); if (btBalance > 0) { - //record income - add_revenue(block.number, btBalance); fre.collectBT(_freParams.exchangeId, btBalance); } } diff --git a/test/unit/datatokens/ERC20TemplatePredictoor.test.js b/test/unit/datatokens/ERC20TemplatePredictoor.test.js index a0b9b82a0..594e83265 100644 --- a/test/unit/datatokens/ERC20TemplatePredictoor.test.js +++ b/test/unit/datatokens/ERC20TemplatePredictoor.test.js @@ -154,7 +154,7 @@ describe("ERC20TemplatePredictoor", () => { ); templateERC20 = await ERC20TemplatePredictoor.deploy(); - + // SETUP ERC721 Factory with template templateERC721 = await ERC721Template.deploy(); From 0d0bb1a77ccb0daf781d0335588a91ba218f469e Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Mon, 8 May 2023 21:28:14 -0700 Subject: [PATCH 026/120] add condition --- .../templates/ERC20TemplatePredictoor.sol | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/contracts/templates/ERC20TemplatePredictoor.sol b/contracts/templates/ERC20TemplatePredictoor.sol index 9ff140256..822ebd325 100644 --- a/contracts/templates/ERC20TemplatePredictoor.sol +++ b/contracts/templates/ERC20TemplatePredictoor.sol @@ -1144,17 +1144,19 @@ contract ERC20TemplatePredictoor is } function add_revenue(uint256 blocknum, uint256 amount) internal { - blocknum = rail_blocknum_to_slot(blocknum); - uint256 num_epochs = blocks_per_subscription / blocks_per_epoch; - uint256 amt_per_epoch = amount / num_epochs; - // for loop and add revenue for blocks_per_epoch blocks - for ( - uint256 i = 0; - i < num_epochs; - i++ - ) { - // TODO FIND A WAY TO ACHIEVE THIS WITHOUT A LOOP - subscription_revenue_at_block[blocknum + blocks_per_epoch * (i+1)] += amt_per_epoch; + if(amount>0){ + blocknum = rail_blocknum_to_slot(blocknum); + uint256 num_epochs = blocks_per_subscription / blocks_per_epoch; + uint256 amt_per_epoch = amount / num_epochs; + // for loop and add revenue for blocks_per_epoch blocks + for ( + uint256 i = 0; + i < num_epochs; + i++ + ) { + // TODO FIND A WAY TO ACHIEVE THIS WITHOUT A LOOP + subscription_revenue_at_block[blocknum + blocks_per_epoch * (i+1)] += amt_per_epoch; + } } } } From 6d875ebb70cdd946f3d5d691094df35cc20b3ea1 Mon Sep 17 00:00:00 2001 From: trizin <25263018+trizin@users.noreply.github.com> Date: Tue, 9 May 2023 09:22:38 +0000 Subject: [PATCH 027/120] Bug fix: should check <= --- .../templates/ERC20TemplatePredictoor.sol | 22 ++++++++----------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/contracts/templates/ERC20TemplatePredictoor.sol b/contracts/templates/ERC20TemplatePredictoor.sol index 822ebd325..1ce6e2797 100644 --- a/contracts/templates/ERC20TemplatePredictoor.sol +++ b/contracts/templates/ERC20TemplatePredictoor.sol @@ -45,7 +45,7 @@ contract ERC20TemplatePredictoor is address private publishMarketFeeAddress; address private publishMarketFeeToken; uint256 private publishMarketFeeAmount; - uint256 private rate =0; + uint256 private rate = 0; uint256 public constant BASE = 1e18; // -------------------------- PREDICTOOR -------------------------- @@ -351,7 +351,7 @@ contract ERC20TemplatePredictoor is address[] memory addresses, uint256[] memory uints ) external onlyERC20Deployer nonReentrant returns (bytes32 exchangeId) { - require(fixedRateExchanges.length==0, "Fixed rate already present"); + require(fixedRateExchanges.length == 0, "Fixed rate already present"); require( stake_token == addresses[0], "Cannot create FRE with baseToken!=stake_token" @@ -374,8 +374,6 @@ contract ERC20TemplatePredictoor is rate = uints[2]; //set rate to be added in add_revenue } - - /** * @dev mint * Only the minter address can call it. @@ -469,7 +467,7 @@ contract ERC20TemplatePredictoor is subscriptions[consumer] = sub; //record income add_revenue(block.number, rate); - + burn(amount); } @@ -1019,7 +1017,7 @@ contract ERC20TemplatePredictoor is uint256 stake, uint256 blocknum ) external blocknumOnSlot(blocknum) { - require(blocknum > soonest_block_to_predict(), "too late to submit"); + require(blocknum <= soonest_block_to_predict(), "too late to submit"); require(!submitted_predval(blocknum, msg.sender), "already submitted"); require(paused == false, "paused"); @@ -1144,18 +1142,16 @@ contract ERC20TemplatePredictoor is } function add_revenue(uint256 blocknum, uint256 amount) internal { - if(amount>0){ + if (amount > 0) { blocknum = rail_blocknum_to_slot(blocknum); uint256 num_epochs = blocks_per_subscription / blocks_per_epoch; uint256 amt_per_epoch = amount / num_epochs; // for loop and add revenue for blocks_per_epoch blocks - for ( - uint256 i = 0; - i < num_epochs; - i++ - ) { + for (uint256 i = 0; i < num_epochs; i++) { // TODO FIND A WAY TO ACHIEVE THIS WITHOUT A LOOP - subscription_revenue_at_block[blocknum + blocks_per_epoch * (i+1)] += amt_per_epoch; + subscription_revenue_at_block[ + blocknum + blocks_per_epoch * (i + 1) + ] += amt_per_epoch; } } } From d79dbf3a59542f5250559ba97c0d0622f586dc79 Mon Sep 17 00:00:00 2001 From: trizin <25263018+trizin@users.noreply.github.com> Date: Tue, 9 May 2023 09:29:14 +0000 Subject: [PATCH 028/120] Use camelcase --- test/unit/datatokens/ERC20TemplatePredictoor.test.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/test/unit/datatokens/ERC20TemplatePredictoor.test.js b/test/unit/datatokens/ERC20TemplatePredictoor.test.js index 594e83265..f9b692686 100644 --- a/test/unit/datatokens/ERC20TemplatePredictoor.test.js +++ b/test/unit/datatokens/ERC20TemplatePredictoor.test.js @@ -1003,8 +1003,9 @@ describe("ERC20TemplatePredictoor", () => { "No subscription" ); }); - it("#is_valid_subscription - without subscription, should return false", async () =>{ - const is_valid_subscription = await erc20Token.is_valid_subscription(erc20Token.address); - assert(is_valid_subscription == false, "Subscription must be invalid"); + it("#is_valid_subscription - without subscription, should return false", async () => { + const isValidSubscription = await erc20Token.is_valid_subscription(erc20Token.address); + assert(isValidSubscription == false, "Subscription must be invalid"); + }); }); }); From 73cb231350e5459615000ace225baab67f3c987d Mon Sep 17 00:00:00 2001 From: trizin <25263018+trizin@users.noreply.github.com> Date: Tue, 9 May 2023 10:12:25 +0000 Subject: [PATCH 029/120] Fix submit predval require logic and other improvements --- contracts/templates/ERC20TemplatePredictoor.sol | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/contracts/templates/ERC20TemplatePredictoor.sol b/contracts/templates/ERC20TemplatePredictoor.sol index 1ce6e2797..1948bd435 100644 --- a/contracts/templates/ERC20TemplatePredictoor.sol +++ b/contracts/templates/ERC20TemplatePredictoor.sol @@ -1017,7 +1017,7 @@ contract ERC20TemplatePredictoor is uint256 stake, uint256 blocknum ) external blocknumOnSlot(blocknum) { - require(blocknum <= soonest_block_to_predict(), "too late to submit"); + require(blocknum >= soonest_block_to_predict(), "too late to submit"); require(!submitted_predval(blocknum, msg.sender), "already submitted"); require(paused == false, "paused"); @@ -1110,13 +1110,12 @@ contract ERC20TemplatePredictoor is function update_seconds( uint256 s_per_block, - uint256 s_per_epoch, uint256 s_per_subscription, uint256 _truval_submit_timeout ) external onlyERC20Deployer { _update_seconds( s_per_block, - s_per_epoch, + 0, // can only be set once s_per_subscription, _truval_submit_timeout ); @@ -1130,8 +1129,8 @@ contract ERC20TemplatePredictoor is uint256 s_per_subscription, uint256 _truval_submit_timeout ) internal { - require(s_per_subscription % s_per_block == 0); - require(s_per_epoch % s_per_block == 0); + require(s_per_subscription % s_per_block == 0, "%"); + require(s_per_epoch % s_per_block == 0, "%"); if (blocks_per_epoch == 0) { blocks_per_epoch = s_per_epoch / s_per_block; // immutaable From 4e342d6267186af5a8c92ef4dfd33dd9e938bba9 Mon Sep 17 00:00:00 2001 From: trizin <25263018+trizin@users.noreply.github.com> Date: Tue, 9 May 2023 10:47:11 +0000 Subject: [PATCH 030/120] Make subscriptions public --- contracts/templates/ERC20TemplatePredictoor.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/templates/ERC20TemplatePredictoor.sol b/contracts/templates/ERC20TemplatePredictoor.sol index 1948bd435..fa195fe8e 100644 --- a/contracts/templates/ERC20TemplatePredictoor.sol +++ b/contracts/templates/ERC20TemplatePredictoor.sol @@ -83,7 +83,7 @@ contract ERC20TemplatePredictoor is mapping(uint256 => bool) truevals; mapping(uint256 => bool) truval_submitted; mapping(uint256 => uint256) subscription_revenue_at_block; //income registred - mapping(address => Subscription) subscriptions; // valid subscription per user + mapping(address => Subscription) public subscriptions; // valid subscription per user uint256 public blocks_per_epoch; address public stake_token; uint256 public blocks_per_subscription; From 15a4558f91960e3f9f1cf0830d4c00246117cb83 Mon Sep 17 00:00:00 2001 From: trizin <25263018+trizin@users.noreply.github.com> Date: Tue, 9 May 2023 10:48:29 +0000 Subject: [PATCH 031/120] Set vars as private --- contracts/templates/ERC20TemplatePredictoor.sol | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/contracts/templates/ERC20TemplatePredictoor.sol b/contracts/templates/ERC20TemplatePredictoor.sol index fa195fe8e..9cf456e26 100644 --- a/contracts/templates/ERC20TemplatePredictoor.sol +++ b/contracts/templates/ERC20TemplatePredictoor.sol @@ -77,12 +77,12 @@ contract ERC20TemplatePredictoor is address user; uint256 expires; } - mapping(uint256 => mapping(address => Prediction)) predobjs; // id to prediction object - mapping(uint256 => uint256) agg_predvals_numer; - mapping(uint256 => uint256) agg_predvals_denom; + mapping(uint256 => mapping(address => Prediction)) private predobjs; // id to prediction object + mapping(uint256 => uint256) private agg_predvals_numer; + mapping(uint256 => uint256) private agg_predvals_denom; mapping(uint256 => bool) truevals; mapping(uint256 => bool) truval_submitted; - mapping(uint256 => uint256) subscription_revenue_at_block; //income registred + mapping(uint256 => uint256) private subscription_revenue_at_block; //income registred mapping(address => Subscription) public subscriptions; // valid subscription per user uint256 public blocks_per_epoch; address public stake_token; From 88c5b8c389e3ba10c8e87360a4470b1ed937e14b Mon Sep 17 00:00:00 2001 From: trizin <25263018+trizin@users.noreply.github.com> Date: Tue, 9 May 2023 10:49:24 +0000 Subject: [PATCH 032/120] Remove predefined value --- contracts/templates/ERC20TemplatePredictoor.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/templates/ERC20TemplatePredictoor.sol b/contracts/templates/ERC20TemplatePredictoor.sol index 9cf456e26..79ee19e9e 100644 --- a/contracts/templates/ERC20TemplatePredictoor.sol +++ b/contracts/templates/ERC20TemplatePredictoor.sol @@ -87,7 +87,7 @@ contract ERC20TemplatePredictoor is uint256 public blocks_per_epoch; address public stake_token; uint256 public blocks_per_subscription; - uint256 public truval_submit_timeout_block = 3; + uint256 public truval_submit_timeout_block; bool public paused = false; // -------------------------- PREDICTOOR -------------------------- From 18d96c7c5344f296259c392438779e79b5148c3f Mon Sep 17 00:00:00 2001 From: trizin <25263018+trizin@users.noreply.github.com> Date: Tue, 9 May 2023 10:52:52 +0000 Subject: [PATCH 033/120] Add revenue to soonest block --- contracts/templates/ERC20TemplatePredictoor.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/templates/ERC20TemplatePredictoor.sol b/contracts/templates/ERC20TemplatePredictoor.sol index 79ee19e9e..ad67370c7 100644 --- a/contracts/templates/ERC20TemplatePredictoor.sol +++ b/contracts/templates/ERC20TemplatePredictoor.sol @@ -466,7 +466,7 @@ contract ERC20TemplatePredictoor is ); subscriptions[consumer] = sub; //record income - add_revenue(block.number, rate); + add_revenue(soonest_block_to_predict(), rate); burn(amount); } @@ -1149,7 +1149,7 @@ contract ERC20TemplatePredictoor is for (uint256 i = 0; i < num_epochs; i++) { // TODO FIND A WAY TO ACHIEVE THIS WITHOUT A LOOP subscription_revenue_at_block[ - blocknum + blocks_per_epoch * (i + 1) + blocknum + blocks_per_epoch * (i) ] += amt_per_epoch; } } From b737c050f1b5780ccd0082da3be6e44cee7bb866 Mon Sep 17 00:00:00 2001 From: trizin <25263018+trizin@users.noreply.github.com> Date: Tue, 9 May 2023 10:54:05 +0000 Subject: [PATCH 034/120] Go unit tests go! --- .../ERC20TemplatePredictoor.test.js | 205 +++++++++++++++++- 1 file changed, 204 insertions(+), 1 deletion(-) diff --git a/test/unit/datatokens/ERC20TemplatePredictoor.test.js b/test/unit/datatokens/ERC20TemplatePredictoor.test.js index f9b692686..f1c8f6f70 100644 --- a/test/unit/datatokens/ERC20TemplatePredictoor.test.js +++ b/test/unit/datatokens/ERC20TemplatePredictoor.test.js @@ -154,7 +154,7 @@ describe("ERC20TemplatePredictoor", () => { ); templateERC20 = await ERC20TemplatePredictoor.deploy(); - + // SETUP ERC721 Factory with template templateERC721 = await ERC721Template.deploy(); @@ -1007,5 +1007,208 @@ describe("ERC20TemplatePredictoor", () => { const isValidSubscription = await erc20Token.is_valid_subscription(erc20Token.address); assert(isValidSubscription == false, "Subscription must be invalid"); }); + it("#submit_predval - predictoor submits predval", async () => { + const predval = true; + const stake = 100; + await mockErc20.approve(erc20Token.address, stake); + const soonestBlockToPredict = await erc20Token.soonest_block_to_predict(); + const predictionEpoch = await erc20Token.epoch(soonestBlockToPredict); + + const tx = await erc20Token.submit_predval(predval, stake, soonestBlockToPredict); + const tx_receipt = await tx.wait(); + const event = tx_receipt.events[2]; + expect(event.event).to.equal("PredictionSubmitted"); + expect(event.args[0]).to.equal(owner.address); + expect(event.args[1]).to.equal(predictionEpoch); + expect(event.args[2]).to.equal(stake); + }); + it("#submit_predval - should revert when predictoor submits too early", async () => { + const predval = true; + const stake = 100; + const block = await ethers.provider.getBlockNumber(); + const railed = await erc20Token.rail_blocknum_to_slot(block - 100); + await mockErc20.approve(erc20Token.address, stake); + + await expectRevert( + erc20Token.submit_predval(predval, stake, railed), + "too late to submit" + ); + }); + it("#submit_predval - should revert when predictoor submits duplicate prediction", async () => { + const predval = true; + const stake = 100; + await mockErc20.approve(erc20Token.address, stake * 2); + const soonestBlockToPredict = await erc20Token.soonest_block_to_predict(); + + await erc20Token.submit_predval(predval, stake, soonestBlockToPredict); + + await expectRevert( + erc20Token.submit_predval(predval, stake, soonestBlockToPredict), + "already submitted" + ); + }); + it("#pause_predictions - should pause and resume predictions", async () => { + await erc20Token.pause_predictions(); + const isPaused = await erc20Token.paused(); + assert(isPaused == true, "Predictions should be paused"); + + // submit predval should revert + const predval = true; + const stake = 100; + await mockErc20.approve(erc20Token.address, stake); + const soonestBlockToPredict = await erc20Token.soonest_block_to_predict(); + await expectRevert( + erc20Token.submit_predval(predval, stake, soonestBlockToPredict), + "paused" + ); + + await erc20Token.pause_predictions(); + const isResumed = await erc20Token.paused(); + assert(isResumed == false, "Predictions should be resumed"); + }); + + it("#update_seconds - should revert when seconds per subscription is not divisible by seconds per block", async () => { + const s_per_block = 3; + const s_per_subscription = 10; + const _truval_submit_timeout = 30; + + await expectRevert( + erc20Token.update_seconds(s_per_block, s_per_subscription, _truval_submit_timeout), + "%" + ); + }); + + it("#subscriptions - user2 must be subscribed", async () => { + //MINT SOME DT20 to USER2 so he can start order + await erc20Token.connect(user3).mint(user2.address, web3.utils.toWei("10")); + assert( + (await erc20Token.balanceOf(user2.address)) == web3.utils.toWei("10") + ); + const consumer = user2.address; // could be different user + const serviceIndex = 1; // dummy index + const providerFeeAddress = user5.address; // marketplace fee Collector + const providerFeeAmount = 0; // fee to be collected on top, requires approval + const providerFeeToken = mockErc20.address; // token address for the feeAmount, + const consumeMarketFeeAddress = user5.address; // marketplace fee Collector + const consumeMarketFeeAmount = 0; // fee to be collected on top, requires approval + const consumeMarketFeeToken = mockErc20.address; // token address for the feeAmount, + const providerValidUntil = 0; + //sign provider data + const providerData = JSON.stringify({ "timeout": 0 }) + const message = ethers.utils.solidityKeccak256( + ["bytes", "address", "address", "uint256", "uint256"], + [ + ethers.utils.hexlify(ethers.utils.toUtf8Bytes(providerData)), + providerFeeAddress, + providerFeeToken, + providerFeeAmount, + providerValidUntil + ] + ); + const signedMessage = await signMessage(message, providerFeeAddress); + const tx = await erc20Token + .connect(user2) + .startOrder( + consumer, + serviceIndex, + { + providerFeeAddress: providerFeeAddress, + providerFeeToken: providerFeeToken, + providerFeeAmount: providerFeeAmount, + v: signedMessage.v, + r: signedMessage.r, + s: signedMessage.s, + providerData: ethers.utils.hexlify(ethers.utils.toUtf8Bytes(providerData)), + validUntil: providerValidUntil + }, + { + consumeMarketFeeAddress: consumeMarketFeeAddress, + consumeMarketFeeToken: consumeMarketFeeToken, + consumeMarketFeeAmount: consumeMarketFeeAmount, + } + ); + + + const subscription = await erc20Token.subscriptions(user2.address); + // check if subscription is valid + const currentBlock = await ethers.provider.getBlockNumber(); + expect(subscription.expires).to.be.gt(currentBlock); + expect(subscription.user).to.be.eq(user2.address); + }); + + + // can read get_agg_predval with a valid subscription + it("#get_agg_predval - should return agg_predval if caller has a valid subscription", async () => { + //MINT SOME DT20 to USER2 so he can start order + await erc20Token.connect(user3).mint(user2.address, web3.utils.toWei("10")); + assert( + (await erc20Token.balanceOf(user2.address)) == web3.utils.toWei("10") + ); + const consumer = user2.address; // could be different user + const serviceIndex = 1; // dummy index + const providerFeeAddress = user5.address; // marketplace fee Collector + const providerFeeAmount = 0; // fee to be collected on top, requires approval + const providerFeeToken = mockErc20.address; // token address for the feeAmount, + const consumeMarketFeeAddress = user5.address; // marketplace fee Collector + const consumeMarketFeeAmount = 0; // fee to be collected on top, requires approval + const consumeMarketFeeToken = mockErc20.address; // token address for the feeAmount, + const providerValidUntil = 0; + //sign provider data + const providerData = JSON.stringify({ "timeout": 0 }) + const message = ethers.utils.solidityKeccak256( + ["bytes", "address", "address", "uint256", "uint256"], + [ + ethers.utils.hexlify(ethers.utils.toUtf8Bytes(providerData)), + providerFeeAddress, + providerFeeToken, + providerFeeAmount, + providerValidUntil + ] + ); + const signedMessage = await signMessage(message, providerFeeAddress); + await erc20Token + .connect(user2) + .startOrder( + consumer, + serviceIndex, + { + providerFeeAddress: providerFeeAddress, + providerFeeToken: providerFeeToken, + providerFeeAmount: providerFeeAmount, + v: signedMessage.v, + r: signedMessage.r, + s: signedMessage.s, + providerData: ethers.utils.hexlify(ethers.utils.toUtf8Bytes(providerData)), + validUntil: providerValidUntil + }, + { + consumeMarketFeeAddress: consumeMarketFeeAddress, + consumeMarketFeeToken: consumeMarketFeeToken, + consumeMarketFeeAmount: consumeMarketFeeAmount, + } + ); + + + let soonestBlockToPredict = await erc20Token.soonest_block_to_predict(); + const [numer, denom] = await erc20Token.connect(user2).get_agg_predval(soonestBlockToPredict); + expect(numer).to.be.eq(0); + expect(denom).to.be.eq(0); + + // user2 makes a prediction + const predval = true; + const stake = web3.utils.toWei("1"); + await mockErc20.transfer(user3.address, stake); + await mockErc20.connect(user3).approve(erc20Token.address, stake); + soonestBlockToPredict = await erc20Token.soonest_block_to_predict(); + await erc20Token.connect(user3).submit_predval(predval, stake, soonestBlockToPredict); + + soonestBlockToPredict = await erc20Token.soonest_block_to_predict(); + const [numer2, denom2] = await erc20Token.connect(user2).get_agg_predval(soonestBlockToPredict); + expect(numer2).to.be.eq(web3.utils.toWei("1")); + expect(denom2).to.be.eq(web3.utils.toWei("1")); + + // check subscription revenue + const revenue = await erc20Token.get_subscription_revenue_at_block(soonestBlockToPredict); + expect(revenue).to.be.gt(0); }); }); From 6b1e50f3a28e16e587f40fb7f07db7254c0ae0b3 Mon Sep 17 00:00:00 2001 From: trizin <25263018+trizin@users.noreply.github.com> Date: Tue, 9 May 2023 11:40:15 +0000 Subject: [PATCH 035/120] Use current block number --- contracts/templates/ERC20TemplatePredictoor.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/templates/ERC20TemplatePredictoor.sol b/contracts/templates/ERC20TemplatePredictoor.sol index ad67370c7..08cc91ffd 100644 --- a/contracts/templates/ERC20TemplatePredictoor.sol +++ b/contracts/templates/ERC20TemplatePredictoor.sol @@ -466,7 +466,7 @@ contract ERC20TemplatePredictoor is ); subscriptions[consumer] = sub; //record income - add_revenue(soonest_block_to_predict(), rate); + add_revenue(block.number, rate); burn(amount); } From 40a5725144e1d413dea13b5632413641ae3f45f1 Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Tue, 9 May 2023 05:24:27 -0700 Subject: [PATCH 036/120] add caller buys a subscription --- .../templates/ERC20TemplatePredictoor.sol | 34 +------- .../ERC20TemplatePredictoor.test.js | 85 ++++++++++++++++++- 2 files changed, 86 insertions(+), 33 deletions(-) diff --git a/contracts/templates/ERC20TemplatePredictoor.sol b/contracts/templates/ERC20TemplatePredictoor.sol index ad67370c7..3e7509fa8 100644 --- a/contracts/templates/ERC20TemplatePredictoor.sol +++ b/contracts/templates/ERC20TemplatePredictoor.sol @@ -19,12 +19,10 @@ import "../utils/ERC20Roles.sol"; /** * @title DatatokenTemplate * - * @dev ERC20TemplateEnterprise is an ERC20 compliant token template + * @dev ERC20TemplatePredictoor is an ERC20 compliant token template * Used by the factory contract as a bytecode reference to * deploy new Datatokens. * IMPORTANT CHANGES: - * - buyFromFreAndOrder function: one call to buy a DT from the minting capable FRE, startOrder and burn the DT - * - buyFromDispenserAndOrder function: one call to fetch a DT from the Dispenser, startOrder and burn the DT * - creation of pools is not allowed */ contract ERC20TemplatePredictoor is @@ -357,7 +355,6 @@ contract ERC20TemplatePredictoor is "Cannot create FRE with baseToken!=stake_token" ); //force FRE allowedSwapper to this contract address. no one else can swap because we need to record the income - addresses[3] = address(this); if (uints[4] > 0) _addMinter(fixedPriceAddress); exchangeId = IFactoryRouter(router).deployFixedRate( fixedPriceAddress, @@ -869,34 +866,7 @@ contract ERC20TemplatePredictoor is _orderParams._consumeMarketFee ); } - - /** - * @dev buyFromDispenserAndOrder - * Gets DT from dispenser and then startsOrder, while burning that DT - */ - function buyFromDispenserAndOrder( - OrderParams calldata _orderParams, - address dispenserContract - ) external nonReentrant { - uint256 amount = 1e18; - //get DT - IDispenser(dispenserContract).dispense( - address(this), - amount, - msg.sender - ); - require( - balanceOf(address(msg.sender)) >= amount, - "Unable to get DT from Dispenser" - ); - //startOrder and burn it - startOrder( - _orderParams.consumer, - _orderParams.serviceIndex, - _orderParams._providerFee, - _orderParams._consumeMarketFee - ); - } + /** * @dev isERC20Deployer diff --git a/test/unit/datatokens/ERC20TemplatePredictoor.test.js b/test/unit/datatokens/ERC20TemplatePredictoor.test.js index f1c8f6f70..b39bc2f8e 100644 --- a/test/unit/datatokens/ERC20TemplatePredictoor.test.js +++ b/test/unit/datatokens/ERC20TemplatePredictoor.test.js @@ -10,6 +10,7 @@ const { web3 } = require("@openzeppelin/test-helpers/src/setup"); const { keccak256 } = require("@ethersproject/keccak256"); const ethers = hre.ethers; const { ecsign, zeroAddress } = require("ethereumjs-util"); +const { ZERO_ADDRESS } = require("@openzeppelin/test-helpers/src/constants"); const getDomainSeparator = (name, tokenAddress, chainId) => { @@ -112,6 +113,7 @@ describe("ERC20TemplatePredictoor", () => { const communityFeeCollector = "0xeE9300b7961e0a01d9f0adb863C7A227A07AaD75"; const publishMarketFeeAmount = "5" const addressZero = '0x0000000000000000000000000000000000000000'; + const noLimit = web3.utils.toWei('100000000000000000000'); beforeEach("init contracts for each test", async () => { const ERC721Template = await ethers.getContractFactory("ERC721Template"); @@ -1209,6 +1211,87 @@ describe("ERC20TemplatePredictoor", () => { // check subscription revenue const revenue = await erc20Token.get_subscription_revenue_at_block(soonestBlockToPredict); - expect(revenue).to.be.gt(0); + expect(revenue).to.be.eq(0); + }); + + // can read get_agg_predval with a valid subscription + it("#get_agg_predval - should return agg_predval if caller buys a subscription", async () => { + const consumer = user2.address; // could be different user + const serviceIndex = 1; // dummy index + const providerFeeAddress = user5.address; // marketplace fee Collector + const providerFeeAmount = 0; // fee to be collected on top, requires approval + const providerFeeToken = mockErc20.address; // token address for the feeAmount, + const consumeMarketFeeAddress = user5.address; // marketplace fee Collector + const consumeMarketFeeAmount = 0; // fee to be collected on top, requires approval + const consumeMarketFeeToken = mockErc20.address; // token address for the feeAmount, + const providerValidUntil = 0; + const marketFee = 1e15 // 0.1% + const marketFeeCollector = addressZero + const rate = web3.utils.toWei("2"); // 2 tokens per dt + const amountDT = web3.utils.toWei("1"); + + //create fixed rate + const tx = await erc20Token.connect(owner).createFixedRate( + fixedRateExchange.address, + [mockErc20.address, owner.address, marketFeeCollector, addressZero], + [18, 18, rate, marketFee, 1]) + const txReceipt = await tx.wait(); + let event = getEventFromTx(txReceipt, 'NewFixedRate') + assert(event, "Cannot find NewFixedRate event") + exchangeId = event.args.exchangeId + const exchangeInfo = await fixedRateExchange.calcBaseInGivenOutDT(exchangeId,amountDT,0) + + //let's buy a DT + await mockErc20.transfer(user2.address, exchangeInfo.baseTokenAmount); + await mockErc20.connect(user2).approve(fixedRateExchange.address, exchangeInfo.baseTokenAmount); + // user buys DT + await fixedRateExchange.connect(user2).buyDT(exchangeId, amountDT, exchangeInfo.baseTokenAmount ,addressZero, 0) + const balance = await erc20Token.balanceOf(user2.address) + assert(balance > 0 , "Failed to buy DT") + //sign provider data + const providerData = JSON.stringify({ "timeout": 0 }) + const message = ethers.utils.solidityKeccak256( + ["bytes", "address", "address", "uint256", "uint256"], + [ + ethers.utils.hexlify(ethers.utils.toUtf8Bytes(providerData)), + providerFeeAddress, + providerFeeToken, + providerFeeAmount, + providerValidUntil + ] + ); + const signedMessage = await signMessage(message, providerFeeAddress); + let soonestBlockToPredict = await erc20Token.soonest_block_to_predict(); + let revenue_at_block = await erc20Token.connect(user2).get_subscription_revenue_at_block(soonestBlockToPredict) + expect(revenue_at_block).to.be.eq(0); + + await erc20Token + .connect(user2) + .startOrder( + consumer, + serviceIndex, + { + providerFeeAddress: providerFeeAddress, + providerFeeToken: providerFeeToken, + providerFeeAmount: providerFeeAmount, + v: signedMessage.v, + r: signedMessage.r, + s: signedMessage.s, + providerData: ethers.utils.hexlify(ethers.utils.toUtf8Bytes(providerData)), + validUntil: providerValidUntil + }, + { + consumeMarketFeeAddress: consumeMarketFeeAddress, + consumeMarketFeeToken: consumeMarketFeeToken, + consumeMarketFeeAmount: consumeMarketFeeAmount, + } + ); + + revenue_at_block = await erc20Token.connect(user2).get_subscription_revenue_at_block(soonestBlockToPredict) + expect(revenue_at_block).to.be.gt(0); + + + + }); }); From b281774fc2842453c1ef5fa85176709abed08af0 Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Tue, 9 May 2023 05:42:52 -0700 Subject: [PATCH 037/120] prevent Reentrancy --- .../templates/ERC20TemplatePredictoor.sol | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/contracts/templates/ERC20TemplatePredictoor.sol b/contracts/templates/ERC20TemplatePredictoor.sol index 11b5757de..b6fd64e56 100644 --- a/contracts/templates/ERC20TemplatePredictoor.sol +++ b/contracts/templates/ERC20TemplatePredictoor.sol @@ -1000,14 +1000,14 @@ contract ERC20TemplatePredictoor is predobjs[blocknum][msg.sender] = predobj; - // safe transfer stake - IERC20(stake_token).safeTransferFrom(msg.sender, address(this), stake); - + // update agg_predvals agg_predvals_numer[blocknum] += stake * (predval ? 1 : 0); agg_predvals_denom[blocknum] += stake; emit PredictionSubmitted(msg.sender, epoch(blocknum), stake); + // safe transfer stake + IERC20(stake_token).safeTransferFrom(msg.sender, address(this), stake); } function payout( @@ -1023,7 +1023,6 @@ contract ERC20TemplatePredictoor is block.number > blocknum + truval_submit_timeout_block && !truval_submitted[blocknum] ) { - IERC20(stake_token).safeTransfer(predobj.predictoor, predobj.stake); predobj.paid = true; emit PredictionPayout( predictoor_addr, @@ -1033,6 +1032,7 @@ contract ERC20TemplatePredictoor is predobj.predval, truevals[blocknum] ); + IERC20(stake_token).safeTransfer(predobj.predictoor, predobj.stake); return; } @@ -1046,13 +1046,7 @@ contract ERC20TemplatePredictoor is agg_predvals_denom[blocknum] * get_subscription_revenue_at_block(blocknum)) / swe; - IERC20(stake_token).safeTransferFrom( - address(this), - predobj.predictoor, - payout_amt - ); predobj.paid = true; - emit PredictionPayout( predictoor_addr, epoch(blocknum), @@ -1061,6 +1055,11 @@ contract ERC20TemplatePredictoor is predobj.predval, truevals[blocknum] ); + IERC20(stake_token).safeTransferFrom( + address(this), + predobj.predictoor, + payout_amt + ); } // ----------------------- ADMIN FUNCTIONS ----------------------- From 9c6da8aef360620bb112ffc629e10d0a5f578615 Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Tue, 9 May 2023 05:47:20 -0700 Subject: [PATCH 038/120] remove hardcoded events index --- test/unit/datatokens/ERC20TemplatePredictoor.test.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/unit/datatokens/ERC20TemplatePredictoor.test.js b/test/unit/datatokens/ERC20TemplatePredictoor.test.js index b39bc2f8e..249f15854 100644 --- a/test/unit/datatokens/ERC20TemplatePredictoor.test.js +++ b/test/unit/datatokens/ERC20TemplatePredictoor.test.js @@ -1017,8 +1017,9 @@ describe("ERC20TemplatePredictoor", () => { const predictionEpoch = await erc20Token.epoch(soonestBlockToPredict); const tx = await erc20Token.submit_predval(predval, stake, soonestBlockToPredict); - const tx_receipt = await tx.wait(); - const event = tx_receipt.events[2]; + const txReceipt = await tx.wait(); + const event = getEventFromTx(txReceipt, 'PredictionSubmitted') + assert(event, "Cannot find PredictionSubmitted event") expect(event.event).to.equal("PredictionSubmitted"); expect(event.args[0]).to.equal(owner.address); expect(event.args[1]).to.equal(predictionEpoch); From 6f2dabceafbf667d3e2ef12be2e4ba5ab1e2e832 Mon Sep 17 00:00:00 2001 From: trizin <25263018+trizin@users.noreply.github.com> Date: Wed, 10 May 2023 09:09:48 +0000 Subject: [PATCH 039/120] Add multi payout function --- contracts/templates/ERC20TemplatePredictoor.sol | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/contracts/templates/ERC20TemplatePredictoor.sol b/contracts/templates/ERC20TemplatePredictoor.sol index b6fd64e56..40c6cd802 100644 --- a/contracts/templates/ERC20TemplatePredictoor.sol +++ b/contracts/templates/ERC20TemplatePredictoor.sol @@ -1010,10 +1010,19 @@ contract ERC20TemplatePredictoor is IERC20(stake_token).safeTransferFrom(msg.sender, address(this), stake); } + function payout_mul( + uint256[] calldata blocknums, + address predictoor_addr + ) external { + for (uint i = 0; i < blocknums.length; i++) { + payout(blocknums[i], predictoor_addr); + } + } + function payout( uint256 blocknum, address predictoor_addr - ) external blocknumOnSlot(blocknum) nonReentrant { + ) public blocknumOnSlot(blocknum) nonReentrant { Prediction memory predobj = get_prediction(blocknum, predictoor_addr); require(predobj.paid == false, "already paid"); From 8fed545401101d1b7ea3728afc7a315c3cb1349f Mon Sep 17 00:00:00 2001 From: trizin <25263018+trizin@users.noreply.github.com> Date: Wed, 10 May 2023 09:10:14 +0000 Subject: [PATCH 040/120] Payout fix transfer --- contracts/templates/ERC20TemplatePredictoor.sol | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/contracts/templates/ERC20TemplatePredictoor.sol b/contracts/templates/ERC20TemplatePredictoor.sol index 40c6cd802..eaff21211 100644 --- a/contracts/templates/ERC20TemplatePredictoor.sol +++ b/contracts/templates/ERC20TemplatePredictoor.sol @@ -1064,8 +1064,7 @@ contract ERC20TemplatePredictoor is predobj.predval, truevals[blocknum] ); - IERC20(stake_token).safeTransferFrom( - address(this), + IERC20(stake_token).safeTransfer( predobj.predictoor, payout_amt ); From ea3ed5755161bd1fcbc30c7ef0c02d2fdcf2061d Mon Sep 17 00:00:00 2001 From: trizin <25263018+trizin@users.noreply.github.com> Date: Wed, 10 May 2023 09:12:26 +0000 Subject: [PATCH 041/120] More tests for get_predval --- .../ERC20TemplatePredictoor.test.js | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/test/unit/datatokens/ERC20TemplatePredictoor.test.js b/test/unit/datatokens/ERC20TemplatePredictoor.test.js index 249f15854..34713cf68 100644 --- a/test/unit/datatokens/ERC20TemplatePredictoor.test.js +++ b/test/unit/datatokens/ERC20TemplatePredictoor.test.js @@ -1025,6 +1025,34 @@ describe("ERC20TemplatePredictoor", () => { expect(event.args[1]).to.equal(predictionEpoch); expect(event.args[2]).to.equal(stake); }); + it("#submit_predval - predictoor can read their submitted predval", async () => { + const predval = true; + const stake = 100; + await mockErc20.approve(erc20Token.address, stake); + const soonestBlockToPredict = await erc20Token.soonest_block_to_predict(); + + await erc20Token.submit_predval(predval, stake, soonestBlockToPredict); + const prediction = await erc20Token.get_prediction(soonestBlockToPredict, owner.address); + + expect(prediction.predval).to.be.eq(predval); + expect(prediction.stake).to.be.eq(stake); + expect(prediction.predictoor).to.be.eq(owner.address); + expect(prediction.paid).to.be.eq(false); + }); + it("#submit_predval - others cannot read submitted predictions", async () => { + const predval = true; + const stake = 100; + await mockErc20.approve(erc20Token.address, stake); + const soonestBlockToPredict = await erc20Token.soonest_block_to_predict(); + + await erc20Token.submit_predval(predval, stake, soonestBlockToPredict); + expectRevert(erc20Token.connect(user2).get_prediction(soonestBlockToPredict, owner.address)); + // fast forward blocks until next epoch + Array(30).fill(0).map(async _ => await ethers.provider.send("evm_mine")); + // user2 should be able to read the predval now + const prediction = await erc20Token.connect(user2).get_prediction(soonestBlockToPredict, owner.address); + expect(prediction.predval).to.be.eq(predval); + }); it("#submit_predval - should revert when predictoor submits too early", async () => { const predval = true; const stake = 100; From d9445e908b8e84fa5b2e485fdd2f72b9d502a762 Mon Sep 17 00:00:00 2001 From: trizin <25263018+trizin@users.noreply.github.com> Date: Wed, 10 May 2023 11:57:49 +0000 Subject: [PATCH 042/120] Fix payout formula --- contracts/templates/ERC20TemplatePredictoor.sol | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/contracts/templates/ERC20TemplatePredictoor.sol b/contracts/templates/ERC20TemplatePredictoor.sol index eaff21211..dd9713824 100644 --- a/contracts/templates/ERC20TemplatePredictoor.sol +++ b/contracts/templates/ERC20TemplatePredictoor.sol @@ -1051,10 +1051,7 @@ contract ERC20TemplatePredictoor is uint256 swe = truevals[blocknum] ? agg_predvals_numer[blocknum] : agg_predvals_denom[blocknum] - agg_predvals_numer[blocknum]; - uint256 payout_amt = (predobj.stake * - agg_predvals_denom[blocknum] * - get_subscription_revenue_at_block(blocknum)) / swe; - + uint256 payout_amt = predobj.stake * (agg_predvals_denom[blocknum] + get_subscription_revenue_at_block(blocknum)) / swe; predobj.paid = true; emit PredictionPayout( predictoor_addr, From dbd30a4f6d0d27bb6e4f71394301905ef07a6bb7 Mon Sep 17 00:00:00 2001 From: trizin <25263018+trizin@users.noreply.github.com> Date: Wed, 10 May 2023 11:59:30 +0000 Subject: [PATCH 043/120] Predictoor gets paid test --- .../ERC20TemplatePredictoor.test.js | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/test/unit/datatokens/ERC20TemplatePredictoor.test.js b/test/unit/datatokens/ERC20TemplatePredictoor.test.js index 34713cf68..531e320c4 100644 --- a/test/unit/datatokens/ERC20TemplatePredictoor.test.js +++ b/test/unit/datatokens/ERC20TemplatePredictoor.test.js @@ -1244,7 +1244,7 @@ describe("ERC20TemplatePredictoor", () => { }); // can read get_agg_predval with a valid subscription - it("#get_agg_predval - should return agg_predval if caller buys a subscription", async () => { + it("predictoor gets paid", async () => { const consumer = user2.address; // could be different user const serviceIndex = 1; // dummy index const providerFeeAddress = user5.address; // marketplace fee Collector @@ -1318,9 +1318,25 @@ describe("ERC20TemplatePredictoor", () => { revenue_at_block = await erc20Token.connect(user2).get_subscription_revenue_at_block(soonestBlockToPredict) expect(revenue_at_block).to.be.gt(0); - + // predictoor makes a prediction + const predval = true; + const stake = web3.utils.toWei("1"); + await mockErc20.transfer(user3.address, stake); + await mockErc20.connect(user3).approve(erc20Token.address, stake); + await erc20Token.connect(user3).submit_predval(predval, stake, soonestBlockToPredict); - + expectRevert(erc20Token.connect(user3).payout(soonestBlockToPredict, user3.address)); + Array(30).fill(0).map(async _ => await ethers.provider.send("evm_mine")); + + expectRevert(erc20Token.connect(user3).payout(soonestBlockToPredict, user3.address)); + // opf submits truval + await erc20Token.submit_trueval(soonestBlockToPredict, predval); + const balBefore = await mockErc20.balanceOf(user3.address); + await erc20Token.connect(user3).payout(soonestBlockToPredict, user3.address); + const balAfter = await mockErc20.balanceOf(user3.address); + expect(balAfter).to.be.gt(balBefore); + + expectRevert(erc20Token.connect(user3).payout(soonestBlockToPredict, user3.address), "already paid out"); }); }); From b0acc30c17d26dbc3da0ee282fc3dc883564dfa7 Mon Sep 17 00:00:00 2001 From: trizin <25263018+trizin@users.noreply.github.com> Date: Wed, 10 May 2023 13:12:28 +0000 Subject: [PATCH 044/120] Add redeem_unused_sub_revenue admin func --- contracts/templates/ERC20TemplatePredictoor.sol | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/contracts/templates/ERC20TemplatePredictoor.sol b/contracts/templates/ERC20TemplatePredictoor.sol index dd9713824..683f4ec68 100644 --- a/contracts/templates/ERC20TemplatePredictoor.sol +++ b/contracts/templates/ERC20TemplatePredictoor.sol @@ -1068,6 +1068,16 @@ contract ERC20TemplatePredictoor is } // ----------------------- ADMIN FUNCTIONS ----------------------- + function redeem_unused_sub_revenue(uint256 blocknum) external onlyERC20Deployer { + require(block.number > blocknum); + require(agg_predvals_denom[blocknum] == 0); + IERC20(stake_token).safeTransfer( + msg.sender, + subscription_revenue_at_block(blocknum) + ); + } + + function pause_predictions() external onlyERC20Deployer { paused = !paused; } From 8342dad4dc932f74ecd5ef05bae4f200b1dd4449 Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Wed, 10 May 2023 06:14:21 -0700 Subject: [PATCH 045/120] add debug statements --- .../templates/ERC20TemplatePredictoor.sol | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/contracts/templates/ERC20TemplatePredictoor.sol b/contracts/templates/ERC20TemplatePredictoor.sol index dd9713824..519de6430 100644 --- a/contracts/templates/ERC20TemplatePredictoor.sol +++ b/contracts/templates/ERC20TemplatePredictoor.sol @@ -15,6 +15,7 @@ import "@openzeppelin/contracts/utils/math/SafeMath.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; import "../utils/ERC20Roles.sol"; +import "hardhat/console.sol"; /** * @title DatatokenTemplate @@ -850,7 +851,7 @@ contract ERC20TemplatePredictoor is * @dev buyFromFreAndOrder * Buys 1 DT from the FRE and then startsOrder, while burning that DT */ - function buyFromFreAndOrder( + /*function buyFromFreAndOrder( OrderParams calldata _orderParams, FreParams calldata _freParams ) external nonReentrant { @@ -866,6 +867,7 @@ contract ERC20TemplatePredictoor is _orderParams._consumeMarketFee ); } + */ /** @@ -1051,7 +1053,13 @@ contract ERC20TemplatePredictoor is uint256 swe = truevals[blocknum] ? agg_predvals_numer[blocknum] : agg_predvals_denom[blocknum] - agg_predvals_numer[blocknum]; - uint256 payout_amt = predobj.stake * (agg_predvals_denom[blocknum] + get_subscription_revenue_at_block(blocknum)) / swe; + + uint256 revenue=get_subscription_revenue_at_block(blocknum); + console.log("revenue:%s",revenue); + uint256 payout_amt = predobj.stake * (agg_predvals_denom[blocknum] + revenue) / swe; + console.log("predobj.stake:%s",predobj.stake); + console.log("agg_predvals_denom: %s",agg_predvals_denom[blocknum]); + console.log("Payout_amt: %s",payout_amt); predobj.paid = true; emit PredictionPayout( predictoor_addr, @@ -1061,6 +1069,13 @@ contract ERC20TemplatePredictoor is predobj.predval, truevals[blocknum] ); + uint256 contractBalance=IERC20(stake_token).balanceOf(address(this)); + console.log( + "Transferring from to %s %s tokens, while our balance is %s", + predobj.predictoor, + payout_amt, + contractBalance + ); IERC20(stake_token).safeTransfer( predobj.predictoor, payout_amt From 4f44a068f242bc5813c76fd5d28d5d03a0730550 Mon Sep 17 00:00:00 2001 From: trizin <25263018+trizin@users.noreply.github.com> Date: Wed, 10 May 2023 13:18:19 +0000 Subject: [PATCH 046/120] Fix --- contracts/templates/ERC20TemplatePredictoor.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/templates/ERC20TemplatePredictoor.sol b/contracts/templates/ERC20TemplatePredictoor.sol index 683f4ec68..d977a488f 100644 --- a/contracts/templates/ERC20TemplatePredictoor.sol +++ b/contracts/templates/ERC20TemplatePredictoor.sol @@ -1073,7 +1073,7 @@ contract ERC20TemplatePredictoor is require(agg_predvals_denom[blocknum] == 0); IERC20(stake_token).safeTransfer( msg.sender, - subscription_revenue_at_block(blocknum) + subscription_revenue_at_block[blocknum] ); } From 4c2fc18978dca822fe86ba3f37d3848530e8516f Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Wed, 10 May 2023 06:40:06 -0700 Subject: [PATCH 047/120] collectbt --- .../templates/ERC20TemplatePredictoor.sol | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/contracts/templates/ERC20TemplatePredictoor.sol b/contracts/templates/ERC20TemplatePredictoor.sol index f06dc8b8f..067ae4fab 100644 --- a/contracts/templates/ERC20TemplatePredictoor.sol +++ b/contracts/templates/ERC20TemplatePredictoor.sol @@ -320,7 +320,7 @@ contract ERC20TemplatePredictoor is assembly { chainId := chainid() } - DOMAIN_SEPARATOR = keccak256( + /*DOMAIN_SEPARATOR = keccak256( abi.encode( keccak256( "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" @@ -331,6 +331,7 @@ contract ERC20TemplatePredictoor is address(this) ) ); + */ stake_token = addresses_[4]; _update_seconds(uints_[2], uints_[3], uints_[4], uints_[5]); @@ -458,6 +459,18 @@ contract ERC20TemplatePredictoor is _consumeMarketFee.consumeMarketFeeAmount ); } + //fetch bastetokens from fre + if(fixedRateExchanges.length>0){ + IFixedRateExchange fre = IFixedRateExchange( + fixedRateExchanges[0].contractAddress + ); + (, , , , , , , , , , uint256 btBalance, ) = fre.getExchange( + fixedRateExchanges[0].id + ); + if (btBalance > 0) { + fre.collectBT(fixedRateExchanges[0].id, btBalance); + } + } Subscription memory sub = Subscription( consumer, block.number + blocks_per_subscription @@ -594,14 +607,6 @@ contract ERC20TemplatePredictoor is curentLen++; } } - // loop though dispenser and preserve the minter rols if exists - for (i = 0; i < dispensers.length; i++) { - IDispenser(dispensers[i]).ownerWithdraw(address(this)); - if (isMinter(dispensers[i])) { - previousMinters[curentLen] = dispensers[i]; - curentLen++; - } - } // clear all permisions _cleanPermissions(); // set collector to 0 From b71d0ab000a1c8ecb8aa6a181573a0a25b22e7a8 Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Wed, 10 May 2023 07:25:16 -0700 Subject: [PATCH 048/120] fix tests --- contracts/templates/ERC20TemplatePredictoor.sol | 2 +- test/unit/datatokens/ERC20TemplatePredictoor.test.js | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/contracts/templates/ERC20TemplatePredictoor.sol b/contracts/templates/ERC20TemplatePredictoor.sol index 067ae4fab..05f864402 100644 --- a/contracts/templates/ERC20TemplatePredictoor.sol +++ b/contracts/templates/ERC20TemplatePredictoor.sol @@ -1058,7 +1058,7 @@ contract ERC20TemplatePredictoor is uint256 swe = truevals[blocknum] ? agg_predvals_numer[blocknum] : agg_predvals_denom[blocknum] - agg_predvals_numer[blocknum]; - + console.log("swe:%s",swe); uint256 revenue=get_subscription_revenue_at_block(blocknum); console.log("revenue:%s",revenue); uint256 payout_amt = predobj.stake * (agg_predvals_denom[blocknum] + revenue) / swe; diff --git a/test/unit/datatokens/ERC20TemplatePredictoor.test.js b/test/unit/datatokens/ERC20TemplatePredictoor.test.js index 531e320c4..fac51a744 100644 --- a/test/unit/datatokens/ERC20TemplatePredictoor.test.js +++ b/test/unit/datatokens/ERC20TemplatePredictoor.test.js @@ -1326,13 +1326,14 @@ describe("ERC20TemplatePredictoor", () => { await mockErc20.connect(user3).approve(erc20Token.address, stake); await erc20Token.connect(user3).submit_predval(predval, stake, soonestBlockToPredict); - expectRevert(erc20Token.connect(user3).payout(soonestBlockToPredict, user3.address)); + await expectRevert(erc20Token.connect(user3).payout(soonestBlockToPredict, user3.address),"trueval not submitted") Array(30).fill(0).map(async _ => await ethers.provider.send("evm_mine")); - expectRevert(erc20Token.connect(user3).payout(soonestBlockToPredict, user3.address)); + await expectRevert(erc20Token.connect(user3).payout(soonestBlockToPredict, user3.address),"trueval not submitted"); // opf submits truval await erc20Token.submit_trueval(soonestBlockToPredict, predval); const balBefore = await mockErc20.balanceOf(user3.address); + console.log("Doing payout") await erc20Token.connect(user3).payout(soonestBlockToPredict, user3.address); const balAfter = await mockErc20.balanceOf(user3.address); expect(balAfter).to.be.gt(balBefore); From b145479ffff6e07617338bc8d0d592f7d9b32adb Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Wed, 10 May 2023 07:35:24 -0700 Subject: [PATCH 049/120] cleanup --- contracts/templates/ERC20TemplatePredictoor.sol | 17 ++--------------- .../datatokens/ERC20TemplatePredictoor.test.js | 1 - 2 files changed, 2 insertions(+), 16 deletions(-) diff --git a/contracts/templates/ERC20TemplatePredictoor.sol b/contracts/templates/ERC20TemplatePredictoor.sol index 05f864402..f2cbc605e 100644 --- a/contracts/templates/ERC20TemplatePredictoor.sol +++ b/contracts/templates/ERC20TemplatePredictoor.sol @@ -15,7 +15,6 @@ import "@openzeppelin/contracts/utils/math/SafeMath.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; import "../utils/ERC20Roles.sol"; -import "hardhat/console.sol"; /** * @title DatatokenTemplate @@ -856,7 +855,7 @@ contract ERC20TemplatePredictoor is * @dev buyFromFreAndOrder * Buys 1 DT from the FRE and then startsOrder, while burning that DT */ - /*function buyFromFreAndOrder( + function buyFromFreAndOrder( OrderParams calldata _orderParams, FreParams calldata _freParams ) external nonReentrant { @@ -872,7 +871,7 @@ contract ERC20TemplatePredictoor is _orderParams._consumeMarketFee ); } - */ + /** @@ -1058,13 +1057,8 @@ contract ERC20TemplatePredictoor is uint256 swe = truevals[blocknum] ? agg_predvals_numer[blocknum] : agg_predvals_denom[blocknum] - agg_predvals_numer[blocknum]; - console.log("swe:%s",swe); uint256 revenue=get_subscription_revenue_at_block(blocknum); - console.log("revenue:%s",revenue); uint256 payout_amt = predobj.stake * (agg_predvals_denom[blocknum] + revenue) / swe; - console.log("predobj.stake:%s",predobj.stake); - console.log("agg_predvals_denom: %s",agg_predvals_denom[blocknum]); - console.log("Payout_amt: %s",payout_amt); predobj.paid = true; emit PredictionPayout( predictoor_addr, @@ -1074,13 +1068,6 @@ contract ERC20TemplatePredictoor is predobj.predval, truevals[blocknum] ); - uint256 contractBalance=IERC20(stake_token).balanceOf(address(this)); - console.log( - "Transferring from to %s %s tokens, while our balance is %s", - predobj.predictoor, - payout_amt, - contractBalance - ); IERC20(stake_token).safeTransfer( predobj.predictoor, payout_amt diff --git a/test/unit/datatokens/ERC20TemplatePredictoor.test.js b/test/unit/datatokens/ERC20TemplatePredictoor.test.js index fac51a744..a6e64bd9b 100644 --- a/test/unit/datatokens/ERC20TemplatePredictoor.test.js +++ b/test/unit/datatokens/ERC20TemplatePredictoor.test.js @@ -1333,7 +1333,6 @@ describe("ERC20TemplatePredictoor", () => { // opf submits truval await erc20Token.submit_trueval(soonestBlockToPredict, predval); const balBefore = await mockErc20.balanceOf(user3.address); - console.log("Doing payout") await erc20Token.connect(user3).payout(soonestBlockToPredict, user3.address); const balAfter = await mockErc20.balanceOf(user3.address); expect(balAfter).to.be.gt(balBefore); From 3fb10f916e8dcc5024eced1750b2ff1dc62bde55 Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Wed, 10 May 2023 08:03:54 -0700 Subject: [PATCH 050/120] cache compilers --- Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Dockerfile b/Dockerfile index b5d7a9439..ab4088184 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,6 +10,7 @@ WORKDIR /ocean-contracts RUN rm package-lock.json RUN rm -rf ./node-modules/ RUN npm i +RUN rm -rf /root/.cache/hardhat-nodejs/ RUN npx hardhat clean RUN npx hardhat compile --force ENV SLEEP_FOR_GANACHE=10 From 12aba6293e58dcb429b951a74b19c6363c91582f Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Wed, 10 May 2023 08:31:50 -0700 Subject: [PATCH 051/120] optimize --- Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index ab4088184..7d9691397 100644 --- a/Dockerfile +++ b/Dockerfile @@ -11,8 +11,8 @@ RUN rm package-lock.json RUN rm -rf ./node-modules/ RUN npm i RUN rm -rf /root/.cache/hardhat-nodejs/ -RUN npx hardhat clean -RUN npx hardhat compile --force +RUN npx hardhat clean --global +RUN npx hardhat compile ENV SLEEP_FOR_GANACHE=10 RUN cp hardhat.config.barge.js hardhat.config.js ENV NETWORK=barge From be4a3803de066f6bf5682c1d350c3a828fb83383 Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Wed, 10 May 2023 08:55:53 -0700 Subject: [PATCH 052/120] revert Docker changes, will handle in another PR --- Dockerfile | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Dockerfile b/Dockerfile index 7d9691397..831707082 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,11 +10,9 @@ WORKDIR /ocean-contracts RUN rm package-lock.json RUN rm -rf ./node-modules/ RUN npm i -RUN rm -rf /root/.cache/hardhat-nodejs/ -RUN npx hardhat clean --global -RUN npx hardhat compile ENV SLEEP_FOR_GANACHE=10 RUN cp hardhat.config.barge.js hardhat.config.js ENV NETWORK=barge ENV NETWORK_RPC_URL=127.0.0.1:8545 -ENTRYPOINT ["/ocean-contracts/scripts/deploy_docker.sh"] +RUN npx hardhat compile +ENTRYPOINT ["/ocean-contracts/scripts/deploy_docker.sh"] \ No newline at end of file From f7e21dfa0f96e59444b2c1e8483d496844554967 Mon Sep 17 00:00:00 2001 From: trizin <25263018+trizin@users.noreply.github.com> Date: Wed, 10 May 2023 18:01:53 +0000 Subject: [PATCH 053/120] Compare expected price --- .../ERC20TemplatePredictoor.test.js | 66 ++++++++++--------- 1 file changed, 35 insertions(+), 31 deletions(-) diff --git a/test/unit/datatokens/ERC20TemplatePredictoor.test.js b/test/unit/datatokens/ERC20TemplatePredictoor.test.js index a6e64bd9b..c3600073e 100644 --- a/test/unit/datatokens/ERC20TemplatePredictoor.test.js +++ b/test/unit/datatokens/ERC20TemplatePredictoor.test.js @@ -1026,32 +1026,32 @@ describe("ERC20TemplatePredictoor", () => { expect(event.args[2]).to.equal(stake); }); it("#submit_predval - predictoor can read their submitted predval", async () => { - const predval = true; - const stake = 100; - await mockErc20.approve(erc20Token.address, stake); - const soonestBlockToPredict = await erc20Token.soonest_block_to_predict(); - - await erc20Token.submit_predval(predval, stake, soonestBlockToPredict); - const prediction = await erc20Token.get_prediction(soonestBlockToPredict, owner.address); - - expect(prediction.predval).to.be.eq(predval); - expect(prediction.stake).to.be.eq(stake); - expect(prediction.predictoor).to.be.eq(owner.address); - expect(prediction.paid).to.be.eq(false); + const predval = true; + const stake = 100; + await mockErc20.approve(erc20Token.address, stake); + const soonestBlockToPredict = await erc20Token.soonest_block_to_predict(); + + await erc20Token.submit_predval(predval, stake, soonestBlockToPredict); + const prediction = await erc20Token.get_prediction(soonestBlockToPredict, owner.address); + + expect(prediction.predval).to.be.eq(predval); + expect(prediction.stake).to.be.eq(stake); + expect(prediction.predictoor).to.be.eq(owner.address); + expect(prediction.paid).to.be.eq(false); }); it("#submit_predval - others cannot read submitted predictions", async () => { - const predval = true; - const stake = 100; - await mockErc20.approve(erc20Token.address, stake); - const soonestBlockToPredict = await erc20Token.soonest_block_to_predict(); - - await erc20Token.submit_predval(predval, stake, soonestBlockToPredict); - expectRevert(erc20Token.connect(user2).get_prediction(soonestBlockToPredict, owner.address)); - // fast forward blocks until next epoch - Array(30).fill(0).map(async _ => await ethers.provider.send("evm_mine")); - // user2 should be able to read the predval now - const prediction = await erc20Token.connect(user2).get_prediction(soonestBlockToPredict, owner.address); - expect(prediction.predval).to.be.eq(predval); + const predval = true; + const stake = 100; + await mockErc20.approve(erc20Token.address, stake); + const soonestBlockToPredict = await erc20Token.soonest_block_to_predict(); + + await erc20Token.submit_predval(predval, stake, soonestBlockToPredict); + expectRevert(erc20Token.connect(user2).get_prediction(soonestBlockToPredict, owner.address)); + // fast forward blocks until next epoch + Array(30).fill(0).map(async _ => await ethers.provider.send("evm_mine")); + // user2 should be able to read the predval now + const prediction = await erc20Token.connect(user2).get_prediction(soonestBlockToPredict, owner.address); + expect(prediction.predval).to.be.eq(predval); }); it("#submit_predval - should revert when predictoor submits too early", async () => { const predval = true; @@ -1268,15 +1268,15 @@ describe("ERC20TemplatePredictoor", () => { let event = getEventFromTx(txReceipt, 'NewFixedRate') assert(event, "Cannot find NewFixedRate event") exchangeId = event.args.exchangeId - const exchangeInfo = await fixedRateExchange.calcBaseInGivenOutDT(exchangeId,amountDT,0) + const exchangeInfo = await fixedRateExchange.calcBaseInGivenOutDT(exchangeId, amountDT, 0) //let's buy a DT await mockErc20.transfer(user2.address, exchangeInfo.baseTokenAmount); await mockErc20.connect(user2).approve(fixedRateExchange.address, exchangeInfo.baseTokenAmount); - // user buys DT - await fixedRateExchange.connect(user2).buyDT(exchangeId, amountDT, exchangeInfo.baseTokenAmount ,addressZero, 0) + // user buys DT + await fixedRateExchange.connect(user2).buyDT(exchangeId, amountDT, exchangeInfo.baseTokenAmount, addressZero, 0) const balance = await erc20Token.balanceOf(user2.address) - assert(balance > 0 , "Failed to buy DT") + assert(balance > 0, "Failed to buy DT") //sign provider data const providerData = JSON.stringify({ "timeout": 0 }) const message = ethers.utils.solidityKeccak256( @@ -1293,7 +1293,7 @@ describe("ERC20TemplatePredictoor", () => { let soonestBlockToPredict = await erc20Token.soonest_block_to_predict(); let revenue_at_block = await erc20Token.connect(user2).get_subscription_revenue_at_block(soonestBlockToPredict) expect(revenue_at_block).to.be.eq(0); - + await erc20Token .connect(user2) .startOrder( @@ -1326,10 +1326,10 @@ describe("ERC20TemplatePredictoor", () => { await mockErc20.connect(user3).approve(erc20Token.address, stake); await erc20Token.connect(user3).submit_predval(predval, stake, soonestBlockToPredict); - await expectRevert(erc20Token.connect(user3).payout(soonestBlockToPredict, user3.address),"trueval not submitted") + await expectRevert(erc20Token.connect(user3).payout(soonestBlockToPredict, user3.address), "trueval not submitted") Array(30).fill(0).map(async _ => await ethers.provider.send("evm_mine")); + await expectRevert(erc20Token.connect(user3).payout(soonestBlockToPredict, user3.address), "trueval not submitted"); - await expectRevert(erc20Token.connect(user3).payout(soonestBlockToPredict, user3.address),"trueval not submitted"); // opf submits truval await erc20Token.submit_trueval(soonestBlockToPredict, predval); const balBefore = await mockErc20.balanceOf(user3.address); @@ -1337,6 +1337,10 @@ describe("ERC20TemplatePredictoor", () => { const balAfter = await mockErc20.balanceOf(user3.address); expect(balAfter).to.be.gt(balBefore); + const profit = balAfter.sub(balBefore); + const expectedProfit = 1 + (2 / parseInt(3600 / parseInt(300 / 24))) + expect(parseFloat(web3.utils.fromWei(profit.toString()))).to.be.eq(expectedProfit); + expectRevert(erc20Token.connect(user3).payout(soonestBlockToPredict, user3.address), "already paid out"); }); }); From bb2d2245bf1cf65d5351537a98589e4bfbb04e34 Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Wed, 10 May 2023 21:01:40 -0700 Subject: [PATCH 054/120] add nonReentrant --- contracts/templates/ERC20TemplatePredictoor.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/templates/ERC20TemplatePredictoor.sol b/contracts/templates/ERC20TemplatePredictoor.sol index f2cbc605e..4eb9e9315 100644 --- a/contracts/templates/ERC20TemplatePredictoor.sol +++ b/contracts/templates/ERC20TemplatePredictoor.sol @@ -402,7 +402,7 @@ contract ERC20TemplatePredictoor is uint256 serviceIndex, providerFee calldata _providerFee, consumeMarketFee calldata _consumeMarketFee - ) public { + ) public nonReentrant{ uint256 amount = 1e18; // we always pay 1 DT. No more, no less require( balanceOf(msg.sender) >= amount, @@ -801,7 +801,7 @@ contract ERC20TemplatePredictoor is * @dev buyFromFre * Buys 1 DT from the FRE */ - function buyFromFre(FreParams calldata _freParams) public { + function buyFromFre(FreParams calldata _freParams) public nonReentrant { // get exchange info IFixedRateExchange fre = IFixedRateExchange( _freParams.exchangeContract From 62daed6885ab61e6fca3a36512a8754880e262af Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Wed, 10 May 2023 21:03:47 -0700 Subject: [PATCH 055/120] add misiing await --- test/unit/datatokens/ERC20TemplatePredictoor.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit/datatokens/ERC20TemplatePredictoor.test.js b/test/unit/datatokens/ERC20TemplatePredictoor.test.js index c3600073e..1195e51f8 100644 --- a/test/unit/datatokens/ERC20TemplatePredictoor.test.js +++ b/test/unit/datatokens/ERC20TemplatePredictoor.test.js @@ -1341,6 +1341,6 @@ describe("ERC20TemplatePredictoor", () => { const expectedProfit = 1 + (2 / parseInt(3600 / parseInt(300 / 24))) expect(parseFloat(web3.utils.fromWei(profit.toString()))).to.be.eq(expectedProfit); - expectRevert(erc20Token.connect(user3).payout(soonestBlockToPredict, user3.address), "already paid out"); + await expectRevert(erc20Token.connect(user3).payout(soonestBlockToPredict, user3.address), "already paid out"); }); }); From eb650d3b55dfbbb9f40edbc9cf3d1cc62e13430c Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Wed, 10 May 2023 21:31:41 -0700 Subject: [PATCH 056/120] more tests fixed --- contracts/templates/ERC20TemplatePredictoor.sol | 10 ++++++---- test/unit/datatokens/ERC20TemplatePredictoor.test.js | 5 +++-- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/contracts/templates/ERC20TemplatePredictoor.sol b/contracts/templates/ERC20TemplatePredictoor.sol index 4eb9e9315..265f74ba0 100644 --- a/contracts/templates/ERC20TemplatePredictoor.sol +++ b/contracts/templates/ERC20TemplatePredictoor.sol @@ -993,9 +993,10 @@ contract ERC20TemplatePredictoor is uint256 stake, uint256 blocknum ) external blocknumOnSlot(blocknum) { + require(paused == false, "paused"); require(blocknum >= soonest_block_to_predict(), "too late to submit"); require(!submitted_predval(blocknum, msg.sender), "already submitted"); - require(paused == false, "paused"); + Prediction memory predobj = Prediction( predval, @@ -1029,7 +1030,8 @@ contract ERC20TemplatePredictoor is uint256 blocknum, address predictoor_addr ) public blocknumOnSlot(blocknum) nonReentrant { - Prediction memory predobj = get_prediction(blocknum, predictoor_addr); + Prediction memory predobj = predobjs[blocknum][predictoor_addr]; + require(predobj.paid == false, "already paid"); // if OPF hasn't submitted trueval in truval_submit_timeout days @@ -1038,7 +1040,7 @@ contract ERC20TemplatePredictoor is block.number > blocknum + truval_submit_timeout_block && !truval_submitted[blocknum] ) { - predobj.paid = true; + predobjs[blocknum][predictoor_addr].paid = true; emit PredictionPayout( predictoor_addr, epoch(blocknum), @@ -1059,7 +1061,7 @@ contract ERC20TemplatePredictoor is : agg_predvals_denom[blocknum] - agg_predvals_numer[blocknum]; uint256 revenue=get_subscription_revenue_at_block(blocknum); uint256 payout_amt = predobj.stake * (agg_predvals_denom[blocknum] + revenue) / swe; - predobj.paid = true; + predobjs[blocknum][predictoor_addr].paid = true; emit PredictionPayout( predictoor_addr, epoch(blocknum), diff --git a/test/unit/datatokens/ERC20TemplatePredictoor.test.js b/test/unit/datatokens/ERC20TemplatePredictoor.test.js index 1195e51f8..b9c6ed50a 100644 --- a/test/unit/datatokens/ERC20TemplatePredictoor.test.js +++ b/test/unit/datatokens/ERC20TemplatePredictoor.test.js @@ -975,7 +975,8 @@ describe("ERC20TemplatePredictoor", () => { const blocksPerEpoch = (await erc20Token.blocks_per_epoch()) const slot = parseInt(blockNum / blocksPerEpoch) * blocksPerEpoch; assert((await erc20Token.rail_blocknum_to_slot(blockNum)) == slot); - assert((await erc20Token.blocknum_is_on_a_slot(blockNum)) == true); + const isOnSlot = await erc20Token.blocknum_is_on_a_slot(blockNum) + assert(isOnSlot == true, isOnSlot +" should be true"); }); it("#soonest_block_to_predict - should return soonest block to predict", async () => { const soonestBlockToPredict = await erc20Token.soonest_block_to_predict(); @@ -1341,6 +1342,6 @@ describe("ERC20TemplatePredictoor", () => { const expectedProfit = 1 + (2 / parseInt(3600 / parseInt(300 / 24))) expect(parseFloat(web3.utils.fromWei(profit.toString()))).to.be.eq(expectedProfit); - await expectRevert(erc20Token.connect(user3).payout(soonestBlockToPredict, user3.address), "already paid out"); + await expectRevert(erc20Token.connect(user3).payout(soonestBlockToPredict, user3.address), "already paid"); }); }); From f0358479190701dfd35c30470cf3bf7ad843f4d2 Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Wed, 10 May 2023 22:03:42 -0700 Subject: [PATCH 057/120] enforce solidity math --- contracts/templates/ERC20TemplatePredictoor.sol | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/contracts/templates/ERC20TemplatePredictoor.sol b/contracts/templates/ERC20TemplatePredictoor.sol index 265f74ba0..f9ca5640c 100644 --- a/contracts/templates/ERC20TemplatePredictoor.sol +++ b/contracts/templates/ERC20TemplatePredictoor.sol @@ -930,7 +930,8 @@ contract ERC20TemplatePredictoor is function rail_blocknum_to_slot( uint256 blocknum ) public view returns (uint256) { - return (blocknum / blocks_per_epoch) * blocks_per_epoch; + uint256 rounded = blocknum / blocks_per_epoch; + return (rounded * blocks_per_epoch); } function blocknum_is_on_a_slot( From 79e0dedb93851b2e2262556e59c810ab03d922a5 Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Wed, 10 May 2023 22:14:46 -0700 Subject: [PATCH 058/120] add debug logs --- contracts/templates/ERC20TemplatePredictoor.sol | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/contracts/templates/ERC20TemplatePredictoor.sol b/contracts/templates/ERC20TemplatePredictoor.sol index f9ca5640c..55a46d3a2 100644 --- a/contracts/templates/ERC20TemplatePredictoor.sol +++ b/contracts/templates/ERC20TemplatePredictoor.sol @@ -15,7 +15,7 @@ import "@openzeppelin/contracts/utils/math/SafeMath.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; import "../utils/ERC20Roles.sol"; - +import "hardhat/console.sol"; /** * @title DatatokenTemplate * @@ -931,7 +931,9 @@ contract ERC20TemplatePredictoor is uint256 blocknum ) public view returns (uint256) { uint256 rounded = blocknum / blocks_per_epoch; - return (rounded * blocks_per_epoch); + uint256 epochStart = rounded * blocks_per_epoch; + console.log("Blocknum: %s, Rounded: %s, epochStart: %s",blocknum,rounded,epochStart); + return epochStart; } function blocknum_is_on_a_slot( From b897e35a84ffa757752173ce59f69d53409971d4 Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Wed, 10 May 2023 22:26:52 -0700 Subject: [PATCH 059/120] debug tests --- test/unit/datatokens/ERC20TemplatePredictoor.test.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/unit/datatokens/ERC20TemplatePredictoor.test.js b/test/unit/datatokens/ERC20TemplatePredictoor.test.js index b9c6ed50a..9653d8038 100644 --- a/test/unit/datatokens/ERC20TemplatePredictoor.test.js +++ b/test/unit/datatokens/ERC20TemplatePredictoor.test.js @@ -972,8 +972,11 @@ describe("ERC20TemplatePredictoor", () => { }); it("#rail_blocknum_to_slot, blocknum_is_on_a_slot - should rail blocknum to slot", async () => { const blockNum = await ethers.provider.getBlockNumber(); + console.log("BlockNum:"+blockNum) const blocksPerEpoch = (await erc20Token.blocks_per_epoch()) + console.log("blocksPerEpoch:"+blocksPerEpoch) const slot = parseInt(blockNum / blocksPerEpoch) * blocksPerEpoch; + console.log("slot:"+slot) assert((await erc20Token.rail_blocknum_to_slot(blockNum)) == slot); const isOnSlot = await erc20Token.blocknum_is_on_a_slot(blockNum) assert(isOnSlot == true, isOnSlot +" should be true"); From 88a06abffda5077a7633c243720d8715ce8fa956 Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Wed, 10 May 2023 22:49:01 -0700 Subject: [PATCH 060/120] fix tests --- test/unit/datatokens/ERC20TemplatePredictoor.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit/datatokens/ERC20TemplatePredictoor.test.js b/test/unit/datatokens/ERC20TemplatePredictoor.test.js index 9653d8038..cef6f0a3c 100644 --- a/test/unit/datatokens/ERC20TemplatePredictoor.test.js +++ b/test/unit/datatokens/ERC20TemplatePredictoor.test.js @@ -978,7 +978,7 @@ describe("ERC20TemplatePredictoor", () => { const slot = parseInt(blockNum / blocksPerEpoch) * blocksPerEpoch; console.log("slot:"+slot) assert((await erc20Token.rail_blocknum_to_slot(blockNum)) == slot); - const isOnSlot = await erc20Token.blocknum_is_on_a_slot(blockNum) + const isOnSlot = await erc20Token.blocknum_is_on_a_slot(slot) assert(isOnSlot == true, isOnSlot +" should be true"); }); it("#soonest_block_to_predict - should return soonest block to predict", async () => { From 0b0b7645b3f2c535cd300530bedbe4cef412a861 Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Wed, 10 May 2023 22:56:51 -0700 Subject: [PATCH 061/120] cleanup log msg --- contracts/templates/ERC20TemplatePredictoor.sol | 2 -- test/unit/datatokens/ERC20TemplatePredictoor.test.js | 3 --- 2 files changed, 5 deletions(-) diff --git a/contracts/templates/ERC20TemplatePredictoor.sol b/contracts/templates/ERC20TemplatePredictoor.sol index 55a46d3a2..1cd9c4db7 100644 --- a/contracts/templates/ERC20TemplatePredictoor.sol +++ b/contracts/templates/ERC20TemplatePredictoor.sol @@ -15,7 +15,6 @@ import "@openzeppelin/contracts/utils/math/SafeMath.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; import "../utils/ERC20Roles.sol"; -import "hardhat/console.sol"; /** * @title DatatokenTemplate * @@ -932,7 +931,6 @@ contract ERC20TemplatePredictoor is ) public view returns (uint256) { uint256 rounded = blocknum / blocks_per_epoch; uint256 epochStart = rounded * blocks_per_epoch; - console.log("Blocknum: %s, Rounded: %s, epochStart: %s",blocknum,rounded,epochStart); return epochStart; } diff --git a/test/unit/datatokens/ERC20TemplatePredictoor.test.js b/test/unit/datatokens/ERC20TemplatePredictoor.test.js index cef6f0a3c..cc20ec0dc 100644 --- a/test/unit/datatokens/ERC20TemplatePredictoor.test.js +++ b/test/unit/datatokens/ERC20TemplatePredictoor.test.js @@ -972,11 +972,8 @@ describe("ERC20TemplatePredictoor", () => { }); it("#rail_blocknum_to_slot, blocknum_is_on_a_slot - should rail blocknum to slot", async () => { const blockNum = await ethers.provider.getBlockNumber(); - console.log("BlockNum:"+blockNum) const blocksPerEpoch = (await erc20Token.blocks_per_epoch()) - console.log("blocksPerEpoch:"+blocksPerEpoch) const slot = parseInt(blockNum / blocksPerEpoch) * blocksPerEpoch; - console.log("slot:"+slot) assert((await erc20Token.rail_blocknum_to_slot(blockNum)) == slot); const isOnSlot = await erc20Token.blocknum_is_on_a_slot(slot) assert(isOnSlot == true, isOnSlot +" should be true"); From 2c527292f3ffc35182cc7901709f59b40b5f9a5e Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Thu, 11 May 2023 00:31:18 -0700 Subject: [PATCH 062/120] optimize --- contracts/templates/ERC20TemplatePredictoor.sol | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/contracts/templates/ERC20TemplatePredictoor.sol b/contracts/templates/ERC20TemplatePredictoor.sol index 1cd9c4db7..8ba8d0471 100644 --- a/contracts/templates/ERC20TemplatePredictoor.sol +++ b/contracts/templates/ERC20TemplatePredictoor.sol @@ -930,8 +930,7 @@ contract ERC20TemplatePredictoor is uint256 blocknum ) public view returns (uint256) { uint256 rounded = blocknum / blocks_per_epoch; - uint256 epochStart = rounded * blocks_per_epoch; - return epochStart; + return (rounded * blocks_per_epoch); } function blocknum_is_on_a_slot( From 9949e08c9e882cf32986b61e25e25665e46e7898 Mon Sep 17 00:00:00 2001 From: trizin <25263018+trizin@users.noreply.github.com> Date: Thu, 11 May 2023 07:58:23 +0000 Subject: [PATCH 063/120] Make truval mappings public --- contracts/templates/ERC20TemplatePredictoor.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/templates/ERC20TemplatePredictoor.sol b/contracts/templates/ERC20TemplatePredictoor.sol index 8ba8d0471..1b8c6bbec 100644 --- a/contracts/templates/ERC20TemplatePredictoor.sol +++ b/contracts/templates/ERC20TemplatePredictoor.sol @@ -77,8 +77,8 @@ contract ERC20TemplatePredictoor is mapping(uint256 => mapping(address => Prediction)) private predobjs; // id to prediction object mapping(uint256 => uint256) private agg_predvals_numer; mapping(uint256 => uint256) private agg_predvals_denom; - mapping(uint256 => bool) truevals; - mapping(uint256 => bool) truval_submitted; + mapping(uint256 => bool) public truevals; + mapping(uint256 => bool) public truval_submitted; mapping(uint256 => uint256) private subscription_revenue_at_block; //income registred mapping(address => Subscription) public subscriptions; // valid subscription per user uint256 public blocks_per_epoch; From fdb466aeb5574560516bc0405ba0ad06ec25417e Mon Sep 17 00:00:00 2001 From: trizin <25263018+trizin@users.noreply.github.com> Date: Thu, 11 May 2023 09:12:35 +0000 Subject: [PATCH 064/120] Optimization --- contracts/templates/ERC20TemplatePredictoor.sol | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/contracts/templates/ERC20TemplatePredictoor.sol b/contracts/templates/ERC20TemplatePredictoor.sol index 1b8c6bbec..5b56e1b2c 100644 --- a/contracts/templates/ERC20TemplatePredictoor.sol +++ b/contracts/templates/ERC20TemplatePredictoor.sol @@ -996,18 +996,14 @@ contract ERC20TemplatePredictoor is require(paused == false, "paused"); require(blocknum >= soonest_block_to_predict(), "too late to submit"); require(!submitted_predval(blocknum, msg.sender), "already submitted"); - - Prediction memory predobj = Prediction( + predobjs[blocknum][msg.sender] = Prediction( predval, stake, msg.sender, false ); - predobjs[blocknum][msg.sender] = predobj; - - // update agg_predvals agg_predvals_numer[blocknum] += stake * (predval ? 1 : 0); agg_predvals_denom[blocknum] += stake; From f5614ae429e2d16cc2c73a335a2a12269ab44de8 Mon Sep 17 00:00:00 2001 From: trizin <25263018+trizin@users.noreply.github.com> Date: Thu, 11 May 2023 11:18:34 +0000 Subject: [PATCH 065/120] Add multi predictoor test --- .../ERC20TemplatePredictoor.test.js | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/test/unit/datatokens/ERC20TemplatePredictoor.test.js b/test/unit/datatokens/ERC20TemplatePredictoor.test.js index cc20ec0dc..dabd8f3b7 100644 --- a/test/unit/datatokens/ERC20TemplatePredictoor.test.js +++ b/test/unit/datatokens/ERC20TemplatePredictoor.test.js @@ -1344,4 +1344,60 @@ describe("ERC20TemplatePredictoor", () => { await expectRevert(erc20Token.connect(user3).payout(soonestBlockToPredict, user3.address), "already paid"); }); + + // can read get_agg_predval with a valid subscription + it("multiple predictoor compete and some gets paid", async () => { + // predictoor makes a predictions + let predictoors = [reciever, user2, user3, user4, user5, user6]; + let predictions = []; + let stakes = []; + for(const predictoor of predictoors){ + const amt = web3.utils.toWei("200"); + await mockErc20.transfer(predictoor.address, amt); + await mockErc20.connect(predictoor).approve(erc20Token.address, amt); + } + + const blocksPerEpoch = await erc20Token.blocks_per_epoch(); + const currentBlock = await ethers.provider.getBlockNumber(); + const soonestBlockToPredict = await erc20Token.soonest_block_to_predict(); + Array(soonestBlockToPredict - currentBlock + 1).fill(0).map(async _ => await ethers.provider.send("evm_mine")); + const predictionBlock = await erc20Token.soonest_block_to_predict(); + + for(const predictoor of predictoors){ + const stake = 10 + Math.random() * 100; + const stakeWei = web3.utils.toWei(stake.toString()); + const p = Math.random() > 0.5; + predictions.push(p); + stakes.push(stake); + await erc20Token.connect(predictoor).submit_predval(p, stakeWei, predictionBlock) + } + + Array(blocksPerEpoch * 2).fill(0).map(async _ => await ethers.provider.send("evm_mine")); + const truval = Math.random() > 0.5; + const winners = predictions.map((x,i)=>x==truval?i:null).filter(x=>x!=null); + const totalStake = stakes.reduce((a,b)=>a+b, 0); + const winnersStake = winners.map(x=>stakes[x]).reduce((a,b)=>a+b, 0); + + // opf submits truval + await erc20Token.submit_trueval(predictionBlock, truval); + + // each predictoor calls payout function + for (let i = 0; i < predictoors.length; i++){ + let predictoor = predictoors[i]; + if (winners.includes(i)) { + const balBefore = await mockErc20.balanceOf(predictoor.address); + await erc20Token.connect(predictoor).payout(predictionBlock, predictoor.address); + const balAfter = await mockErc20.balanceOf(predictoor.address); + expect(balAfter).to.be.gt(balBefore); + const profit = balAfter.sub(balBefore); + const expectedProfitSub = (2 / parseInt(3600 / parseInt(300 / 24))) + const expectedProfitStake = stakes[i] / winnersStake * totalStake + const expectedProfit = expectedProfitSub + expectedProfitStake + expect(parseFloat(web3.utils.fromWei(profit.toString()))).to.be.closeTo(expectedProfit, 0.2); + } else { + await expectRevert(erc20Token.connect(predictoor).payout(predictionBlock, predictoor.address), "wrong prediction"); + } + } + + }); }); From 1386a711567da3589410d59ddf245b20d00f1f97 Mon Sep 17 00:00:00 2001 From: trizin <25263018+trizin@users.noreply.github.com> Date: Thu, 11 May 2023 11:21:07 +0000 Subject: [PATCH 066/120] Remove comment --- test/unit/datatokens/ERC20TemplatePredictoor.test.js | 1 - 1 file changed, 1 deletion(-) diff --git a/test/unit/datatokens/ERC20TemplatePredictoor.test.js b/test/unit/datatokens/ERC20TemplatePredictoor.test.js index dabd8f3b7..e13d69b17 100644 --- a/test/unit/datatokens/ERC20TemplatePredictoor.test.js +++ b/test/unit/datatokens/ERC20TemplatePredictoor.test.js @@ -1345,7 +1345,6 @@ describe("ERC20TemplatePredictoor", () => { await expectRevert(erc20Token.connect(user3).payout(soonestBlockToPredict, user3.address), "already paid"); }); - // can read get_agg_predval with a valid subscription it("multiple predictoor compete and some gets paid", async () => { // predictoor makes a predictions let predictoors = [reciever, user2, user3, user4, user5, user6]; From 69c85632533b49935148c0e45a4b69b63779a1d7 Mon Sep 17 00:00:00 2001 From: trizin <25263018+trizin@users.noreply.github.com> Date: Thu, 11 May 2023 11:56:28 +0000 Subject: [PATCH 067/120] Bug fix --- contracts/templates/ERC20TemplatePredictoor.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/templates/ERC20TemplatePredictoor.sol b/contracts/templates/ERC20TemplatePredictoor.sol index 5b56e1b2c..7809c8485 100644 --- a/contracts/templates/ERC20TemplatePredictoor.sol +++ b/contracts/templates/ERC20TemplatePredictoor.sol @@ -1092,7 +1092,7 @@ contract ERC20TemplatePredictoor is bool trueval ) external blocknumOnSlot(blocknum) onlyERC20Deployer { // TODO, is onlyERC20Deployer the right modifier? - require(blocknum < soonest_block_to_predict(), "too early to submit"); + require(blocknum < block.number, "too early to submit"); truevals[blocknum] = trueval; truval_submitted[blocknum] = true; } From 162aaed89fdf07c8991567769aed7b18a8992396 Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Thu, 11 May 2023 05:34:39 -0700 Subject: [PATCH 068/120] optimize docker --- Dockerfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 831707082..3f2432305 100644 --- a/Dockerfile +++ b/Dockerfile @@ -14,5 +14,6 @@ ENV SLEEP_FOR_GANACHE=10 RUN cp hardhat.config.barge.js hardhat.config.js ENV NETWORK=barge ENV NETWORK_RPC_URL=127.0.0.1:8545 -RUN npx hardhat compile +RUN npx hardhat clean --global +RUN npx hardhat compile --force ENTRYPOINT ["/ocean-contracts/scripts/deploy_docker.sh"] \ No newline at end of file From 434393f7c354c7b7404b738fd45c4074e93ce383 Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Thu, 11 May 2023 05:42:09 -0700 Subject: [PATCH 069/120] optimize more --- Dockerfile | 1 - 1 file changed, 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 3f2432305..f562d07a8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -11,7 +11,6 @@ RUN rm package-lock.json RUN rm -rf ./node-modules/ RUN npm i ENV SLEEP_FOR_GANACHE=10 -RUN cp hardhat.config.barge.js hardhat.config.js ENV NETWORK=barge ENV NETWORK_RPC_URL=127.0.0.1:8545 RUN npx hardhat clean --global From 947deee25da5fb966965006728d5643119c71a9d Mon Sep 17 00:00:00 2001 From: trizin <25263018+trizin@users.noreply.github.com> Date: Thu, 11 May 2023 12:43:27 +0000 Subject: [PATCH 070/120] Fix test math --- test/unit/datatokens/ERC20TemplatePredictoor.test.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/test/unit/datatokens/ERC20TemplatePredictoor.test.js b/test/unit/datatokens/ERC20TemplatePredictoor.test.js index e13d69b17..88871365d 100644 --- a/test/unit/datatokens/ERC20TemplatePredictoor.test.js +++ b/test/unit/datatokens/ERC20TemplatePredictoor.test.js @@ -1389,9 +1389,7 @@ describe("ERC20TemplatePredictoor", () => { const balAfter = await mockErc20.balanceOf(predictoor.address); expect(balAfter).to.be.gt(balBefore); const profit = balAfter.sub(balBefore); - const expectedProfitSub = (2 / parseInt(3600 / parseInt(300 / 24))) - const expectedProfitStake = stakes[i] / winnersStake * totalStake - const expectedProfit = expectedProfitSub + expectedProfitStake + const expectedProfit = stakes[i] / winnersStake * totalStake expect(parseFloat(web3.utils.fromWei(profit.toString()))).to.be.closeTo(expectedProfit, 0.2); } else { await expectRevert(erc20Token.connect(predictoor).payout(predictionBlock, predictoor.address), "wrong prediction"); From 17009a61853bb2e2711db84b09e0dcba4eab7a80 Mon Sep 17 00:00:00 2001 From: trizin <25263018+trizin@users.noreply.github.com> Date: Thu, 11 May 2023 12:45:01 +0000 Subject: [PATCH 071/120] Add tests for redeem_unused_sub_revenue --- .../ERC20TemplatePredictoor.test.js | 90 +++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/test/unit/datatokens/ERC20TemplatePredictoor.test.js b/test/unit/datatokens/ERC20TemplatePredictoor.test.js index 88871365d..56cd22830 100644 --- a/test/unit/datatokens/ERC20TemplatePredictoor.test.js +++ b/test/unit/datatokens/ERC20TemplatePredictoor.test.js @@ -1397,4 +1397,94 @@ describe("ERC20TemplatePredictoor", () => { } }); + it("#redeem_unused_sub_revenue - admin should be able to redeem unused sub revenue for epoch", async()=>{ + const consumer = user2.address; // could be different user + const serviceIndex = 1; // dummy index + const providerFeeAddress = user5.address; // marketplace fee Collector + const providerFeeAmount = 0; // fee to be collected on top, requires approval + const providerFeeToken = mockErc20.address; // token address for the feeAmount, + const consumeMarketFeeAddress = user5.address; // marketplace fee Collector + const consumeMarketFeeAmount = 0; // fee to be collected on top, requires approval + const consumeMarketFeeToken = mockErc20.address; // token address for the feeAmount, + const providerValidUntil = 0; + const marketFee = 1e15 // 0.1% + const marketFeeCollector = addressZero + const rate = web3.utils.toWei("2"); // 2 tokens per dt + const amountDT = web3.utils.toWei("1"); + + //create fixed rate + const tx = await erc20Token.connect(owner).createFixedRate( + fixedRateExchange.address, + [mockErc20.address, owner.address, marketFeeCollector, addressZero], + [18, 18, rate, marketFee, 1]) + const txReceipt = await tx.wait(); + let event = getEventFromTx(txReceipt, 'NewFixedRate') + assert(event, "Cannot find NewFixedRate event") + exchangeId = event.args.exchangeId + const exchangeInfo = await fixedRateExchange.calcBaseInGivenOutDT(exchangeId, amountDT, 0) + + //let's buy a DT + await mockErc20.transfer(user2.address, exchangeInfo.baseTokenAmount); + await mockErc20.connect(user2).approve(fixedRateExchange.address, exchangeInfo.baseTokenAmount); + // user buys DT + await fixedRateExchange.connect(user2).buyDT(exchangeId, amountDT, exchangeInfo.baseTokenAmount, addressZero, 0) + const balance = await erc20Token.balanceOf(user2.address) + assert(balance > 0, "Failed to buy DT") + //sign provider data + const providerData = JSON.stringify({ "timeout": 0 }) + const message = ethers.utils.solidityKeccak256( + ["bytes", "address", "address", "uint256", "uint256"], + [ + ethers.utils.hexlify(ethers.utils.toUtf8Bytes(providerData)), + providerFeeAddress, + providerFeeToken, + providerFeeAmount, + providerValidUntil + ] + ); + const signedMessage = await signMessage(message, providerFeeAddress); + let soonestBlockToPredict = await erc20Token.soonest_block_to_predict(); + let revenue_at_block = await erc20Token.connect(user2).get_subscription_revenue_at_block(soonestBlockToPredict) + expect(revenue_at_block).to.be.eq(0); + + await erc20Token + .connect(user2) + .startOrder( + consumer, + serviceIndex, + { + providerFeeAddress: providerFeeAddress, + providerFeeToken: providerFeeToken, + providerFeeAmount: providerFeeAmount, + v: signedMessage.v, + r: signedMessage.r, + s: signedMessage.s, + providerData: ethers.utils.hexlify(ethers.utils.toUtf8Bytes(providerData)), + validUntil: providerValidUntil + }, + { + consumeMarketFeeAddress: consumeMarketFeeAddress, + consumeMarketFeeToken: consumeMarketFeeToken, + consumeMarketFeeAmount: consumeMarketFeeAmount, + } + ); + + revenue_at_block = await erc20Token.connect(user2).get_subscription_revenue_at_block(soonestBlockToPredict) + Array(100).fill(0).map(async _ => await ethers.provider.send("evm_mine")); + const blocksPerEpoch = await erc20Token.blocks_per_epoch(); + const currentBlock = await ethers.provider.getBlockNumber(); + const railedBlock = await erc20Token.rail_blocknum_to_slot(currentBlock) - blocksPerEpoch; + const tx_2 = await erc20Token.redeem_unused_sub_revenue(railedBlock); + const txReceipt_2 = await tx_2.wait(); + let event_2 = getEventFromTx(txReceipt_2, 'Transfer') + expect(event_2.args.from).to.be.eq(erc20Token.address); + expect(event_2.args.to).to.be.eq(owner.address); + expect(event_2.args.value).to.be.eq(6666666666666666); + }) + it("#redeem_unused_sub_revenue - admin should not be able to redeem for future epoch", async()=>{ + const blocksPerEpoch = await erc20Token.blocks_per_epoch(); + const currentBlock = await ethers.provider.getBlockNumber(); + const railedBlock = await erc20Token.rail_blocknum_to_slot(currentBlock) + blocksPerEpoch; + expectRevert(erc20Token.redeem_unused_sub_revenue(railedBlock)); + }) }); From 3d375e9e5a4989438cd739110a69f4fa1086e4f5 Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Thu, 11 May 2023 05:49:22 -0700 Subject: [PATCH 072/120] fix Docker --- Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index f562d07a8..940981910 100644 --- a/Dockerfile +++ b/Dockerfile @@ -11,8 +11,8 @@ RUN rm package-lock.json RUN rm -rf ./node-modules/ RUN npm i ENV SLEEP_FOR_GANACHE=10 -ENV NETWORK=barge -ENV NETWORK_RPC_URL=127.0.0.1:8545 RUN npx hardhat clean --global RUN npx hardhat compile --force +ENV NETWORK=barge +ENV NETWORK_RPC_URL=127.0.0.1:8545 ENTRYPOINT ["/ocean-contracts/scripts/deploy_docker.sh"] \ No newline at end of file From ac417c5b13c4c4b5c3d61b8b464183138ee9f906 Mon Sep 17 00:00:00 2001 From: trizin <25263018+trizin@users.noreply.github.com> Date: Thu, 11 May 2023 12:54:22 +0000 Subject: [PATCH 073/120] Make seconds global --- test/unit/datatokens/ERC20TemplatePredictoor.test.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/test/unit/datatokens/ERC20TemplatePredictoor.test.js b/test/unit/datatokens/ERC20TemplatePredictoor.test.js index 56cd22830..089f4a26c 100644 --- a/test/unit/datatokens/ERC20TemplatePredictoor.test.js +++ b/test/unit/datatokens/ERC20TemplatePredictoor.test.js @@ -36,7 +36,10 @@ const PERMIT_TYPEHASH = keccak256( "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)" ) ); - +const sPerBlock = 24; +const sPerEpoch = 300; +const sPerSubscription = 24 * 60 * 60; +const truevalSubmitTimeout = 24 * 60 * 60 * 3; const getApprovalDigest = async ( token, owner, @@ -210,11 +213,10 @@ describe("ERC20TemplatePredictoor", () => { // [user3.address, user6.address, user3.address, addressZero, mockErc20.address], - const trxERC20 = await tokenERC721.connect(user3).createERC20(1, ["ERC20DT3", "ERC20DT3Symbol"], [user3.address, user6.address, user3.address, addressZero, mockErc20.address], - [cap, 0, 24, 288, 24 * 60 * 60, 24 * 60 * 60 * 3], + [cap, 0, sPerBlock, sPerEpoch, sPerSubscription, truevalSubmitTimeout], [] ); From 2c0bed6b723c298d1294e0301e0697a32380f5dc Mon Sep 17 00:00:00 2001 From: trizin <25263018+trizin@users.noreply.github.com> Date: Thu, 11 May 2023 13:01:25 +0000 Subject: [PATCH 074/120] Fix number --- .../datatokens/ERC20TemplatePredictoor.test.js | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/test/unit/datatokens/ERC20TemplatePredictoor.test.js b/test/unit/datatokens/ERC20TemplatePredictoor.test.js index 089f4a26c..b2aa160c8 100644 --- a/test/unit/datatokens/ERC20TemplatePredictoor.test.js +++ b/test/unit/datatokens/ERC20TemplatePredictoor.test.js @@ -37,7 +37,7 @@ const PERMIT_TYPEHASH = keccak256( ) ); const sPerBlock = 24; -const sPerEpoch = 300; +const sPerEpoch = 288; const sPerSubscription = 24 * 60 * 60; const truevalSubmitTimeout = 24 * 60 * 60 * 3; const getApprovalDigest = async ( @@ -1489,4 +1489,20 @@ describe("ERC20TemplatePredictoor", () => { const railedBlock = await erc20Token.rail_blocknum_to_slot(currentBlock) + blocksPerEpoch; expectRevert(erc20Token.redeem_unused_sub_revenue(railedBlock)); }) + it("predictoor redeems their stake if OPF does not submit", async() => { + const stake = 100; + await mockErc20.transfer(user2.address, stake); + await mockErc20.connect(user2).approve(erc20Token.address, stake); + const prediction = true; + const soonestBlockToPredict = await erc20Token.soonest_block_to_predict(); + await erc20Token.connect(user2).predict(soonestBlockToPredict, prediction, stake); + + // set timeout to 1 minute + await erc20Token.update_seconds(sPerBlock, sPerEpoch, sPerSubscription, (sPerEpoch * 5)); + expectRevert(erc20Token.connect(user2).payout(soonestBlockToPredict, user2.address), "too early"); + Array(30).fill(0).map(async _ => await ethers.provider.send("evm_mine")); + expectRevert(erc20Token.connect(user2).payout(soonestBlockToPredict, user2.address), "truval not submitted"); + Array(300).fill(0).map(async _ => await ethers.provider.send("evm_mine")); + const tx = await erc20Token.connect(user2).payout(soonestBlockToPredict, user2.address); + }) }); From 59be399143500b9ddd251b191fe0bc53ebeafea8 Mon Sep 17 00:00:00 2001 From: trizin <25263018+trizin@users.noreply.github.com> Date: Thu, 11 May 2023 13:16:18 +0000 Subject: [PATCH 075/120] Fix comment --- contracts/templates/ERC20TemplatePredictoor.sol | 2 +- test/unit/datatokens/ERC20TemplatePredictoor.test.js | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/contracts/templates/ERC20TemplatePredictoor.sol b/contracts/templates/ERC20TemplatePredictoor.sol index 7809c8485..9c8992dd1 100644 --- a/contracts/templates/ERC20TemplatePredictoor.sol +++ b/contracts/templates/ERC20TemplatePredictoor.sol @@ -1030,7 +1030,7 @@ contract ERC20TemplatePredictoor is require(predobj.paid == false, "already paid"); - // if OPF hasn't submitted trueval in truval_submit_timeout days + // if OPF hasn't submitted trueval in truval_submit_timeout blocks // refund stake to predictoor and cancel round if ( block.number > blocknum + truval_submit_timeout_block && diff --git a/test/unit/datatokens/ERC20TemplatePredictoor.test.js b/test/unit/datatokens/ERC20TemplatePredictoor.test.js index b2aa160c8..2506060f9 100644 --- a/test/unit/datatokens/ERC20TemplatePredictoor.test.js +++ b/test/unit/datatokens/ERC20TemplatePredictoor.test.js @@ -1495,13 +1495,19 @@ describe("ERC20TemplatePredictoor", () => { await mockErc20.connect(user2).approve(erc20Token.address, stake); const prediction = true; const soonestBlockToPredict = await erc20Token.soonest_block_to_predict(); - await erc20Token.connect(user2).predict(soonestBlockToPredict, prediction, stake); + console.log("Im here"); + await erc20Token.connect(user2).submit_predval(soonestBlockToPredict, prediction, stake); // set timeout to 1 minute - await erc20Token.update_seconds(sPerBlock, sPerEpoch, sPerSubscription, (sPerEpoch * 5)); + console.log("Im here"); + await erc20Token.update_seconds(sPerBlock, sPerEpoch, sPerSubscription, sPerEpoch * 3); + console.log("Im here"); expectRevert(erc20Token.connect(user2).payout(soonestBlockToPredict, user2.address), "too early"); + console.log("Im here"); Array(30).fill(0).map(async _ => await ethers.provider.send("evm_mine")); + console.log("Im here"); expectRevert(erc20Token.connect(user2).payout(soonestBlockToPredict, user2.address), "truval not submitted"); + console.log("Im here"); Array(300).fill(0).map(async _ => await ethers.provider.send("evm_mine")); const tx = await erc20Token.connect(user2).payout(soonestBlockToPredict, user2.address); }) From e46c8a2f543c24f4d672f4f9a60b48816fe3d3d1 Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Thu, 11 May 2023 06:17:23 -0700 Subject: [PATCH 076/120] more docker --- Dockerfile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Dockerfile b/Dockerfile index 940981910..42bef6300 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,12 +7,15 @@ RUN bash /tmp/nodesource_setup.sh RUN apt install nodejs COPY . /ocean-contracts WORKDIR /ocean-contracts +RUN rm -rf /ocean-contracts/artifacts/* RUN rm package-lock.json RUN rm -rf ./node-modules/ RUN npm i ENV SLEEP_FOR_GANACHE=10 RUN npx hardhat clean --global RUN npx hardhat compile --force +RUN npx hardhat compile --force ENV NETWORK=barge ENV NETWORK_RPC_URL=127.0.0.1:8545 +RUN /ocean-contracts/artifacts/* -name "*.dbg.json" -type f -delete ENTRYPOINT ["/ocean-contracts/scripts/deploy_docker.sh"] \ No newline at end of file From 4c67beec8bf2d755bdfb69c696790c44a458bc81 Mon Sep 17 00:00:00 2001 From: trizin <25263018+trizin@users.noreply.github.com> Date: Thu, 11 May 2023 13:20:05 +0000 Subject: [PATCH 077/120] Check if predictoor has submitted in payout func --- contracts/templates/ERC20TemplatePredictoor.sol | 1 + 1 file changed, 1 insertion(+) diff --git a/contracts/templates/ERC20TemplatePredictoor.sol b/contracts/templates/ERC20TemplatePredictoor.sol index 9c8992dd1..f95de2291 100644 --- a/contracts/templates/ERC20TemplatePredictoor.sol +++ b/contracts/templates/ERC20TemplatePredictoor.sol @@ -1026,6 +1026,7 @@ contract ERC20TemplatePredictoor is uint256 blocknum, address predictoor_addr ) public blocknumOnSlot(blocknum) nonReentrant { + require(submitted_predval(blocknum, predictoor_addr), "not submitted"); Prediction memory predobj = predobjs[blocknum][predictoor_addr]; require(predobj.paid == false, "already paid"); From 5bb2b74bfc6eee5aecd938aad81eea75a399eb17 Mon Sep 17 00:00:00 2001 From: trizin <25263018+trizin@users.noreply.github.com> Date: Thu, 11 May 2023 13:22:45 +0000 Subject: [PATCH 078/120] Add test:predictoor can redeem stake if OPF does not submit --- .../ERC20TemplatePredictoor.test.js | 27 +++++++++++-------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/test/unit/datatokens/ERC20TemplatePredictoor.test.js b/test/unit/datatokens/ERC20TemplatePredictoor.test.js index 2506060f9..3a63e46a5 100644 --- a/test/unit/datatokens/ERC20TemplatePredictoor.test.js +++ b/test/unit/datatokens/ERC20TemplatePredictoor.test.js @@ -1489,26 +1489,31 @@ describe("ERC20TemplatePredictoor", () => { const railedBlock = await erc20Token.rail_blocknum_to_slot(currentBlock) + blocksPerEpoch; expectRevert(erc20Token.redeem_unused_sub_revenue(railedBlock)); }) - it("predictoor redeems their stake if OPF does not submit", async() => { + it("predictoor can redeem stake if OPF does not submit", async() => { + await erc20Token.update_seconds(sPerBlock, sPerSubscription, sPerEpoch * 3); + const stake = 100; await mockErc20.transfer(user2.address, stake); await mockErc20.connect(user2).approve(erc20Token.address, stake); const prediction = true; const soonestBlockToPredict = await erc20Token.soonest_block_to_predict(); - console.log("Im here"); - await erc20Token.connect(user2).submit_predval(soonestBlockToPredict, prediction, stake); - - // set timeout to 1 minute - console.log("Im here"); - await erc20Token.update_seconds(sPerBlock, sPerEpoch, sPerSubscription, sPerEpoch * 3); - console.log("Im here"); + await erc20Token.connect(user2).submit_predval(prediction, stake, soonestBlockToPredict); + expectRevert(erc20Token.connect(user2).payout(soonestBlockToPredict, user2.address), "too early"); - console.log("Im here"); + Array(30).fill(0).map(async _ => await ethers.provider.send("evm_mine")); - console.log("Im here"); + expectRevert(erc20Token.connect(user2).payout(soonestBlockToPredict, user2.address), "truval not submitted"); - console.log("Im here"); + Array(300).fill(0).map(async _ => await ethers.provider.send("evm_mine")); + const tx = await erc20Token.connect(user2).payout(soonestBlockToPredict, user2.address); + const txReceipt = await tx.wait(); + const event = getEventFromTx(txReceipt, 'Transfer') + expect(event.args.from).to.be.eq(erc20Token.address); + expect(event.args.to).to.be.eq(user2.address); + expect(event.args.value).to.be.eq(stake); + + await erc20Token.update_seconds(sPerBlock, sPerSubscription, truevalSubmitTimeout); }) }); From 974a3330a47bf17064cb6545cdc0924f46f57180 Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Thu, 11 May 2023 06:32:11 -0700 Subject: [PATCH 079/120] fix --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 42bef6300..cba70ff8c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -17,5 +17,5 @@ RUN npx hardhat compile --force RUN npx hardhat compile --force ENV NETWORK=barge ENV NETWORK_RPC_URL=127.0.0.1:8545 -RUN /ocean-contracts/artifacts/* -name "*.dbg.json" -type f -delete +RUN find /ocean-contracts/artifacts/* -name "*.dbg.json" -type f -delete ENTRYPOINT ["/ocean-contracts/scripts/deploy_docker.sh"] \ No newline at end of file From cf36d066fd8c1251363f4238c2406f118a54519b Mon Sep 17 00:00:00 2001 From: trizin <25263018+trizin@users.noreply.github.com> Date: Thu, 11 May 2023 13:33:37 +0000 Subject: [PATCH 080/120] Fix expect reverts --- .../datatokens/ERC20TemplatePredictoor.test.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/test/unit/datatokens/ERC20TemplatePredictoor.test.js b/test/unit/datatokens/ERC20TemplatePredictoor.test.js index 3a63e46a5..9152dcf28 100644 --- a/test/unit/datatokens/ERC20TemplatePredictoor.test.js +++ b/test/unit/datatokens/ERC20TemplatePredictoor.test.js @@ -1049,7 +1049,7 @@ describe("ERC20TemplatePredictoor", () => { const soonestBlockToPredict = await erc20Token.soonest_block_to_predict(); await erc20Token.submit_predval(predval, stake, soonestBlockToPredict); - expectRevert(erc20Token.connect(user2).get_prediction(soonestBlockToPredict, owner.address)); + await expectRevert(erc20Token.connect(user2).get_prediction(soonestBlockToPredict, owner.address), "you shall not pass"); // fast forward blocks until next epoch Array(30).fill(0).map(async _ => await ethers.provider.send("evm_mine")); // user2 should be able to read the predval now @@ -1487,7 +1487,7 @@ describe("ERC20TemplatePredictoor", () => { const blocksPerEpoch = await erc20Token.blocks_per_epoch(); const currentBlock = await ethers.provider.getBlockNumber(); const railedBlock = await erc20Token.rail_blocknum_to_slot(currentBlock) + blocksPerEpoch; - expectRevert(erc20Token.redeem_unused_sub_revenue(railedBlock)); + await expectRevert.unspecified(erc20Token.redeem_unused_sub_revenue(railedBlock)); }) it("predictoor can redeem stake if OPF does not submit", async() => { await erc20Token.update_seconds(sPerBlock, sPerSubscription, sPerEpoch * 3); @@ -1498,14 +1498,14 @@ describe("ERC20TemplatePredictoor", () => { const prediction = true; const soonestBlockToPredict = await erc20Token.soonest_block_to_predict(); await erc20Token.connect(user2).submit_predval(prediction, stake, soonestBlockToPredict); + const blocksPerEpoch = await erc20Token.blocks_per_epoch(); + await expectRevert(erc20Token.connect(user2).payout(soonestBlockToPredict, user2.address), "too early"); - expectRevert(erc20Token.connect(user2).payout(soonestBlockToPredict, user2.address), "too early"); - - Array(30).fill(0).map(async _ => await ethers.provider.send("evm_mine")); + Array(blocksPerEpoch).fill(0).map(async _ => await ethers.provider.send("evm_mine")); - expectRevert(erc20Token.connect(user2).payout(soonestBlockToPredict, user2.address), "truval not submitted"); + await expectRevert(erc20Token.connect(user2).payout(soonestBlockToPredict, user2.address), "truval not submitted"); - Array(300).fill(0).map(async _ => await ethers.provider.send("evm_mine")); + Array(blocksPerEpoch * 3).fill(0).map(async _ => await ethers.provider.send("evm_mine")); const tx = await erc20Token.connect(user2).payout(soonestBlockToPredict, user2.address); const txReceipt = await tx.wait(); From 77fff1c87482241acf10fe3b24670828979b3429 Mon Sep 17 00:00:00 2001 From: trizin <25263018+trizin@users.noreply.github.com> Date: Thu, 11 May 2023 13:40:01 +0000 Subject: [PATCH 081/120] Fix test --- test/unit/datatokens/ERC20TemplatePredictoor.test.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/unit/datatokens/ERC20TemplatePredictoor.test.js b/test/unit/datatokens/ERC20TemplatePredictoor.test.js index 9152dcf28..ceeca0708 100644 --- a/test/unit/datatokens/ERC20TemplatePredictoor.test.js +++ b/test/unit/datatokens/ERC20TemplatePredictoor.test.js @@ -1499,11 +1499,11 @@ describe("ERC20TemplatePredictoor", () => { const soonestBlockToPredict = await erc20Token.soonest_block_to_predict(); await erc20Token.connect(user2).submit_predval(prediction, stake, soonestBlockToPredict); const blocksPerEpoch = await erc20Token.blocks_per_epoch(); - await expectRevert(erc20Token.connect(user2).payout(soonestBlockToPredict, user2.address), "too early"); + await expectRevert(erc20Token.connect(user2).payout(soonestBlockToPredict, user2.address), "trueval not submitted"); - Array(blocksPerEpoch).fill(0).map(async _ => await ethers.provider.send("evm_mine")); + Array(blocksPerEpoch * 2).fill(0).map(async _ => await ethers.provider.send("evm_mine")); - await expectRevert(erc20Token.connect(user2).payout(soonestBlockToPredict, user2.address), "truval not submitted"); + await expectRevert(erc20Token.connect(user2).payout(soonestBlockToPredict, user2.address), "trueval not submitted"); Array(blocksPerEpoch * 3).fill(0).map(async _ => await ethers.provider.send("evm_mine")); From fc4d94db67fce23c75eeca85e9ec85afdf64cacd Mon Sep 17 00:00:00 2001 From: trizin <25263018+trizin@users.noreply.github.com> Date: Thu, 11 May 2023 13:41:43 +0000 Subject: [PATCH 082/120] Set payout amount to 0 if swe is 0 --- contracts/templates/ERC20TemplatePredictoor.sol | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/contracts/templates/ERC20TemplatePredictoor.sol b/contracts/templates/ERC20TemplatePredictoor.sol index f95de2291..d025f317f 100644 --- a/contracts/templates/ERC20TemplatePredictoor.sol +++ b/contracts/templates/ERC20TemplatePredictoor.sol @@ -982,7 +982,7 @@ contract ERC20TemplatePredictoor is returns (Prediction memory prediction) { //allow predictoors to see their own submissions - require(msg.sender == predictoor || blocknum < block.number); + require(msg.sender == predictoor || blocknum < block.number, "you shall not pass"); prediction = predobjs[blocknum][predictoor]; } @@ -1056,8 +1056,11 @@ contract ERC20TemplatePredictoor is uint256 swe = truevals[blocknum] ? agg_predvals_numer[blocknum] : agg_predvals_denom[blocknum] - agg_predvals_numer[blocknum]; - uint256 revenue=get_subscription_revenue_at_block(blocknum); - uint256 payout_amt = predobj.stake * (agg_predvals_denom[blocknum] + revenue) / swe; + uint256 payout_amt = 0; + if(swe > 0) { + uint256 revenue=get_subscription_revenue_at_block(blocknum); + payout_amt = predobj.stake * (agg_predvals_denom[blocknum] + revenue) / swe; + } predobjs[blocknum][predictoor_addr].paid = true; emit PredictionPayout( predictoor_addr, From d3969e42fdd850dd167dfc560cf404efa9ef3eb1 Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Thu, 11 May 2023 06:51:13 -0700 Subject: [PATCH 083/120] more docker --- Dockerfile | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index cba70ff8c..41c5ac60b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,7 +7,10 @@ RUN bash /tmp/nodesource_setup.sh RUN apt install nodejs COPY . /ocean-contracts WORKDIR /ocean-contracts -RUN rm -rf /ocean-contracts/artifacts/* +RUN ls -lh /ocean-contracts/ +RUN ls -lh /ocean-contracts/contracts/ +RUN ls -lh /ocean-contracts/contracts/utils/ +RUN mkdir /ocean-contracts/artifacts/ RUN rm package-lock.json RUN rm -rf ./node-modules/ RUN npm i From 51afc72e9296b77dc2cbfe9477034e2dc77b5115 Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Thu, 11 May 2023 07:40:12 -0700 Subject: [PATCH 084/120] docker debug --- Dockerfile | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Dockerfile b/Dockerfile index 41c5ac60b..9daf2505c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -15,10 +15,10 @@ RUN rm package-lock.json RUN rm -rf ./node-modules/ RUN npm i ENV SLEEP_FOR_GANACHE=10 -RUN npx hardhat clean --global -RUN npx hardhat compile --force -RUN npx hardhat compile --force +RUN npx hardhat clean --verbose +RUN npx hardhat clean --global --verbose +RUN npx hardhat compile --force --verbose ENV NETWORK=barge ENV NETWORK_RPC_URL=127.0.0.1:8545 RUN find /ocean-contracts/artifacts/* -name "*.dbg.json" -type f -delete -ENTRYPOINT ["/ocean-contracts/scripts/deploy_docker.sh"] \ No newline at end of file +ENTRYPOINT ["/ocean-contracts/scripts/deploy_docker.sh"] \ No newline at end of file From a5873c45b06931ca22ffbabfe8106765489a900e Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Thu, 11 May 2023 07:59:41 -0700 Subject: [PATCH 085/120] restore original docker --- Dockerfile | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/Dockerfile b/Dockerfile index 9daf2505c..f9305422e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,18 +7,12 @@ RUN bash /tmp/nodesource_setup.sh RUN apt install nodejs COPY . /ocean-contracts WORKDIR /ocean-contracts -RUN ls -lh /ocean-contracts/ -RUN ls -lh /ocean-contracts/contracts/ -RUN ls -lh /ocean-contracts/contracts/utils/ -RUN mkdir /ocean-contracts/artifacts/ RUN rm package-lock.json RUN rm -rf ./node-modules/ RUN npm i ENV SLEEP_FOR_GANACHE=10 -RUN npx hardhat clean --verbose -RUN npx hardhat clean --global --verbose -RUN npx hardhat compile --force --verbose +RUN cp hardhat.config.barge.js hardhat.config.js ENV NETWORK=barge ENV NETWORK_RPC_URL=127.0.0.1:8545 -RUN find /ocean-contracts/artifacts/* -name "*.dbg.json" -type f -delete -ENTRYPOINT ["/ocean-contracts/scripts/deploy_docker.sh"] \ No newline at end of file +RUN npx hardhat compile +ENTRYPOINT ["/ocean-contracts/scripts/deploy_docker.sh \ No newline at end of file From 550e46d1f981662d84b543a38c04bda5f58be9ae Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Thu, 11 May 2023 08:01:27 -0700 Subject: [PATCH 086/120] fix typo --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index f9305422e..831707082 100644 --- a/Dockerfile +++ b/Dockerfile @@ -15,4 +15,4 @@ RUN cp hardhat.config.barge.js hardhat.config.js ENV NETWORK=barge ENV NETWORK_RPC_URL=127.0.0.1:8545 RUN npx hardhat compile -ENTRYPOINT ["/ocean-contracts/scripts/deploy_docker.sh \ No newline at end of file +ENTRYPOINT ["/ocean-contracts/scripts/deploy_docker.sh"] \ No newline at end of file From cea3d387509a150980d5ea72e6e4c7209f1b9ab3 Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Thu, 11 May 2023 08:01:57 -0700 Subject: [PATCH 087/120] fix typo --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 831707082..f4fb2955a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -15,4 +15,4 @@ RUN cp hardhat.config.barge.js hardhat.config.js ENV NETWORK=barge ENV NETWORK_RPC_URL=127.0.0.1:8545 RUN npx hardhat compile -ENTRYPOINT ["/ocean-contracts/scripts/deploy_docker.sh"] \ No newline at end of file +ENTRYPOINT ["/ocean-contracts/scripts/deploy_docker.sh"] From e815d6f129e97148c2b801fcfc14016623ba5192 Mon Sep 17 00:00:00 2001 From: trizin <25263018+trizin@users.noreply.github.com> Date: Fri, 12 May 2023 08:54:06 +0000 Subject: [PATCH 088/120] Add tests for submit_trueval --- .../ERC20TemplatePredictoor.test.js | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/test/unit/datatokens/ERC20TemplatePredictoor.test.js b/test/unit/datatokens/ERC20TemplatePredictoor.test.js index ceeca0708..921b19d37 100644 --- a/test/unit/datatokens/ERC20TemplatePredictoor.test.js +++ b/test/unit/datatokens/ERC20TemplatePredictoor.test.js @@ -1112,6 +1112,25 @@ describe("ERC20TemplatePredictoor", () => { ); }); + it("#submit_trueval - should revert submitting for a future block", async () => { + const soonestBlockToPredict = await erc20Token.soonest_block_to_predict(); + await expectRevert(erc20Token.submit_trueval(soonestBlockToPredict, true), "too early to submit"); + }); + + it("#submit_trueval - should submit for a block in the past", async () => { + const blocksPerEpoch = await erc20Token.blocks_per_epoch(); + const soonestBlockToPredict = await erc20Token.soonest_block_to_predict(); + const submissionBlock = soonestBlockToPredict - blocksPerEpoch * 2; + const tx = await erc20Token.submit_trueval(submissionBlock, true); + const tx_receipt = await tx.wait(); + const event = getEventFromTx(tx_receipt, "TruevalSubmitted"); + expect(event.args[0]).to.equal(submissionBlock); + expect(event.args[1]).to.equal(true); + + const trueval = await erc20Token.truevals(submissionBlock); + expect(trueval).to.be.true; + }); + it("#subscriptions - user2 must be subscribed", async () => { //MINT SOME DT20 to USER2 so he can start order await erc20Token.connect(user3).mint(user2.address, web3.utils.toWei("10")); From ec52843003863a80597e84c5fb8a44795b0d0607 Mon Sep 17 00:00:00 2001 From: trizin <25263018+trizin@users.noreply.github.com> Date: Fri, 12 May 2023 08:55:37 +0000 Subject: [PATCH 089/120] Test subscription expire --- .../ERC20TemplatePredictoor.test.js | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/test/unit/datatokens/ERC20TemplatePredictoor.test.js b/test/unit/datatokens/ERC20TemplatePredictoor.test.js index 921b19d37..06774f0d4 100644 --- a/test/unit/datatokens/ERC20TemplatePredictoor.test.js +++ b/test/unit/datatokens/ERC20TemplatePredictoor.test.js @@ -1187,6 +1187,71 @@ describe("ERC20TemplatePredictoor", () => { const currentBlock = await ethers.provider.getBlockNumber(); expect(subscription.expires).to.be.gt(currentBlock); expect(subscription.user).to.be.eq(user2.address); + + const valid = await erc20Token.is_valid_subscription(user2.address); + expect(valid).to.be.true; + }); + + it("#subscriptions - user2 subscription should expire", async () => { + //MINT SOME DT20 to USER2 so he can start order + await erc20Token.connect(user3).mint(user2.address, web3.utils.toWei("10")); + assert( + (await erc20Token.balanceOf(user2.address)) == web3.utils.toWei("10") + ); + const consumer = user2.address; // could be different user + const serviceIndex = 1; // dummy index + const providerFeeAddress = user5.address; // marketplace fee Collector + const providerFeeAmount = 0; // fee to be collected on top, requires approval + const providerFeeToken = mockErc20.address; // token address for the feeAmount, + const consumeMarketFeeAddress = user5.address; // marketplace fee Collector + const consumeMarketFeeAmount = 0; // fee to be collected on top, requires approval + const consumeMarketFeeToken = mockErc20.address; // token address for the feeAmount, + const providerValidUntil = 0; + //sign provider data + const providerData = JSON.stringify({ "timeout": 0 }) + const message = ethers.utils.solidityKeccak256( + ["bytes", "address", "address", "uint256", "uint256"], + [ + ethers.utils.hexlify(ethers.utils.toUtf8Bytes(providerData)), + providerFeeAddress, + providerFeeToken, + providerFeeAmount, + providerValidUntil + ] + ); + const signedMessage = await signMessage(message, providerFeeAddress); + + // reduce subscription time + await erc20Token.update_seconds(sPerBlock, sPerBlock, truevalSubmitTimeout); + // set back to normal + const tx = await erc20Token + .connect(user2) + .startOrder( + consumer, + serviceIndex, + { + providerFeeAddress: providerFeeAddress, + providerFeeToken: providerFeeToken, + providerFeeAmount: providerFeeAmount, + v: signedMessage.v, + r: signedMessage.r, + s: signedMessage.s, + providerData: ethers.utils.hexlify(ethers.utils.toUtf8Bytes(providerData)), + validUntil: providerValidUntil + }, + { + consumeMarketFeeAddress: consumeMarketFeeAddress, + consumeMarketFeeToken: consumeMarketFeeToken, + consumeMarketFeeAmount: consumeMarketFeeAmount, + } + ); + + + Array(100).fill(0).map(async () => await ethers.provider.send("evm_mine", [])); + const valid = await erc20Token.is_valid_subscription(user2.address); + expect(valid).to.be.false; + // set back to normal + await erc20Token.update_seconds(sPerBlock, sPerSubscription, truevalSubmitTimeout); }); From 048a53e54449e7a7081dbd5c7b2bc33a65cda538 Mon Sep 17 00:00:00 2001 From: trizin <25263018+trizin@users.noreply.github.com> Date: Fri, 12 May 2023 08:55:47 +0000 Subject: [PATCH 090/120] Rename test --- test/unit/datatokens/ERC20TemplatePredictoor.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit/datatokens/ERC20TemplatePredictoor.test.js b/test/unit/datatokens/ERC20TemplatePredictoor.test.js index 06774f0d4..81bc595cf 100644 --- a/test/unit/datatokens/ERC20TemplatePredictoor.test.js +++ b/test/unit/datatokens/ERC20TemplatePredictoor.test.js @@ -1331,7 +1331,7 @@ describe("ERC20TemplatePredictoor", () => { }); // can read get_agg_predval with a valid subscription - it("predictoor gets paid", async () => { + it("#payout - predictoor should get paid", async () => { const consumer = user2.address; // could be different user const serviceIndex = 1; // dummy index const providerFeeAddress = user5.address; // marketplace fee Collector From 524403e0daaafd1b6434fc9270b04fcb3ef39c2d Mon Sep 17 00:00:00 2001 From: trizin <25263018+trizin@users.noreply.github.com> Date: Fri, 12 May 2023 08:56:05 +0000 Subject: [PATCH 091/120] Test payout_mul function --- .../ERC20TemplatePredictoor.test.js | 102 +++++++++++++++++- 1 file changed, 101 insertions(+), 1 deletion(-) diff --git a/test/unit/datatokens/ERC20TemplatePredictoor.test.js b/test/unit/datatokens/ERC20TemplatePredictoor.test.js index 81bc595cf..fee3b4718 100644 --- a/test/unit/datatokens/ERC20TemplatePredictoor.test.js +++ b/test/unit/datatokens/ERC20TemplatePredictoor.test.js @@ -1431,6 +1431,106 @@ describe("ERC20TemplatePredictoor", () => { await expectRevert(erc20Token.connect(user3).payout(soonestBlockToPredict, user3.address), "already paid"); }); + it("#payout_mul - predictoor should get paid", async () => { + const consumer = user2.address; // could be different user + const serviceIndex = 1; // dummy index + const providerFeeAddress = user5.address; // marketplace fee Collector + const providerFeeAmount = 0; // fee to be collected on top, requires approval + const providerFeeToken = mockErc20.address; // token address for the feeAmount, + const consumeMarketFeeAddress = user5.address; // marketplace fee Collector + const consumeMarketFeeAmount = 0; // fee to be collected on top, requires approval + const consumeMarketFeeToken = mockErc20.address; // token address for the feeAmount, + const providerValidUntil = 0; + const marketFee = 1e15 // 0.1% + const marketFeeCollector = addressZero + const rate = web3.utils.toWei("2"); // 2 tokens per dt + const amountDT = web3.utils.toWei("1"); + + //create fixed rate + const tx = await erc20Token.connect(owner).createFixedRate( + fixedRateExchange.address, + [mockErc20.address, owner.address, marketFeeCollector, addressZero], + [18, 18, rate, marketFee, 1]) + const txReceipt = await tx.wait(); + let event = getEventFromTx(txReceipt, 'NewFixedRate') + assert(event, "Cannot find NewFixedRate event") + exchangeId = event.args.exchangeId + const exchangeInfo = await fixedRateExchange.calcBaseInGivenOutDT(exchangeId, amountDT, 0) + + //let's buy a DT + await mockErc20.transfer(user2.address, exchangeInfo.baseTokenAmount); + await mockErc20.connect(user2).approve(fixedRateExchange.address, exchangeInfo.baseTokenAmount); + // user buys DT + await fixedRateExchange.connect(user2).buyDT(exchangeId, amountDT, exchangeInfo.baseTokenAmount, addressZero, 0) + const balance = await erc20Token.balanceOf(user2.address) + assert(balance > 0, "Failed to buy DT") + //sign provider data + const providerData = JSON.stringify({ "timeout": 0 }) + const message = ethers.utils.solidityKeccak256( + ["bytes", "address", "address", "uint256", "uint256"], + [ + ethers.utils.hexlify(ethers.utils.toUtf8Bytes(providerData)), + providerFeeAddress, + providerFeeToken, + providerFeeAmount, + providerValidUntil + ] + ); + const signedMessage = await signMessage(message, providerFeeAddress); + let soonestBlockToPredict = await erc20Token.soonest_block_to_predict(); + let revenue_at_block = await erc20Token.connect(user2).get_subscription_revenue_at_block(soonestBlockToPredict) + expect(revenue_at_block).to.be.eq(0); + + await erc20Token + .connect(user2) + .startOrder( + consumer, + serviceIndex, + { + providerFeeAddress: providerFeeAddress, + providerFeeToken: providerFeeToken, + providerFeeAmount: providerFeeAmount, + v: signedMessage.v, + r: signedMessage.r, + s: signedMessage.s, + providerData: ethers.utils.hexlify(ethers.utils.toUtf8Bytes(providerData)), + validUntil: providerValidUntil + }, + { + consumeMarketFeeAddress: consumeMarketFeeAddress, + consumeMarketFeeToken: consumeMarketFeeToken, + consumeMarketFeeAmount: consumeMarketFeeAmount, + } + ); + + revenue_at_block = await erc20Token.connect(user2).get_subscription_revenue_at_block(soonestBlockToPredict) + expect(revenue_at_block).to.be.gt(0); + + // predictoor makes a prediction + const predval = true; + const stake = web3.utils.toWei("1"); + await mockErc20.transfer(user3.address, stake); + await mockErc20.connect(user3).approve(erc20Token.address, stake); + await erc20Token.connect(user3).submit_predval(predval, stake, soonestBlockToPredict); + + await expectRevert(erc20Token.connect(user3).payout_mul([soonestBlockToPredict], user3.address), "trueval not submitted") + Array(30).fill(0).map(async _ => await ethers.provider.send("evm_mine")); + await expectRevert(erc20Token.connect(user3).payout_mul([soonestBlockToPredict], user3.address), "trueval not submitted"); + + // opf submits truval + await erc20Token.submit_trueval(soonestBlockToPredict, predval); + const balBefore = await mockErc20.balanceOf(user3.address); + await erc20Token.connect(user3).payout_mul([soonestBlockToPredict], user3.address); + const balAfter = await mockErc20.balanceOf(user3.address); + expect(balAfter).to.be.gt(balBefore); + + const profit = balAfter.sub(balBefore); + const expectedProfit = 1 + (2 / parseInt(3600 / parseInt(300 / 24))) + expect(parseFloat(web3.utils.fromWei(profit.toString()))).to.be.eq(expectedProfit); + + await expectRevert(erc20Token.connect(user3).payout_mul([soonestBlockToPredict], user3.address), "already paid"); + }); + it("multiple predictoor compete and some gets paid", async () => { // predictoor makes a predictions let predictoors = [reciever, user2, user3, user4, user5, user6]; @@ -1481,8 +1581,8 @@ describe("ERC20TemplatePredictoor", () => { await expectRevert(erc20Token.connect(predictoor).payout(predictionBlock, predictoor.address), "wrong prediction"); } } - }); + it("#redeem_unused_sub_revenue - admin should be able to redeem unused sub revenue for epoch", async()=>{ const consumer = user2.address; // could be different user const serviceIndex = 1; // dummy index From 4cfbd0e30e18a5e092a7f15abf1a0dd38ad0d02a Mon Sep 17 00:00:00 2001 From: trizin <25263018+trizin@users.noreply.github.com> Date: Fri, 12 May 2023 09:50:23 +0000 Subject: [PATCH 092/120] Add TruevalSubmitted event --- contracts/templates/ERC20TemplatePredictoor.sol | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/contracts/templates/ERC20TemplatePredictoor.sol b/contracts/templates/ERC20TemplatePredictoor.sol index d025f317f..362337187 100644 --- a/contracts/templates/ERC20TemplatePredictoor.sol +++ b/contracts/templates/ERC20TemplatePredictoor.sol @@ -64,6 +64,10 @@ contract ERC20TemplatePredictoor is uint256 expires, uint256 blocknum ); + event TruevalSubmitted( + uint256 indexed block, + bool trueval + ); struct Prediction { bool predval; uint256 stake; @@ -1099,6 +1103,7 @@ contract ERC20TemplatePredictoor is require(blocknum < block.number, "too early to submit"); truevals[blocknum] = trueval; truval_submitted[blocknum] = true; + emit TruevalSubmitted(blocknum, trueval); } function update_seconds( From a96a5c48594134d95fbd3a0bce238831877acce4 Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Fri, 12 May 2023 03:55:00 -0700 Subject: [PATCH 093/120] make buyFromFre internal --- contracts/templates/ERC20TemplatePredictoor.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/templates/ERC20TemplatePredictoor.sol b/contracts/templates/ERC20TemplatePredictoor.sol index 362337187..49fda06bc 100644 --- a/contracts/templates/ERC20TemplatePredictoor.sol +++ b/contracts/templates/ERC20TemplatePredictoor.sol @@ -804,7 +804,7 @@ contract ERC20TemplatePredictoor is * @dev buyFromFre * Buys 1 DT from the FRE */ - function buyFromFre(FreParams calldata _freParams) public nonReentrant { + function buyFromFre(FreParams calldata _freParams) internal { // get exchange info IFixedRateExchange fre = IFixedRateExchange( _freParams.exchangeContract From b103c0b37e86435670c44a802723cc83ba0c0433 Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Fri, 12 May 2023 04:26:17 -0700 Subject: [PATCH 094/120] deploy on oasis_saphire_testnet --- addresses/address.json | 18 ++++++++++++++++++ hardhat.config.js | 6 ++++++ scripts/deploy-contracts.js | 31 +++++++++++++++++++++++++++---- 3 files changed, 51 insertions(+), 4 deletions(-) diff --git a/addresses/address.json b/addresses/address.json index a581187c3..3c0561e54 100644 --- a/addresses/address.json +++ b/addresses/address.json @@ -251,5 +251,23 @@ }, "Dispenser": "0xDEfD0018969cd2d4E648209F876ADe184815f038", "ERC721Factory": "0x9C9eE07b8Ce907D2f9244F8317C1Ed29A3193bAe" + }, + "oasis_saphire_testnet": { + "chainId": 23295, + "Ocean": "0x4dD281EB67DED07E76E413Df16176D66ae69e240", + "OPFCommunityFeeCollector": "0xe8c6Dc39602031A152440311e364818ba25C2Bc1", + "startBlock": 1178607, + "Router": "0xA4E108a4fa07C931cFe4bF2c1c6f9C1579d5a819", + "FixedPrice": "0x41cee92fEA5E07F8E3D1Eaf9A19dE3D83e24FA53", + "ERC20Template": { + "1": "0x9b87E416a76d8bCAf59A5c746D5ceB57119F08Cb", + "2": "0x12bB8D85a091A69A07E22E52d4567dBB91568f52", + "3": "0x9497d1d64F2aFeBcd4f9916Eef3d9094E5Df962f" + }, + "ERC721Template": { + "1": "0x7b0576CF01E868bce46cca91b2a8E674141b0355" + }, + "Dispenser": "0x8389bC1306208488D665F939AFB4079adf6f5a06", + "ERC721Factory": "0xc97fa83746aDe91b0eeB16cb51326a0A980Af7c3" } } \ No newline at end of file diff --git a/hardhat.config.js b/hardhat.config.js index 3bd33a739..2bd6f6282 100644 --- a/hardhat.config.js +++ b/hardhat.config.js @@ -138,6 +138,12 @@ module.exports = { accounts: process.env.PRIVATE_KEY !== undefined ? [process.env.PRIVATE_KEY] : [], }, + oasis_saphire_testnet: { + url: + process.env.NETWORK_RPC_URL !== undefined ? process.env.NETWORK_RPC_URL : "", + accounts: + process.env.PRIVATE_KEY !== undefined ? [process.env.PRIVATE_KEY] : [], + }, }, etherscan: { diff --git a/scripts/deploy-contracts.js b/scripts/deploy-contracts.js index 32726d40b..893c05c50 100644 --- a/scripts/deploy-contracts.js +++ b/scripts/deploy-contracts.js @@ -205,6 +205,18 @@ async function main() { gasPrice = null gasLimit = null break; + case 23295: + networkName = "oasis_saphire_testnet"; + OPFOwner = '0xC7EC1970B09224B317c52d92f37F5e1E4fF6B687' + routerOwner = OPFOwner; + sleepAmount = 30 + shouldDeployOceanToken = true; + shouldDeployV4 = true; + shouldDeployDF = false; + shouldDeployVE = false; + gasPrice = null + gasLimit = null + break; case 44787: networkName = "alfajores"; OPFOwner = '0x06100AB868206861a4D7936166A91668c2Ce1312' @@ -274,11 +286,19 @@ async function main() { console.log("\tRun the following to verify on etherscan"); console.log("\tnpx hardhat verify --network " + networkName + " " + ocean.address + " " + owner.address) } + if (sleepAmount > 0) await sleep(sleepAmount) + //send 1 mil tokens to deployer + let ownershiptx + console.log("\tMinting 1 mil tokens to owner"); + ownershiptx = await ocean.connect(owner).mint(owner.address,ethers.utils.parseUnits('1000000', 'ether'), options); + await ownershiptx.wait() + if (sleepAmount > 0) await sleep(sleepAmount) if (OPFOwner != owner.address) { - let ownershiptx + console.log("\tTransfer ownership of Ocean Token"); if (options) ownershiptx = await ocean.connect(owner).transferOwnership(OPFOwner, options); else ownershiptx = await ocean.connect(owner).transferOwnership(OPFOwner); await ownershiptx.wait() + if (sleepAmount > 0) await sleep(sleepAmount) } } else { @@ -427,8 +447,9 @@ async function main() { console.log("\tRun the following to verify on etherscan"); console.log("\tnpx hardhat verify --network " + networkName + " " + templateERC20Enterprise.address) } + if (sleepAmount > 0) await sleep(sleepAmount) - if (logging) console.info("Deploying ERC20 Enterprise Template"); + if (logging) console.info("Deploying ERC20TemplatePredictoor"); const ERC20PredictoorTemplate = await ethers.getContractFactory( "ERC20TemplatePredictoor", owner @@ -441,6 +462,7 @@ async function main() { console.log("\tRun the following to verify on etherscan"); console.log("\tnpx hardhat verify --network " + networkName + " " + templateERC20TemplatePredictoor.address) } + addresses.ERC721Template = {}; if (sleepAmount > 0) await sleep(sleepAmount) @@ -506,16 +528,17 @@ async function main() { let nftCount if (options) nftCount = await factoryERC721.getCurrentNFTTemplateCount(options); else nftCount = await factoryERC721.getCurrentNFTTemplateCount(); - + if (sleepAmount > 0) await sleep(sleepAmount) let nftTemplate if (options) nftTemplate = await factoryERC721.getNFTTemplate(nftCount, options); else nftTemplate = await factoryERC721.getNFTTemplate(nftCount); addresses.ERC721Template[nftCount.toString()] = templateERC721.address; + if (sleepAmount > 0) await sleep(sleepAmount) let currentTokenCount if (options) currentTokenCount = await factoryERC721.getCurrentTemplateCount(options); else currentTokenCount = await factoryERC721.getCurrentTemplateCount(); - + if (sleepAmount > 0) await sleep(sleepAmount) let tokenTemplate if (options) tokenTemplate = await factoryERC721.getTokenTemplate(currentTokenCount, options); else tokenTemplate = await factoryERC721.getTokenTemplate(currentTokenCount); From da8bb9f92e92868315fa9851bdd15bee6fa596b8 Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Sat, 13 May 2023 01:28:16 -0700 Subject: [PATCH 095/120] changes, see comments --- ...platePredictoor.sol => ERC20Template3.sol} | 201 +++++++++------- scripts/deploy-contracts.js | 24 +- ...dictoor.test.js => ERC20Template3.test.js} | 225 +++++++++--------- 3 files changed, 243 insertions(+), 207 deletions(-) rename contracts/templates/{ERC20TemplatePredictoor.sol => ERC20Template3.sol} (88%) rename test/unit/datatokens/{ERC20TemplatePredictoor.test.js => ERC20Template3.test.js} (91%) diff --git a/contracts/templates/ERC20TemplatePredictoor.sol b/contracts/templates/ERC20Template3.sol similarity index 88% rename from contracts/templates/ERC20TemplatePredictoor.sol rename to contracts/templates/ERC20Template3.sol index 49fda06bc..85f2c9b40 100644 --- a/contracts/templates/ERC20TemplatePredictoor.sol +++ b/contracts/templates/ERC20Template3.sol @@ -18,13 +18,14 @@ import "../utils/ERC20Roles.sol"; /** * @title DatatokenTemplate * - * @dev ERC20TemplatePredictoor is an ERC20 compliant token template + * @dev ERC20Template3 is an ERC20 compliant token template * Used by the factory contract as a bytecode reference to * deploy new Datatokens. * IMPORTANT CHANGES: - * - creation of pools is not allowed + * - creation of pools/dispensers is not allowed + * - creation of additional fixed rates is not allowed (only one can be created) */ -contract ERC20TemplatePredictoor is +contract ERC20Template3 is ERC20("test", "testSymbol"), ERC20Roles, ERC20Burnable, @@ -46,18 +47,24 @@ contract ERC20TemplatePredictoor is uint256 public constant BASE = 1e18; // -------------------------- PREDICTOOR -------------------------- + enum Status { + Pending, + Paying, + Canceled + } event PredictionSubmitted( address indexed predictoor, - uint256 indexed epoch, + uint256 indexed slot, uint256 stake ); event PredictionPayout( address indexed predictoor, - uint256 indexed epoch, + uint256 indexed slot, uint256 stake, uint256 payout, bool prediction, - bool truval + bool truval, + Status status ); event NewSubscription( address indexed user, @@ -65,8 +72,9 @@ contract ERC20TemplatePredictoor is uint256 blocknum ); event TruevalSubmitted( - uint256 indexed block, - bool trueval + uint256 indexed slot, + bool trueval, + Status status ); struct Prediction { bool predval; @@ -78,11 +86,16 @@ contract ERC20TemplatePredictoor is address user; uint256 expires; } + + + + // All mappings below are using slot as key. + // Whenever we have functions that take block as argumens, we rail it to slot automaticly mapping(uint256 => mapping(address => Prediction)) private predobjs; // id to prediction object mapping(uint256 => uint256) private agg_predvals_numer; mapping(uint256 => uint256) private agg_predvals_denom; mapping(uint256 => bool) public truevals; - mapping(uint256 => bool) public truval_submitted; + mapping(uint256 => Status) public truval_submitted; mapping(uint256 => uint256) private subscription_revenue_at_block; //income registred mapping(address => Subscription) public subscriptions; // valid subscription per user uint256 public blocks_per_epoch; @@ -202,14 +215,7 @@ contract ERC20TemplatePredictoor is _; } - modifier blocknumOnSlot(uint256 num) { - require( - blocknum_is_on_a_slot(num), - "Predictoor: blocknum must be on a slot" - ); - _; - } - + /** * @dev initialize * Called prior contract initialization (e.g creating new Datatoken instance) @@ -484,17 +490,6 @@ contract ERC20TemplatePredictoor is burn(amount); } - /** - * @dev addMinter - * Only ERC20Deployer (at 721 level) can update. - * There can be multiple minters - * @param _minter new minter address - */ - - function addMinter(address _minter) external onlyERC20Deployer { - _addMinter(_minter); - } - /** * @dev removeMinter * Only ERC20Deployer (at 721 level) can update. @@ -959,21 +954,24 @@ contract ERC20TemplatePredictoor is function submitted_predval( uint256 blocknum, address predictoor - ) public view blocknumOnSlot(blocknum) returns (bool) { - return predobjs[blocknum][predictoor].predictoor != address(0); + ) public view returns (bool) { + uint256 slot = rail_blocknum_to_slot(blocknum); + return predobjs[slot][predictoor].predictoor != address(0); } function get_agg_predval( uint256 blocknum - ) public view blocknumOnSlot(blocknum) returns (uint256, uint256) { + ) public view returns (uint256, uint256) { require(is_valid_subscription(msg.sender), "No subscription"); - return (agg_predvals_numer[blocknum], agg_predvals_denom[blocknum]); + uint256 slot = rail_blocknum_to_slot(blocknum); + return (agg_predvals_numer[slot], agg_predvals_denom[slot]); } function get_subscription_revenue_at_block( uint256 blocknum - ) public view blocknumOnSlot(blocknum) returns (uint256) { - return (subscription_revenue_at_block[blocknum]); + ) public view returns (uint256) { + uint256 slot = rail_blocknum_to_slot(blocknum); + return (subscription_revenue_at_block[slot]); } function get_prediction( @@ -982,12 +980,12 @@ contract ERC20TemplatePredictoor is ) public view - blocknumOnSlot(blocknum) returns (Prediction memory prediction) { //allow predictoors to see their own submissions require(msg.sender == predictoor || blocknum < block.number, "you shall not pass"); - prediction = predobjs[blocknum][predictoor]; + uint256 slot = rail_blocknum_to_slot(blocknum); + prediction = predobjs[slot][predictoor]; } // ----------------------- MUTATING FUNCTIONS ----------------------- @@ -996,12 +994,13 @@ contract ERC20TemplatePredictoor is bool predval, uint256 stake, uint256 blocknum - ) external blocknumOnSlot(blocknum) { + ) external { require(paused == false, "paused"); - require(blocknum >= soonest_block_to_predict(), "too late to submit"); - require(!submitted_predval(blocknum, msg.sender), "already submitted"); - - predobjs[blocknum][msg.sender] = Prediction( + uint256 slot = rail_blocknum_to_slot(blocknum); + require(slot >= soonest_block_to_predict(), "too late to submit"); + require(!submitted_predval(slot, msg.sender), "already submitted"); + + predobjs[slot][msg.sender] = Prediction( predval, stake, msg.sender, @@ -1009,10 +1008,10 @@ contract ERC20TemplatePredictoor is ); // update agg_predvals - agg_predvals_numer[blocknum] += stake * (predval ? 1 : 0); - agg_predvals_denom[blocknum] += stake; + agg_predvals_numer[slot] += stake * (predval ? 1 : 0); + agg_predvals_denom[slot] += stake; - emit PredictionSubmitted(msg.sender, epoch(blocknum), stake); + emit PredictionSubmitted(msg.sender, slot, stake); // safe transfer stake IERC20(stake_token).safeTransferFrom(msg.sender, address(this), stake); } @@ -1029,64 +1028,83 @@ contract ERC20TemplatePredictoor is function payout( uint256 blocknum, address predictoor_addr - ) public blocknumOnSlot(blocknum) nonReentrant { + ) public nonReentrant { require(submitted_predval(blocknum, predictoor_addr), "not submitted"); - Prediction memory predobj = predobjs[blocknum][predictoor_addr]; + uint256 slot = rail_blocknum_to_slot(blocknum); + Prediction memory predobj = predobjs[slot][predictoor_addr]; + if(predobj.paid) return; // just return if already paid, in order not to break payout_mul - require(predobj.paid == false, "already paid"); + // if OPF hasn't submitted trueval in truval_submit_timeout blocks then cancel round + if (block.number > slot + truval_submit_timeout_block && truval_submitted[slot]==Status.Pending){ + truval_submitted[slot]=Status.Canceled; + } - // if OPF hasn't submitted trueval in truval_submit_timeout blocks - // refund stake to predictoor and cancel round - if ( - block.number > blocknum + truval_submit_timeout_block && - !truval_submitted[blocknum] - ) { - predobjs[blocknum][predictoor_addr].paid = true; + if(truval_submitted[slot]==Status.Pending){ + // if Status is Pending, do nothing, just return + return; + } + if(truval_submitted[slot]==Status.Canceled){ + predobjs[slot][predictoor_addr].paid = true; emit PredictionPayout( predictoor_addr, - epoch(blocknum), + slot, predobj.stake, predobj.stake, predobj.predval, - truevals[blocknum] + truevals[slot], + truval_submitted[slot] ); IERC20(stake_token).safeTransfer(predobj.predictoor, predobj.stake); return; } - - require(truval_submitted[blocknum], "trueval not submitted"); - require(truevals[blocknum] == predobj.predval, "wrong prediction"); - - uint256 swe = truevals[blocknum] - ? agg_predvals_numer[blocknum] - : agg_predvals_denom[blocknum] - agg_predvals_numer[blocknum]; - uint256 payout_amt = 0; - if(swe > 0) { - uint256 revenue=get_subscription_revenue_at_block(blocknum); - payout_amt = predobj.stake * (agg_predvals_denom[blocknum] + revenue) / swe; + if(truval_submitted[slot]==Status.Paying){ + if(truevals[slot] != predobj.predval){ + //user got wrong prediction, there is no payout for him + predobjs[slot][predictoor_addr].paid = true; + emit PredictionPayout( + predictoor_addr, + slot, + predobj.stake, + 0, + predobj.predval, + truevals[slot], + truval_submitted[slot] + ); + return; + } + uint256 swe = truevals[slot] + ? agg_predvals_numer[slot] + : agg_predvals_denom[slot] - agg_predvals_numer[slot]; + uint256 payout_amt = 0; + if(swe > 0) { + uint256 revenue=get_subscription_revenue_at_block(slot); + payout_amt = predobj.stake * (agg_predvals_denom[slot] + revenue) / swe; + } + predobjs[slot][predictoor_addr].paid = true; + emit PredictionPayout( + predictoor_addr, + slot, + predobj.stake, + payout_amt, + predobj.predval, + truevals[slot], + truval_submitted[slot] + ); + IERC20(stake_token).safeTransfer( + predobj.predictoor, + payout_amt + ); } - predobjs[blocknum][predictoor_addr].paid = true; - emit PredictionPayout( - predictoor_addr, - epoch(blocknum), - predobj.stake, - payout_amt, - predobj.predval, - truevals[blocknum] - ); - IERC20(stake_token).safeTransfer( - predobj.predictoor, - payout_amt - ); } // ----------------------- ADMIN FUNCTIONS ----------------------- function redeem_unused_sub_revenue(uint256 blocknum) external onlyERC20Deployer { require(block.number > blocknum); - require(agg_predvals_denom[blocknum] == 0); + uint256 slot = rail_blocknum_to_slot(blocknum); + require(agg_predvals_denom[slot] == 0); IERC20(stake_token).safeTransfer( msg.sender, - subscription_revenue_at_block[blocknum] + subscription_revenue_at_block[slot] ); } @@ -1098,12 +1116,19 @@ contract ERC20TemplatePredictoor is function submit_trueval( uint256 blocknum, bool trueval - ) external blocknumOnSlot(blocknum) onlyERC20Deployer { + ) external onlyERC20Deployer { // TODO, is onlyERC20Deployer the right modifier? require(blocknum < block.number, "too early to submit"); - truevals[blocknum] = trueval; - truval_submitted[blocknum] = true; - emit TruevalSubmitted(blocknum, trueval); + uint256 slot = rail_blocknum_to_slot(blocknum); + require(truval_submitted[slot]==Status.Pending, "already settled"); + if (block.number > slot + truval_submit_timeout_block && truval_submitted[slot]==Status.Pending){ + truval_submitted[slot]=Status.Canceled; + } + else{ + truevals[slot] = trueval; + truval_submitted[slot] = Status.Paying; + } + emit TruevalSubmitted(slot, trueval,truval_submitted[slot]); } function update_seconds( @@ -1140,14 +1165,14 @@ contract ERC20TemplatePredictoor is function add_revenue(uint256 blocknum, uint256 amount) internal { if (amount > 0) { - blocknum = rail_blocknum_to_slot(blocknum); + uint256 slot = rail_blocknum_to_slot(blocknum); uint256 num_epochs = blocks_per_subscription / blocks_per_epoch; uint256 amt_per_epoch = amount / num_epochs; // for loop and add revenue for blocks_per_epoch blocks for (uint256 i = 0; i < num_epochs; i++) { // TODO FIND A WAY TO ACHIEVE THIS WITHOUT A LOOP subscription_revenue_at_block[ - blocknum + blocks_per_epoch * (i) + slot + blocks_per_epoch * (i) ] += amt_per_epoch; } } diff --git a/scripts/deploy-contracts.js b/scripts/deploy-contracts.js index 893c05c50..5e9ddb127 100644 --- a/scripts/deploy-contracts.js +++ b/scripts/deploy-contracts.js @@ -449,18 +449,18 @@ async function main() { } if (sleepAmount > 0) await sleep(sleepAmount) - if (logging) console.info("Deploying ERC20TemplatePredictoor"); - const ERC20PredictoorTemplate = await ethers.getContractFactory( - "ERC20TemplatePredictoor", + if (logging) console.info("Deploying ERC20Template3"); + const ERC203Template = await ethers.getContractFactory( + "ERC20Template3", owner ); - let templateERC20TemplatePredictoor - if (options) templateERC20TemplatePredictoor = await ERC20PredictoorTemplate.connect(owner).deploy(options); - else templateERC20TemplatePredictoor = await ERC20PredictoorTemplate.connect(owner).deploy(); - await templateERC20TemplatePredictoor.deployTransaction.wait(); + let templateERC20Template3 + if (options) templateERC20Template3 = await ERC203Template.connect(owner).deploy(options); + else templateERC20Template3 = await ERC203Template.connect(owner).deploy(); + await templateERC20Template3.deployTransaction.wait(); if (show_verify) { console.log("\tRun the following to verify on etherscan"); - console.log("\tnpx hardhat verify --network " + networkName + " " + templateERC20TemplatePredictoor.address) + console.log("\tnpx hardhat verify --network " + networkName + " " + templateERC20Template3.address) } @@ -560,9 +560,9 @@ async function main() { addresses.ERC20Template[currentTokenCount.toString()] = templateERC20Enterprise.address; - if (logging) console.info("Adding ERC20TemplatePredictoor to ERC721Factory"); - if (options) templateadd = await factoryERC721.connect(owner).addTokenTemplate(templateERC20TemplatePredictoor.address, options); - else templateadd = await factoryERC721.connect(owner).addTokenTemplate(templateERC20TemplatePredictoor.address); + if (logging) console.info("Adding ERC20Template3 to ERC721Factory"); + if (options) templateadd = await factoryERC721.connect(owner).addTokenTemplate(templateERC20Template3.address, options); + else templateadd = await factoryERC721.connect(owner).addTokenTemplate(templateERC20Template3.address); await templateadd.wait(); if (sleepAmount > 0) await sleep(sleepAmount) if (options) currentTokenCount = await factoryERC721.getCurrentTemplateCount(options); @@ -572,7 +572,7 @@ async function main() { else tokenTemplate = await factoryERC721.getTokenTemplate(currentTokenCount); addresses.ERC20Template[currentTokenCount.toString()] = - templateERC20TemplatePredictoor.address; + templateERC20Template3.address; // SET REQUIRED ADDRESS if (sleepAmount > 0) await sleep(sleepAmount) diff --git a/test/unit/datatokens/ERC20TemplatePredictoor.test.js b/test/unit/datatokens/ERC20Template3.test.js similarity index 91% rename from test/unit/datatokens/ERC20TemplatePredictoor.test.js rename to test/unit/datatokens/ERC20Template3.test.js index fee3b4718..3fe5f1ede 100644 --- a/test/unit/datatokens/ERC20TemplatePredictoor.test.js +++ b/test/unit/datatokens/ERC20Template3.test.js @@ -87,7 +87,7 @@ async function signMessage(message, address) { } -describe("ERC20TemplatePredictoor", () => { +describe("ERC20Template3", () => { let name, symbol, owner, @@ -120,7 +120,7 @@ describe("ERC20TemplatePredictoor", () => { beforeEach("init contracts for each test", async () => { const ERC721Template = await ethers.getContractFactory("ERC721Template"); - const ERC20TemplatePredictoor = await ethers.getContractFactory("ERC20TemplatePredictoor"); + const ERC20Template3 = await ethers.getContractFactory("ERC20Template3"); const ERC721Factory = await ethers.getContractFactory("ERC721Factory"); const Router = await ethers.getContractFactory("FactoryRouter"); @@ -158,7 +158,7 @@ describe("ERC20TemplatePredictoor", () => { router.address ); - templateERC20 = await ERC20TemplatePredictoor.deploy(); + templateERC20 = await ERC20Template3.deploy(); // SETUP ERC721 Factory with template @@ -225,7 +225,7 @@ describe("ERC20TemplatePredictoor", () => { assert(event, "Cannot find TokenCreated event") erc20Address = event.args[0]; - erc20Token = await ethers.getContractAt("ERC20TemplatePredictoor", erc20Address); + erc20Token = await ethers.getContractAt("ERC20Template3", erc20Address); assert((await erc20Token.permissions(user3.address)).minter == true); @@ -242,7 +242,7 @@ describe("ERC20TemplatePredictoor", () => { assert(event, "Cannot find TokenCreated event") erc20AddressWithPublishFee = event.args[0]; - erc20TokenWithPublishFee = await ethers.getContractAt("ERC20TemplatePredictoor", erc20AddressWithPublishFee); + erc20TokenWithPublishFee = await ethers.getContractAt("ERC20Template3", erc20AddressWithPublishFee); assert((await erc20TokenWithPublishFee.permissions(user3.address)).minter == true); }); @@ -292,75 +292,7 @@ describe("ERC20TemplatePredictoor", () => { assert(address, "Not able to get the parent ERC721 address") }); - it("#addMinter - should fail to addMinter if not erc20Deployer (permission to deploy the erc20Contract at 721 level)", async () => { - assert((await erc20Token.permissions(user2.address)).minter == false); - - await expectRevert( - erc20Token.connect(user2).addMinter(user2.address), - "ERC20Template: NOT DEPLOYER ROLE" - ); - - assert((await erc20Token.permissions(user2.address)).minter == false); - }); - - it("#addMinter - should fail to addMinter if it's already minter", async () => { - assert((await erc20Token.permissions(user2.address)).minter == false); - - await erc20Token.connect(user3).addMinter(user2.address); - - assert((await erc20Token.permissions(user2.address)).minter == true); - - await expectRevert( - erc20Token.connect(user3).addMinter(user2.address), - "ERC20Roles: ALREADY A MINTER" - ); - }); - - it("#addMinter - should succeed to addMinter if erc20Deployer (permission to deploy the erc20Contract at 721 level)", async () => { - assert((await erc20Token.permissions(user2.address)).minter == false); - - // owner is already erc20Deployer - await erc20Token.connect(user3).addMinter(user2.address); - - assert((await erc20Token.permissions(user2.address)).minter == true); - }); - - it("#removeMinter - should fail to removeMinter if NOT erc20Deployer", async () => { - await erc20Token.connect(user3).addMinter(user2.address); - assert((await erc20Token.permissions(user2.address)).minter == true); - - await expectRevert( - erc20Token.connect(user2).removeMinter(user2.address), - "ERC20Template: NOT DEPLOYER ROLE" - ); - - assert((await erc20Token.permissions(user2.address)).minter == true); - }); - - it("#removeMinter - should fail to removeMinter even if it's minter", async () => { - await erc20Token.connect(user3).addMinter(user2.address); - - assert((await erc20Token.permissions(user2.address)).minter == true); - - await expectRevert( - erc20Token.connect(user4).removeMinter(user2.address), - "ERC20Template: NOT DEPLOYER ROLE" - ); - - assert((await erc20Token.permissions(user2.address)).minter == true); - }); - - it("#removeMinter - should succeed to removeMinter if erc20Deployer", async () => { - await erc20Token.connect(user3).addMinter(user2.address); - - assert((await erc20Token.permissions(user2.address)).minter == true); - - assert((await tokenERC721.getPermissions(user3.address)).deployERC20 == true) - - await erc20Token.connect(user3).removeMinter(user2.address); - - assert((await erc20Token.permissions(user2.address)).minter == false); - }); + it("#addPaymentManager - should fail to addPaymentManager if not erc20Deployer (permission to deploy the erc20Contract at 721 level)", async () => { assert((await erc20Token.permissions(user2.address)).paymentManager == false); @@ -469,12 +401,7 @@ describe("ERC20TemplatePredictoor", () => { assert((await erc20Token.permissions(user3.address)).minter == true); await erc20Token.connect(user3).addPaymentManager(owner.address); - // WE add 2 more minters - await erc20Token.connect(user3).addMinter(user2.address); - await erc20Token.connect(user3).addMinter(user4.address); - assert((await erc20Token.permissions(user2.address)).minter == true); - assert((await erc20Token.permissions(user4.address)).minter == true); - + // NFT Owner cleans await erc20Token.cleanPermissions(); @@ -1017,7 +944,7 @@ describe("ERC20TemplatePredictoor", () => { const stake = 100; await mockErc20.approve(erc20Token.address, stake); const soonestBlockToPredict = await erc20Token.soonest_block_to_predict(); - const predictionEpoch = await erc20Token.epoch(soonestBlockToPredict); + const predictionSlot = await erc20Token.rail_blocknum_to_slot(soonestBlockToPredict); const tx = await erc20Token.submit_predval(predval, stake, soonestBlockToPredict); const txReceipt = await tx.wait(); @@ -1025,7 +952,7 @@ describe("ERC20TemplatePredictoor", () => { assert(event, "Cannot find PredictionSubmitted event") expect(event.event).to.equal("PredictionSubmitted"); expect(event.args[0]).to.equal(owner.address); - expect(event.args[1]).to.equal(predictionEpoch); + expect(event.args[1]).to.equal(predictionSlot); expect(event.args[2]).to.equal(stake); }); it("#submit_predval - predictoor can read their submitted predval", async () => { @@ -1347,11 +1274,11 @@ describe("ERC20TemplatePredictoor", () => { const amountDT = web3.utils.toWei("1"); //create fixed rate - const tx = await erc20Token.connect(owner).createFixedRate( + let tx = await erc20Token.connect(owner).createFixedRate( fixedRateExchange.address, [mockErc20.address, owner.address, marketFeeCollector, addressZero], [18, 18, rate, marketFee, 1]) - const txReceipt = await tx.wait(); + let txReceipt = await tx.wait(); let event = getEventFromTx(txReceipt, 'NewFixedRate') assert(event, "Cannot find NewFixedRate event") exchangeId = event.args.exchangeId @@ -1412,23 +1339,54 @@ describe("ERC20TemplatePredictoor", () => { await mockErc20.transfer(user3.address, stake); await mockErc20.connect(user3).approve(erc20Token.address, stake); await erc20Token.connect(user3).submit_predval(predval, stake, soonestBlockToPredict); - - await expectRevert(erc20Token.connect(user3).payout(soonestBlockToPredict, user3.address), "trueval not submitted") + let mockErc20Balance = await mockErc20.balanceOf(user3.address) + tx = await erc20Token.connect(user3).payout(soonestBlockToPredict, user3.address) + txReceipt = await tx.wait(); + event = getEventFromTx(txReceipt, 'PredictionPayout') + assert(event==null, "PredictionPayout event found") + //we are not getting anything, round is stil in progress + expect(await mockErc20.balanceOf(user3.address)).to.be.eq(mockErc20Balance); + const oceanBalance = await mockErc20.balanceOf(user2.address) Array(30).fill(0).map(async _ => await ethers.provider.send("evm_mine")); - await expectRevert(erc20Token.connect(user3).payout(soonestBlockToPredict, user3.address), "trueval not submitted"); + + mockErc20Balance = await mockErc20.balanceOf(user3.address) + tx = await erc20Token.connect(user3).payout(soonestBlockToPredict, user3.address) + txReceipt = await tx.wait(); + //we are not getting anything, round is stil in progress + event = getEventFromTx(txReceipt, 'PredictionPayout') + assert(event==null, "PredictionPayout event found") + expect(await mockErc20.balanceOf(user3.address)).to.be.eq(mockErc20Balance); + // opf submits truval - await erc20Token.submit_trueval(soonestBlockToPredict, predval); + tx = await erc20Token.submit_trueval(soonestBlockToPredict, predval); + txReceipt = await tx.wait(); + event = getEventFromTx(txReceipt, 'TruevalSubmitted') + assert(event, "TruevalSubmitted event not found") + assert(event.args.status==1, 'Status missmatch') // round status should be 1 == Status.Paying + + const balBefore = await mockErc20.balanceOf(user3.address); - await erc20Token.connect(user3).payout(soonestBlockToPredict, user3.address); + tx = await erc20Token.connect(user3).payout(soonestBlockToPredict, user3.address); + txReceipt = await tx.wait(); + event = getEventFromTx(txReceipt, 'PredictionPayout') + assert(event, "PredictionPayout event not found") + assert(event.args.status==1, 'Status missmatch') // round status should be 1 == Status.Paying const balAfter = await mockErc20.balanceOf(user3.address); expect(balAfter).to.be.gt(balBefore); - const profit = balAfter.sub(balBefore); const expectedProfit = 1 + (2 / parseInt(3600 / parseInt(300 / 24))) expect(parseFloat(web3.utils.fromWei(profit.toString()))).to.be.eq(expectedProfit); - await expectRevert(erc20Token.connect(user3).payout(soonestBlockToPredict, user3.address), "already paid"); + // user tries to call payout for the same slot + mockErc20Balance = await mockErc20.balanceOf(user3.address) + tx = await erc20Token.connect(user3).payout(soonestBlockToPredict, user3.address) + txReceipt = await tx.wait(); + event = getEventFromTx(txReceipt, 'PredictionPayout') + //we are not getting anything, we have been paid already + assert(event==null, "PredictionPayout event found") + expect(await mockErc20.balanceOf(user3.address)).to.be.eq(mockErc20Balance); + }); it("#payout_mul - predictoor should get paid", async () => { @@ -1447,11 +1405,11 @@ describe("ERC20TemplatePredictoor", () => { const amountDT = web3.utils.toWei("1"); //create fixed rate - const tx = await erc20Token.connect(owner).createFixedRate( + let tx = await erc20Token.connect(owner).createFixedRate( fixedRateExchange.address, [mockErc20.address, owner.address, marketFeeCollector, addressZero], [18, 18, rate, marketFee, 1]) - const txReceipt = await tx.wait(); + let txReceipt = await tx.wait(); let event = getEventFromTx(txReceipt, 'NewFixedRate') assert(event, "Cannot find NewFixedRate event") exchangeId = event.args.exchangeId @@ -1513,22 +1471,51 @@ describe("ERC20TemplatePredictoor", () => { await mockErc20.connect(user3).approve(erc20Token.address, stake); await erc20Token.connect(user3).submit_predval(predval, stake, soonestBlockToPredict); - await expectRevert(erc20Token.connect(user3).payout_mul([soonestBlockToPredict], user3.address), "trueval not submitted") + let mockErc20Balance = await mockErc20.balanceOf(user3.address) + tx = await erc20Token.connect(user3).payout_mul([soonestBlockToPredict], user3.address) + txReceipt = await tx.wait(); + event = getEventFromTx(txReceipt, 'PredictionPayout') + //we are not getting anything, round is still in progress + assert(event==null, "PredictionPayout event found") + expect(await mockErc20.balanceOf(user3.address)).to.be.eq(mockErc20Balance); Array(30).fill(0).map(async _ => await ethers.provider.send("evm_mine")); - await expectRevert(erc20Token.connect(user3).payout_mul([soonestBlockToPredict], user3.address), "trueval not submitted"); + mockErc20Balance = await mockErc20.balanceOf(user3.address) + tx = await erc20Token.connect(user3).payout_mul([soonestBlockToPredict], user3.address) + txReceipt = await tx.wait(); + event = getEventFromTx(txReceipt, 'PredictionPayout') + //we are not getting anything, round is still in progress + assert(event==null, "PredictionPayout event found") + expect(await mockErc20.balanceOf(user3.address)).to.be.eq(mockErc20Balance); // opf submits truval - await erc20Token.submit_trueval(soonestBlockToPredict, predval); + tx = await erc20Token.submit_trueval(soonestBlockToPredict, predval); + txReceipt = await tx.wait(); + event = getEventFromTx(txReceipt, 'TruevalSubmitted') + assert(event, "TruevalSubmitted event not found") + assert(event.args.status==1, 'Status missmatch') // round status should be 1 == Status.Paying + + const balBefore = await mockErc20.balanceOf(user3.address); - await erc20Token.connect(user3).payout_mul([soonestBlockToPredict], user3.address); + tx = await erc20Token.connect(user3).payout_mul([soonestBlockToPredict], user3.address); + txReceipt = await tx.wait(); + event = getEventFromTx(txReceipt, 'PredictionPayout') + assert(event, "PredictionPayout event not found") + assert(event.args.status==1, 'Status missmatch') // round status should be 1 == Status.Paying const balAfter = await mockErc20.balanceOf(user3.address); expect(balAfter).to.be.gt(balBefore); const profit = balAfter.sub(balBefore); const expectedProfit = 1 + (2 / parseInt(3600 / parseInt(300 / 24))) expect(parseFloat(web3.utils.fromWei(profit.toString()))).to.be.eq(expectedProfit); - - await expectRevert(erc20Token.connect(user3).payout_mul([soonestBlockToPredict], user3.address), "already paid"); + + mockErc20Balance = await mockErc20.balanceOf(user3.address) + tx = await erc20Token.connect(user3).payout_mul([soonestBlockToPredict], user3.address) + txReceipt = await tx.wait(); + event = getEventFromTx(txReceipt, 'PredictionPayout') + //we are not getting anything, we got the payment already + assert(event==null, "PredictionPayout event found") + expect(await mockErc20.balanceOf(user3.address)).to.be.eq(mockErc20Balance); + }); it("multiple predictoor compete and some gets paid", async () => { @@ -1536,6 +1523,7 @@ describe("ERC20TemplatePredictoor", () => { let predictoors = [reciever, user2, user3, user4, user5, user6]; let predictions = []; let stakes = []; + let tx,txReceipt, event for(const predictoor of predictoors){ const amt = web3.utils.toWei("200"); await mockErc20.transfer(predictoor.address, amt); @@ -1578,7 +1566,12 @@ describe("ERC20TemplatePredictoor", () => { const expectedProfit = stakes[i] / winnersStake * totalStake expect(parseFloat(web3.utils.fromWei(profit.toString()))).to.be.closeTo(expectedProfit, 0.2); } else { - await expectRevert(erc20Token.connect(predictoor).payout(predictionBlock, predictoor.address), "wrong prediction"); + tx = await erc20Token.connect(predictoor).payout(predictionBlock, predictoor.address); + txReceipt = await tx.wait(); + event = getEventFromTx(txReceipt, 'PredictionPayout') + assert(event, "PredictionPayout event not found") + expect(event.args.payout).to.be.eq(0) + } } }); @@ -1683,21 +1676,39 @@ describe("ERC20TemplatePredictoor", () => { const soonestBlockToPredict = await erc20Token.soonest_block_to_predict(); await erc20Token.connect(user2).submit_predval(prediction, stake, soonestBlockToPredict); const blocksPerEpoch = await erc20Token.blocks_per_epoch(); - await expectRevert(erc20Token.connect(user2).payout(soonestBlockToPredict, user2.address), "trueval not submitted"); + + let mockErc20Balance = await mockErc20.balanceOf(user2.address) + let tx = await erc20Token.connect(user2).payout(soonestBlockToPredict, user2.address) + let txReceipt = await tx.wait(); + let event = getEventFromTx(txReceipt, 'PredictionPayout') + //we are not getting anything, round is still in progress + assert(event==null, "PredictionPayout event found") + expect(await mockErc20.balanceOf(user2.address)).to.be.eq(mockErc20Balance); Array(blocksPerEpoch * 2).fill(0).map(async _ => await ethers.provider.send("evm_mine")); - await expectRevert(erc20Token.connect(user2).payout(soonestBlockToPredict, user2.address), "trueval not submitted"); + mockErc20Balance = await mockErc20.balanceOf(user2.address) + tx = await erc20Token.connect(user2).payout(soonestBlockToPredict, user2.address) + txReceipt = await tx.wait(); + event = getEventFromTx(txReceipt, 'PredictionPayout') + //we are not getting anything, round is still in progress + assert(event==null, "PredictionPayout event found") + expect(await mockErc20.balanceOf(user2.address)).to.be.eq(mockErc20Balance); Array(blocksPerEpoch * 3).fill(0).map(async _ => await ethers.provider.send("evm_mine")); - - const tx = await erc20Token.connect(user2).payout(soonestBlockToPredict, user2.address); - const txReceipt = await tx.wait(); - const event = getEventFromTx(txReceipt, 'Transfer') + + // opf is late + tx = await erc20Token.connect(user2).payout(soonestBlockToPredict, user2.address); + txReceipt = await tx.wait(); + event = getEventFromTx(txReceipt, 'Transfer') expect(event.args.from).to.be.eq(erc20Token.address); expect(event.args.to).to.be.eq(user2.address); expect(event.args.value).to.be.eq(stake); - + event = getEventFromTx(txReceipt, 'PredictionPayout') + assert(event, "PredictionPayout event not found") + assert(event.args.status==2, "Status should be 2 = Canceled") + expect(event.args.payout).to.be.eq(event.args.stake) + expect(event.args.payout).to.be.eq(stake) await erc20Token.update_seconds(sPerBlock, sPerSubscription, truevalSubmitTimeout); }) }); From c4dd008cd5a4dd64feca56e0bf87a3f1a4f229bc Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Sat, 13 May 2023 04:25:48 -0700 Subject: [PATCH 096/120] updates --- contracts/templates/ERC20Template3.sol | 37 ++-- test/unit/datatokens/ERC20Template3.test.js | 192 +++++++++++--------- 2 files changed, 126 insertions(+), 103 deletions(-) diff --git a/contracts/templates/ERC20Template3.sol b/contracts/templates/ERC20Template3.sol index 85f2c9b40..6f7b0a8d9 100644 --- a/contracts/templates/ERC20Template3.sol +++ b/contracts/templates/ERC20Template3.sol @@ -15,6 +15,7 @@ import "@openzeppelin/contracts/utils/math/SafeMath.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; import "../utils/ERC20Roles.sol"; +import 'hardhat/console.sol'; /** * @title DatatokenTemplate * @@ -87,7 +88,12 @@ contract ERC20Template3 is uint256 expires; } - + event SettingChanged( + uint256 blocks_per_epoch, + uint256 blocks_per_subscription, + uint256 truval_submit_timeout_block, + address stakeToken + ); // All mappings below are using slot as key. // Whenever we have functions that take block as argumens, we rail it to slot automaticly @@ -310,8 +316,6 @@ contract ERC20Template3 is _erc721Address = erc721Address; initialized = true; - // add a default minter, similar to what happens with manager in the 721 contract - _addMinter(addresses_[0]); // set payment collector to this contract, so we can get the $$$ _setPaymentCollector(address(this)); @@ -893,10 +897,11 @@ contract ERC20Template3 is /** * @dev getDispensers * Returns the list of dispensers created for this datatoken - */ + function getDispensers() public view returns (address[] memory) { return (dispensers); } + */ function _pullUnderlying( address erc20, @@ -930,7 +935,7 @@ contract ERC20Template3 is ) public view returns (uint256) { uint256 rounded = blocknum / blocks_per_epoch; return (rounded * blocks_per_epoch); - } + } function blocknum_is_on_a_slot( uint256 blocknum @@ -939,16 +944,13 @@ contract ERC20Template3 is return blocknum == rail_blocknum_to_slot(blocknum); } - function soonest_block_to_predict() public view returns (uint256) { - uint256 slotted_blocknum = rail_blocknum_to_slot(block.number); - - uint256 _blocknum; - if (slotted_blocknum == block.number) { - _blocknum = slotted_blocknum + blocks_per_epoch; - } else { - _blocknum = slotted_blocknum + 2 * blocks_per_epoch; - } - return _blocknum; + function soonest_block_to_predict(uint256 prediction_block) public view returns (uint256) { + /* + Epoch i: predictoors submit predval for the beginning of epoch i+2. + Predval is: "does trueval go UP or DOWN between the start of epoch i+1 and the start of epoch i+2?" + Once epoch i ends, predictoors cannot submit predvals for epoch i+2 + */ + return(rail_blocknum_to_slot(prediction_block)+ blocks_per_epoch * 2); } function submitted_predval( @@ -997,7 +999,7 @@ contract ERC20Template3 is ) external { require(paused == false, "paused"); uint256 slot = rail_blocknum_to_slot(blocknum); - require(slot >= soonest_block_to_predict(), "too late to submit"); + require(slot >= soonest_block_to_predict(block.number), "too late to submit"); require(!submitted_predval(slot, msg.sender), "already submitted"); predobjs[slot][msg.sender] = Prediction( @@ -1161,12 +1163,15 @@ contract ERC20Template3 is blocks_per_subscription = s_per_subscription / s_per_block; truval_submit_timeout_block = _truval_submit_timeout / s_per_block; + emit SettingChanged(blocks_per_epoch,blocks_per_subscription,truval_submit_timeout_block,stake_token); } function add_revenue(uint256 blocknum, uint256 amount) internal { if (amount > 0) { uint256 slot = rail_blocknum_to_slot(blocknum); uint256 num_epochs = blocks_per_subscription / blocks_per_epoch; + if(num_epochs<1) + num_epochs=1; uint256 amt_per_epoch = amount / num_epochs; // for loop and add revenue for blocks_per_epoch blocks for (uint256 i = 0; i < num_epochs; i++) { diff --git a/test/unit/datatokens/ERC20Template3.test.js b/test/unit/datatokens/ERC20Template3.test.js index 3fe5f1ede..09ce48996 100644 --- a/test/unit/datatokens/ERC20Template3.test.js +++ b/test/unit/datatokens/ERC20Template3.test.js @@ -112,12 +112,34 @@ describe("ERC20Template3", () => { cap = web3.utils.toWei("100000"); const fakeUSDAmount = cap - + const addressZero = '0x0000000000000000000000000000000000000000'; + const freRate = web3.utils.toWei("2"); // 2 tokens per dt + const freMarketFee = 1e15 // 0.1% + const freMarketFeeCollector = addressZero + + const communityFeeCollector = "0xeE9300b7961e0a01d9f0adb863C7A227A07AaD75"; const publishMarketFeeAmount = "5" - const addressZero = '0x0000000000000000000000000000000000000000'; + const noLimit = web3.utils.toWei('100000000000000000000'); + async function buyDTFromFixedRate(datatokenAddress,user,amount){ + amount=String(amount) + const datatokenContract = await ethers.getContractAt("ERC20Template3",datatokenAddress) + const fixedRates = await datatokenContract.connect(owner).getFixedRates() + if(fixedRates.length>0){ + fixedRateExchange = await ethers.getContractAt("FixedRateExchange", fixedRates[0].contractAddress); + fixedRateId=fixedRates[0].id + //get details + const details=await fixedRateExchange.connect(owner).getExchange(fixedRateId) + const needed=await fixedRateExchange.connect(owner).calcBaseInGivenOutDT(fixedRateId,web3.utils.toWei(amount),0); + erc20Contract = await ethers.getContractAt("MockERC20",details.baseToken) + await erc20Contract.connect(owner).approve(fixedRateExchange.address,needed.baseTokenAmount) + await fixedRateExchange.connect(owner).buyDT(fixedRateId,web3.utils.toWei(amount),needed.baseTokenAmount,ZERO_ADDRESS,0) + await datatokenContract.connect(owner).transfer(user,web3.utils.toWei(amount)) + } + } + beforeEach("init contracts for each test", async () => { const ERC721Template = await ethers.getContractFactory("ERC721Template"); const ERC20Template3 = await ethers.getContractFactory("ERC20Template3"); @@ -226,8 +248,20 @@ describe("ERC20Template3", () => { erc20Address = event.args[0]; erc20Token = await ethers.getContractAt("ERC20Template3", erc20Address); - assert((await erc20Token.permissions(user3.address)).minter == true); + assert((await erc20Token.permissions(user3.address)).minter == false); //nobody external can mint + //test that we cannot create a fixedrate with another baseToken != stakeToken + await expectRevert( + erc20Token.connect(owner).createFixedRate( + fixedRateExchange.address, + [mockErc20Decimals.address, owner.address, freMarketFeeCollector, addressZero], + [18, 18, freRate , freMarketFee , 1]), + "Cannot create FRE with baseToken!=stake_token" + ); + await erc20Token.connect(owner).createFixedRate( + fixedRateExchange.address, + [mockErc20.address, owner.address, freMarketFeeCollector, addressZero], + [18, 18, freRate , freMarketFee, 1]) // create an ERC20 with publish Fee ( 5 USDC, going to publishMarketAddress) const trxERC20WithPublishFee = await tokenERC721.connect(user3).createERC20(1, @@ -243,8 +277,14 @@ describe("ERC20Template3", () => { erc20AddressWithPublishFee = event.args[0]; erc20TokenWithPublishFee = await ethers.getContractAt("ERC20Template3", erc20AddressWithPublishFee); - assert((await erc20TokenWithPublishFee.permissions(user3.address)).minter == true); + assert((await erc20TokenWithPublishFee.permissions(user3.address)).minter == false); + await erc20TokenWithPublishFee.connect(owner).createFixedRate( + fixedRateExchange.address, + [mockErc20.address, owner.address, freMarketFeeCollector , addressZero], + [18, 18, freMarketFee, freMarketFee , 1]) + + Array(100).fill(0).map(async _ => await ethers.provider.send("evm_mine")); }); @@ -265,20 +305,22 @@ describe("ERC20Template3", () => { ); }); - it("#mint - user3 (minter role) should succeed to mint 1 ERC20Token to user2", async () => { - await erc20Token.connect(user3).mint(user2.address, web3.utils.toWei("1")); - assert( - (await erc20Token.balanceOf(user2.address)) == web3.utils.toWei("1") - ); - }); - it("#mint - should fail to mint 1 ERC20Token to user2 if NOT MINTER", async () => { await expectRevert( erc20Token.connect(user2).mint(user2.address, web3.utils.toWei("1")), "ERC20Template: NOT MINTER" ); }); - + it("#mint - should fail to create another fixed rate", async () => { + await expectRevert( + erc20Token.connect(owner).createFixedRate( + fixedRateExchange.address, + [mockErc20Decimals.address, owner.address, freMarketFeeCollector , addressZero], + [18, 18, freRate , freMarketFee , 1] + ), + "Fixed rate already present" + ) + }); it("#setPaymentCollector - should not modify paymentCollector address", async () => { await erc20Token.connect(user3).setPaymentCollector(owner.address); assert((await erc20Token.getPaymentCollector()) == erc20Token.address, 'PaymentCollector is not erc20Token'); @@ -387,19 +429,15 @@ describe("ERC20Template3", () => { }); it("#cleanPermissions - should fail to call cleanPermissions if NOT NFTOwner", async () => { - assert((await erc20Token.permissions(user3.address)).minter == true); await expectRevert( erc20Token.connect(user2).cleanPermissions(), "ERC20Template: not NFTOwner" ); - - assert((await erc20Token.permissions(user3.address)).minter == true); }); it("#cleanPermissions - should succeed to call cleanPermissions if NFTOwner", async () => { // user3 is already minter - assert((await erc20Token.permissions(user3.address)).minter == true); await erc20Token.connect(user3).addPaymentManager(owner.address); // NFT Owner cleans @@ -417,7 +455,7 @@ describe("ERC20Template3", () => { it("#startOrder - user should succeed to call startOrder on a ERC20 without publishFee", async () => { //MINT SOME DT20 to USER2 so he can start order - await erc20Token.connect(user3).mint(user2.address, web3.utils.toWei("10")); + await buyDTFromFixedRate(erc20Token.address,user2.address,10) assert( (await erc20Token.balanceOf(user2.address)) == web3.utils.toWei("10") ); @@ -495,7 +533,7 @@ describe("ERC20Template3", () => { it("#startOrder - user should succeed to call startOrder on a ERC20 without publishFee and provider Fee", async () => { //MINT SOME DT20 to USER2 so he can start order - await erc20Token.connect(user3).mint(user2.address, web3.utils.toWei("10")); + await buyDTFromFixedRate(erc20Token.address,user2.address,10) assert( (await erc20Token.balanceOf(user2.address)) == web3.utils.toWei("10") ); @@ -600,7 +638,7 @@ describe("ERC20Template3", () => { it("#startOrder - user should succeed to call startOrder on a ERC20 with 5 USDC publishFee, providerFee is ZERO and 5 USDC consumeFee", async () => { //MINT SOME DT20 to USER2 so he can start order - await erc20TokenWithPublishFee.connect(user3).mint(user2.address, web3.utils.toWei("10")); + await buyDTFromFixedRate(erc20TokenWithPublishFee.address,user2.address,10) assert( (await erc20TokenWithPublishFee.balanceOf(user2.address)) == web3.utils.toWei("10") ); @@ -698,7 +736,7 @@ describe("ERC20Template3", () => { it("#startOrder - user should succeed to call startOrder on a ERC20 with 5 USDC publishFee, providerFee is not ZEO", async () => { //MINT SOME DT20 to USER2 so he can start order - await erc20TokenWithPublishFee.connect(user3).mint(user2.address, web3.utils.toWei("10")); + await buyDTFromFixedRate(erc20TokenWithPublishFee.address,user2.address,10) assert( (await erc20TokenWithPublishFee.balanceOf(user2.address)) == web3.utils.toWei("10") ); @@ -828,7 +866,7 @@ describe("ERC20Template3", () => { it("#burn - user should succeed to burn tokens", async () => { //MINT SOME DT20 to USER2 so he can try to burn - await erc20Token.connect(user3).mint(user2.address, web3.utils.toWei("10")); + await buyDTFromFixedRate(erc20Token.address,user2.address,10) const burnAmount = web3.utils.toWei("2") assert( (await erc20Token.balanceOf(user2.address)) == web3.utils.toWei("10") @@ -854,7 +892,7 @@ describe("ERC20Template3", () => { it("#burnFrom - user3 should succeed to burn some user2's tokens using burnFrom", async () => { //MINT SOME DT20 to USER2 so he can try to burn - await erc20Token.connect(user3).mint(user2.address, web3.utils.toWei("10")); + await buyDTFromFixedRate(erc20Token.address,user2.address,10) const burnAmount = web3.utils.toWei("2") assert( (await erc20Token.balanceOf(user2.address)) == web3.utils.toWei("10") @@ -908,13 +946,13 @@ describe("ERC20Template3", () => { assert(isOnSlot == true, isOnSlot +" should be true"); }); it("#soonest_block_to_predict - should return soonest block to predict", async () => { - const soonestBlockToPredict = await erc20Token.soonest_block_to_predict(); + const soonestBlockToPredict = await erc20Token.soonest_block_to_predict((await ethers.provider.getBlockNumber())); // this should be equal to // 1 + (currentBlock - 1) / 100 const blocksPerEpoch = (await erc20Token.blocks_per_epoch()) const blockNumber = await ethers.provider.getBlockNumber(); const railed = parseInt(blockNumber / blocksPerEpoch) * blocksPerEpoch - const expected = railed + blocksPerEpoch * (railed == blockNumber ? 1 : 2); + const expected = railed + blocksPerEpoch * 2; assert(soonestBlockToPredict == expected, 'Invalid soonest block to predict'); }); it("#get_agg_predval - without subscription, should revert", async () => { @@ -943,7 +981,7 @@ describe("ERC20Template3", () => { const predval = true; const stake = 100; await mockErc20.approve(erc20Token.address, stake); - const soonestBlockToPredict = await erc20Token.soonest_block_to_predict(); + const soonestBlockToPredict = await erc20Token.soonest_block_to_predict((await ethers.provider.getBlockNumber())+1); const predictionSlot = await erc20Token.rail_blocknum_to_slot(soonestBlockToPredict); const tx = await erc20Token.submit_predval(predval, stake, soonestBlockToPredict); @@ -958,9 +996,9 @@ describe("ERC20Template3", () => { it("#submit_predval - predictoor can read their submitted predval", async () => { const predval = true; const stake = 100; - await mockErc20.approve(erc20Token.address, stake); - const soonestBlockToPredict = await erc20Token.soonest_block_to_predict(); - + tx = await mockErc20.approve(erc20Token.address, stake); + await tx.wait() + const soonestBlockToPredict = await erc20Token.soonest_block_to_predict((await ethers.provider.getBlockNumber())+1); await erc20Token.submit_predval(predval, stake, soonestBlockToPredict); const prediction = await erc20Token.get_prediction(soonestBlockToPredict, owner.address); @@ -972,9 +1010,9 @@ describe("ERC20Template3", () => { it("#submit_predval - others cannot read submitted predictions", async () => { const predval = true; const stake = 100; - await mockErc20.approve(erc20Token.address, stake); - const soonestBlockToPredict = await erc20Token.soonest_block_to_predict(); - + tx = await mockErc20.approve(erc20Token.address, stake); + await tx.wait() + const soonestBlockToPredict = await erc20Token.soonest_block_to_predict((await ethers.provider.getBlockNumber())+1); await erc20Token.submit_predval(predval, stake, soonestBlockToPredict); await expectRevert(erc20Token.connect(user2).get_prediction(soonestBlockToPredict, owner.address), "you shall not pass"); // fast forward blocks until next epoch @@ -999,7 +1037,7 @@ describe("ERC20Template3", () => { const predval = true; const stake = 100; await mockErc20.approve(erc20Token.address, stake * 2); - const soonestBlockToPredict = await erc20Token.soonest_block_to_predict(); + const soonestBlockToPredict = await erc20Token.soonest_block_to_predict((await ethers.provider.getBlockNumber())+1); await erc20Token.submit_predval(predval, stake, soonestBlockToPredict); @@ -1017,7 +1055,7 @@ describe("ERC20Template3", () => { const predval = true; const stake = 100; await mockErc20.approve(erc20Token.address, stake); - const soonestBlockToPredict = await erc20Token.soonest_block_to_predict(); + const soonestBlockToPredict = await erc20Token.soonest_block_to_predict((await ethers.provider.getBlockNumber())+1); await expectRevert( erc20Token.submit_predval(predval, stake, soonestBlockToPredict), "paused" @@ -1040,13 +1078,13 @@ describe("ERC20Template3", () => { }); it("#submit_trueval - should revert submitting for a future block", async () => { - const soonestBlockToPredict = await erc20Token.soonest_block_to_predict(); + const soonestBlockToPredict = await erc20Token.soonest_block_to_predict((await ethers.provider.getBlockNumber())+1); await expectRevert(erc20Token.submit_trueval(soonestBlockToPredict, true), "too early to submit"); }); it("#submit_trueval - should submit for a block in the past", async () => { const blocksPerEpoch = await erc20Token.blocks_per_epoch(); - const soonestBlockToPredict = await erc20Token.soonest_block_to_predict(); + const soonestBlockToPredict = await erc20Token.soonest_block_to_predict((await ethers.provider.getBlockNumber())); const submissionBlock = soonestBlockToPredict - blocksPerEpoch * 2; const tx = await erc20Token.submit_trueval(submissionBlock, true); const tx_receipt = await tx.wait(); @@ -1060,7 +1098,7 @@ describe("ERC20Template3", () => { it("#subscriptions - user2 must be subscribed", async () => { //MINT SOME DT20 to USER2 so he can start order - await erc20Token.connect(user3).mint(user2.address, web3.utils.toWei("10")); + await buyDTFromFixedRate(erc20Token.address,user2.address,10) assert( (await erc20Token.balanceOf(user2.address)) == web3.utils.toWei("10") ); @@ -1121,7 +1159,7 @@ describe("ERC20Template3", () => { it("#subscriptions - user2 subscription should expire", async () => { //MINT SOME DT20 to USER2 so he can start order - await erc20Token.connect(user3).mint(user2.address, web3.utils.toWei("10")); + await buyDTFromFixedRate(erc20Token.address,user2.address,10) assert( (await erc20Token.balanceOf(user2.address)) == web3.utils.toWei("10") ); @@ -1145,7 +1183,7 @@ describe("ERC20Template3", () => { providerFeeAmount, providerValidUntil ] - ); + ); const signedMessage = await signMessage(message, providerFeeAddress); // reduce subscription time @@ -1173,7 +1211,6 @@ describe("ERC20Template3", () => { } ); - Array(100).fill(0).map(async () => await ethers.provider.send("evm_mine", [])); const valid = await erc20Token.is_valid_subscription(user2.address); expect(valid).to.be.false; @@ -1185,7 +1222,7 @@ describe("ERC20Template3", () => { // can read get_agg_predval with a valid subscription it("#get_agg_predval - should return agg_predval if caller has a valid subscription", async () => { //MINT SOME DT20 to USER2 so he can start order - await erc20Token.connect(user3).mint(user2.address, web3.utils.toWei("10")); + await buyDTFromFixedRate(erc20Token.address,user2.address,10) assert( (await erc20Token.balanceOf(user2.address)) == web3.utils.toWei("10") ); @@ -1234,7 +1271,7 @@ describe("ERC20Template3", () => { ); - let soonestBlockToPredict = await erc20Token.soonest_block_to_predict(); + let soonestBlockToPredict = await erc20Token.soonest_block_to_predict((await ethers.provider.getBlockNumber())+1); const [numer, denom] = await erc20Token.connect(user2).get_agg_predval(soonestBlockToPredict); expect(numer).to.be.eq(0); expect(denom).to.be.eq(0); @@ -1244,17 +1281,17 @@ describe("ERC20Template3", () => { const stake = web3.utils.toWei("1"); await mockErc20.transfer(user3.address, stake); await mockErc20.connect(user3).approve(erc20Token.address, stake); - soonestBlockToPredict = await erc20Token.soonest_block_to_predict(); + soonestBlockToPredict = await erc20Token.soonest_block_to_predict((await ethers.provider.getBlockNumber())+1); await erc20Token.connect(user3).submit_predval(predval, stake, soonestBlockToPredict); - soonestBlockToPredict = await erc20Token.soonest_block_to_predict(); + soonestBlockToPredict = await erc20Token.soonest_block_to_predict((await ethers.provider.getBlockNumber())+1); const [numer2, denom2] = await erc20Token.connect(user2).get_agg_predval(soonestBlockToPredict); expect(numer2).to.be.eq(web3.utils.toWei("1")); expect(denom2).to.be.eq(web3.utils.toWei("1")); // check subscription revenue const revenue = await erc20Token.get_subscription_revenue_at_block(soonestBlockToPredict); - expect(revenue).to.be.eq(0); + expect(revenue).to.be.gt(0); }); // can read get_agg_predval with a valid subscription @@ -1268,20 +1305,12 @@ describe("ERC20Template3", () => { const consumeMarketFeeAmount = 0; // fee to be collected on top, requires approval const consumeMarketFeeToken = mockErc20.address; // token address for the feeAmount, const providerValidUntil = 0; - const marketFee = 1e15 // 0.1% - const marketFeeCollector = addressZero - const rate = web3.utils.toWei("2"); // 2 tokens per dt const amountDT = web3.utils.toWei("1"); - //create fixed rate - let tx = await erc20Token.connect(owner).createFixedRate( - fixedRateExchange.address, - [mockErc20.address, owner.address, marketFeeCollector, addressZero], - [18, 18, rate, marketFee, 1]) - let txReceipt = await tx.wait(); - let event = getEventFromTx(txReceipt, 'NewFixedRate') - assert(event, "Cannot find NewFixedRate event") - exchangeId = event.args.exchangeId + + const exchanges = await erc20Token.getFixedRates() + const fixedRateExchange = await ethers.getContractAt("FixedRateExchange",exchanges[0].contractAddress) + const exchangeId = exchanges[0].id const exchangeInfo = await fixedRateExchange.calcBaseInGivenOutDT(exchangeId, amountDT, 0) //let's buy a DT @@ -1304,7 +1333,7 @@ describe("ERC20Template3", () => { ] ); const signedMessage = await signMessage(message, providerFeeAddress); - let soonestBlockToPredict = await erc20Token.soonest_block_to_predict(); + let soonestBlockToPredict = await erc20Token.soonest_block_to_predict((await ethers.provider.getBlockNumber())+4);//we have 3 more txes till our prediction let revenue_at_block = await erc20Token.connect(user2).get_subscription_revenue_at_block(soonestBlockToPredict) expect(revenue_at_block).to.be.eq(0); @@ -1342,7 +1371,7 @@ describe("ERC20Template3", () => { let mockErc20Balance = await mockErc20.balanceOf(user3.address) tx = await erc20Token.connect(user3).payout(soonestBlockToPredict, user3.address) txReceipt = await tx.wait(); - event = getEventFromTx(txReceipt, 'PredictionPayout') + let event = getEventFromTx(txReceipt, 'PredictionPayout') assert(event==null, "PredictionPayout event found") //we are not getting anything, round is stil in progress expect(await mockErc20.balanceOf(user3.address)).to.be.eq(mockErc20Balance); @@ -1399,20 +1428,11 @@ describe("ERC20Template3", () => { const consumeMarketFeeAmount = 0; // fee to be collected on top, requires approval const consumeMarketFeeToken = mockErc20.address; // token address for the feeAmount, const providerValidUntil = 0; - const marketFee = 1e15 // 0.1% - const marketFeeCollector = addressZero - const rate = web3.utils.toWei("2"); // 2 tokens per dt const amountDT = web3.utils.toWei("1"); - //create fixed rate - let tx = await erc20Token.connect(owner).createFixedRate( - fixedRateExchange.address, - [mockErc20.address, owner.address, marketFeeCollector, addressZero], - [18, 18, rate, marketFee, 1]) - let txReceipt = await tx.wait(); - let event = getEventFromTx(txReceipt, 'NewFixedRate') - assert(event, "Cannot find NewFixedRate event") - exchangeId = event.args.exchangeId + const exchanges = await erc20Token.getFixedRates() + const fixedRateExchange = await ethers.getContractAt("FixedRateExchange",exchanges[0].contractAddress) + const exchangeId = exchanges[0].id const exchangeInfo = await fixedRateExchange.calcBaseInGivenOutDT(exchangeId, amountDT, 0) //let's buy a DT @@ -1435,7 +1455,7 @@ describe("ERC20Template3", () => { ] ); const signedMessage = await signMessage(message, providerFeeAddress); - let soonestBlockToPredict = await erc20Token.soonest_block_to_predict(); + let soonestBlockToPredict = await erc20Token.soonest_block_to_predict((await ethers.provider.getBlockNumber())+2);//because we also have startOrder let revenue_at_block = await erc20Token.connect(user2).get_subscription_revenue_at_block(soonestBlockToPredict) expect(revenue_at_block).to.be.eq(0); @@ -1532,9 +1552,9 @@ describe("ERC20Template3", () => { const blocksPerEpoch = await erc20Token.blocks_per_epoch(); const currentBlock = await ethers.provider.getBlockNumber(); - const soonestBlockToPredict = await erc20Token.soonest_block_to_predict(); + const soonestBlockToPredict = await erc20Token.soonest_block_to_predict((await ethers.provider.getBlockNumber())+1); Array(soonestBlockToPredict - currentBlock + 1).fill(0).map(async _ => await ethers.provider.send("evm_mine")); - const predictionBlock = await erc20Token.soonest_block_to_predict(); + const predictionBlock = await erc20Token.soonest_block_to_predict((await ethers.provider.getBlockNumber())+1); for(const predictoor of predictoors){ const stake = 10 + Math.random() * 100; @@ -1586,20 +1606,11 @@ describe("ERC20Template3", () => { const consumeMarketFeeAmount = 0; // fee to be collected on top, requires approval const consumeMarketFeeToken = mockErc20.address; // token address for the feeAmount, const providerValidUntil = 0; - const marketFee = 1e15 // 0.1% - const marketFeeCollector = addressZero - const rate = web3.utils.toWei("2"); // 2 tokens per dt const amountDT = web3.utils.toWei("1"); - //create fixed rate - const tx = await erc20Token.connect(owner).createFixedRate( - fixedRateExchange.address, - [mockErc20.address, owner.address, marketFeeCollector, addressZero], - [18, 18, rate, marketFee, 1]) - const txReceipt = await tx.wait(); - let event = getEventFromTx(txReceipt, 'NewFixedRate') - assert(event, "Cannot find NewFixedRate event") - exchangeId = event.args.exchangeId + const exchanges = await erc20Token.getFixedRates() + const fixedRateExchange = await ethers.getContractAt("FixedRateExchange",exchanges[0].contractAddress) + const exchangeId = exchanges[0].id const exchangeInfo = await fixedRateExchange.calcBaseInGivenOutDT(exchangeId, amountDT, 0) //let's buy a DT @@ -1622,7 +1633,7 @@ describe("ERC20Template3", () => { ] ); const signedMessage = await signMessage(message, providerFeeAddress); - let soonestBlockToPredict = await erc20Token.soonest_block_to_predict(); + let soonestBlockToPredict = await erc20Token.soonest_block_to_predict((await ethers.provider.getBlockNumber())+1); let revenue_at_block = await erc20Token.connect(user2).get_subscription_revenue_at_block(soonestBlockToPredict) expect(revenue_at_block).to.be.eq(0); @@ -1673,7 +1684,14 @@ describe("ERC20Template3", () => { await mockErc20.transfer(user2.address, stake); await mockErc20.connect(user2).approve(erc20Token.address, stake); const prediction = true; - const soonestBlockToPredict = await erc20Token.soonest_block_to_predict(); + const soonestBlockToPredict = await erc20Token.soonest_block_to_predict((await ethers.provider.getBlockNumber())+1); + const blockNum = await ethers.provider.getBlockNumber(); + const slot = await erc20Token.rail_blocknum_to_slot(soonestBlockToPredict); + console.log("Slot:"+slot) + console.log("soonestBlockToPredict:"+soonestBlockToPredict) + console.log("blockNum:"+blockNum) + + await erc20Token.connect(user2).submit_predval(prediction, stake, soonestBlockToPredict); const blocksPerEpoch = await erc20Token.blocks_per_epoch(); From 39066058a971a3b284399c37283933ae68adb290 Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Sat, 13 May 2023 04:28:03 -0700 Subject: [PATCH 097/120] remove console --- contracts/templates/ERC20Template3.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/templates/ERC20Template3.sol b/contracts/templates/ERC20Template3.sol index 6f7b0a8d9..e0bd36455 100644 --- a/contracts/templates/ERC20Template3.sol +++ b/contracts/templates/ERC20Template3.sol @@ -15,7 +15,7 @@ import "@openzeppelin/contracts/utils/math/SafeMath.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; import "../utils/ERC20Roles.sol"; -import 'hardhat/console.sol'; + /** * @title DatatokenTemplate * From 37f80c9a35465c19f4bec7c25bb73f1187b7c488 Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Sat, 13 May 2023 04:31:23 -0700 Subject: [PATCH 098/120] add compat function bck --- contracts/templates/ERC20Template3.sol | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/contracts/templates/ERC20Template3.sol b/contracts/templates/ERC20Template3.sol index e0bd36455..e9fef6927 100644 --- a/contracts/templates/ERC20Template3.sol +++ b/contracts/templates/ERC20Template3.sol @@ -897,11 +897,10 @@ contract ERC20Template3 is /** * @dev getDispensers * Returns the list of dispensers created for this datatoken - + */ function getDispensers() public view returns (address[] memory) { return (dispensers); } - */ function _pullUnderlying( address erc20, From 3984eadcd1aa582b66f1241dd4f41cbe2fbf59b0 Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Sat, 13 May 2023 20:56:31 -0700 Subject: [PATCH 099/120] comments fixes --- contracts/templates/ERC20Template3.sol | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/contracts/templates/ERC20Template3.sol b/contracts/templates/ERC20Template3.sol index e9fef6927..63b318589 100644 --- a/contracts/templates/ERC20Template3.sol +++ b/contracts/templates/ERC20Template3.sol @@ -63,8 +63,9 @@ contract ERC20Template3 is uint256 indexed slot, uint256 stake, uint256 payout, - bool prediction, - bool truval, + bool predval, + bool trueval, + uint256 aggPredval, Status status ); event NewSubscription( @@ -127,6 +128,8 @@ contract ERC20Template3 is fixedRate[] fixedRateExchanges; address[] dispensers; + // this structure is here only for compatibility reasons with other datatoken templates + // it's not validated or used anywhere in the code, except as unused argument to startOrder function struct providerFee { address providerFeeAddress; address providerFeeToken; // address of the token @@ -1053,6 +1056,7 @@ contract ERC20Template3 is predobj.stake, predobj.predval, truevals[slot], + agg_predvals_numer[slot] / agg_predvals_denom[slot], truval_submitted[slot] ); IERC20(stake_token).safeTransfer(predobj.predictoor, predobj.stake); @@ -1069,6 +1073,7 @@ contract ERC20Template3 is 0, predobj.predval, truevals[slot], + agg_predvals_numer[slot] / agg_predvals_denom[slot], truval_submitted[slot] ); return; @@ -1089,6 +1094,7 @@ contract ERC20Template3 is payout_amt, predobj.predval, truevals[slot], + agg_predvals_numer[slot] / agg_predvals_denom[slot], truval_submitted[slot] ); IERC20(stake_token).safeTransfer( From d7c150463a549875dd251a3871c06863e984e3a6 Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Sat, 13 May 2023 22:39:38 -0700 Subject: [PATCH 100/120] remove payment managers + add event --- contracts/templates/ERC20Template3.sol | 27 +------------------------- 1 file changed, 1 insertion(+), 26 deletions(-) diff --git a/contracts/templates/ERC20Template3.sol b/contracts/templates/ERC20Template3.sol index 63b318589..bbaae9fe9 100644 --- a/contracts/templates/ERC20Template3.sol +++ b/contracts/templates/ERC20Template3.sol @@ -491,6 +491,7 @@ contract ERC20Template3 is block.number + blocks_per_subscription ); subscriptions[consumer] = sub; + emit NewSubscription(consumer, block.number + blocks_per_subscription,block.number); //record income add_revenue(block.number, rate); @@ -508,32 +509,6 @@ contract ERC20Template3 is _removeMinter(_minter); } - /** - * @dev addPaymentManager (can set who's going to collect fee when consuming orders) - * Only ERC20Deployer (at 721 level) can update. - * There can be multiple paymentCollectors - * @param _paymentManager new minter address - */ - - function addPaymentManager( - address _paymentManager - ) external onlyERC20Deployer { - _addPaymentManager(_paymentManager); - } - - /** - * @dev removePaymentManager - * Only ERC20Deployer (at 721 level) can update. - * There can be multiple paymentManagers - * @param _paymentManager _paymentManager address to remove - */ - - function removePaymentManager( - address _paymentManager - ) external onlyERC20Deployer { - _removePaymentManager(_paymentManager); - } - /** * @dev setData * Only ERC20Deployer (at 721 level) can call it. From 6247931898a0e1b7e5dfe71674b26e4dd6cefeb7 Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Sat, 13 May 2023 22:50:43 -0700 Subject: [PATCH 101/120] optimize contract size --- contracts/templates/ERC20Template3.sol | 65 +++++------------ test/unit/datatokens/ERC20Template3.test.js | 81 --------------------- 2 files changed, 20 insertions(+), 126 deletions(-) diff --git a/contracts/templates/ERC20Template3.sol b/contracts/templates/ERC20Template3.sol index bbaae9fe9..883cb8c47 100644 --- a/contracts/templates/ERC20Template3.sol +++ b/contracts/templates/ERC20Template3.sol @@ -1022,61 +1022,36 @@ contract ERC20Template3 is // if Status is Pending, do nothing, just return return; } + uint256 payout_amt = 0; + predobjs[slot][predictoor_addr].paid = true; if(truval_submitted[slot]==Status.Canceled){ - predobjs[slot][predictoor_addr].paid = true; - emit PredictionPayout( - predictoor_addr, - slot, - predobj.stake, - predobj.stake, - predobj.predval, - truevals[slot], - agg_predvals_numer[slot] / agg_predvals_denom[slot], - truval_submitted[slot] - ); - IERC20(stake_token).safeTransfer(predobj.predictoor, predobj.stake); - return; + payout_amt = predobj.stake; + } + else{ // Status.Paying + if(truevals[slot] == predobj.predval){ + // he got it. + uint256 swe = truevals[slot] + ? agg_predvals_numer[slot] + : agg_predvals_denom[slot] - agg_predvals_numer[slot]; + if(swe > 0) { + uint256 revenue=get_subscription_revenue_at_block(slot); + payout_amt = predobj.stake * (agg_predvals_denom[slot] + revenue) / swe; + } + } + // else payout_amt is already 0 } - if(truval_submitted[slot]==Status.Paying){ - if(truevals[slot] != predobj.predval){ - //user got wrong prediction, there is no payout for him - predobjs[slot][predictoor_addr].paid = true; - emit PredictionPayout( + emit PredictionPayout( predictoor_addr, slot, predobj.stake, - 0, + payout_amt, predobj.predval, truevals[slot], agg_predvals_numer[slot] / agg_predvals_denom[slot], truval_submitted[slot] ); - return; - } - uint256 swe = truevals[slot] - ? agg_predvals_numer[slot] - : agg_predvals_denom[slot] - agg_predvals_numer[slot]; - uint256 payout_amt = 0; - if(swe > 0) { - uint256 revenue=get_subscription_revenue_at_block(slot); - payout_amt = predobj.stake * (agg_predvals_denom[slot] + revenue) / swe; - } - predobjs[slot][predictoor_addr].paid = true; - emit PredictionPayout( - predictoor_addr, - slot, - predobj.stake, - payout_amt, - predobj.predval, - truevals[slot], - agg_predvals_numer[slot] / agg_predvals_denom[slot], - truval_submitted[slot] - ); - IERC20(stake_token).safeTransfer( - predobj.predictoor, - payout_amt - ); - } + if(payout_amt>0) + IERC20(stake_token).safeTransfer(predobj.predictoor, payout_amt); } // ----------------------- ADMIN FUNCTIONS ----------------------- diff --git a/test/unit/datatokens/ERC20Template3.test.js b/test/unit/datatokens/ERC20Template3.test.js index 09ce48996..61ed5abcc 100644 --- a/test/unit/datatokens/ERC20Template3.test.js +++ b/test/unit/datatokens/ERC20Template3.test.js @@ -334,79 +334,6 @@ describe("ERC20Template3", () => { assert(address, "Not able to get the parent ERC721 address") }); - - - it("#addPaymentManager - should fail to addPaymentManager if not erc20Deployer (permission to deploy the erc20Contract at 721 level)", async () => { - assert((await erc20Token.permissions(user2.address)).paymentManager == false); - - await expectRevert( - erc20Token.connect(user2).addPaymentManager(user2.address), - "ERC20Template: NOT DEPLOYER ROLE" - ); - - assert((await erc20Token.permissions(user2.address)).paymentManager == false); - }); - - it("#addPaymentManager - should fail to addPaymentManager if it's already feeManager", async () => { - assert((await erc20Token.permissions(user2.address)).paymentManager == false); - - await erc20Token.connect(user3).addPaymentManager(user2.address); - - assert((await erc20Token.permissions(user2.address)).paymentManager == true); - - await expectRevert( - erc20Token.connect(user3).addPaymentManager(user2.address), - "ERC20Roles: ALREADY A FEE MANAGER" - ); - }); - - it("#addPaymentManager - should succeed to addPaymentManager if erc20Deployer (permission to deploy the erc20Contract at 721 level)", async () => { - assert((await erc20Token.permissions(user2.address)).paymentManager == false); - - // owner is already erc20Deployer - await erc20Token.connect(user3).addPaymentManager(user2.address); - - assert((await erc20Token.permissions(user2.address)).paymentManager == true); - }); - - it("#removeFeeManager - should fail to removeFeeManager if NOT erc20Deployer", async () => { - await erc20Token.connect(user3).addPaymentManager(owner.address); - - assert((await erc20Token.permissions(owner.address)).paymentManager == true); - - await expectRevert( - erc20Token.connect(user2).removePaymentManager(owner.address), - "ERC20Template: NOT DEPLOYER ROLE" - ); - - assert((await erc20Token.permissions(owner.address)).paymentManager == true); - }); - - it("#removeFeeManager - should fail to removeFeeManager even if it's feeManager", async () => { - // ERC20 deployer role add himself as manager and user2 - await erc20Token.connect(user3).addPaymentManager(owner.address); - await erc20Token.connect(user3).addPaymentManager(user2.address); - - assert((await erc20Token.permissions(user2.address)).paymentManager == true); - - await expectRevert( - erc20Token.connect(user2).removePaymentManager(owner.address), - "ERC20Template: NOT DEPLOYER ROLE" - ); - - assert((await erc20Token.permissions(owner.address)).paymentManager == true); - }); - - it("#removeFeeManager - should succeed to removeFeeManager if erc20Deployer", async () => { - await erc20Token.connect(user3).addPaymentManager(user2.address); - - assert((await erc20Token.permissions(user2.address)).paymentManager == true); - - await erc20Token.connect(user3).removePaymentManager(user2.address); - - assert((await erc20Token.permissions(user2.address)).paymentManager == false); - }); - it("#setData - should fail to setData if NOT erc20Deployer", async () => { const key = web3.utils.keccak256(erc20Token.address); const value = web3.utils.asciiToHex("SomeData"); @@ -436,10 +363,6 @@ describe("ERC20Template3", () => { }); it("#cleanPermissions - should succeed to call cleanPermissions if NFTOwner", async () => { - // user3 is already minter - - await erc20Token.connect(user3).addPaymentManager(owner.address); - // NFT Owner cleans await erc20Token.cleanPermissions(); @@ -1687,10 +1610,6 @@ describe("ERC20Template3", () => { const soonestBlockToPredict = await erc20Token.soonest_block_to_predict((await ethers.provider.getBlockNumber())+1); const blockNum = await ethers.provider.getBlockNumber(); const slot = await erc20Token.rail_blocknum_to_slot(soonestBlockToPredict); - console.log("Slot:"+slot) - console.log("soonestBlockToPredict:"+soonestBlockToPredict) - console.log("blockNum:"+blockNum) - await erc20Token.connect(user2).submit_predval(prediction, stake, soonestBlockToPredict); const blocksPerEpoch = await erc20Token.blocks_per_epoch(); From 1cb4ecb01bc69e386ed4cf0e0e8fa7e84f603a21 Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Sat, 13 May 2023 23:07:44 -0700 Subject: [PATCH 102/120] add RevenueAdded --- contracts/templates/ERC20Template3.sol | 11 ++++++++++- test/unit/datatokens/ERC20Template3.test.js | 8 ++++++-- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/contracts/templates/ERC20Template3.sol b/contracts/templates/ERC20Template3.sol index 883cb8c47..ec8227995 100644 --- a/contracts/templates/ERC20Template3.sol +++ b/contracts/templates/ERC20Template3.sol @@ -94,7 +94,15 @@ contract ERC20Template3 is uint256 blocks_per_subscription, uint256 truval_submit_timeout_block, address stakeToken - ); + ); + + event RevenueAdded( + uint256 totalAmount, + uint256 slot, + uint256 amountPerEpoch, + uint256 numEpochs, + uint256 blocksPerEpoch + ); // All mappings below are using slot as key. // Whenever we have functions that take block as argumens, we rail it to slot automaticly @@ -1135,6 +1143,7 @@ contract ERC20Template3 is slot + blocks_per_epoch * (i) ] += amt_per_epoch; } + emit RevenueAdded(amount,slot,amt_per_epoch,num_epochs,blocks_per_epoch); } } } diff --git a/test/unit/datatokens/ERC20Template3.test.js b/test/unit/datatokens/ERC20Template3.test.js index 61ed5abcc..bd59ae32a 100644 --- a/test/unit/datatokens/ERC20Template3.test.js +++ b/test/unit/datatokens/ERC20Template3.test.js @@ -432,8 +432,12 @@ describe("ERC20Template3", () => { //make sure that we don't have 'PublishMarketFee') event event = getEventFromTx(txReceipt, 'PublishMarketFee') assert.typeOf(event, 'undefined', "PublishMarketFee event found") - //make sure that we have ProviderFee event - event = getEventFromTx(txReceipt, 'ProviderFee') + //make sure that we have NewSubscription event + event = getEventFromTx(txReceipt, 'NewSubscription') + assert(event, "Cannot find NewSubscription event") + //make sure that we have NewSubscription event + event = getEventFromTx(txReceipt, 'RevenueAdded') + assert(event, "Cannot find RevenueAdded event") assert( (await erc20Token.balanceOf(user2.address)) == web3.utils.toWei("9"), 'Invalid user balance, DT was not substracted' From e71260580565411d1eca7e22e148332a4280d3bc Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Sat, 13 May 2023 23:30:32 -0700 Subject: [PATCH 103/120] use mixedCase --- contracts/templates/ERC20Template3.sol | 187 ++++++++-------- test/unit/datatokens/ERC20Template3.test.js | 226 ++++++++++---------- 2 files changed, 207 insertions(+), 206 deletions(-) diff --git a/contracts/templates/ERC20Template3.sol b/contracts/templates/ERC20Template3.sol index ec8227995..57f5b0d9b 100644 --- a/contracts/templates/ERC20Template3.sol +++ b/contracts/templates/ERC20Template3.sol @@ -90,9 +90,9 @@ contract ERC20Template3 is } event SettingChanged( - uint256 blocks_per_epoch, - uint256 blocks_per_subscription, - uint256 truval_submit_timeout_block, + uint256 blocksPerEpoch, + uint256 blocksPerSubscription, + uint256 truevalSubmitTimeoutBlock, address stakeToken ); @@ -106,17 +106,17 @@ contract ERC20Template3 is // All mappings below are using slot as key. // Whenever we have functions that take block as argumens, we rail it to slot automaticly - mapping(uint256 => mapping(address => Prediction)) private predobjs; // id to prediction object - mapping(uint256 => uint256) private agg_predvals_numer; - mapping(uint256 => uint256) private agg_predvals_denom; + mapping(uint256 => mapping(address => Prediction)) private predictions; // id to prediction object + mapping(uint256 => uint256) private aggPredvalsNumer; + mapping(uint256 => uint256) private aggPredValsDenom; mapping(uint256 => bool) public truevals; - mapping(uint256 => Status) public truval_submitted; - mapping(uint256 => uint256) private subscription_revenue_at_block; //income registred + mapping(uint256 => Status) public epochStatus; + mapping(uint256 => uint256) private subscriptionRevenueAtBlock; //income registred mapping(address => Subscription) public subscriptions; // valid subscription per user - uint256 public blocks_per_epoch; - address public stake_token; - uint256 public blocks_per_subscription; - uint256 public truval_submit_timeout_block; + uint256 public blocksPerEpoch; + address public stakeToken; + uint256 public blocksPerSubscription; + uint256 public trueValSubmitTimeoutBlock; bool public paused = false; // -------------------------- PREDICTOOR -------------------------- @@ -356,8 +356,8 @@ contract ERC20Template3 is ); */ - stake_token = addresses_[4]; - _update_seconds(uints_[2], uints_[3], uints_[4], uints_[5]); + stakeToken = addresses_[4]; + _updateSeconds(uints_[2], uints_[3], uints_[4], uints_[5]); return initialized; } @@ -376,8 +376,8 @@ contract ERC20Template3 is ) external onlyERC20Deployer nonReentrant returns (bytes32 exchangeId) { require(fixedRateExchanges.length == 0, "Fixed rate already present"); require( - stake_token == addresses[0], - "Cannot create FRE with baseToken!=stake_token" + stakeToken == addresses[0], + "Cannot create FRE with baseToken!=stakeToken" ); //force FRE allowedSwapper to this contract address. no one else can swap because we need to record the income if (uints[4] > 0) _addMinter(fixedPriceAddress); @@ -496,10 +496,10 @@ contract ERC20Template3 is } Subscription memory sub = Subscription( consumer, - block.number + blocks_per_subscription + block.number + blocksPerSubscription ); subscriptions[consumer] = sub; - emit NewSubscription(consumer, block.number + blocks_per_subscription,block.number); + emit NewSubscription(consumer, block.number + blocksPerSubscription,block.number); //record income add_revenue(block.number, rate); @@ -903,65 +903,65 @@ contract ERC20Template3 is } // ------------ PREDICTOOR ------------ - function is_valid_subscription(address user) public view returns (bool) { + function isValidSubscription(address user) public view returns (bool) { return subscriptions[user].expires <= block.number ? false : true; } function epoch(uint256 blocknum) public view returns (uint256) { - return blocknum / blocks_per_epoch; + return blocknum / blocksPerEpoch; } - function cur_epoch() public view returns (uint256) { + function curEpoch() public view returns (uint256) { return epoch(block.number); } - function rail_blocknum_to_slot( + function railBlocknumToSlot( uint256 blocknum ) public view returns (uint256) { - uint256 rounded = blocknum / blocks_per_epoch; - return (rounded * blocks_per_epoch); + uint256 rounded = blocknum / blocksPerEpoch; + return (rounded * blocksPerEpoch); } - function blocknum_is_on_a_slot( + function blocknumIsOnSlot( uint256 blocknum ) public view returns (bool) { // a slot == beginning/end of an epoch - return blocknum == rail_blocknum_to_slot(blocknum); + return blocknum == railBlocknumToSlot(blocknum); } - function soonest_block_to_predict(uint256 prediction_block) public view returns (uint256) { + function soonestBlockToPredict(uint256 prediction_block) public view returns (uint256) { /* Epoch i: predictoors submit predval for the beginning of epoch i+2. Predval is: "does trueval go UP or DOWN between the start of epoch i+1 and the start of epoch i+2?" Once epoch i ends, predictoors cannot submit predvals for epoch i+2 */ - return(rail_blocknum_to_slot(prediction_block)+ blocks_per_epoch * 2); + return(railBlocknumToSlot(prediction_block)+ blocksPerEpoch * 2); } - function submitted_predval( + function submittedPredval( uint256 blocknum, address predictoor ) public view returns (bool) { - uint256 slot = rail_blocknum_to_slot(blocknum); - return predobjs[slot][predictoor].predictoor != address(0); + uint256 slot = railBlocknumToSlot(blocknum); + return predictions[slot][predictoor].predictoor != address(0); } - function get_agg_predval( + function getAggPredval( uint256 blocknum ) public view returns (uint256, uint256) { - require(is_valid_subscription(msg.sender), "No subscription"); - uint256 slot = rail_blocknum_to_slot(blocknum); - return (agg_predvals_numer[slot], agg_predvals_denom[slot]); + require(isValidSubscription(msg.sender), "No subscription"); + uint256 slot = railBlocknumToSlot(blocknum); + return (aggPredvalsNumer[slot], aggPredValsDenom[slot]); } - function get_subscription_revenue_at_block( + function getSubscriptionRevenueAtBlock( uint256 blocknum ) public view returns (uint256) { - uint256 slot = rail_blocknum_to_slot(blocknum); - return (subscription_revenue_at_block[slot]); + uint256 slot = railBlocknumToSlot(blocknum); + return (subscriptionRevenueAtBlock[slot]); } - function get_prediction( + function getPrediction( uint256 blocknum, address predictoor ) @@ -971,23 +971,23 @@ contract ERC20Template3 is { //allow predictoors to see their own submissions require(msg.sender == predictoor || blocknum < block.number, "you shall not pass"); - uint256 slot = rail_blocknum_to_slot(blocknum); - prediction = predobjs[slot][predictoor]; + uint256 slot = railBlocknumToSlot(blocknum); + prediction = predictions[slot][predictoor]; } // ----------------------- MUTATING FUNCTIONS ----------------------- - function submit_predval( + function submitPredval( bool predval, uint256 stake, uint256 blocknum ) external { require(paused == false, "paused"); - uint256 slot = rail_blocknum_to_slot(blocknum); - require(slot >= soonest_block_to_predict(block.number), "too late to submit"); - require(!submitted_predval(slot, msg.sender), "already submitted"); + uint256 slot = railBlocknumToSlot(blocknum); + require(slot >= soonestBlockToPredict(block.number), "too late to submit"); + require(!submittedPredval(slot, msg.sender), "already submitted"); - predobjs[slot][msg.sender] = Prediction( + predictions[slot][msg.sender] = Prediction( predval, stake, msg.sender, @@ -995,15 +995,15 @@ contract ERC20Template3 is ); // update agg_predvals - agg_predvals_numer[slot] += stake * (predval ? 1 : 0); - agg_predvals_denom[slot] += stake; + aggPredvalsNumer[slot] += stake * (predval ? 1 : 0); + aggPredValsDenom[slot] += stake; emit PredictionSubmitted(msg.sender, slot, stake); // safe transfer stake - IERC20(stake_token).safeTransferFrom(msg.sender, address(this), stake); + IERC20(stakeToken).safeTransferFrom(msg.sender, address(this), stake); } - function payout_mul( + function payoutMultiple( uint256[] calldata blocknums, address predictoor_addr ) external { @@ -1016,34 +1016,34 @@ contract ERC20Template3 is uint256 blocknum, address predictoor_addr ) public nonReentrant { - require(submitted_predval(blocknum, predictoor_addr), "not submitted"); - uint256 slot = rail_blocknum_to_slot(blocknum); - Prediction memory predobj = predobjs[slot][predictoor_addr]; - if(predobj.paid) return; // just return if already paid, in order not to break payout_mul + require(submittedPredval(blocknum, predictoor_addr), "not submitted"); + uint256 slot = railBlocknumToSlot(blocknum); + Prediction memory predobj = predictions[slot][predictoor_addr]; + if(predobj.paid) return; // just return if already paid, in order not to break payoutMultiple // if OPF hasn't submitted trueval in truval_submit_timeout blocks then cancel round - if (block.number > slot + truval_submit_timeout_block && truval_submitted[slot]==Status.Pending){ - truval_submitted[slot]=Status.Canceled; + if (block.number > slot + trueValSubmitTimeoutBlock && epochStatus[slot]==Status.Pending){ + epochStatus[slot]=Status.Canceled; } - if(truval_submitted[slot]==Status.Pending){ + if(epochStatus[slot]==Status.Pending){ // if Status is Pending, do nothing, just return return; } uint256 payout_amt = 0; - predobjs[slot][predictoor_addr].paid = true; - if(truval_submitted[slot]==Status.Canceled){ + predictions[slot][predictoor_addr].paid = true; + if(epochStatus[slot]==Status.Canceled){ payout_amt = predobj.stake; } else{ // Status.Paying if(truevals[slot] == predobj.predval){ // he got it. uint256 swe = truevals[slot] - ? agg_predvals_numer[slot] - : agg_predvals_denom[slot] - agg_predvals_numer[slot]; + ? aggPredvalsNumer[slot] + : aggPredValsDenom[slot] - aggPredvalsNumer[slot]; if(swe > 0) { - uint256 revenue=get_subscription_revenue_at_block(slot); - payout_amt = predobj.stake * (agg_predvals_denom[slot] + revenue) / swe; + uint256 revenue=getSubscriptionRevenueAtBlock(slot); + payout_amt = predobj.stake * (aggPredValsDenom[slot] + revenue) / swe; } } // else payout_amt is already 0 @@ -1055,53 +1055,54 @@ contract ERC20Template3 is payout_amt, predobj.predval, truevals[slot], - agg_predvals_numer[slot] / agg_predvals_denom[slot], - truval_submitted[slot] + aggPredvalsNumer[slot] / aggPredValsDenom[slot], + epochStatus[slot] ); if(payout_amt>0) - IERC20(stake_token).safeTransfer(predobj.predictoor, payout_amt); + IERC20(stakeToken).safeTransfer(predobj.predictoor, payout_amt); } // ----------------------- ADMIN FUNCTIONS ----------------------- - function redeem_unused_sub_revenue(uint256 blocknum) external onlyERC20Deployer { + function redeemUnusedSlotRevenue(uint256 blocknum) external onlyERC20Deployer { require(block.number > blocknum); - uint256 slot = rail_blocknum_to_slot(blocknum); - require(agg_predvals_denom[slot] == 0); - IERC20(stake_token).safeTransfer( + uint256 slot = railBlocknumToSlot(blocknum); + require(aggPredValsDenom[slot] == 0); + IERC20(stakeToken).safeTransfer( msg.sender, - subscription_revenue_at_block[slot] + subscriptionRevenueAtBlock[slot] ); } - function pause_predictions() external onlyERC20Deployer { + function pausePredictions() external onlyERC20Deployer { paused = !paused; + // TODO - pause FRE as well } - function submit_trueval( + function submitTrueVal( uint256 blocknum, bool trueval ) external onlyERC20Deployer { // TODO, is onlyERC20Deployer the right modifier? require(blocknum < block.number, "too early to submit"); - uint256 slot = rail_blocknum_to_slot(blocknum); - require(truval_submitted[slot]==Status.Pending, "already settled"); - if (block.number > slot + truval_submit_timeout_block && truval_submitted[slot]==Status.Pending){ - truval_submitted[slot]=Status.Canceled; + uint256 slot = railBlocknumToSlot(blocknum); + require(epochStatus[slot]==Status.Pending, "already settled"); + if (block.number > slot + trueValSubmitTimeoutBlock && epochStatus[slot]==Status.Pending){ + epochStatus[slot]=Status.Canceled; } else{ truevals[slot] = trueval; - truval_submitted[slot] = Status.Paying; + epochStatus[slot] = Status.Paying; } - emit TruevalSubmitted(slot, trueval,truval_submitted[slot]); + emit TruevalSubmitted(slot, trueval,epochStatus[slot]); } - function update_seconds( + function updateSeconds( uint256 s_per_block, uint256 s_per_subscription, uint256 _truval_submit_timeout ) external onlyERC20Deployer { - _update_seconds( + _updateSeconds( s_per_block, 0, // can only be set once s_per_subscription, @@ -1111,7 +1112,7 @@ contract ERC20Template3 is // ----------------------- INTERNAL FUNCTIONS ----------------------- - function _update_seconds( + function _updateSeconds( uint256 s_per_block, uint256 s_per_epoch, uint256 s_per_subscription, @@ -1120,30 +1121,30 @@ contract ERC20Template3 is require(s_per_subscription % s_per_block == 0, "%"); require(s_per_epoch % s_per_block == 0, "%"); - if (blocks_per_epoch == 0) { - blocks_per_epoch = s_per_epoch / s_per_block; // immutaable + if (blocksPerEpoch == 0) { + blocksPerEpoch = s_per_epoch / s_per_block; // immutaable } - blocks_per_subscription = s_per_subscription / s_per_block; - truval_submit_timeout_block = _truval_submit_timeout / s_per_block; - emit SettingChanged(blocks_per_epoch,blocks_per_subscription,truval_submit_timeout_block,stake_token); + blocksPerSubscription = s_per_subscription / s_per_block; + trueValSubmitTimeoutBlock = _truval_submit_timeout / s_per_block; + emit SettingChanged(blocksPerEpoch,blocksPerSubscription,trueValSubmitTimeoutBlock,stakeToken); } function add_revenue(uint256 blocknum, uint256 amount) internal { if (amount > 0) { - uint256 slot = rail_blocknum_to_slot(blocknum); - uint256 num_epochs = blocks_per_subscription / blocks_per_epoch; + uint256 slot = railBlocknumToSlot(blocknum); + uint256 num_epochs = blocksPerSubscription / blocksPerEpoch; if(num_epochs<1) num_epochs=1; uint256 amt_per_epoch = amount / num_epochs; - // for loop and add revenue for blocks_per_epoch blocks + // for loop and add revenue for blocksPerEpoch blocks for (uint256 i = 0; i < num_epochs; i++) { // TODO FIND A WAY TO ACHIEVE THIS WITHOUT A LOOP - subscription_revenue_at_block[ - slot + blocks_per_epoch * (i) + subscriptionRevenueAtBlock[ + slot + blocksPerEpoch * (i) ] += amt_per_epoch; } - emit RevenueAdded(amount,slot,amt_per_epoch,num_epochs,blocks_per_epoch); + emit RevenueAdded(amount,slot,amt_per_epoch,num_epochs,blocksPerEpoch); } } } diff --git a/test/unit/datatokens/ERC20Template3.test.js b/test/unit/datatokens/ERC20Template3.test.js index bd59ae32a..dd302d0a7 100644 --- a/test/unit/datatokens/ERC20Template3.test.js +++ b/test/unit/datatokens/ERC20Template3.test.js @@ -256,7 +256,7 @@ describe("ERC20Template3", () => { fixedRateExchange.address, [mockErc20Decimals.address, owner.address, freMarketFeeCollector, addressZero], [18, 18, freRate , freMarketFee , 1]), - "Cannot create FRE with baseToken!=stake_token" + "Cannot create FRE with baseToken!=stakeToken" ); await erc20Token.connect(owner).createFixedRate( fixedRateExchange.address, @@ -845,73 +845,73 @@ describe("ERC20Template3", () => { }); // PREDICTOOR - it("#blocks_per_epoch - blocks_per_epoch should be set", async () => { - const blocksPerEpoch = await erc20Token.blocks_per_epoch(); - assert(blocksPerEpoch > 0, 'Invalid blocks_per_epoch'); + it("#blocksPerEpoch - blocksPerEpoch should be set", async () => { + const blocksPerEpoch = await erc20Token.blocksPerEpoch(); + assert(blocksPerEpoch > 0, 'Invalid blocksPerEpoch'); }); - it("#stake_tokens - stake token should be set", async () => { - const stakeToken = await erc20Token.stake_token(); - assert(stakeToken == mockErc20.address, 'Invalid stake_token'); + it("#stakeTokens - stake token should be set", async () => { + const stakeToken = await erc20Token.stakeToken(); + assert(stakeToken == mockErc20.address, 'Invalid stakeToken'); }); - it("#blocks_per_subscription - blocks_per_subscription should be set", async () => { - const blocksPerSubscription = await erc20Token.blocks_per_subscription(); - assert(blocksPerSubscription > 0, 'Invalid blocks_per_subscription'); + it("#blocksPerSubscription - blocksPerSubscription should be set", async () => { + const blocksPerSubscription = await erc20Token.blocksPerSubscription(); + assert(blocksPerSubscription > 0, 'Invalid blocksPerSubscription'); }); - it("#epoch, cur_epoch - should return currenct epoch", async () => { + it("#epoch, curEpoch - should return currenct epoch", async () => { const blockNum = await ethers.provider.getBlockNumber(); - const blocksPerEpoch = (await erc20Token.blocks_per_epoch()) + const blocksPerEpoch = (await erc20Token.blocksPerEpoch()) const epoch = parseInt(blockNum / blocksPerEpoch); assert((await erc20Token.epoch(blockNum))) == epoch; - assert((await erc20Token.cur_epoch())) == epoch; + assert((await erc20Token.curEpoch())) == epoch; }); - it("#rail_blocknum_to_slot, blocknum_is_on_a_slot - should rail blocknum to slot", async () => { + it("#railBlocknumToSlot, blocknumIsOnSlot - should rail blocknum to slot", async () => { const blockNum = await ethers.provider.getBlockNumber(); - const blocksPerEpoch = (await erc20Token.blocks_per_epoch()) + const blocksPerEpoch = (await erc20Token.blocksPerEpoch()) const slot = parseInt(blockNum / blocksPerEpoch) * blocksPerEpoch; - assert((await erc20Token.rail_blocknum_to_slot(blockNum)) == slot); - const isOnSlot = await erc20Token.blocknum_is_on_a_slot(slot) + assert((await erc20Token.railBlocknumToSlot(blockNum)) == slot); + const isOnSlot = await erc20Token.blocknumIsOnSlot(slot) assert(isOnSlot == true, isOnSlot +" should be true"); }); - it("#soonest_block_to_predict - should return soonest block to predict", async () => { - const soonestBlockToPredict = await erc20Token.soonest_block_to_predict((await ethers.provider.getBlockNumber())); + it("#soonestBlockToPredict - should return soonest block to predict", async () => { + const soonestBlockToPredict = await erc20Token.soonestBlockToPredict((await ethers.provider.getBlockNumber())); // this should be equal to // 1 + (currentBlock - 1) / 100 - const blocksPerEpoch = (await erc20Token.blocks_per_epoch()) + const blocksPerEpoch = (await erc20Token.blocksPerEpoch()) const blockNumber = await ethers.provider.getBlockNumber(); const railed = parseInt(blockNumber / blocksPerEpoch) * blocksPerEpoch const expected = railed + blocksPerEpoch * 2; assert(soonestBlockToPredict == expected, 'Invalid soonest block to predict'); }); - it("#get_agg_predval - without subscription, should revert", async () => { + it("#getAggPredval - without subscription, should revert", async () => { const blockNumber = await ethers.provider.getBlockNumber() - const blocksPerEpoch = (await erc20Token.blocks_per_epoch()) + const blocksPerEpoch = (await erc20Token.blocksPerEpoch()) const railed = parseInt(blockNumber / blocksPerEpoch) * blocksPerEpoch await expectRevert( - erc20Token.get_agg_predval(railed), + erc20Token.getAggPredval(railed), "No subscription" ); }); - it("#get_agg_predval - without subscription, should revert", async () => { + it("#getAggPredval - without subscription, should revert", async () => { const blockNumber = await ethers.provider.getBlockNumber() - const blocksPerEpoch = (await erc20Token.blocks_per_epoch()) + const blocksPerEpoch = (await erc20Token.blocksPerEpoch()) const railed = parseInt(blockNumber / blocksPerEpoch) * blocksPerEpoch await expectRevert( - erc20Token.get_agg_predval(railed), + erc20Token.getAggPredval(railed), "No subscription" ); }); - it("#is_valid_subscription - without subscription, should return false", async () => { - const isValidSubscription = await erc20Token.is_valid_subscription(erc20Token.address); + it("#isValidSubscription - without subscription, should return false", async () => { + const isValidSubscription = await erc20Token.isValidSubscription(erc20Token.address); assert(isValidSubscription == false, "Subscription must be invalid"); }); - it("#submit_predval - predictoor submits predval", async () => { + it("#submitPredval - predictoor submits predval", async () => { const predval = true; const stake = 100; await mockErc20.approve(erc20Token.address, stake); - const soonestBlockToPredict = await erc20Token.soonest_block_to_predict((await ethers.provider.getBlockNumber())+1); - const predictionSlot = await erc20Token.rail_blocknum_to_slot(soonestBlockToPredict); + const soonestBlockToPredict = await erc20Token.soonestBlockToPredict((await ethers.provider.getBlockNumber())+1); + const predictionSlot = await erc20Token.railBlocknumToSlot(soonestBlockToPredict); - const tx = await erc20Token.submit_predval(predval, stake, soonestBlockToPredict); + const tx = await erc20Token.submitPredval(predval, stake, soonestBlockToPredict); const txReceipt = await tx.wait(); const event = getEventFromTx(txReceipt, 'PredictionSubmitted') assert(event, "Cannot find PredictionSubmitted event") @@ -920,61 +920,61 @@ describe("ERC20Template3", () => { expect(event.args[1]).to.equal(predictionSlot); expect(event.args[2]).to.equal(stake); }); - it("#submit_predval - predictoor can read their submitted predval", async () => { + it("#submitPredval - predictoor can read their submitted predval", async () => { const predval = true; const stake = 100; tx = await mockErc20.approve(erc20Token.address, stake); await tx.wait() - const soonestBlockToPredict = await erc20Token.soonest_block_to_predict((await ethers.provider.getBlockNumber())+1); - await erc20Token.submit_predval(predval, stake, soonestBlockToPredict); - const prediction = await erc20Token.get_prediction(soonestBlockToPredict, owner.address); + const soonestBlockToPredict = await erc20Token.soonestBlockToPredict((await ethers.provider.getBlockNumber())+1); + await erc20Token.submitPredval(predval, stake, soonestBlockToPredict); + const prediction = await erc20Token.getPrediction(soonestBlockToPredict, owner.address); expect(prediction.predval).to.be.eq(predval); expect(prediction.stake).to.be.eq(stake); expect(prediction.predictoor).to.be.eq(owner.address); expect(prediction.paid).to.be.eq(false); }); - it("#submit_predval - others cannot read submitted predictions", async () => { + it("#submitPredval - others cannot read submitted predictions", async () => { const predval = true; const stake = 100; tx = await mockErc20.approve(erc20Token.address, stake); await tx.wait() - const soonestBlockToPredict = await erc20Token.soonest_block_to_predict((await ethers.provider.getBlockNumber())+1); - await erc20Token.submit_predval(predval, stake, soonestBlockToPredict); - await expectRevert(erc20Token.connect(user2).get_prediction(soonestBlockToPredict, owner.address), "you shall not pass"); + const soonestBlockToPredict = await erc20Token.soonestBlockToPredict((await ethers.provider.getBlockNumber())+1); + await erc20Token.submitPredval(predval, stake, soonestBlockToPredict); + await expectRevert(erc20Token.connect(user2).getPrediction(soonestBlockToPredict, owner.address), "you shall not pass"); // fast forward blocks until next epoch Array(30).fill(0).map(async _ => await ethers.provider.send("evm_mine")); // user2 should be able to read the predval now - const prediction = await erc20Token.connect(user2).get_prediction(soonestBlockToPredict, owner.address); + const prediction = await erc20Token.connect(user2).getPrediction(soonestBlockToPredict, owner.address); expect(prediction.predval).to.be.eq(predval); }); - it("#submit_predval - should revert when predictoor submits too early", async () => { + it("#submitPredval - should revert when predictoor submits too early", async () => { const predval = true; const stake = 100; const block = await ethers.provider.getBlockNumber(); - const railed = await erc20Token.rail_blocknum_to_slot(block - 100); + const railed = await erc20Token.railBlocknumToSlot(block - 100); await mockErc20.approve(erc20Token.address, stake); await expectRevert( - erc20Token.submit_predval(predval, stake, railed), + erc20Token.submitPredval(predval, stake, railed), "too late to submit" ); }); - it("#submit_predval - should revert when predictoor submits duplicate prediction", async () => { + it("#submitPredval - should revert when predictoor submits duplicate prediction", async () => { const predval = true; const stake = 100; await mockErc20.approve(erc20Token.address, stake * 2); - const soonestBlockToPredict = await erc20Token.soonest_block_to_predict((await ethers.provider.getBlockNumber())+1); + const soonestBlockToPredict = await erc20Token.soonestBlockToPredict((await ethers.provider.getBlockNumber())+1); - await erc20Token.submit_predval(predval, stake, soonestBlockToPredict); + await erc20Token.submitPredval(predval, stake, soonestBlockToPredict); await expectRevert( - erc20Token.submit_predval(predval, stake, soonestBlockToPredict), + erc20Token.submitPredval(predval, stake, soonestBlockToPredict), "already submitted" ); }); - it("#pause_predictions - should pause and resume predictions", async () => { - await erc20Token.pause_predictions(); + it("#pausePredictions - should pause and resume predictions", async () => { + await erc20Token.pausePredictions(); const isPaused = await erc20Token.paused(); assert(isPaused == true, "Predictions should be paused"); @@ -982,38 +982,38 @@ describe("ERC20Template3", () => { const predval = true; const stake = 100; await mockErc20.approve(erc20Token.address, stake); - const soonestBlockToPredict = await erc20Token.soonest_block_to_predict((await ethers.provider.getBlockNumber())+1); + const soonestBlockToPredict = await erc20Token.soonestBlockToPredict((await ethers.provider.getBlockNumber())+1); await expectRevert( - erc20Token.submit_predval(predval, stake, soonestBlockToPredict), + erc20Token.submitPredval(predval, stake, soonestBlockToPredict), "paused" ); - await erc20Token.pause_predictions(); + await erc20Token.pausePredictions(); const isResumed = await erc20Token.paused(); assert(isResumed == false, "Predictions should be resumed"); }); - it("#update_seconds - should revert when seconds per subscription is not divisible by seconds per block", async () => { + it("#updateSeconds - should revert when seconds per subscription is not divisible by seconds per block", async () => { const s_per_block = 3; const s_per_subscription = 10; const _truval_submit_timeout = 30; await expectRevert( - erc20Token.update_seconds(s_per_block, s_per_subscription, _truval_submit_timeout), + erc20Token.updateSeconds(s_per_block, s_per_subscription, _truval_submit_timeout), "%" ); }); - it("#submit_trueval - should revert submitting for a future block", async () => { - const soonestBlockToPredict = await erc20Token.soonest_block_to_predict((await ethers.provider.getBlockNumber())+1); - await expectRevert(erc20Token.submit_trueval(soonestBlockToPredict, true), "too early to submit"); + it("#submitTrueVal - should revert submitting for a future block", async () => { + const soonestBlockToPredict = await erc20Token.soonestBlockToPredict((await ethers.provider.getBlockNumber())+1); + await expectRevert(erc20Token.submitTrueVal(soonestBlockToPredict, true), "too early to submit"); }); - it("#submit_trueval - should submit for a block in the past", async () => { - const blocksPerEpoch = await erc20Token.blocks_per_epoch(); - const soonestBlockToPredict = await erc20Token.soonest_block_to_predict((await ethers.provider.getBlockNumber())); + it("#submitTrueVal - should submit for a block in the past", async () => { + const blocksPerEpoch = await erc20Token.blocksPerEpoch(); + const soonestBlockToPredict = await erc20Token.soonestBlockToPredict((await ethers.provider.getBlockNumber())); const submissionBlock = soonestBlockToPredict - blocksPerEpoch * 2; - const tx = await erc20Token.submit_trueval(submissionBlock, true); + const tx = await erc20Token.submitTrueVal(submissionBlock, true); const tx_receipt = await tx.wait(); const event = getEventFromTx(tx_receipt, "TruevalSubmitted"); expect(event.args[0]).to.equal(submissionBlock); @@ -1080,7 +1080,7 @@ describe("ERC20Template3", () => { expect(subscription.expires).to.be.gt(currentBlock); expect(subscription.user).to.be.eq(user2.address); - const valid = await erc20Token.is_valid_subscription(user2.address); + const valid = await erc20Token.isValidSubscription(user2.address); expect(valid).to.be.true; }); @@ -1114,7 +1114,7 @@ describe("ERC20Template3", () => { const signedMessage = await signMessage(message, providerFeeAddress); // reduce subscription time - await erc20Token.update_seconds(sPerBlock, sPerBlock, truevalSubmitTimeout); + await erc20Token.updateSeconds(sPerBlock, sPerBlock, truevalSubmitTimeout); // set back to normal const tx = await erc20Token .connect(user2) @@ -1139,15 +1139,15 @@ describe("ERC20Template3", () => { ); Array(100).fill(0).map(async () => await ethers.provider.send("evm_mine", [])); - const valid = await erc20Token.is_valid_subscription(user2.address); + const valid = await erc20Token.isValidSubscription(user2.address); expect(valid).to.be.false; // set back to normal - await erc20Token.update_seconds(sPerBlock, sPerSubscription, truevalSubmitTimeout); + await erc20Token.updateSeconds(sPerBlock, sPerSubscription, truevalSubmitTimeout); }); - // can read get_agg_predval with a valid subscription - it("#get_agg_predval - should return agg_predval if caller has a valid subscription", async () => { + // can read getAggPredval with a valid subscription + it("#getAggPredval - should return agg_predval if caller has a valid subscription", async () => { //MINT SOME DT20 to USER2 so he can start order await buyDTFromFixedRate(erc20Token.address,user2.address,10) assert( @@ -1198,8 +1198,8 @@ describe("ERC20Template3", () => { ); - let soonestBlockToPredict = await erc20Token.soonest_block_to_predict((await ethers.provider.getBlockNumber())+1); - const [numer, denom] = await erc20Token.connect(user2).get_agg_predval(soonestBlockToPredict); + let soonestBlockToPredict = await erc20Token.soonestBlockToPredict((await ethers.provider.getBlockNumber())+1); + const [numer, denom] = await erc20Token.connect(user2).getAggPredval(soonestBlockToPredict); expect(numer).to.be.eq(0); expect(denom).to.be.eq(0); @@ -1208,20 +1208,20 @@ describe("ERC20Template3", () => { const stake = web3.utils.toWei("1"); await mockErc20.transfer(user3.address, stake); await mockErc20.connect(user3).approve(erc20Token.address, stake); - soonestBlockToPredict = await erc20Token.soonest_block_to_predict((await ethers.provider.getBlockNumber())+1); - await erc20Token.connect(user3).submit_predval(predval, stake, soonestBlockToPredict); + soonestBlockToPredict = await erc20Token.soonestBlockToPredict((await ethers.provider.getBlockNumber())+1); + await erc20Token.connect(user3).submitPredval(predval, stake, soonestBlockToPredict); - soonestBlockToPredict = await erc20Token.soonest_block_to_predict((await ethers.provider.getBlockNumber())+1); - const [numer2, denom2] = await erc20Token.connect(user2).get_agg_predval(soonestBlockToPredict); + soonestBlockToPredict = await erc20Token.soonestBlockToPredict((await ethers.provider.getBlockNumber())+1); + const [numer2, denom2] = await erc20Token.connect(user2).getAggPredval(soonestBlockToPredict); expect(numer2).to.be.eq(web3.utils.toWei("1")); expect(denom2).to.be.eq(web3.utils.toWei("1")); // check subscription revenue - const revenue = await erc20Token.get_subscription_revenue_at_block(soonestBlockToPredict); + const revenue = await erc20Token.getSubscriptionRevenueAtBlock(soonestBlockToPredict); expect(revenue).to.be.gt(0); }); - // can read get_agg_predval with a valid subscription + // can read getAggPredval with a valid subscription it("#payout - predictoor should get paid", async () => { const consumer = user2.address; // could be different user const serviceIndex = 1; // dummy index @@ -1260,8 +1260,8 @@ describe("ERC20Template3", () => { ] ); const signedMessage = await signMessage(message, providerFeeAddress); - let soonestBlockToPredict = await erc20Token.soonest_block_to_predict((await ethers.provider.getBlockNumber())+4);//we have 3 more txes till our prediction - let revenue_at_block = await erc20Token.connect(user2).get_subscription_revenue_at_block(soonestBlockToPredict) + let soonestBlockToPredict = await erc20Token.soonestBlockToPredict((await ethers.provider.getBlockNumber())+4);//we have 3 more txes till our prediction + let revenue_at_block = await erc20Token.connect(user2).getSubscriptionRevenueAtBlock(soonestBlockToPredict) expect(revenue_at_block).to.be.eq(0); await erc20Token @@ -1286,7 +1286,7 @@ describe("ERC20Template3", () => { } ); - revenue_at_block = await erc20Token.connect(user2).get_subscription_revenue_at_block(soonestBlockToPredict) + revenue_at_block = await erc20Token.connect(user2).getSubscriptionRevenueAtBlock(soonestBlockToPredict) expect(revenue_at_block).to.be.gt(0); // predictoor makes a prediction @@ -1294,7 +1294,7 @@ describe("ERC20Template3", () => { const stake = web3.utils.toWei("1"); await mockErc20.transfer(user3.address, stake); await mockErc20.connect(user3).approve(erc20Token.address, stake); - await erc20Token.connect(user3).submit_predval(predval, stake, soonestBlockToPredict); + await erc20Token.connect(user3).submitPredval(predval, stake, soonestBlockToPredict); let mockErc20Balance = await mockErc20.balanceOf(user3.address) tx = await erc20Token.connect(user3).payout(soonestBlockToPredict, user3.address) txReceipt = await tx.wait(); @@ -1315,7 +1315,7 @@ describe("ERC20Template3", () => { // opf submits truval - tx = await erc20Token.submit_trueval(soonestBlockToPredict, predval); + tx = await erc20Token.submitTrueVal(soonestBlockToPredict, predval); txReceipt = await tx.wait(); event = getEventFromTx(txReceipt, 'TruevalSubmitted') assert(event, "TruevalSubmitted event not found") @@ -1345,7 +1345,7 @@ describe("ERC20Template3", () => { }); - it("#payout_mul - predictoor should get paid", async () => { + it("#payoutMultiple - predictoor should get paid", async () => { const consumer = user2.address; // could be different user const serviceIndex = 1; // dummy index const providerFeeAddress = user5.address; // marketplace fee Collector @@ -1382,8 +1382,8 @@ describe("ERC20Template3", () => { ] ); const signedMessage = await signMessage(message, providerFeeAddress); - let soonestBlockToPredict = await erc20Token.soonest_block_to_predict((await ethers.provider.getBlockNumber())+2);//because we also have startOrder - let revenue_at_block = await erc20Token.connect(user2).get_subscription_revenue_at_block(soonestBlockToPredict) + let soonestBlockToPredict = await erc20Token.soonestBlockToPredict((await ethers.provider.getBlockNumber())+2);//because we also have startOrder + let revenue_at_block = await erc20Token.connect(user2).getSubscriptionRevenueAtBlock(soonestBlockToPredict) expect(revenue_at_block).to.be.eq(0); await erc20Token @@ -1408,7 +1408,7 @@ describe("ERC20Template3", () => { } ); - revenue_at_block = await erc20Token.connect(user2).get_subscription_revenue_at_block(soonestBlockToPredict) + revenue_at_block = await erc20Token.connect(user2).getSubscriptionRevenueAtBlock(soonestBlockToPredict) expect(revenue_at_block).to.be.gt(0); // predictoor makes a prediction @@ -1416,10 +1416,10 @@ describe("ERC20Template3", () => { const stake = web3.utils.toWei("1"); await mockErc20.transfer(user3.address, stake); await mockErc20.connect(user3).approve(erc20Token.address, stake); - await erc20Token.connect(user3).submit_predval(predval, stake, soonestBlockToPredict); + await erc20Token.connect(user3).submitPredval(predval, stake, soonestBlockToPredict); let mockErc20Balance = await mockErc20.balanceOf(user3.address) - tx = await erc20Token.connect(user3).payout_mul([soonestBlockToPredict], user3.address) + tx = await erc20Token.connect(user3).payoutMultiple([soonestBlockToPredict], user3.address) txReceipt = await tx.wait(); event = getEventFromTx(txReceipt, 'PredictionPayout') //we are not getting anything, round is still in progress @@ -1427,7 +1427,7 @@ describe("ERC20Template3", () => { expect(await mockErc20.balanceOf(user3.address)).to.be.eq(mockErc20Balance); Array(30).fill(0).map(async _ => await ethers.provider.send("evm_mine")); mockErc20Balance = await mockErc20.balanceOf(user3.address) - tx = await erc20Token.connect(user3).payout_mul([soonestBlockToPredict], user3.address) + tx = await erc20Token.connect(user3).payoutMultiple([soonestBlockToPredict], user3.address) txReceipt = await tx.wait(); event = getEventFromTx(txReceipt, 'PredictionPayout') //we are not getting anything, round is still in progress @@ -1435,7 +1435,7 @@ describe("ERC20Template3", () => { expect(await mockErc20.balanceOf(user3.address)).to.be.eq(mockErc20Balance); // opf submits truval - tx = await erc20Token.submit_trueval(soonestBlockToPredict, predval); + tx = await erc20Token.submitTrueVal(soonestBlockToPredict, predval); txReceipt = await tx.wait(); event = getEventFromTx(txReceipt, 'TruevalSubmitted') assert(event, "TruevalSubmitted event not found") @@ -1443,7 +1443,7 @@ describe("ERC20Template3", () => { const balBefore = await mockErc20.balanceOf(user3.address); - tx = await erc20Token.connect(user3).payout_mul([soonestBlockToPredict], user3.address); + tx = await erc20Token.connect(user3).payoutMultiple([soonestBlockToPredict], user3.address); txReceipt = await tx.wait(); event = getEventFromTx(txReceipt, 'PredictionPayout') assert(event, "PredictionPayout event not found") @@ -1456,7 +1456,7 @@ describe("ERC20Template3", () => { expect(parseFloat(web3.utils.fromWei(profit.toString()))).to.be.eq(expectedProfit); mockErc20Balance = await mockErc20.balanceOf(user3.address) - tx = await erc20Token.connect(user3).payout_mul([soonestBlockToPredict], user3.address) + tx = await erc20Token.connect(user3).payoutMultiple([soonestBlockToPredict], user3.address) txReceipt = await tx.wait(); event = getEventFromTx(txReceipt, 'PredictionPayout') //we are not getting anything, we got the payment already @@ -1477,11 +1477,11 @@ describe("ERC20Template3", () => { await mockErc20.connect(predictoor).approve(erc20Token.address, amt); } - const blocksPerEpoch = await erc20Token.blocks_per_epoch(); + const blocksPerEpoch = await erc20Token.blocksPerEpoch(); const currentBlock = await ethers.provider.getBlockNumber(); - const soonestBlockToPredict = await erc20Token.soonest_block_to_predict((await ethers.provider.getBlockNumber())+1); + const soonestBlockToPredict = await erc20Token.soonestBlockToPredict((await ethers.provider.getBlockNumber())+1); Array(soonestBlockToPredict - currentBlock + 1).fill(0).map(async _ => await ethers.provider.send("evm_mine")); - const predictionBlock = await erc20Token.soonest_block_to_predict((await ethers.provider.getBlockNumber())+1); + const predictionBlock = await erc20Token.soonestBlockToPredict((await ethers.provider.getBlockNumber())+1); for(const predictoor of predictoors){ const stake = 10 + Math.random() * 100; @@ -1489,7 +1489,7 @@ describe("ERC20Template3", () => { const p = Math.random() > 0.5; predictions.push(p); stakes.push(stake); - await erc20Token.connect(predictoor).submit_predval(p, stakeWei, predictionBlock) + await erc20Token.connect(predictoor).submitPredval(p, stakeWei, predictionBlock) } Array(blocksPerEpoch * 2).fill(0).map(async _ => await ethers.provider.send("evm_mine")); @@ -1499,7 +1499,7 @@ describe("ERC20Template3", () => { const winnersStake = winners.map(x=>stakes[x]).reduce((a,b)=>a+b, 0); // opf submits truval - await erc20Token.submit_trueval(predictionBlock, truval); + await erc20Token.submitTrueVal(predictionBlock, truval); // each predictoor calls payout function for (let i = 0; i < predictoors.length; i++){ @@ -1523,7 +1523,7 @@ describe("ERC20Template3", () => { } }); - it("#redeem_unused_sub_revenue - admin should be able to redeem unused sub revenue for epoch", async()=>{ + it("#redeemUnusedSlotRevenue - admin should be able to redeem unused sub revenue for epoch", async()=>{ const consumer = user2.address; // could be different user const serviceIndex = 1; // dummy index const providerFeeAddress = user5.address; // marketplace fee Collector @@ -1560,8 +1560,8 @@ describe("ERC20Template3", () => { ] ); const signedMessage = await signMessage(message, providerFeeAddress); - let soonestBlockToPredict = await erc20Token.soonest_block_to_predict((await ethers.provider.getBlockNumber())+1); - let revenue_at_block = await erc20Token.connect(user2).get_subscription_revenue_at_block(soonestBlockToPredict) + let soonestBlockToPredict = await erc20Token.soonestBlockToPredict((await ethers.provider.getBlockNumber())+1); + let revenue_at_block = await erc20Token.connect(user2).getSubscriptionRevenueAtBlock(soonestBlockToPredict) expect(revenue_at_block).to.be.eq(0); await erc20Token @@ -1586,37 +1586,37 @@ describe("ERC20Template3", () => { } ); - revenue_at_block = await erc20Token.connect(user2).get_subscription_revenue_at_block(soonestBlockToPredict) + revenue_at_block = await erc20Token.connect(user2).getSubscriptionRevenueAtBlock(soonestBlockToPredict) Array(100).fill(0).map(async _ => await ethers.provider.send("evm_mine")); - const blocksPerEpoch = await erc20Token.blocks_per_epoch(); + const blocksPerEpoch = await erc20Token.blocksPerEpoch(); const currentBlock = await ethers.provider.getBlockNumber(); - const railedBlock = await erc20Token.rail_blocknum_to_slot(currentBlock) - blocksPerEpoch; - const tx_2 = await erc20Token.redeem_unused_sub_revenue(railedBlock); + const railedBlock = await erc20Token.railBlocknumToSlot(currentBlock) - blocksPerEpoch; + const tx_2 = await erc20Token.redeemUnusedSlotRevenue(railedBlock); const txReceipt_2 = await tx_2.wait(); let event_2 = getEventFromTx(txReceipt_2, 'Transfer') expect(event_2.args.from).to.be.eq(erc20Token.address); expect(event_2.args.to).to.be.eq(owner.address); expect(event_2.args.value).to.be.eq(6666666666666666); }) - it("#redeem_unused_sub_revenue - admin should not be able to redeem for future epoch", async()=>{ - const blocksPerEpoch = await erc20Token.blocks_per_epoch(); + it("#redeemUnusedSlotRevenue - admin should not be able to redeem for future epoch", async()=>{ + const blocksPerEpoch = await erc20Token.blocksPerEpoch(); const currentBlock = await ethers.provider.getBlockNumber(); - const railedBlock = await erc20Token.rail_blocknum_to_slot(currentBlock) + blocksPerEpoch; - await expectRevert.unspecified(erc20Token.redeem_unused_sub_revenue(railedBlock)); + const railedBlock = await erc20Token.railBlocknumToSlot(currentBlock) + blocksPerEpoch; + await expectRevert.unspecified(erc20Token.redeemUnusedSlotRevenue(railedBlock)); }) it("predictoor can redeem stake if OPF does not submit", async() => { - await erc20Token.update_seconds(sPerBlock, sPerSubscription, sPerEpoch * 3); + await erc20Token.updateSeconds(sPerBlock, sPerSubscription, sPerEpoch * 3); const stake = 100; await mockErc20.transfer(user2.address, stake); await mockErc20.connect(user2).approve(erc20Token.address, stake); const prediction = true; - const soonestBlockToPredict = await erc20Token.soonest_block_to_predict((await ethers.provider.getBlockNumber())+1); + const soonestBlockToPredict = await erc20Token.soonestBlockToPredict((await ethers.provider.getBlockNumber())+1); const blockNum = await ethers.provider.getBlockNumber(); - const slot = await erc20Token.rail_blocknum_to_slot(soonestBlockToPredict); + const slot = await erc20Token.railBlocknumToSlot(soonestBlockToPredict); - await erc20Token.connect(user2).submit_predval(prediction, stake, soonestBlockToPredict); - const blocksPerEpoch = await erc20Token.blocks_per_epoch(); + await erc20Token.connect(user2).submitPredval(prediction, stake, soonestBlockToPredict); + const blocksPerEpoch = await erc20Token.blocksPerEpoch(); let mockErc20Balance = await mockErc20.balanceOf(user2.address) let tx = await erc20Token.connect(user2).payout(soonestBlockToPredict, user2.address) @@ -1650,6 +1650,6 @@ describe("ERC20Template3", () => { assert(event.args.status==2, "Status should be 2 = Canceled") expect(event.args.payout).to.be.eq(event.args.stake) expect(event.args.payout).to.be.eq(stake) - await erc20Token.update_seconds(sPerBlock, sPerSubscription, truevalSubmitTimeout); + await erc20Token.updateSeconds(sPerBlock, sPerSubscription, truevalSubmitTimeout); }) }); From 33926b6910afc41a95bc1d8c15849ef1e63c3ab4 Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Sat, 13 May 2023 23:34:51 -0700 Subject: [PATCH 104/120] more mixedCase --- contracts/templates/ERC20Template3.sol | 64 +++++++++---------- test/unit/datatokens/ERC20Template3.test.js | 70 ++++++++++----------- 2 files changed, 67 insertions(+), 67 deletions(-) diff --git a/contracts/templates/ERC20Template3.sol b/contracts/templates/ERC20Template3.sol index 57f5b0d9b..b0b7ad26e 100644 --- a/contracts/templates/ERC20Template3.sol +++ b/contracts/templates/ERC20Template3.sol @@ -63,9 +63,9 @@ contract ERC20Template3 is uint256 indexed slot, uint256 stake, uint256 payout, - bool predval, - bool trueval, - uint256 aggPredval, + bool predictedValue, + bool trueValue, + uint256 aggregatedPredictedValue, Status status ); event NewSubscription( @@ -75,11 +75,11 @@ contract ERC20Template3 is ); event TruevalSubmitted( uint256 indexed slot, - bool trueval, + bool trueValue, Status status ); struct Prediction { - bool predval; + bool predictedValue; uint256 stake; address predictoor; bool paid; @@ -92,7 +92,7 @@ contract ERC20Template3 is event SettingChanged( uint256 blocksPerEpoch, uint256 blocksPerSubscription, - uint256 truevalSubmitTimeoutBlock, + uint256 trueValueSubmitTimeoutBlock, address stakeToken ); @@ -107,10 +107,10 @@ contract ERC20Template3 is // All mappings below are using slot as key. // Whenever we have functions that take block as argumens, we rail it to slot automaticly mapping(uint256 => mapping(address => Prediction)) private predictions; // id to prediction object - mapping(uint256 => uint256) private aggPredvalsNumer; - mapping(uint256 => uint256) private aggPredValsDenom; - mapping(uint256 => bool) public truevals; - mapping(uint256 => Status) public epochStatus; + mapping(uint256 => uint256) private aggregatedPredictedValuesNumer; + mapping(uint256 => uint256) private aggregatedPredictedValuesDenom; + mapping(uint256 => bool) public trueValues; // true values submited by owner + mapping(uint256 => Status) public epochStatus; // status of each epoch mapping(uint256 => uint256) private subscriptionRevenueAtBlock; //income registred mapping(address => Subscription) public subscriptions; // valid subscription per user uint256 public blocksPerEpoch; @@ -931,9 +931,9 @@ contract ERC20Template3 is function soonestBlockToPredict(uint256 prediction_block) public view returns (uint256) { /* - Epoch i: predictoors submit predval for the beginning of epoch i+2. - Predval is: "does trueval go UP or DOWN between the start of epoch i+1 and the start of epoch i+2?" - Once epoch i ends, predictoors cannot submit predvals for epoch i+2 + Epoch i: predictoors submit predictedValue for the beginning of epoch i+2. + Predval is: "does trueValue go UP or DOWN between the start of epoch i+1 and the start of epoch i+2?" + Once epoch i ends, predictoors cannot submit predictedValues for epoch i+2 */ return(railBlocknumToSlot(prediction_block)+ blocksPerEpoch * 2); } @@ -951,7 +951,7 @@ contract ERC20Template3 is ) public view returns (uint256, uint256) { require(isValidSubscription(msg.sender), "No subscription"); uint256 slot = railBlocknumToSlot(blocknum); - return (aggPredvalsNumer[slot], aggPredValsDenom[slot]); + return (aggregatedPredictedValuesNumer[slot], aggregatedPredictedValuesDenom[slot]); } function getSubscriptionRevenueAtBlock( @@ -978,7 +978,7 @@ contract ERC20Template3 is // ----------------------- MUTATING FUNCTIONS ----------------------- function submitPredval( - bool predval, + bool predictedValue, uint256 stake, uint256 blocknum ) external { @@ -988,15 +988,15 @@ contract ERC20Template3 is require(!submittedPredval(slot, msg.sender), "already submitted"); predictions[slot][msg.sender] = Prediction( - predval, + predictedValue, stake, msg.sender, false ); - // update agg_predvals - aggPredvalsNumer[slot] += stake * (predval ? 1 : 0); - aggPredValsDenom[slot] += stake; + // update agg_predictedValues + aggregatedPredictedValuesNumer[slot] += stake * (predictedValue ? 1 : 0); + aggregatedPredictedValuesDenom[slot] += stake; emit PredictionSubmitted(msg.sender, slot, stake); // safe transfer stake @@ -1021,7 +1021,7 @@ contract ERC20Template3 is Prediction memory predobj = predictions[slot][predictoor_addr]; if(predobj.paid) return; // just return if already paid, in order not to break payoutMultiple - // if OPF hasn't submitted trueval in truval_submit_timeout blocks then cancel round + // if OPF hasn't submitted trueValue in truval_submit_timeout blocks then cancel round if (block.number > slot + trueValSubmitTimeoutBlock && epochStatus[slot]==Status.Pending){ epochStatus[slot]=Status.Canceled; } @@ -1036,14 +1036,14 @@ contract ERC20Template3 is payout_amt = predobj.stake; } else{ // Status.Paying - if(truevals[slot] == predobj.predval){ + if(trueValues[slot] == predobj.predictedValue){ // he got it. - uint256 swe = truevals[slot] - ? aggPredvalsNumer[slot] - : aggPredValsDenom[slot] - aggPredvalsNumer[slot]; + uint256 swe = trueValues[slot] + ? aggregatedPredictedValuesNumer[slot] + : aggregatedPredictedValuesDenom[slot] - aggregatedPredictedValuesNumer[slot]; if(swe > 0) { uint256 revenue=getSubscriptionRevenueAtBlock(slot); - payout_amt = predobj.stake * (aggPredValsDenom[slot] + revenue) / swe; + payout_amt = predobj.stake * (aggregatedPredictedValuesDenom[slot] + revenue) / swe; } } // else payout_amt is already 0 @@ -1053,9 +1053,9 @@ contract ERC20Template3 is slot, predobj.stake, payout_amt, - predobj.predval, - truevals[slot], - aggPredvalsNumer[slot] / aggPredValsDenom[slot], + predobj.predictedValue, + trueValues[slot], + aggregatedPredictedValuesNumer[slot] / aggregatedPredictedValuesDenom[slot], epochStatus[slot] ); if(payout_amt>0) @@ -1066,7 +1066,7 @@ contract ERC20Template3 is function redeemUnusedSlotRevenue(uint256 blocknum) external onlyERC20Deployer { require(block.number > blocknum); uint256 slot = railBlocknumToSlot(blocknum); - require(aggPredValsDenom[slot] == 0); + require(aggregatedPredictedValuesDenom[slot] == 0); IERC20(stakeToken).safeTransfer( msg.sender, subscriptionRevenueAtBlock[slot] @@ -1081,7 +1081,7 @@ contract ERC20Template3 is function submitTrueVal( uint256 blocknum, - bool trueval + bool trueValue ) external onlyERC20Deployer { // TODO, is onlyERC20Deployer the right modifier? require(blocknum < block.number, "too early to submit"); @@ -1091,10 +1091,10 @@ contract ERC20Template3 is epochStatus[slot]=Status.Canceled; } else{ - truevals[slot] = trueval; + trueValues[slot] = trueValue; epochStatus[slot] = Status.Paying; } - emit TruevalSubmitted(slot, trueval,epochStatus[slot]); + emit TruevalSubmitted(slot, trueValue,epochStatus[slot]); } function updateSeconds( diff --git a/test/unit/datatokens/ERC20Template3.test.js b/test/unit/datatokens/ERC20Template3.test.js index dd302d0a7..b32b48e85 100644 --- a/test/unit/datatokens/ERC20Template3.test.js +++ b/test/unit/datatokens/ERC20Template3.test.js @@ -39,7 +39,7 @@ const PERMIT_TYPEHASH = keccak256( const sPerBlock = 24; const sPerEpoch = 288; const sPerSubscription = 24 * 60 * 60; -const truevalSubmitTimeout = 24 * 60 * 60 * 3; +const trueValueSubmitTimeout = 24 * 60 * 60 * 3; const getApprovalDigest = async ( token, owner, @@ -238,7 +238,7 @@ describe("ERC20Template3", () => { const trxERC20 = await tokenERC721.connect(user3).createERC20(1, ["ERC20DT3", "ERC20DT3Symbol"], [user3.address, user6.address, user3.address, addressZero, mockErc20.address], - [cap, 0, sPerBlock, sPerEpoch, sPerSubscription, truevalSubmitTimeout], + [cap, 0, sPerBlock, sPerEpoch, sPerSubscription, trueValueSubmitTimeout], [] ); @@ -904,14 +904,14 @@ describe("ERC20Template3", () => { const isValidSubscription = await erc20Token.isValidSubscription(erc20Token.address); assert(isValidSubscription == false, "Subscription must be invalid"); }); - it("#submitPredval - predictoor submits predval", async () => { - const predval = true; + it("#submitPredval - predictoor submits predictedValue", async () => { + const predictedValue = true; const stake = 100; await mockErc20.approve(erc20Token.address, stake); const soonestBlockToPredict = await erc20Token.soonestBlockToPredict((await ethers.provider.getBlockNumber())+1); const predictionSlot = await erc20Token.railBlocknumToSlot(soonestBlockToPredict); - const tx = await erc20Token.submitPredval(predval, stake, soonestBlockToPredict); + const tx = await erc20Token.submitPredval(predictedValue, stake, soonestBlockToPredict); const txReceipt = await tx.wait(); const event = getEventFromTx(txReceipt, 'PredictionSubmitted') assert(event, "Cannot find PredictionSubmitted event") @@ -920,56 +920,56 @@ describe("ERC20Template3", () => { expect(event.args[1]).to.equal(predictionSlot); expect(event.args[2]).to.equal(stake); }); - it("#submitPredval - predictoor can read their submitted predval", async () => { - const predval = true; + it("#submitPredval - predictoor can read their submitted predictedValue", async () => { + const predictedValue = true; const stake = 100; tx = await mockErc20.approve(erc20Token.address, stake); await tx.wait() const soonestBlockToPredict = await erc20Token.soonestBlockToPredict((await ethers.provider.getBlockNumber())+1); - await erc20Token.submitPredval(predval, stake, soonestBlockToPredict); + await erc20Token.submitPredval(predictedValue, stake, soonestBlockToPredict); const prediction = await erc20Token.getPrediction(soonestBlockToPredict, owner.address); - expect(prediction.predval).to.be.eq(predval); + expect(prediction.predictedValue).to.be.eq(predictedValue); expect(prediction.stake).to.be.eq(stake); expect(prediction.predictoor).to.be.eq(owner.address); expect(prediction.paid).to.be.eq(false); }); it("#submitPredval - others cannot read submitted predictions", async () => { - const predval = true; + const predictedValue = true; const stake = 100; tx = await mockErc20.approve(erc20Token.address, stake); await tx.wait() const soonestBlockToPredict = await erc20Token.soonestBlockToPredict((await ethers.provider.getBlockNumber())+1); - await erc20Token.submitPredval(predval, stake, soonestBlockToPredict); + await erc20Token.submitPredval(predictedValue, stake, soonestBlockToPredict); await expectRevert(erc20Token.connect(user2).getPrediction(soonestBlockToPredict, owner.address), "you shall not pass"); // fast forward blocks until next epoch Array(30).fill(0).map(async _ => await ethers.provider.send("evm_mine")); - // user2 should be able to read the predval now + // user2 should be able to read the predictedValue now const prediction = await erc20Token.connect(user2).getPrediction(soonestBlockToPredict, owner.address); - expect(prediction.predval).to.be.eq(predval); + expect(prediction.predictedValue).to.be.eq(predictedValue); }); it("#submitPredval - should revert when predictoor submits too early", async () => { - const predval = true; + const predictedValue = true; const stake = 100; const block = await ethers.provider.getBlockNumber(); const railed = await erc20Token.railBlocknumToSlot(block - 100); await mockErc20.approve(erc20Token.address, stake); await expectRevert( - erc20Token.submitPredval(predval, stake, railed), + erc20Token.submitPredval(predictedValue, stake, railed), "too late to submit" ); }); it("#submitPredval - should revert when predictoor submits duplicate prediction", async () => { - const predval = true; + const predictedValue = true; const stake = 100; await mockErc20.approve(erc20Token.address, stake * 2); const soonestBlockToPredict = await erc20Token.soonestBlockToPredict((await ethers.provider.getBlockNumber())+1); - await erc20Token.submitPredval(predval, stake, soonestBlockToPredict); + await erc20Token.submitPredval(predictedValue, stake, soonestBlockToPredict); await expectRevert( - erc20Token.submitPredval(predval, stake, soonestBlockToPredict), + erc20Token.submitPredval(predictedValue, stake, soonestBlockToPredict), "already submitted" ); }); @@ -978,13 +978,13 @@ describe("ERC20Template3", () => { const isPaused = await erc20Token.paused(); assert(isPaused == true, "Predictions should be paused"); - // submit predval should revert - const predval = true; + // submit predictedValue should revert + const predictedValue = true; const stake = 100; await mockErc20.approve(erc20Token.address, stake); const soonestBlockToPredict = await erc20Token.soonestBlockToPredict((await ethers.provider.getBlockNumber())+1); await expectRevert( - erc20Token.submitPredval(predval, stake, soonestBlockToPredict), + erc20Token.submitPredval(predictedValue, stake, soonestBlockToPredict), "paused" ); @@ -1019,8 +1019,8 @@ describe("ERC20Template3", () => { expect(event.args[0]).to.equal(submissionBlock); expect(event.args[1]).to.equal(true); - const trueval = await erc20Token.truevals(submissionBlock); - expect(trueval).to.be.true; + const trueValue = await erc20Token.trueValues(submissionBlock); + expect(trueValue).to.be.true; }); it("#subscriptions - user2 must be subscribed", async () => { @@ -1114,7 +1114,7 @@ describe("ERC20Template3", () => { const signedMessage = await signMessage(message, providerFeeAddress); // reduce subscription time - await erc20Token.updateSeconds(sPerBlock, sPerBlock, truevalSubmitTimeout); + await erc20Token.updateSeconds(sPerBlock, sPerBlock, trueValueSubmitTimeout); // set back to normal const tx = await erc20Token .connect(user2) @@ -1142,12 +1142,12 @@ describe("ERC20Template3", () => { const valid = await erc20Token.isValidSubscription(user2.address); expect(valid).to.be.false; // set back to normal - await erc20Token.updateSeconds(sPerBlock, sPerSubscription, truevalSubmitTimeout); + await erc20Token.updateSeconds(sPerBlock, sPerSubscription, trueValueSubmitTimeout); }); // can read getAggPredval with a valid subscription - it("#getAggPredval - should return agg_predval if caller has a valid subscription", async () => { + it("#getAggPredval - should return agg_predictedValue if caller has a valid subscription", async () => { //MINT SOME DT20 to USER2 so he can start order await buyDTFromFixedRate(erc20Token.address,user2.address,10) assert( @@ -1204,12 +1204,12 @@ describe("ERC20Template3", () => { expect(denom).to.be.eq(0); // user2 makes a prediction - const predval = true; + const predictedValue = true; const stake = web3.utils.toWei("1"); await mockErc20.transfer(user3.address, stake); await mockErc20.connect(user3).approve(erc20Token.address, stake); soonestBlockToPredict = await erc20Token.soonestBlockToPredict((await ethers.provider.getBlockNumber())+1); - await erc20Token.connect(user3).submitPredval(predval, stake, soonestBlockToPredict); + await erc20Token.connect(user3).submitPredval(predictedValue, stake, soonestBlockToPredict); soonestBlockToPredict = await erc20Token.soonestBlockToPredict((await ethers.provider.getBlockNumber())+1); const [numer2, denom2] = await erc20Token.connect(user2).getAggPredval(soonestBlockToPredict); @@ -1290,11 +1290,11 @@ describe("ERC20Template3", () => { expect(revenue_at_block).to.be.gt(0); // predictoor makes a prediction - const predval = true; + const predictedValue = true; const stake = web3.utils.toWei("1"); await mockErc20.transfer(user3.address, stake); await mockErc20.connect(user3).approve(erc20Token.address, stake); - await erc20Token.connect(user3).submitPredval(predval, stake, soonestBlockToPredict); + await erc20Token.connect(user3).submitPredval(predictedValue, stake, soonestBlockToPredict); let mockErc20Balance = await mockErc20.balanceOf(user3.address) tx = await erc20Token.connect(user3).payout(soonestBlockToPredict, user3.address) txReceipt = await tx.wait(); @@ -1315,7 +1315,7 @@ describe("ERC20Template3", () => { // opf submits truval - tx = await erc20Token.submitTrueVal(soonestBlockToPredict, predval); + tx = await erc20Token.submitTrueVal(soonestBlockToPredict, predictedValue); txReceipt = await tx.wait(); event = getEventFromTx(txReceipt, 'TruevalSubmitted') assert(event, "TruevalSubmitted event not found") @@ -1412,11 +1412,11 @@ describe("ERC20Template3", () => { expect(revenue_at_block).to.be.gt(0); // predictoor makes a prediction - const predval = true; + const predictedValue = true; const stake = web3.utils.toWei("1"); await mockErc20.transfer(user3.address, stake); await mockErc20.connect(user3).approve(erc20Token.address, stake); - await erc20Token.connect(user3).submitPredval(predval, stake, soonestBlockToPredict); + await erc20Token.connect(user3).submitPredval(predictedValue, stake, soonestBlockToPredict); let mockErc20Balance = await mockErc20.balanceOf(user3.address) tx = await erc20Token.connect(user3).payoutMultiple([soonestBlockToPredict], user3.address) @@ -1435,7 +1435,7 @@ describe("ERC20Template3", () => { expect(await mockErc20.balanceOf(user3.address)).to.be.eq(mockErc20Balance); // opf submits truval - tx = await erc20Token.submitTrueVal(soonestBlockToPredict, predval); + tx = await erc20Token.submitTrueVal(soonestBlockToPredict, predictedValue); txReceipt = await tx.wait(); event = getEventFromTx(txReceipt, 'TruevalSubmitted') assert(event, "TruevalSubmitted event not found") @@ -1650,6 +1650,6 @@ describe("ERC20Template3", () => { assert(event.args.status==2, "Status should be 2 = Canceled") expect(event.args.payout).to.be.eq(event.args.stake) expect(event.args.payout).to.be.eq(stake) - await erc20Token.updateSeconds(sPerBlock, sPerSubscription, truevalSubmitTimeout); + await erc20Token.updateSeconds(sPerBlock, sPerSubscription, trueValueSubmitTimeout); }) }); From 5a97487ed674f946f517b3637f5e0550c29ec6a2 Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Sun, 14 May 2023 04:10:34 -0700 Subject: [PATCH 105/120] fix tests --- test/unit/datatokens/ERC20Template3.test.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/test/unit/datatokens/ERC20Template3.test.js b/test/unit/datatokens/ERC20Template3.test.js index b32b48e85..31e7b1997 100644 --- a/test/unit/datatokens/ERC20Template3.test.js +++ b/test/unit/datatokens/ERC20Template3.test.js @@ -1198,7 +1198,7 @@ describe("ERC20Template3", () => { ); - let soonestBlockToPredict = await erc20Token.soonestBlockToPredict((await ethers.provider.getBlockNumber())+1); + let soonestBlockToPredict = await erc20Token.soonestBlockToPredict((await ethers.provider.getBlockNumber())+3); const [numer, denom] = await erc20Token.connect(user2).getAggPredval(soonestBlockToPredict); expect(numer).to.be.eq(0); expect(denom).to.be.eq(0); @@ -1208,10 +1208,8 @@ describe("ERC20Template3", () => { const stake = web3.utils.toWei("1"); await mockErc20.transfer(user3.address, stake); await mockErc20.connect(user3).approve(erc20Token.address, stake); - soonestBlockToPredict = await erc20Token.soonestBlockToPredict((await ethers.provider.getBlockNumber())+1); await erc20Token.connect(user3).submitPredval(predictedValue, stake, soonestBlockToPredict); - soonestBlockToPredict = await erc20Token.soonestBlockToPredict((await ethers.provider.getBlockNumber())+1); const [numer2, denom2] = await erc20Token.connect(user2).getAggPredval(soonestBlockToPredict); expect(numer2).to.be.eq(web3.utils.toWei("1")); expect(denom2).to.be.eq(web3.utils.toWei("1")); From fb684c7529b469a0ff1db871af6823cfc8b0699b Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Sun, 14 May 2023 06:47:15 -0700 Subject: [PATCH 106/120] fix bug --- contracts/templates/ERC20Template3.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/templates/ERC20Template3.sol b/contracts/templates/ERC20Template3.sol index b0b7ad26e..b930738ea 100644 --- a/contracts/templates/ERC20Template3.sol +++ b/contracts/templates/ERC20Template3.sol @@ -1055,7 +1055,7 @@ contract ERC20Template3 is payout_amt, predobj.predictedValue, trueValues[slot], - aggregatedPredictedValuesNumer[slot] / aggregatedPredictedValuesDenom[slot], + aggregatedPredictedValuesNumer[slot] * 1e18 / aggregatedPredictedValuesDenom[slot], epochStatus[slot] ); if(payout_amt>0) From 0bab055f29bddde6c9820337cef35dbd512a0a9f Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Thu, 18 May 2023 03:08:33 -0700 Subject: [PATCH 107/120] remove nonReentrant --- contracts/templates/ERC20Template3.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/templates/ERC20Template3.sol b/contracts/templates/ERC20Template3.sol index b930738ea..e2ec667e8 100644 --- a/contracts/templates/ERC20Template3.sol +++ b/contracts/templates/ERC20Template3.sol @@ -846,7 +846,7 @@ contract ERC20Template3 is function buyFromFreAndOrder( OrderParams calldata _orderParams, FreParams calldata _freParams - ) external nonReentrant { + ) external { //first buy 1.0 DT buyFromFre(_freParams); //we need the following because startOrder expects msg.sender to have dt From 1eb80e74c6c913e341d18727263a72994f45fd1d Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Thu, 18 May 2023 03:11:02 -0700 Subject: [PATCH 108/120] fix nonReentrant --- contracts/templates/ERC20Template3.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/templates/ERC20Template3.sol b/contracts/templates/ERC20Template3.sol index e2ec667e8..4fae058f9 100644 --- a/contracts/templates/ERC20Template3.sol +++ b/contracts/templates/ERC20Template3.sol @@ -426,7 +426,7 @@ contract ERC20Template3 is uint256 serviceIndex, providerFee calldata _providerFee, consumeMarketFee calldata _consumeMarketFee - ) public nonReentrant{ + ) public { uint256 amount = 1e18; // we always pay 1 DT. No more, no less require( balanceOf(msg.sender) >= amount, @@ -846,7 +846,7 @@ contract ERC20Template3 is function buyFromFreAndOrder( OrderParams calldata _orderParams, FreParams calldata _freParams - ) external { + ) external nonReentrant{ //first buy 1.0 DT buyFromFre(_freParams); //we need the following because startOrder expects msg.sender to have dt From 510daa40e9117944ab3b49748d27735eeba64ede Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Thu, 18 May 2023 21:38:42 -0700 Subject: [PATCH 109/120] add floatValue --- contracts/templates/ERC20Template3.sol | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/contracts/templates/ERC20Template3.sol b/contracts/templates/ERC20Template3.sol index 4fae058f9..6966367b7 100644 --- a/contracts/templates/ERC20Template3.sol +++ b/contracts/templates/ERC20Template3.sol @@ -76,6 +76,7 @@ contract ERC20Template3 is event TruevalSubmitted( uint256 indexed slot, bool trueValue, + uint256 floatValue, Status status ); struct Prediction { @@ -110,9 +111,12 @@ contract ERC20Template3 is mapping(uint256 => uint256) private aggregatedPredictedValuesNumer; mapping(uint256 => uint256) private aggregatedPredictedValuesDenom; mapping(uint256 => bool) public trueValues; // true values submited by owner + mapping(uint256 => uint256) public floatValues; // real values submited by owner mapping(uint256 => Status) public epochStatus; // status of each epoch mapping(uint256 => uint256) private subscriptionRevenueAtBlock; //income registred mapping(address => Subscription) public subscriptions; // valid subscription per user + + uint256 public blocksPerEpoch; address public stakeToken; uint256 public blocksPerSubscription; @@ -1079,22 +1083,33 @@ contract ERC20Template3 is // TODO - pause FRE as well } + /** + * @dev submitTrueVal + * Called by owner to settle one epoch + * @param blocknum epoch block number + * @param trueValue trueValue for that epoch (0 - down, 1 - up) + * @param floatValue float value of pair for that epoch + * @param cancelRound If true, cancel that epoch + */ function submitTrueVal( uint256 blocknum, - bool trueValue + bool trueValue, + uint256 floatValue, + bool cancelRound ) external onlyERC20Deployer { // TODO, is onlyERC20Deployer the right modifier? require(blocknum < block.number, "too early to submit"); uint256 slot = railBlocknumToSlot(blocknum); require(epochStatus[slot]==Status.Pending, "already settled"); - if (block.number > slot + trueValSubmitTimeoutBlock && epochStatus[slot]==Status.Pending){ + floatValues[slot] = floatValue; + if (cancelRound || (block.number > slot + trueValSubmitTimeoutBlock && epochStatus[slot]==Status.Pending)){ epochStatus[slot]=Status.Canceled; } else{ trueValues[slot] = trueValue; epochStatus[slot] = Status.Paying; } - emit TruevalSubmitted(slot, trueValue,epochStatus[slot]); + emit TruevalSubmitted(slot, trueValue,floatValue,epochStatus[slot]); } function updateSeconds( From 260ffb221351bc381f525469acba3c6a6050b2a2 Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Thu, 18 May 2023 21:53:34 -0700 Subject: [PATCH 110/120] fix feeCollector & slash all edge case --- contracts/templates/ERC20Template3.sol | 30 ++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/contracts/templates/ERC20Template3.sol b/contracts/templates/ERC20Template3.sol index 6966367b7..409a672c0 100644 --- a/contracts/templates/ERC20Template3.sol +++ b/contracts/templates/ERC20Template3.sol @@ -115,8 +115,9 @@ contract ERC20Template3 is mapping(uint256 => Status) public epochStatus; // status of each epoch mapping(uint256 => uint256) private subscriptionRevenueAtBlock; //income registred mapping(address => Subscription) public subscriptions; // valid subscription per user - - + mapping(uint256 => uint256) private noOfTruePredictionsPerEpoch; // how many Ture predictions we have per epoch + mapping(uint256 => uint256) private noOfFalsePredictionsPerEpoch; // how many False predictions we have per epoch + address public feeCollector; //who will get FRE fees, slashes stakes, revenue per epoch if no predictoors uint256 public blocksPerEpoch; address public stakeToken; uint256 public blocksPerSubscription; @@ -383,8 +384,10 @@ contract ERC20Template3 is stakeToken == addresses[0], "Cannot create FRE with baseToken!=stakeToken" ); + require(addresses[2] != address(0),"FeeCollector cannot be zero"); //force FRE allowedSwapper to this contract address. no one else can swap because we need to record the income if (uints[4] > 0) _addMinter(fixedPriceAddress); + addresses[1]=address(this); //make this contract FRE owner exchangeId = IFactoryRouter(router).deployFixedRate( fixedPriceAddress, addresses, @@ -398,6 +401,7 @@ contract ERC20Template3 is ); fixedRateExchanges.push(fixedRate(fixedPriceAddress, exchangeId)); rate = uints[2]; //set rate to be added in add_revenue + feeCollector = addresses[2]; } /** @@ -997,7 +1001,11 @@ contract ERC20Template3 is msg.sender, false ); - + if (predictedValue){ + noOfTruePredictionsPerEpoch[slot]++; + } + else + noOfFalsePredictionsPerEpoch[slot]++; // update agg_predictedValues aggregatedPredictedValuesNumer[slot] += stake * (predictedValue ? 1 : 0); aggregatedPredictedValuesDenom[slot] += stake; @@ -1071,8 +1079,9 @@ contract ERC20Template3 is require(block.number > blocknum); uint256 slot = railBlocknumToSlot(blocknum); require(aggregatedPredictedValuesDenom[slot] == 0); + require(feeCollector != address(0), "Cannot send fees to address 0"); IERC20(stakeToken).safeTransfer( - msg.sender, + feeCollector, subscriptionRevenueAtBlock[slot] ); } @@ -1109,6 +1118,19 @@ contract ERC20Template3 is trueValues[slot] = trueValue; epochStatus[slot] = Status.Paying; } + // edge case where all stakers are submiting a value, but they are all wrong + if ( + (trueValue && noOfTruePredictionsPerEpoch[slot]==0 && noOfFalsePredictionsPerEpoch[slot]>0) + || + (!trueValue && noOfTruePredictionsPerEpoch[slot]>0 && noOfFalsePredictionsPerEpoch[slot]==0) + ){ + // everyone gets slashed + require(feeCollector != address(0), "Cannot send slashed stakes to address 0"); + IERC20(stakeToken).safeTransfer( + feeCollector, + aggregatedPredictedValuesDenom[slot] + ); + } emit TruevalSubmitted(slot, trueValue,floatValue,epochStatus[slot]); } From 958657a61edf2e5d539f9ae98f04f9b15ceba99d Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Thu, 18 May 2023 22:04:58 -0700 Subject: [PATCH 111/120] disable Fre when contract is paused --- contracts/interfaces/IFixedRateExchange.sol | 1 + contracts/templates/ERC20Template3.sol | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/contracts/interfaces/IFixedRateExchange.sol b/contracts/interfaces/IFixedRateExchange.sol index 2b4ffafc9..6716bd324 100644 --- a/contracts/interfaces/IFixedRateExchange.sol +++ b/contracts/interfaces/IFixedRateExchange.sol @@ -72,4 +72,5 @@ interface IFixedRateExchange { function getId() pure external returns (uint8); function collectBT(bytes32 exchangeId, uint256 amount) external; function collectDT(bytes32 exchangeId, uint256 amount) external; + function toggleExchangeState(bytes32 exchangeId) external; } \ No newline at end of file diff --git a/contracts/templates/ERC20Template3.sol b/contracts/templates/ERC20Template3.sol index 409a672c0..71a00d94d 100644 --- a/contracts/templates/ERC20Template3.sol +++ b/contracts/templates/ERC20Template3.sol @@ -1089,6 +1089,13 @@ contract ERC20Template3 is function pausePredictions() external onlyERC20Deployer { paused = !paused; + if (fixedRateExchanges.length>0){ + IFixedRateExchange fre = IFixedRateExchange(fixedRateExchanges[0].contractAddress); + bool freActive = fre.isActive(fixedRateExchanges[0].id); + if ((paused && freActive) || (!paused && !freActive)){ + fre.toggleExchangeState(fixedRateExchanges[0].id); + } + } // TODO - pause FRE as well } From 0ee0fe53138f4222a7679df0aa416ce71e86a83d Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Thu, 18 May 2023 22:34:53 -0700 Subject: [PATCH 112/120] fix tests --- contracts/templates/ERC20Template3.sol | 7 ++++-- test/unit/datatokens/ERC20Template3.test.js | 28 +++++++++++---------- 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/contracts/templates/ERC20Template3.sol b/contracts/templates/ERC20Template3.sol index 71a00d94d..181be5955 100644 --- a/contracts/templates/ERC20Template3.sol +++ b/contracts/templates/ERC20Template3.sol @@ -387,7 +387,7 @@ contract ERC20Template3 is require(addresses[2] != address(0),"FeeCollector cannot be zero"); //force FRE allowedSwapper to this contract address. no one else can swap because we need to record the income if (uints[4] > 0) _addMinter(fixedPriceAddress); - addresses[1]=address(this); //make this contract FRE owner + // create the exchange exchangeId = IFactoryRouter(router).deployFixedRate( fixedPriceAddress, addresses, @@ -1089,6 +1089,8 @@ contract ERC20Template3 is function pausePredictions() external onlyERC20Deployer { paused = !paused; + // we cannot pause FixedRate as well, so be aware when triggering this function + /* keeping code here until we decide if (fixedRateExchanges.length>0){ IFixedRateExchange fre = IFixedRateExchange(fixedRateExchanges[0].contractAddress); bool freActive = fre.isActive(fixedRateExchanges[0].id); @@ -1096,7 +1098,8 @@ contract ERC20Template3 is fre.toggleExchangeState(fixedRateExchanges[0].id); } } - // TODO - pause FRE as well + */ + } /** diff --git a/test/unit/datatokens/ERC20Template3.test.js b/test/unit/datatokens/ERC20Template3.test.js index 31e7b1997..ba7779dec 100644 --- a/test/unit/datatokens/ERC20Template3.test.js +++ b/test/unit/datatokens/ERC20Template3.test.js @@ -108,14 +108,14 @@ describe("ERC20Template3", () => { publishMarketFeeAddress, mockErc20, mockErc20Decimals, - publishMarketFeeToken + publishMarketFeeToken, + freMarketFeeCollector cap = web3.utils.toWei("100000"); const fakeUSDAmount = cap const addressZero = '0x0000000000000000000000000000000000000000'; const freRate = web3.utils.toWei("2"); // 2 tokens per dt const freMarketFee = 1e15 // 0.1% - const freMarketFeeCollector = addressZero const communityFeeCollector = "0xeE9300b7961e0a01d9f0adb863C7A227A07AaD75"; @@ -153,7 +153,7 @@ describe("ERC20Template3", () => { const MockErc20 = await ethers.getContractFactory('MockERC20'); const MockErc20Decimals = await ethers.getContractFactory('MockERC20Decimals'); - [owner, reciever, user2, user3, user4, user5, user6, opcCollector, marketFeeCollector, publishMarketAccount] = await ethers.getSigners(); + [owner, reciever, user2, user3, user4, user5, user6, opcCollector, freMarketFeeCollector, marketFeeCollector,publishMarketAccount] = await ethers.getSigners(); publishMarketFeeAddress = publishMarketAccount.address data = web3.utils.asciiToHex(constants.blob[0]); flags = web3.utils.asciiToHex(constants.blob[0]); @@ -254,13 +254,13 @@ describe("ERC20Template3", () => { await expectRevert( erc20Token.connect(owner).createFixedRate( fixedRateExchange.address, - [mockErc20Decimals.address, owner.address, freMarketFeeCollector, addressZero], + [mockErc20Decimals.address, owner.address, freMarketFeeCollector.address, addressZero], [18, 18, freRate , freMarketFee , 1]), "Cannot create FRE with baseToken!=stakeToken" ); await erc20Token.connect(owner).createFixedRate( fixedRateExchange.address, - [mockErc20.address, owner.address, freMarketFeeCollector, addressZero], + [mockErc20.address, owner.address, freMarketFeeCollector.address, addressZero], [18, 18, freRate , freMarketFee, 1]) // create an ERC20 with publish Fee ( 5 USDC, going to publishMarketAddress) @@ -281,7 +281,7 @@ describe("ERC20Template3", () => { await erc20TokenWithPublishFee.connect(owner).createFixedRate( fixedRateExchange.address, - [mockErc20.address, owner.address, freMarketFeeCollector , addressZero], + [mockErc20.address, owner.address, freMarketFeeCollector.address , addressZero], [18, 18, freMarketFee, freMarketFee , 1]) Array(100).fill(0).map(async _ => await ethers.provider.send("evm_mine")); @@ -315,7 +315,7 @@ describe("ERC20Template3", () => { await expectRevert( erc20Token.connect(owner).createFixedRate( fixedRateExchange.address, - [mockErc20Decimals.address, owner.address, freMarketFeeCollector , addressZero], + [mockErc20Decimals.address, owner.address, freMarketFeeCollector.address , addressZero], [18, 18, freRate , freMarketFee , 1] ), "Fixed rate already present" @@ -1006,18 +1006,20 @@ describe("ERC20Template3", () => { it("#submitTrueVal - should revert submitting for a future block", async () => { const soonestBlockToPredict = await erc20Token.soonestBlockToPredict((await ethers.provider.getBlockNumber())+1); - await expectRevert(erc20Token.submitTrueVal(soonestBlockToPredict, true), "too early to submit"); + await expectRevert(erc20Token.submitTrueVal(soonestBlockToPredict, true,web3.utils.toWei("230.43"),false), "too early to submit"); }); it("#submitTrueVal - should submit for a block in the past", async () => { const blocksPerEpoch = await erc20Token.blocksPerEpoch(); const soonestBlockToPredict = await erc20Token.soonestBlockToPredict((await ethers.provider.getBlockNumber())); const submissionBlock = soonestBlockToPredict - blocksPerEpoch * 2; - const tx = await erc20Token.submitTrueVal(submissionBlock, true); + const tx = await erc20Token.submitTrueVal(submissionBlock, true,web3.utils.toWei("230.43"),false); const tx_receipt = await tx.wait(); const event = getEventFromTx(tx_receipt, "TruevalSubmitted"); expect(event.args[0]).to.equal(submissionBlock); expect(event.args[1]).to.equal(true); + expect(event.args[2]).to.equal(web3.utils.toWei("230.43")); + expect(event.args[3]).to.equal(1); const trueValue = await erc20Token.trueValues(submissionBlock); expect(trueValue).to.be.true; @@ -1313,7 +1315,7 @@ describe("ERC20Template3", () => { // opf submits truval - tx = await erc20Token.submitTrueVal(soonestBlockToPredict, predictedValue); + tx = await erc20Token.submitTrueVal(soonestBlockToPredict, predictedValue, web3.utils.toWei("230.43"), false); txReceipt = await tx.wait(); event = getEventFromTx(txReceipt, 'TruevalSubmitted') assert(event, "TruevalSubmitted event not found") @@ -1433,7 +1435,7 @@ describe("ERC20Template3", () => { expect(await mockErc20.balanceOf(user3.address)).to.be.eq(mockErc20Balance); // opf submits truval - tx = await erc20Token.submitTrueVal(soonestBlockToPredict, predictedValue); + tx = await erc20Token.submitTrueVal(soonestBlockToPredict, predictedValue, web3.utils.toWei("230.43"), false); txReceipt = await tx.wait(); event = getEventFromTx(txReceipt, 'TruevalSubmitted') assert(event, "TruevalSubmitted event not found") @@ -1497,7 +1499,7 @@ describe("ERC20Template3", () => { const winnersStake = winners.map(x=>stakes[x]).reduce((a,b)=>a+b, 0); // opf submits truval - await erc20Token.submitTrueVal(predictionBlock, truval); + await erc20Token.submitTrueVal(predictionBlock, truval, web3.utils.toWei("230.43"), false); // each predictoor calls payout function for (let i = 0; i < predictoors.length; i++){ @@ -1593,7 +1595,7 @@ describe("ERC20Template3", () => { const txReceipt_2 = await tx_2.wait(); let event_2 = getEventFromTx(txReceipt_2, 'Transfer') expect(event_2.args.from).to.be.eq(erc20Token.address); - expect(event_2.args.to).to.be.eq(owner.address); + expect(event_2.args.to).to.be.eq(freMarketFeeCollector.address); expect(event_2.args.value).to.be.eq(6666666666666666); }) it("#redeemUnusedSlotRevenue - admin should not be able to redeem for future epoch", async()=>{ From 4167804fc8b54c13cb9bcac5807006ae025810e3 Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Thu, 18 May 2023 22:53:10 -0700 Subject: [PATCH 113/120] add cancel epoch test --- test/unit/datatokens/ERC20Template3.test.js | 54 +++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/test/unit/datatokens/ERC20Template3.test.js b/test/unit/datatokens/ERC20Template3.test.js index ba7779dec..f92d402db 100644 --- a/test/unit/datatokens/ERC20Template3.test.js +++ b/test/unit/datatokens/ERC20Template3.test.js @@ -1652,4 +1652,58 @@ describe("ERC20Template3", () => { expect(event.args.payout).to.be.eq(stake) await erc20Token.updateSeconds(sPerBlock, sPerSubscription, trueValueSubmitTimeout); }) + + it("predictoor can redeem stake if OPF cancels the round", async() => { + await erc20Token.updateSeconds(sPerBlock, sPerSubscription, sPerEpoch * 3); + + const stake = 100; + await mockErc20.transfer(user2.address, stake); + await mockErc20.connect(user2).approve(erc20Token.address, stake); + const prediction = true; + const soonestBlockToPredict = await erc20Token.soonestBlockToPredict((await ethers.provider.getBlockNumber())+1); + const blockNum = await ethers.provider.getBlockNumber(); + const slot = await erc20Token.railBlocknumToSlot(soonestBlockToPredict); + + await erc20Token.connect(user2).submitPredval(prediction, stake, soonestBlockToPredict); + const blocksPerEpoch = await erc20Token.blocksPerEpoch(); + + let mockErc20Balance = await mockErc20.balanceOf(user2.address) + let tx = await erc20Token.connect(user2).payout(soonestBlockToPredict, user2.address) + let txReceipt = await tx.wait(); + let event = getEventFromTx(txReceipt, 'PredictionPayout') + //we are not getting anything, round is still in progress + assert(event==null, "PredictionPayout event found") + expect(await mockErc20.balanceOf(user2.address)).to.be.eq(mockErc20Balance); + + Array(blocksPerEpoch * 2).fill(0).map(async _ => await ethers.provider.send("evm_mine")); + + mockErc20Balance = await mockErc20.balanceOf(user2.address) + tx = await erc20Token.connect(user2).payout(soonestBlockToPredict, user2.address) + txReceipt = await tx.wait(); + event = getEventFromTx(txReceipt, 'PredictionPayout') + //we are not getting anything, round is still in progress + assert(event==null, "PredictionPayout event found") + expect(await mockErc20.balanceOf(user2.address)).to.be.eq(mockErc20Balance); + + Array(blocksPerEpoch ).fill(0).map(async _ => await ethers.provider.send("evm_mine")); + // opf cancels the round + tx = await erc20Token.connect(owner).submitTrueVal(soonestBlockToPredict, true,web3.utils.toWei("230.43"),true); + txReceipt = await tx.wait(); + event = getEventFromTx(txReceipt, 'TruevalSubmitted') + assert(event, "TruevalSubmitted event not found") + assert(event.args.status==2, 'Status missmatch') // round status should be 2 == Status.Cancel + + tx = await erc20Token.connect(user2).payout(soonestBlockToPredict, user2.address); + txReceipt = await tx.wait(); + event = getEventFromTx(txReceipt, 'Transfer') + expect(event.args.from).to.be.eq(erc20Token.address); + expect(event.args.to).to.be.eq(user2.address); + expect(event.args.value).to.be.eq(stake); + event = getEventFromTx(txReceipt, 'PredictionPayout') + assert(event, "PredictionPayout event not found") + assert(event.args.status==2, "Status should be 2 = Canceled") + expect(event.args.payout).to.be.eq(event.args.stake) + expect(event.args.payout).to.be.eq(stake) + await erc20Token.updateSeconds(sPerBlock, sPerSubscription, trueValueSubmitTimeout); + }) }); From 20af082367d0379cfeb78d4d6b7810de2bd73393 Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Thu, 18 May 2023 23:25:31 -0700 Subject: [PATCH 114/120] add test for edge case --- test/unit/datatokens/ERC20Template3.test.js | 59 ++++++++++++++++++++- 1 file changed, 57 insertions(+), 2 deletions(-) diff --git a/test/unit/datatokens/ERC20Template3.test.js b/test/unit/datatokens/ERC20Template3.test.js index f92d402db..35de393db 100644 --- a/test/unit/datatokens/ERC20Template3.test.js +++ b/test/unit/datatokens/ERC20Template3.test.js @@ -11,6 +11,7 @@ const { keccak256 } = require("@ethersproject/keccak256"); const ethers = hre.ethers; const { ecsign, zeroAddress } = require("ethereumjs-util"); const { ZERO_ADDRESS } = require("@openzeppelin/test-helpers/src/constants"); +const { BigNumber } = require("ethers"); const getDomainSeparator = (name, tokenAddress, chainId) => { @@ -1499,8 +1500,13 @@ describe("ERC20Template3", () => { const winnersStake = winners.map(x=>stakes[x]).reduce((a,b)=>a+b, 0); // opf submits truval - await erc20Token.submitTrueVal(predictionBlock, truval, web3.utils.toWei("230.43"), false); - + tx = await erc20Token.submitTrueVal(predictionBlock, truval, web3.utils.toWei("230.43"), false); + txReceipt = await tx.wait(); + event = getEventFromTx(txReceipt, 'Transfer') + if(winners.length>0) + assert(event==null, "We should not have any transfer event, winners are present") + else + assert(event, "We should have a transfer event, because everyone was slashed") // each predictoor calls payout function for (let i = 0; i < predictoors.length; i++){ let predictoor = predictoors[i]; @@ -1706,4 +1712,53 @@ describe("ERC20Template3", () => { expect(event.args.payout).to.be.eq(stake) await erc20Token.updateSeconds(sPerBlock, sPerSubscription, trueValueSubmitTimeout); }) + + it("all predictoors are slashed, feeCollector gets the stakes", async () => { + // predictoor makes a predictions + let predictoors = [reciever, user2, user3, user4, user5, user6]; + let predictions = []; + let stakes = []; + let tx,txReceipt, event + for(const predictoor of predictoors){ + const amt = web3.utils.toWei("200"); + await mockErc20.transfer(predictoor.address, amt); + await mockErc20.connect(predictoor).approve(erc20Token.address, amt); + } + + const blocksPerEpoch = await erc20Token.blocksPerEpoch(); + const currentBlock = await ethers.provider.getBlockNumber(); + const soonestBlockToPredict = await erc20Token.soonestBlockToPredict((await ethers.provider.getBlockNumber())+1); + Array(soonestBlockToPredict - currentBlock + 1).fill(0).map(async _ => await ethers.provider.send("evm_mine")); + const predictionBlock = await erc20Token.soonestBlockToPredict((await ethers.provider.getBlockNumber())+1); + let totalStake = new BigNumber.from(0) + for(const predictoor of predictoors){ + const stake = 10 + Math.random() * 100; + const stakeWei = web3.utils.toWei(stake.toString()); + //all predictoors are predicting False + const p = false + predictions.push(p); + stakes.push(stake); + totalStake=totalStake.add(stakeWei) + await erc20Token.connect(predictoor).submitPredval(p, stakeWei, predictionBlock) + } + + Array(blocksPerEpoch * 2).fill(0).map(async _ => await ethers.provider.send("evm_mine")); + const truval = true // + // opf submits truval + tx = await erc20Token.submitTrueVal(predictionBlock, truval, web3.utils.toWei("230.43"), false); + txReceipt = await tx.wait(); + event = getEventFromTx(txReceipt, 'Transfer') + expect(event.args.from).to.be.eq(erc20Token.address); + expect(event.args.to).to.be.eq(freMarketFeeCollector.address); + expect(event.args.value).to.be.eq(totalStake); + // each predictoor calls payout function, they should get nothing + for (let i = 0; i < predictoors.length; i++){ + let predictoor = predictoors[i]; + tx = await erc20Token.connect(predictoor).payout(predictionBlock, predictoor.address); + txReceipt = await tx.wait(); + event = getEventFromTx(txReceipt, 'PredictionPayout') + assert(event, "PredictionPayout event not found") + expect(event.args.payout).to.be.eq(0) + } + }); }); From d4804c74658bb9fb0e82a72649a57eb2b793797a Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Fri, 19 May 2023 02:35:41 -0700 Subject: [PATCH 115/120] optimize --- contracts/templates/ERC20Template3.sol | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/contracts/templates/ERC20Template3.sol b/contracts/templates/ERC20Template3.sol index 181be5955..3fd94852a 100644 --- a/contracts/templates/ERC20Template3.sol +++ b/contracts/templates/ERC20Template3.sol @@ -115,8 +115,6 @@ contract ERC20Template3 is mapping(uint256 => Status) public epochStatus; // status of each epoch mapping(uint256 => uint256) private subscriptionRevenueAtBlock; //income registred mapping(address => Subscription) public subscriptions; // valid subscription per user - mapping(uint256 => uint256) private noOfTruePredictionsPerEpoch; // how many Ture predictions we have per epoch - mapping(uint256 => uint256) private noOfFalsePredictionsPerEpoch; // how many False predictions we have per epoch address public feeCollector; //who will get FRE fees, slashes stakes, revenue per epoch if no predictoors uint256 public blocksPerEpoch; address public stakeToken; @@ -1001,11 +999,6 @@ contract ERC20Template3 is msg.sender, false ); - if (predictedValue){ - noOfTruePredictionsPerEpoch[slot]++; - } - else - noOfFalsePredictionsPerEpoch[slot]++; // update agg_predictedValues aggregatedPredictedValuesNumer[slot] += stake * (predictedValue ? 1 : 0); aggregatedPredictedValuesDenom[slot] += stake; @@ -1129,10 +1122,11 @@ contract ERC20Template3 is epochStatus[slot] = Status.Paying; } // edge case where all stakers are submiting a value, but they are all wrong - if ( - (trueValue && noOfTruePredictionsPerEpoch[slot]==0 && noOfFalsePredictionsPerEpoch[slot]>0) - || - (!trueValue && noOfTruePredictionsPerEpoch[slot]>0 && noOfFalsePredictionsPerEpoch[slot]==0) + if (aggregatedPredictedValuesDenom[slot]>0 && ( + (trueValue && aggregatedPredictedValuesNumer[slot]==0) + || + (!trueValue && aggregatedPredictedValuesNumer[slot]==aggregatedPredictedValuesDenom[slot]) + ) ){ // everyone gets slashed require(feeCollector != address(0), "Cannot send slashed stakes to address 0"); From 97c7ce87a51a8a5e4d99f4fe71a3884930944c17 Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Fri, 19 May 2023 05:17:54 -0700 Subject: [PATCH 116/120] some renames --- contracts/templates/ERC20Template3.sol | 28 +++++++++++++------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/contracts/templates/ERC20Template3.sol b/contracts/templates/ERC20Template3.sol index 3fd94852a..4cdf78cc2 100644 --- a/contracts/templates/ERC20Template3.sol +++ b/contracts/templates/ERC20Template3.sol @@ -108,8 +108,8 @@ contract ERC20Template3 is // All mappings below are using slot as key. // Whenever we have functions that take block as argumens, we rail it to slot automaticly mapping(uint256 => mapping(address => Prediction)) private predictions; // id to prediction object - mapping(uint256 => uint256) private aggregatedPredictedValuesNumer; - mapping(uint256 => uint256) private aggregatedPredictedValuesDenom; + mapping(uint256 => uint256) private roundSumStakesUp; + mapping(uint256 => uint256) private roundSumStakes; mapping(uint256 => bool) public trueValues; // true values submited by owner mapping(uint256 => uint256) public floatValues; // real values submited by owner mapping(uint256 => Status) public epochStatus; // status of each epoch @@ -957,7 +957,7 @@ contract ERC20Template3 is ) public view returns (uint256, uint256) { require(isValidSubscription(msg.sender), "No subscription"); uint256 slot = railBlocknumToSlot(blocknum); - return (aggregatedPredictedValuesNumer[slot], aggregatedPredictedValuesDenom[slot]); + return (roundSumStakesUp[slot], roundSumStakes[slot]); } function getSubscriptionRevenueAtBlock( @@ -1000,8 +1000,8 @@ contract ERC20Template3 is false ); // update agg_predictedValues - aggregatedPredictedValuesNumer[slot] += stake * (predictedValue ? 1 : 0); - aggregatedPredictedValuesDenom[slot] += stake; + roundSumStakesUp[slot] += stake * (predictedValue ? 1 : 0); + roundSumStakes[slot] += stake; emit PredictionSubmitted(msg.sender, slot, stake); // safe transfer stake @@ -1044,11 +1044,11 @@ contract ERC20Template3 is if(trueValues[slot] == predobj.predictedValue){ // he got it. uint256 swe = trueValues[slot] - ? aggregatedPredictedValuesNumer[slot] - : aggregatedPredictedValuesDenom[slot] - aggregatedPredictedValuesNumer[slot]; + ? roundSumStakesUp[slot] + : roundSumStakes[slot] - roundSumStakesUp[slot]; if(swe > 0) { uint256 revenue=getSubscriptionRevenueAtBlock(slot); - payout_amt = predobj.stake * (aggregatedPredictedValuesDenom[slot] + revenue) / swe; + payout_amt = predobj.stake * (roundSumStakes[slot] + revenue) / swe; } } // else payout_amt is already 0 @@ -1060,7 +1060,7 @@ contract ERC20Template3 is payout_amt, predobj.predictedValue, trueValues[slot], - aggregatedPredictedValuesNumer[slot] * 1e18 / aggregatedPredictedValuesDenom[slot], + roundSumStakesUp[slot] * 1e18 / roundSumStakes[slot], epochStatus[slot] ); if(payout_amt>0) @@ -1071,7 +1071,7 @@ contract ERC20Template3 is function redeemUnusedSlotRevenue(uint256 blocknum) external onlyERC20Deployer { require(block.number > blocknum); uint256 slot = railBlocknumToSlot(blocknum); - require(aggregatedPredictedValuesDenom[slot] == 0); + require(roundSumStakes[slot] == 0); require(feeCollector != address(0), "Cannot send fees to address 0"); IERC20(stakeToken).safeTransfer( feeCollector, @@ -1122,17 +1122,17 @@ contract ERC20Template3 is epochStatus[slot] = Status.Paying; } // edge case where all stakers are submiting a value, but they are all wrong - if (aggregatedPredictedValuesDenom[slot]>0 && ( - (trueValue && aggregatedPredictedValuesNumer[slot]==0) + if (roundSumStakes[slot]>0 && ( + (trueValue && roundSumStakesUp[slot]==0) || - (!trueValue && aggregatedPredictedValuesNumer[slot]==aggregatedPredictedValuesDenom[slot]) + (!trueValue && roundSumStakesUp[slot]==roundSumStakes[slot]) ) ){ // everyone gets slashed require(feeCollector != address(0), "Cannot send slashed stakes to address 0"); IERC20(stakeToken).safeTransfer( feeCollector, - aggregatedPredictedValuesDenom[slot] + roundSumStakes[slot] ); } emit TruevalSubmitted(slot, trueValue,floatValue,epochStatus[slot]); From 97eb0d7a3329df390ed608aef7dc1327559b49d3 Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Fri, 19 May 2023 05:19:03 -0700 Subject: [PATCH 117/120] remove comment --- contracts/templates/ERC20Template3.sol | 1 - 1 file changed, 1 deletion(-) diff --git a/contracts/templates/ERC20Template3.sol b/contracts/templates/ERC20Template3.sol index 4cdf78cc2..dd8d0eb1a 100644 --- a/contracts/templates/ERC20Template3.sol +++ b/contracts/templates/ERC20Template3.sol @@ -1109,7 +1109,6 @@ contract ERC20Template3 is uint256 floatValue, bool cancelRound ) external onlyERC20Deployer { - // TODO, is onlyERC20Deployer the right modifier? require(blocknum < block.number, "too early to submit"); uint256 slot = railBlocknumToSlot(blocknum); require(epochStatus[slot]==Status.Pending, "already settled"); From e3a86605c3b1a7813f2d92ed8a7401cb5818d4be Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Fri, 19 May 2023 05:42:29 -0700 Subject: [PATCH 118/120] fixes --- contracts/templates/ERC20Template3.sol | 30 ++++++++++++-------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/contracts/templates/ERC20Template3.sol b/contracts/templates/ERC20Template3.sol index dd8d0eb1a..17bfe09a5 100644 --- a/contracts/templates/ERC20Template3.sol +++ b/contracts/templates/ERC20Template3.sol @@ -111,7 +111,6 @@ contract ERC20Template3 is mapping(uint256 => uint256) private roundSumStakesUp; mapping(uint256 => uint256) private roundSumStakes; mapping(uint256 => bool) public trueValues; // true values submited by owner - mapping(uint256 => uint256) public floatValues; // real values submited by owner mapping(uint256 => Status) public epochStatus; // status of each epoch mapping(uint256 => uint256) private subscriptionRevenueAtBlock; //income registred mapping(address => Subscription) public subscriptions; // valid subscription per user @@ -1112,27 +1111,26 @@ contract ERC20Template3 is require(blocknum < block.number, "too early to submit"); uint256 slot = railBlocknumToSlot(blocknum); require(epochStatus[slot]==Status.Pending, "already settled"); - floatValues[slot] = floatValue; if (cancelRound || (block.number > slot + trueValSubmitTimeoutBlock && epochStatus[slot]==Status.Pending)){ epochStatus[slot]=Status.Canceled; } else{ trueValues[slot] = trueValue; epochStatus[slot] = Status.Paying; - } - // edge case where all stakers are submiting a value, but they are all wrong - if (roundSumStakes[slot]>0 && ( - (trueValue && roundSumStakesUp[slot]==0) - || - (!trueValue && roundSumStakesUp[slot]==roundSumStakes[slot]) - ) - ){ - // everyone gets slashed - require(feeCollector != address(0), "Cannot send slashed stakes to address 0"); - IERC20(stakeToken).safeTransfer( - feeCollector, - roundSumStakes[slot] - ); + // edge case where all stakers are submiting a value, but they are all wrong + if (roundSumStakes[slot]>0 && ( + (trueValue && roundSumStakesUp[slot]==0) + || + (!trueValue && roundSumStakesUp[slot]==roundSumStakes[slot]) + ) + ){ + // everyone gets slashed + require(feeCollector != address(0), "Cannot send slashed stakes to address 0"); + IERC20(stakeToken).safeTransfer( + feeCollector, + roundSumStakes[slot] + ); + } } emit TruevalSubmitted(slot, trueValue,floatValue,epochStatus[slot]); } From fbd75ace494e8bbbd67f187e5f3c2573e4a365db Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Mon, 3 Jul 2023 04:41:58 -0700 Subject: [PATCH 119/120] add test for buyFromFreAndOrder --- test/unit/datatokens/ERC20Template3.test.js | 76 ++++++++++++++++++++- 1 file changed, 74 insertions(+), 2 deletions(-) diff --git a/test/unit/datatokens/ERC20Template3.test.js b/test/unit/datatokens/ERC20Template3.test.js index 35de393db..e13b84930 100644 --- a/test/unit/datatokens/ERC20Template3.test.js +++ b/test/unit/datatokens/ERC20Template3.test.js @@ -1026,7 +1026,7 @@ describe("ERC20Template3", () => { expect(trueValue).to.be.true; }); - it("#subscriptions - user2 must be subscribed", async () => { + it("#subscriptions - user2 must be subscribe by buying dt from fre", async () => { //MINT SOME DT20 to USER2 so he can start order await buyDTFromFixedRate(erc20Token.address,user2.address,10) assert( @@ -1148,7 +1148,79 @@ describe("ERC20Template3", () => { await erc20Token.updateSeconds(sPerBlock, sPerSubscription, trueValueSubmitTimeout); }); + it("#subscriptions - user3 must be able to subscribe by calling buyFromFreAndOrder", async () => { + const fixedRates = await erc20Token.connect(owner).getFixedRates() + fixedRateExchange = await ethers.getContractAt("FixedRateExchange", fixedRates[0].contractAddress); + fixedRateId=fixedRates[0].id + //get details + const details=await fixedRateExchange.connect(owner).getExchange(fixedRateId) + const needed=await fixedRateExchange.connect(owner).calcBaseInGivenOutDT(fixedRateId,web3.utils.toWei("1"),0); + erc20Contract = await ethers.getContractAt("MockERC20",details.baseToken) + await erc20Contract + .connect(owner) + .transfer(user2.address, needed.baseTokenAmount); + await erc20Contract.connect(user2).approve(erc20Token.address,needed.baseTokenAmount) + const consumer = user2.address; // could be different user + const serviceIndex = 1; // dummy index + const providerFeeAddress = user5.address; // marketplace fee Collector + const providerFeeAmount = 0; // fee to be collected on top, requires approval + const providerFeeToken = mockErc20.address; // token address for the feeAmount, + const consumeMarketFeeAddress = user5.address; // marketplace fee Collector + const consumeMarketFeeAmount = 0; // fee to be collected on top, requires approval + const consumeMarketFeeToken = mockErc20.address; // token address for the feeAmount, + const providerValidUntil = 0; + //sign provider data + const providerData = JSON.stringify({ "timeout": 0 }) + const message = ethers.utils.solidityKeccak256( + ["bytes", "address", "address", "uint256", "uint256"], + [ + ethers.utils.hexlify(ethers.utils.toUtf8Bytes(providerData)), + providerFeeAddress, + providerFeeToken, + providerFeeAmount, + providerValidUntil + ] + ); + const signedMessage = await signMessage(message, providerFeeAddress); + tx = await erc20Token + .connect(user2).buyFromFreAndOrder( + { + "consumer": user2.address, + "amount": web3.utils.toWei("1"), + "serviceIndex": 1, + "_providerFee": { + providerFeeAddress: providerFeeAddress, + providerFeeToken:providerFeeToken, + providerFeeAmount:providerFeeAmount, + v:signedMessage.v, + r:signedMessage.r, + s:signedMessage.s, + providerData:ethers.utils.hexlify(ethers.utils.toUtf8Bytes(providerData)), + validUntil:providerValidUntil + }, + "_consumeMarketFee": { + consumeMarketFeeAddress: consumeMarketFeeAddress, + consumeMarketFeeToken: consumeMarketFeeToken, + consumeMarketFeeAmount: consumeMarketFeeAmount, + } + }, + { + "exchangeContract": fixedRateExchange.address, + "exchangeId": fixedRateId, + "maxBaseTokenAmount": needed.baseTokenAmount, + "swapMarketFee":0, + "marketFeeAddress":user5.address + } + ) + const subscription = await erc20Token.subscriptions(user2.address); + // check if subscription is valid + const currentBlock = await ethers.provider.getBlockNumber(); + expect(subscription.expires).to.be.gt(currentBlock); + expect(subscription.user).to.be.eq(user2.address); + const valid = await erc20Token.isValidSubscription(user2.address); + expect(valid).to.be.true; + }); // can read getAggPredval with a valid subscription it("#getAggPredval - should return agg_predictedValue if caller has a valid subscription", async () => { //MINT SOME DT20 to USER2 so he can start order @@ -1383,7 +1455,7 @@ describe("ERC20Template3", () => { ] ); const signedMessage = await signMessage(message, providerFeeAddress); - let soonestBlockToPredict = await erc20Token.soonestBlockToPredict((await ethers.provider.getBlockNumber())+2);//because we also have startOrder + let soonestBlockToPredict = await erc20Token.soonestBlockToPredict((await ethers.provider.getBlockNumber())+4);//because we also have startOrder let revenue_at_block = await erc20Token.connect(user2).getSubscriptionRevenueAtBlock(soonestBlockToPredict) expect(revenue_at_block).to.be.eq(0); From 0f0fff3cb09991898af4b5fae7a00dfa5cc2db28 Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Mon, 3 Jul 2023 05:53:38 -0700 Subject: [PATCH 120/120] more tests --- test/unit/datatokens/ERC20Template3.test.js | 31 +++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/test/unit/datatokens/ERC20Template3.test.js b/test/unit/datatokens/ERC20Template3.test.js index e13b84930..0479ff5cc 100644 --- a/test/unit/datatokens/ERC20Template3.test.js +++ b/test/unit/datatokens/ERC20Template3.test.js @@ -1833,4 +1833,35 @@ describe("ERC20Template3", () => { expect(event.args.payout).to.be.eq(0) } }); + + it("owner can withdraw ETH sent to contract by mistake", async () => { + const balance = await provider.getBalance(owner.address); + const contractBalance = await provider.getBalance(erc20Token.address) + tx = { + to: erc20Token.address, + value: ethers.utils.parseEther('2', 'ether') + }; + const transaction = await owner.sendTransaction(tx); + // reclaim eth + await erc20Token.connect(user2).withdrawETH() + const balanceAfter = await provider.getBalance(owner.address); + const contractBalanceAfter = await provider.getBalance(erc20Token.address) + assert(balance.eq(balanceAfter), "Owner eth balance missmatch") + assert(contractBalance.eq(contractBalanceAfter), "Owner eth balance missmatch") + }); + it("isERC20Deployer works as expected", async () => { + const isDeployer = await erc20Token.connect(user2).isERC20Deployer(owner.address) + assert(isDeployer, "isERC20Deployer failed") + }); + it("getDispensers should be empty", async () => { + const dispensers = await erc20Token.connect(user2).getDispensers() + assert(dispensers.length === 0, "getDispenser should be empty") + }); + it("getters should work as expected", async () => { + assert((await erc20Token.connect(user2).name())==="ERC20DT3", 'name() failed') + assert((await erc20Token.connect(user2).symbol())==="ERC20DT3Symbol", 'symbol() failed') + assert((await erc20Token.connect(user2).decimals())===18, 'decimals() failed') + assert((await erc20Token.connect(user2).getERC721Address()===tokenERC721.address, 'getERC721Address() failed')) + }); + });