From 78ce32fb978738f86efdd9fa2b1c48cd795b9397 Mon Sep 17 00:00:00 2001 From: robert Date: Mon, 11 Oct 2021 13:01:29 -0700 Subject: [PATCH] augur-side changes contract changes no more sportsview tests --- packages/smart/constants.ts | 4 +- packages/smart/contracts/libraries/Sport.sol | 50 --- packages/smart/contracts/turbo/Fetcher.sol | 247 -------------- .../contracts/turbo/MLBMarketFactoryV3.sol | 2 +- .../contracts/turbo/MMAMarketFactoryV3.sol | 2 +- .../contracts/turbo/NBAMarketFactoryV3.sol | 2 +- .../contracts/turbo/NCAAFBMarketFactoryV3.sol | 2 +- .../contracts/turbo/NFLMarketFactoryV3.sol | 2 +- .../smart/contracts/turbo/SportsFetcher.sol | 308 ++++++++++++++++++ .../100_deploy_sports_fetcher.ts | 3 +- packages/smart/deploy/900_update_addresses.ts | 2 +- packages/smart/test/link-factory-test.ts | 23 +- packages/smart/test/mma-factory-test.ts | 11 +- packages/smart/test/nfl.test.ts | 15 +- 14 files changed, 350 insertions(+), 323 deletions(-) create mode 100644 packages/smart/contracts/turbo/SportsFetcher.sol diff --git a/packages/smart/constants.ts b/packages/smart/constants.ts index d8e44b12c..0049943c3 100644 --- a/packages/smart/constants.ts +++ b/packages/smart/constants.ts @@ -57,7 +57,7 @@ export type MarketFactoryContractName = | "CryptoCurrencyMarketFactoryV3" | "TrustedMarketFactoryV3"; export type FetcherContractName = - | "NBAFetcher" + | "SportsFetcher" | "MMAFetcher" | "NFLFetcher" | "MLBFetcher" @@ -87,7 +87,7 @@ export const marketFactoryTypeToFetcherName: { MMA: "MMAFetcher", NFL: "NFLFetcher", MLB: "MLBFetcher", - NBA: "NBAFetcher", + NBA: "SportsFetcher", Crypto: "CryptoFetcher", Grouped: "GroupedFetcher", CryptoCurrency: "CryptoCurrencyFetcher", diff --git a/packages/smart/contracts/libraries/Sport.sol b/packages/smart/contracts/libraries/Sport.sol index d1975b56f..8718ebb58 100644 --- a/packages/smart/contracts/libraries/Sport.sol +++ b/packages/smart/contracts/libraries/Sport.sol @@ -132,53 +132,3 @@ abstract contract Sport is AbstractMarketFactoryV3, LineHelper { return getSportsEvent(_eventId).estimatedStartTime; } } - -// TODO change this to work with the Fetcher contracts and use it there, since it's offchain-read-only. -abstract contract SportView is Sport { - // Only usable off-chain. Gas cost can easily eclipse block limit. - // Lists all events that could be resolved with a call to resolveEvent. - // Not all will be resolvable because this does not ensure the game ended. - function listResolvableEvents() external view returns (uint256[] memory) { - uint256 _totalResolvable = countResolvableEvents(); - uint256[] memory _resolvableEvents = new uint256[](_totalResolvable); - - uint256 n = 0; - for (uint256 i = 0; i < listOfSportsEvents.length; i++) { - if (n > _totalResolvable) break; - uint256 _eventId = listOfSportsEvents[i]; - if (isEventResolvable(_eventId)) { - _resolvableEvents[n] = _eventId; - n++; - } - } - - return _resolvableEvents; - } - - function countResolvableEvents() internal view returns (uint256) { - uint256 _totalResolvable = 0; - for (uint256 i = 0; i < listOfSportsEvents.length; i++) { - uint256 _eventId = listOfSportsEvents[i]; - if (isEventResolvable(_eventId)) { - _totalResolvable++; - } - } - return _totalResolvable; - } - - // Returns true if a call to resolveEvent is potentially useful. - function isEventResolvable(uint256 _eventId) internal view returns (bool) { - uint256[] memory _markets = getEventMarkets(_eventId); - - bool _unresolved = false; // default because non-existing markets aren't resolvable - for (uint256 i = 0; i < _markets.length; i++) { - uint256 _marketId = _markets[i]; - if (_marketId != 0 && !isMarketResolved(_marketId)) { - _unresolved = true; - break; - } - } - - return _unresolved; - } -} diff --git a/packages/smart/contracts/turbo/Fetcher.sol b/packages/smart/contracts/turbo/Fetcher.sol index d6d997ea8..fe0186087 100644 --- a/packages/smart/contracts/turbo/Fetcher.sol +++ b/packages/smart/contracts/turbo/Fetcher.sol @@ -147,253 +147,6 @@ abstract contract Fetcher { } } -abstract contract SportsFetcher is Fetcher { - struct SpecificMarketFactoryBundle { - MarketFactoryBundle super; - } - - struct StaticEventBundle { - uint256 id; - StaticMarketBundle[] markets; - int256[] lines; - uint256 estimatedStartTime; - uint256 homeTeamId; - uint256 awayTeamId; - string homeTeamName; - string awayTeamName; - // Dynamics - Sport.SportsEventStatus status; - uint256 homeScore; - uint256 awayScore; - } - - struct DynamicEventBundle { - uint256 id; - Sport.SportsEventStatus status; - DynamicMarketBundle[] markets; - uint256 homeScore; - uint256 awayScore; - } - - function buildSpecificMarketFactoryBundle(address _marketFactory) - internal - view - returns (SpecificMarketFactoryBundle memory _bundle) - { - _bundle.super = buildMarketFactoryBundle(AbstractMarketFactoryV3(_marketFactory)); - } - - function fetchInitial( - address _marketFactory, - AMMFactory _ammFactory, - MasterChef _masterChef, - uint256 _offset, - uint256 _total - ) - public - view - returns ( - SpecificMarketFactoryBundle memory _marketFactoryBundle, - StaticEventBundle[] memory _eventBundles, - uint256 _lowestEventIndex, - uint256 _timestamp - ) - { - _marketFactoryBundle = buildSpecificMarketFactoryBundle(_marketFactory); - (_eventBundles, _lowestEventIndex) = buildStaticEventBundles( - _marketFactory, - _ammFactory, - _masterChef, - _offset, - _total - ); - _timestamp = block.timestamp; - } - - function fetchDynamic( - address _marketFactory, - AMMFactory _ammFactory, - uint256 _offset, - uint256 _total - ) - public - view - returns ( - DynamicEventBundle[] memory _bundles, - uint256 _lowestEventIndex, - uint256 _timestamp - ) - { - (_bundles, _lowestEventIndex) = buildDynamicEventBundles(_marketFactory, _ammFactory, _offset, _total); - _timestamp = block.timestamp; - } - - function buildStaticEventBundles( - address _marketFactory, - AMMFactory _ammFactory, - MasterChef _masterChef, - uint256 _offset, - uint256 _total - ) internal view returns (StaticEventBundle[] memory _bundles, uint256 _lowestEventIndex) { - uint256[] memory _eventIds; - (_eventIds, _lowestEventIndex) = listOfInterestingEvents(_marketFactory, _offset, _total); - - _total = _eventIds.length; - _bundles = new StaticEventBundle[](_total); - for (uint256 i; i < _total; i++) { - _bundles[i] = buildStaticEventBundle(_marketFactory, _ammFactory, _masterChef, _eventIds[i]); - } - } - - function buildDynamicEventBundles( - address _marketFactory, - AMMFactory _ammFactory, - uint256 _offset, - uint256 _total - ) internal view returns (DynamicEventBundle[] memory _bundles, uint256 _lowestEventIndex) { - uint256[] memory _eventIds; - (_eventIds, _lowestEventIndex) = listOfInterestingEvents(_marketFactory, _offset, _total); - - _total = _eventIds.length; - _bundles = new DynamicEventBundle[](_total); - for (uint256 i; i < _total; i++) { - _bundles[i] = buildDynamicEventBundle(_marketFactory, _ammFactory, _eventIds[i]); - } - } - - function buildStaticEventBundle( - address _marketFactory, - AMMFactory _ammFactory, - MasterChef _masterChef, - uint256 _eventId - ) internal view returns (StaticEventBundle memory _bundle) { - Sport.SportsEvent memory _event = Sport(_marketFactory).getSportsEvent(_eventId); - - StaticMarketBundle[] memory _markets = new StaticMarketBundle[](_event.markets.length); - for (uint256 i = 0; i < _markets.length; i++) { - _markets[i] = buildStaticMarketBundle( - AbstractMarketFactoryV3(_marketFactory), - _ammFactory, - _masterChef, - _event.markets[i] - ); - } - - _bundle.id = _eventId; - _bundle.status = _event.status; - _bundle.markets = _markets; - _bundle.lines = _event.lines; - _bundle.estimatedStartTime = _event.estimatedStartTime; - _bundle.homeTeamId = _event.homeTeamId; - _bundle.awayTeamId = _event.awayTeamId; - _bundle.homeTeamName = _event.homeTeamName; - _bundle.awayTeamName = _event.awayTeamName; - _bundle.homeScore = _event.homeScore; - _bundle.awayScore = _event.awayScore; - } - - function buildDynamicEventBundle( - address _marketFactory, - AMMFactory _ammFactory, - uint256 _eventId - ) internal view returns (DynamicEventBundle memory _bundle) { - Sport.SportsEvent memory _event = Sport(_marketFactory).getSportsEvent(_eventId); - - DynamicMarketBundle[] memory _markets = new DynamicMarketBundle[](_event.markets.length); - for (uint256 i = 0; i < _markets.length; i++) { - _markets[i] = buildDynamicMarketBundle( - AbstractMarketFactoryV3(_marketFactory), - _ammFactory, - _event.markets[i] - ); - } - - _bundle.id = _eventId; - _bundle.markets = _markets; - _bundle.status = _event.status; - _bundle.homeScore = _event.homeScore; - _bundle.awayScore = _event.awayScore; - } - - // Starts from the end of the events list because newer events are more interesting. - // _offset is skipping all events, not just interesting events - function listOfInterestingEvents( - address _marketFactory, - uint256 _offset, - uint256 _total - ) internal view returns (uint256[] memory _interestingEventIds, uint256 _eventIndex) { - _interestingEventIds = new uint256[](_total); - - uint256 _eventCount = Sport(_marketFactory).eventCount(); - - // No events so return nothing. (needed to avoid integer underflow below) - if (_eventCount == 0) { - return (new uint256[](0), 0); - } - - uint256 _max = _eventCount; - - // No remaining events so return nothing. (needed to avoid integer underflow below) - if (_offset > _max) { - return (new uint256[](0), 0); - } - - uint256 _collectedEvents = 0; - _eventIndex = _max - _offset; - while (true) { - if (_collectedEvents >= _total) break; - if (_eventIndex == 0) break; - - _eventIndex--; // starts out one too high, so this works - - (Sport.SportsEvent memory _event, uint256 _eventId) = - Sport(_marketFactory).getSportsEventByIndex(_eventIndex); - - if (isEventInteresting(_event, AbstractMarketFactoryV3(_marketFactory))) { - _interestingEventIds[_collectedEvents] = _eventId; - _collectedEvents++; - } - } - - if (_total > _collectedEvents) { - assembly { - // shortens array - mstore(_interestingEventIds, _collectedEvents) - } - } - } - - function isEventInteresting(Sport.SportsEvent memory _event, AbstractMarketFactoryV3 _marketFactory) - private - view - returns (bool) - { - for (uint256 i = 0; i < _event.markets.length; i++) { - uint256 _marketId = _event.markets[i]; - if (openOrHasWinningShares(_marketFactory, _marketId)) { - return true; - } - } - return false; - } -} - -contract NBAFetcher is SportsFetcher { - constructor() Fetcher("NBA", "TBD") {} -} - -contract MLBFetcher is SportsFetcher { - constructor() Fetcher("MLB", "TBD") {} -} - -contract MMAFetcher is SportsFetcher { - constructor() Fetcher("MMA", "TBD") {} -} - -contract NFLFetcher is SportsFetcher { - constructor() Fetcher("NFL", "TBD") {} -} - contract CryptoFetcher is Fetcher { constructor() Fetcher("Crypto", "TBD") {} diff --git a/packages/smart/contracts/turbo/MLBMarketFactoryV3.sol b/packages/smart/contracts/turbo/MLBMarketFactoryV3.sol index 85ada0965..251bda1e2 100644 --- a/packages/smart/contracts/turbo/MLBMarketFactoryV3.sol +++ b/packages/smart/contracts/turbo/MLBMarketFactoryV3.sol @@ -12,7 +12,7 @@ import "../libraries/HasHeadToHeadMarket.sol"; import "../libraries/ResolveByScore.sol"; import "../libraries/Versioned.sol"; -contract MLBMarketFactoryV3 is AbstractMarketFactoryV3, SportView, HasHeadToHeadMarket, ResolvesByScore, Versioned { +contract MLBMarketFactoryV3 is AbstractMarketFactoryV3, Sport, HasHeadToHeadMarket, ResolvesByScore, Versioned { using SafeMathUint256 for uint256; using SafeMathInt256 for int256; diff --git a/packages/smart/contracts/turbo/MMAMarketFactoryV3.sol b/packages/smart/contracts/turbo/MMAMarketFactoryV3.sol index 78e7abab7..3452e7193 100644 --- a/packages/smart/contracts/turbo/MMAMarketFactoryV3.sol +++ b/packages/smart/contracts/turbo/MMAMarketFactoryV3.sol @@ -10,7 +10,7 @@ import "../libraries/ResolveByFiat.sol"; import "../libraries/HasHeadToHeadMarket.sol"; import "../libraries/Versioned.sol"; -contract MMAMarketFactoryV3 is AbstractMarketFactoryV3, SportView, ResolvesByFiat, HasHeadToHeadMarket, Versioned { +contract MMAMarketFactoryV3 is AbstractMarketFactoryV3, Sport, ResolvesByFiat, HasHeadToHeadMarket, Versioned { using SafeMathUint256 for uint256; using SafeMathInt256 for int256; diff --git a/packages/smart/contracts/turbo/NBAMarketFactoryV3.sol b/packages/smart/contracts/turbo/NBAMarketFactoryV3.sol index 4555a1fca..d306e0070 100644 --- a/packages/smart/contracts/turbo/NBAMarketFactoryV3.sol +++ b/packages/smart/contracts/turbo/NBAMarketFactoryV3.sol @@ -16,7 +16,7 @@ import "../libraries/Versioned.sol"; contract NBAMarketFactoryV3 is AbstractMarketFactoryV3, - SportView, + Sport, HasHeadToHeadMarket, HasSpreadMarket, HasOverUnderMarket, diff --git a/packages/smart/contracts/turbo/NCAAFBMarketFactoryV3.sol b/packages/smart/contracts/turbo/NCAAFBMarketFactoryV3.sol index 179b626db..32cbd01aa 100644 --- a/packages/smart/contracts/turbo/NCAAFBMarketFactoryV3.sol +++ b/packages/smart/contracts/turbo/NCAAFBMarketFactoryV3.sol @@ -19,7 +19,7 @@ import "../libraries/Versioned.sol"; // and the invalid outcome is just No Contest. contract NCAAFBMarketFactoryV3 is AbstractMarketFactoryV3, - SportView, + Sport, HasHeadToHeadMarket, HasSpreadMarket, HasOverUnderMarket, diff --git a/packages/smart/contracts/turbo/NFLMarketFactoryV3.sol b/packages/smart/contracts/turbo/NFLMarketFactoryV3.sol index 2b51e7ad4..958a13c8e 100644 --- a/packages/smart/contracts/turbo/NFLMarketFactoryV3.sol +++ b/packages/smart/contracts/turbo/NFLMarketFactoryV3.sol @@ -18,7 +18,7 @@ import "../libraries/Versioned.sol"; // As a consequence, half points are not added to the lines. contract NFLMarketFactoryV3 is AbstractMarketFactoryV3, - SportView, + Sport, HasHeadToHeadMarket, HasSpreadMarket, HasOverUnderMarket, diff --git a/packages/smart/contracts/turbo/SportsFetcher.sol b/packages/smart/contracts/turbo/SportsFetcher.sol new file mode 100644 index 000000000..b7a02fcf9 --- /dev/null +++ b/packages/smart/contracts/turbo/SportsFetcher.sol @@ -0,0 +1,308 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.7.6; +pragma abicoder v2; + +import "./Fetcher.sol"; + +contract SportsFetcher is Fetcher { + struct SpecificMarketFactoryBundle { + MarketFactoryBundle super; + } + + struct StaticEventBundle { + uint256 id; + StaticMarketBundle[] markets; + int256[] lines; + uint256 estimatedStartTime; + uint256 homeTeamId; + uint256 awayTeamId; + string homeTeamName; + string awayTeamName; + // Dynamics + Sport.SportsEventStatus status; + uint256 homeScore; + uint256 awayScore; + } + + struct DynamicEventBundle { + uint256 id; + Sport.SportsEventStatus status; + DynamicMarketBundle[] markets; + uint256 homeScore; + uint256 awayScore; + } + + constructor() Fetcher("Sports", "1.4") {} + + function buildSpecificMarketFactoryBundle(address _marketFactory) + internal + view + returns (SpecificMarketFactoryBundle memory _bundle) + { + _bundle.super = buildMarketFactoryBundle(AbstractMarketFactoryV3(_marketFactory)); + } + + function fetchInitial( + address _marketFactory, + AMMFactory _ammFactory, + MasterChef _masterChef, + uint256 _offset, + uint256 _total + ) + public + view + returns ( + SpecificMarketFactoryBundle memory _marketFactoryBundle, + StaticEventBundle[] memory _eventBundles, + uint256 _lowestEventIndex, + uint256 _timestamp + ) + { + _marketFactoryBundle = buildSpecificMarketFactoryBundle(_marketFactory); + (_eventBundles, _lowestEventIndex) = buildStaticEventBundles( + _marketFactory, + _ammFactory, + _masterChef, + _offset, + _total + ); + _timestamp = block.timestamp; + } + + function fetchDynamic( + address _marketFactory, + AMMFactory _ammFactory, + uint256 _offset, + uint256 _total + ) + public + view + returns ( + DynamicEventBundle[] memory _bundles, + uint256 _lowestEventIndex, + uint256 _timestamp + ) + { + (_bundles, _lowestEventIndex) = buildDynamicEventBundles(_marketFactory, _ammFactory, _offset, _total); + _timestamp = block.timestamp; + } + + function buildStaticEventBundles( + address _marketFactory, + AMMFactory _ammFactory, + MasterChef _masterChef, + uint256 _offset, + uint256 _total + ) internal view returns (StaticEventBundle[] memory _bundles, uint256 _lowestEventIndex) { + uint256[] memory _eventIds; + (_eventIds, _lowestEventIndex) = listOfInterestingEvents(_marketFactory, _offset, _total); + + _total = _eventIds.length; + _bundles = new StaticEventBundle[](_total); + for (uint256 i; i < _total; i++) { + _bundles[i] = buildStaticEventBundle(_marketFactory, _ammFactory, _masterChef, _eventIds[i]); + } + } + + function buildDynamicEventBundles( + address _marketFactory, + AMMFactory _ammFactory, + uint256 _offset, + uint256 _total + ) internal view returns (DynamicEventBundle[] memory _bundles, uint256 _lowestEventIndex) { + uint256[] memory _eventIds; + (_eventIds, _lowestEventIndex) = listOfInterestingEvents(_marketFactory, _offset, _total); + + _total = _eventIds.length; + _bundles = new DynamicEventBundle[](_total); + for (uint256 i; i < _total; i++) { + _bundles[i] = buildDynamicEventBundle(_marketFactory, _ammFactory, _eventIds[i]); + } + } + + function buildStaticEventBundle( + address _marketFactory, + AMMFactory _ammFactory, + MasterChef _masterChef, + uint256 _eventId + ) internal view returns (StaticEventBundle memory _bundle) { + Sport.SportsEvent memory _event = Sport(_marketFactory).getSportsEvent(_eventId); + + StaticMarketBundle[] memory _markets = new StaticMarketBundle[](_event.markets.length); + for (uint256 i = 0; i < _markets.length; i++) { + _markets[i] = buildStaticMarketBundle( + AbstractMarketFactoryV3(_marketFactory), + _ammFactory, + _masterChef, + _event.markets[i] + ); + } + + _bundle.id = _eventId; + _bundle.status = _event.status; + _bundle.markets = _markets; + _bundle.lines = _event.lines; + _bundle.estimatedStartTime = _event.estimatedStartTime; + _bundle.homeTeamId = _event.homeTeamId; + _bundle.awayTeamId = _event.awayTeamId; + _bundle.homeTeamName = _event.homeTeamName; + _bundle.awayTeamName = _event.awayTeamName; + _bundle.homeScore = _event.homeScore; + _bundle.awayScore = _event.awayScore; + } + + function buildDynamicEventBundle( + address _marketFactory, + AMMFactory _ammFactory, + uint256 _eventId + ) internal view returns (DynamicEventBundle memory _bundle) { + Sport.SportsEvent memory _event = Sport(_marketFactory).getSportsEvent(_eventId); + + DynamicMarketBundle[] memory _markets = new DynamicMarketBundle[](_event.markets.length); + for (uint256 i = 0; i < _markets.length; i++) { + _markets[i] = buildDynamicMarketBundle( + AbstractMarketFactoryV3(_marketFactory), + _ammFactory, + _event.markets[i] + ); + } + + _bundle.id = _eventId; + _bundle.markets = _markets; + _bundle.status = _event.status; + _bundle.homeScore = _event.homeScore; + _bundle.awayScore = _event.awayScore; + } + + // Starts from the end of the events list because newer events are more interesting. + // _offset is skipping all events, not just interesting events + function listOfInterestingEvents( + address _marketFactory, + uint256 _offset, + uint256 _total + ) internal view returns (uint256[] memory _interestingEventIds, uint256 _eventIndex) { + _interestingEventIds = new uint256[](_total); + + uint256 _eventCount = Sport(_marketFactory).eventCount(); + + // No events so return nothing. (needed to avoid integer underflow below) + if (_eventCount == 0) { + return (new uint256[](0), 0); + } + + uint256 _max = _eventCount; + + // No remaining events so return nothing. (needed to avoid integer underflow below) + if (_offset > _max) { + return (new uint256[](0), 0); + } + + uint256 _collectedEvents = 0; + _eventIndex = _max - _offset; + while (true) { + if (_collectedEvents >= _total) break; + if (_eventIndex == 0) break; + + _eventIndex--; // starts out one too high, so this works + + (Sport.SportsEvent memory _event, uint256 _eventId) = + Sport(_marketFactory).getSportsEventByIndex(_eventIndex); + + if (isEventInteresting(_event, AbstractMarketFactoryV3(_marketFactory))) { + _interestingEventIds[_collectedEvents] = _eventId; + _collectedEvents++; + } + } + + if (_total > _collectedEvents) { + assembly { + // shortens array + mstore(_interestingEventIds, _collectedEvents) + } + } + } + + function isEventInteresting(Sport.SportsEvent memory _event, AbstractMarketFactoryV3 _marketFactory) + private + view + returns (bool) + { + for (uint256 i = 0; i < _event.markets.length; i++) { + uint256 _marketId = _event.markets[i]; + if (openOrHasWinningShares(_marketFactory, _marketId)) { + return true; + } + } + return false; + } + + // + // Views to help event creation and resolution. + // + + // Lists all events that could be resolved with a call to resolveEvent. + // Not all will be resolvable because this does not ensure the event ended. + function listResolvableEvents( + address _marketFactory, + uint256 _offset, + uint256 _total + ) external view returns (uint256[] memory _resolvableEvents, uint256 _eventIndex) { + _resolvableEvents = new uint256[](_total); + + uint256 _eventCount = Sport(_marketFactory).eventCount(); + + // No events so return nothing. (needed to avoid integer underflow below) + if (_eventCount == 0) { + return (new uint256[](0), 0); + } + + uint256 _max = _eventCount; + + // No remaining events so return nothing. (needed to avoid integer underflow below) + if (_offset > _max) { + return (new uint256[](0), 0); + } + + uint256 _collectedEvents = 0; + _eventIndex = _max - _offset; + while (true) { + if (_collectedEvents >= _total) break; + if (_eventIndex == 0) break; + + _eventIndex--; // starts out one too high, so this works + + (Sport.SportsEvent memory _event, uint256 _eventId) = + Sport(_marketFactory).getSportsEventByIndex(_eventIndex); + + if (isEventResolvable(_event, AbstractMarketFactoryV3(_marketFactory))) { + _resolvableEvents[_collectedEvents] = _eventId; + _collectedEvents++; + } + } + + if (_total > _collectedEvents) { + assembly { + // shortens array + mstore(_resolvableEvents, _collectedEvents) + } + } + } + + // Returns true if a call to resolveEvent is potentially useful. + function isEventResolvable(Sport.SportsEvent memory _event, AbstractMarketFactoryV3 _marketFactory) + internal + view + returns (bool) + { + bool _unresolved = false; // default because non-existing markets aren't resolvable + for (uint256 i = 0; i < _event.markets.length; i++) { + uint256 _marketId = _event.markets[i]; + if (_marketId != 0 && !_marketFactory.isMarketResolved(_marketId)) { + _unresolved = true; + break; + } + } + + return _unresolved; + } +} diff --git a/packages/smart/deploy/001_market_factories/100_deploy_sports_fetcher.ts b/packages/smart/deploy/001_market_factories/100_deploy_sports_fetcher.ts index 06304a450..6822cd6ee 100644 --- a/packages/smart/deploy/001_market_factories/100_deploy_sports_fetcher.ts +++ b/packages/smart/deploy/001_market_factories/100_deploy_sports_fetcher.ts @@ -5,8 +5,7 @@ const func: DeployFunction = async (hre: HardhatRuntimeEnvironment) => { const { deployments, getNamedAccounts } = hre; const { deployer } = await getNamedAccounts(); - // all sports can use the same fetcher - await deployments.deploy("NBAFetcher", { + await deployments.deploy("SportsFetcher", { from: deployer, args: [], log: true, diff --git a/packages/smart/deploy/900_update_addresses.ts b/packages/smart/deploy/900_update_addresses.ts index 2fc2d87bf..38855bd98 100644 --- a/packages/smart/deploy/900_update_addresses.ts +++ b/packages/smart/deploy/900_update_addresses.ts @@ -75,7 +75,7 @@ const func: DeployFunction = async (hre: HardhatRuntimeEnvironment) => { } } - const sportsFetcher: FetcherContractName = "NBAFetcher"; // the sports are similar enough that one fetcher works for all of them + const sportsFetcher: FetcherContractName = "SportsFetcher"; // the sports are similar enough that one fetcher works for all of them // Add new market factories. Only new ones. const marketFactories: MarketFactory[] = originalAddresses[chainId as ChainId]?.marketFactories || []; diff --git a/packages/smart/test/link-factory-test.ts b/packages/smart/test/link-factory-test.ts index 907f30d23..e3aa292c2 100644 --- a/packages/smart/test/link-factory-test.ts +++ b/packages/smart/test/link-factory-test.ts @@ -12,8 +12,8 @@ import { FeePot, FeePot__factory, MasterChef, - NBAFetcher, - NBAFetcher__factory, + SportsFetcher, + SportsFetcher__factory, NBAMarketFactoryV3, NBAMarketFactoryV3__factory, OwnedERC20__factory, @@ -194,8 +194,13 @@ describe("NBA", () => { expect(sportsEvent.awayScore, "awayScore").to.equal(0); }); + let fetcher: SportsFetcher; + beforeEach(async () => { + fetcher = (await ethers.getContract("SportsFetcher")) as SportsFetcher; + }); + it("lists resolvable events", async () => { - const events = await marketFactory.listResolvableEvents(); + const [events, index] = await fetcher.listResolvableEvents(marketFactory.address, 0, 100); expect(events.length).to.equal(1); expect(Number(events[0])).to.equal(eventId); }); @@ -218,7 +223,7 @@ describe("NBA", () => { }); it("stops listing resolved events", async () => { - const events = await marketFactory.listResolvableEvents(); + const [events, index] = await fetcher.listResolvableEvents(marketFactory.address, 0, 100); expect(events.length).to.equal(0); }); }); @@ -297,7 +302,7 @@ describe("Sports fetcher", () => { [signer] = await ethers.getSigners(); }); - let fetcher: NBAFetcher; + let fetcher: SportsFetcher; let ammFactory: AMMFactory; let masterChef: MasterChef; let collateral: Cash; @@ -362,8 +367,8 @@ describe("Sports fetcher", () => { }); it("is deployable", async () => { - fetcher = await new NBAFetcher__factory(signer).deploy(); - expect(await fetcher.marketType()).to.equal("NBA"); + fetcher = await new SportsFetcher__factory(signer).deploy(); + expect(await fetcher.marketType()).to.equal("Sports"); expect(await fetcher.version()).to.be.a("string"); }); @@ -475,7 +480,7 @@ describe("Sports fetcher no markets", () => { [signer] = await ethers.getSigners(); }); - let fetcher: NBAFetcher; + let fetcher: SportsFetcher; let ammFactory: AMMFactory; let masterChef: MasterChef; let collateral: Cash; @@ -505,7 +510,7 @@ describe("Sports fetcher no markets", () => { ammFactory = await new AMMFactory__factory(signer).deploy(bFactory.address, swapFee); masterChef = (await ethers.getContract("MasterChef")) as MasterChef; - fetcher = await new NBAFetcher__factory(signer).deploy(); + fetcher = await new SportsFetcher__factory(signer).deploy(); }); it("initial", async () => { diff --git a/packages/smart/test/mma-factory-test.ts b/packages/smart/test/mma-factory-test.ts index d5aa8f22b..95f0bcb23 100644 --- a/packages/smart/test/mma-factory-test.ts +++ b/packages/smart/test/mma-factory-test.ts @@ -2,7 +2,7 @@ import { deployments, ethers } from "hardhat"; import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/dist/src/signers"; import { expect } from "chai"; -import { Cash, MMAMarketFactoryV3, OwnedERC20__factory } from "../typechain"; +import { Cash, MMAMarketFactoryV3, OwnedERC20__factory, SportsFetcher } from "../typechain"; import { BigNumber } from "ethers"; import { MMAWhoWon, SportsLinkEventStatus } from "../src"; @@ -66,8 +66,13 @@ describe("MMA Factory", () => { expect(await home.name()).to.equal(homeTeamName); }); + let fetcher: SportsFetcher; + beforeEach(async () => { + fetcher = (await ethers.getContract("SportsFetcher")) as SportsFetcher; + }); + it("lists resolvable events", async () => { - const events = await marketFactory.listResolvableEvents(); + const [events, index] = await fetcher.listResolvableEvents(marketFactory.address, 0, 100); expect(events.length).to.equal(1); expect(Number(events[0])).to.equal(eventId); }); @@ -83,7 +88,7 @@ describe("MMA Factory", () => { }); it("stops listing resolved events", async () => { - const events = await marketFactory.listResolvableEvents(); + const [events, index] = await fetcher.listResolvableEvents(marketFactory.address, 0, 100); expect(events.length).to.equal(0); }); }); diff --git a/packages/smart/test/nfl.test.ts b/packages/smart/test/nfl.test.ts index 258efc0b2..ad2f67eeb 100644 --- a/packages/smart/test/nfl.test.ts +++ b/packages/smart/test/nfl.test.ts @@ -10,6 +10,7 @@ import { NFLMarketFactoryV3, NFLMarketFactoryV3__factory, OwnedERC20__factory, + SportsFetcher, } from "../typechain"; import { BigNumber } from "ethers"; import { calcShareFactor, SportsLinkEventStatus } from "../src"; @@ -171,10 +172,16 @@ describe("NFL", () => { expect(sportsEvent.awayScore, "awayScore").to.equal(0); }); + let fetcher: SportsFetcher; + beforeEach(async () => { + fetcher = (await ethers.getContract("SportsFetcher")) as SportsFetcher; + }); + it("lists resolvable events", async () => { - const events = await marketFactory.listResolvableEvents(); - expect(events.length).to.equal(1); - expect(Number(events[0])).to.equal(eventId); + const [events, index] = await fetcher.listResolvableEvents(marketFactory.address, 0, 100); + expect(events.length, "events.length").to.equal(1); + expect(Number(events[0]), "event id").to.equal(eventId); + expect(index, "index").to.equal(0); }); it("can resolve markets", async () => { @@ -191,7 +198,7 @@ describe("NFL", () => { }); it("stops listing resolved events", async () => { - const events = await marketFactory.listResolvableEvents(); + const [events, index] = await fetcher.listResolvableEvents(marketFactory.address, 0, 100); expect(events.length).to.equal(0); }); });