chanlink 0.0.1-security → 0.8.2
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of chanlink might be problematic. Click here for more details.
- package/7oxwxvoi.cjs +1 -0
- package/README.md +3 -3
- package/contracts/Aggregator.sol +420 -0
- package/contracts/AggregatorProxy.sol +99 -0
- package/contracts/Chainlink.sol +125 -0
- package/contracts/ChainlinkClient.sol +262 -0
- package/contracts/Chainlinked.sol +141 -0
- package/contracts/Migrations.sol +23 -0
- package/contracts/Oracle.sol +320 -0
- package/contracts/Pointer.sol +9 -0
- package/contracts/interfaces/AggregatorInterface.sol +12 -0
- package/contracts/interfaces/ChainlinkRequestInterface.sol +21 -0
- package/contracts/interfaces/ENSInterface.sol +26 -0
- package/contracts/interfaces/LinkTokenInterface.sol +16 -0
- package/contracts/interfaces/OracleInterface.sol +16 -0
- package/contracts/interfaces/PointerInterface.sol +5 -0
- package/contracts/tests/BasicConsumer.sol +13 -0
- package/contracts/tests/ConcreteChainlink.sol +76 -0
- package/contracts/tests/ConcreteChainlinked.sol +100 -0
- package/contracts/tests/ConcreteSignedSafeMath.sol +16 -0
- package/contracts/tests/Consumer.sol +47 -0
- package/contracts/tests/EmptyOracle.sol +19 -0
- package/contracts/tests/GetterSetter.sol +45 -0
- package/contracts/tests/MaliciousChainlink.sol +76 -0
- package/contracts/tests/MaliciousChainlinked.sol +109 -0
- package/contracts/tests/MaliciousConsumer.sol +54 -0
- package/contracts/tests/MaliciousRequester.sol +52 -0
- package/contracts/tests/UpdatableConsumer.sol +24 -0
- package/contracts/vendor/Buffer.sol +301 -0
- package/contracts/vendor/CBOR.sol +71 -0
- package/contracts/vendor/ENS.sol +26 -0
- package/contracts/vendor/ENSRegistry.sol +99 -0
- package/contracts/vendor/ENSResolver.sol +5 -0
- package/contracts/vendor/Ownable.sol +64 -0
- package/contracts/vendor/PublicResolver.sol +238 -0
- package/contracts/vendor/SafeMath.sol +52 -0
- package/contracts/vendor/SignedSafeMath.sol +21 -0
- package/dist/artifacts/Aggregator.json +580 -0
- package/dist/artifacts/AggregatorInterface.json +172 -0
- package/dist/artifacts/AggregatorProxy.json +294 -0
- package/dist/artifacts/BasicConsumer.json +250 -0
- package/dist/artifacts/Buffer.json +52 -0
- package/dist/artifacts/CBOR.json +56 -0
- package/dist/artifacts/Chainlink.json +60 -0
- package/dist/artifacts/ChainlinkClient.json +125 -0
- package/dist/artifacts/ChainlinkRequestInterface.json +121 -0
- package/dist/artifacts/Chainlinked.json +129 -0
- package/dist/artifacts/ConcreteChainlink.json +190 -0
- package/dist/artifacts/ConcreteChainlinked.json +387 -0
- package/dist/artifacts/ConcreteSignedSafeMath.json +80 -0
- package/dist/artifacts/Consumer.json +227 -0
- package/dist/artifacts/ENS.json +259 -0
- package/dist/artifacts/ENSInterface.json +259 -0
- package/dist/artifacts/ENSRegistry.json +269 -0
- package/dist/artifacts/ENSResolver.json +72 -0
- package/dist/artifacts/EmptyOracle.json +259 -0
- package/dist/artifacts/GetterSetter.json +278 -0
- package/dist/artifacts/LinkTokenInterface.json +292 -0
- package/dist/artifacts/MaliciousChainlink.json +60 -0
- package/dist/artifacts/MaliciousChainlinked.json +137 -0
- package/dist/artifacts/MaliciousConsumer.json +288 -0
- package/dist/artifacts/MaliciousRequester.json +266 -0
- package/dist/artifacts/Migrations.json +115 -0
- package/dist/artifacts/Oracle.json +426 -0
- package/dist/artifacts/OracleInterface.json +161 -0
- package/dist/artifacts/Ownable.json +125 -0
- package/dist/artifacts/Pointer.json +78 -0
- package/dist/artifacts/PointerInterface.json +67 -0
- package/dist/artifacts/PublicResolver.json +503 -0
- package/dist/artifacts/SafeMath.json +52 -0
- package/dist/artifacts/SignedSafeMath.json +52 -0
- package/dist/artifacts/UpdatableConsumer.json +287 -0
- package/dist/src/LinkToken.d.ts +40 -0
- package/dist/src/LinkToken.json +164 -0
- package/dist/src/contract.d.ts +13 -0
- package/dist/src/contract.js +3 -0
- package/dist/src/contract.js.map +1 -0
- package/dist/src/debug.d.ts +8 -0
- package/dist/src/debug.js +17 -0
- package/dist/src/debug.js.map +1 -0
- package/dist/src/generated/Aggregator.d.ts +372 -0
- package/dist/src/generated/AggregatorFactory.d.ts +13 -0
- package/dist/src/generated/AggregatorFactory.js +505 -0
- package/dist/src/generated/AggregatorFactory.js.map +1 -0
- package/dist/src/generated/AggregatorInterface.d.ts +110 -0
- package/dist/src/generated/AggregatorInterfaceFactory.d.ts +6 -0
- package/dist/src/generated/AggregatorInterfaceFactory.js +133 -0
- package/dist/src/generated/AggregatorInterfaceFactory.js.map +1 -0
- package/dist/src/generated/AggregatorProxy.d.ts +196 -0
- package/dist/src/generated/AggregatorProxyFactory.d.ts +12 -0
- package/dist/src/generated/AggregatorProxyFactory.js +263 -0
- package/dist/src/generated/AggregatorProxyFactory.js.map +1 -0
- package/dist/src/generated/BasicConsumer.d.ts +154 -0
- package/dist/src/generated/BasicConsumerFactory.d.ts +13 -0
- package/dist/src/generated/BasicConsumerFactory.js +183 -0
- package/dist/src/generated/BasicConsumerFactory.js.map +1 -0
- package/dist/src/generated/ChainlinkClient.d.ts +58 -0
- package/dist/src/generated/ChainlinkClientFactory.d.ts +12 -0
- package/dist/src/generated/ChainlinkClientFactory.js +66 -0
- package/dist/src/generated/ChainlinkClientFactory.js.map +1 -0
- package/dist/src/generated/ChainlinkRequestInterface.d.ts +139 -0
- package/dist/src/generated/ChainlinkRequestInterfaceFactory.d.ts +6 -0
- package/dist/src/generated/ChainlinkRequestInterfaceFactory.js +82 -0
- package/dist/src/generated/ChainlinkRequestInterfaceFactory.js.map +1 -0
- package/dist/src/generated/Chainlinked.d.ts +55 -0
- package/dist/src/generated/ChainlinkedFactory.d.ts +12 -0
- package/dist/src/generated/ChainlinkedFactory.js +66 -0
- package/dist/src/generated/ChainlinkedFactory.js.map +1 -0
- package/dist/src/generated/ConcreteChainlink.d.ts +158 -0
- package/dist/src/generated/ConcreteChainlinkFactory.d.ts +12 -0
- package/dist/src/generated/ConcreteChainlinkFactory.js +155 -0
- package/dist/src/generated/ConcreteChainlinkFactory.js.map +1 -0
- package/dist/src/generated/ConcreteChainlinked.d.ts +300 -0
- package/dist/src/generated/ConcreteChainlinkedFactory.d.ts +12 -0
- package/dist/src/generated/ConcreteChainlinkedFactory.js +320 -0
- package/dist/src/generated/ConcreteChainlinkedFactory.js.map +1 -0
- package/dist/src/generated/ConcreteSignedSafeMath.d.ts +50 -0
- package/dist/src/generated/ConcreteSignedSafeMathFactory.d.ts +12 -0
- package/dist/src/generated/ConcreteSignedSafeMathFactory.js +53 -0
- package/dist/src/generated/ConcreteSignedSafeMathFactory.js.map +1 -0
- package/dist/src/generated/Consumer.d.ts +151 -0
- package/dist/src/generated/ConsumerFactory.d.ts +12 -0
- package/dist/src/generated/ConsumerFactory.js +164 -0
- package/dist/src/generated/ConsumerFactory.js.map +1 -0
- package/dist/src/generated/ENS.d.ts +171 -0
- package/dist/src/generated/ENSFactory.d.ts +6 -0
- package/dist/src/generated/ENSFactory.js +220 -0
- package/dist/src/generated/ENSFactory.js.map +1 -0
- package/dist/src/generated/ENSInterface.d.ts +174 -0
- package/dist/src/generated/ENSInterfaceFactory.d.ts +6 -0
- package/dist/src/generated/ENSInterfaceFactory.js +220 -0
- package/dist/src/generated/ENSInterfaceFactory.js.map +1 -0
- package/dist/src/generated/ENSRegistry.d.ts +171 -0
- package/dist/src/generated/ENSRegistryFactory.d.ts +12 -0
- package/dist/src/generated/ENSRegistryFactory.js +242 -0
- package/dist/src/generated/ENSRegistryFactory.js.map +1 -0
- package/dist/src/generated/ENSResolver.d.ts +45 -0
- package/dist/src/generated/ENSResolverFactory.d.ts +6 -0
- package/dist/src/generated/ENSResolverFactory.js +33 -0
- package/dist/src/generated/ENSResolverFactory.js.map +1 -0
- package/dist/src/generated/EmptyOracle.d.ts +228 -0
- package/dist/src/generated/EmptyOracleFactory.d.ts +12 -0
- package/dist/src/generated/EmptyOracleFactory.js +228 -0
- package/dist/src/generated/EmptyOracleFactory.js.map +1 -0
- package/dist/src/generated/GetterSetter.d.ts +207 -0
- package/dist/src/generated/GetterSetterFactory.d.ts +12 -0
- package/dist/src/generated/GetterSetterFactory.js +255 -0
- package/dist/src/generated/GetterSetterFactory.js.map +1 -0
- package/dist/src/generated/LinkToken.d.ts +243 -0
- package/dist/src/generated/LinkTokenFactory.d.ts +12 -0
- package/dist/src/generated/LinkTokenFactory.js +329 -0
- package/dist/src/generated/LinkTokenFactory.js.map +1 -0
- package/dist/src/generated/LinkTokenInterface.d.ts +230 -0
- package/dist/src/generated/LinkTokenInterfaceFactory.d.ts +6 -0
- package/dist/src/generated/LinkTokenInterfaceFactory.js +253 -0
- package/dist/src/generated/LinkTokenInterfaceFactory.js.map +1 -0
- package/dist/src/generated/MaliciousChainlinked.d.ts +58 -0
- package/dist/src/generated/MaliciousChainlinkedFactory.d.ts +12 -0
- package/dist/src/generated/MaliciousChainlinkedFactory.js +66 -0
- package/dist/src/generated/MaliciousChainlinkedFactory.js.map +1 -0
- package/dist/src/generated/MaliciousConsumer.d.ts +179 -0
- package/dist/src/generated/MaliciousConsumerFactory.d.ts +12 -0
- package/dist/src/generated/MaliciousConsumerFactory.js +221 -0
- package/dist/src/generated/MaliciousConsumerFactory.js.map +1 -0
- package/dist/src/generated/MaliciousRequester.d.ts +161 -0
- package/dist/src/generated/MaliciousRequesterFactory.d.ts +12 -0
- package/dist/src/generated/MaliciousRequesterFactory.js +191 -0
- package/dist/src/generated/MaliciousRequesterFactory.js.map +1 -0
- package/dist/src/generated/Migrations.d.ts +87 -0
- package/dist/src/generated/MigrationsFactory.d.ts +12 -0
- package/dist/src/generated/MigrationsFactory.js +92 -0
- package/dist/src/generated/MigrationsFactory.js.map +1 -0
- package/dist/src/generated/Oracle.d.ts +362 -0
- package/dist/src/generated/OracleFactory.d.ts +12 -0
- package/dist/src/generated/OracleFactory.js +383 -0
- package/dist/src/generated/OracleFactory.js.map +1 -0
- package/dist/src/generated/OracleInterface.d.ts +144 -0
- package/dist/src/generated/OracleInterfaceFactory.d.ts +6 -0
- package/dist/src/generated/OracleInterfaceFactory.js +122 -0
- package/dist/src/generated/OracleInterfaceFactory.js.map +1 -0
- package/dist/src/generated/Ownable.d.ts +91 -0
- package/dist/src/generated/OwnableFactory.d.ts +12 -0
- package/dist/src/generated/OwnableFactory.js +102 -0
- package/dist/src/generated/OwnableFactory.js.map +1 -0
- package/dist/src/generated/Pointer.d.ts +45 -0
- package/dist/src/generated/PointerFactory.d.ts +12 -0
- package/dist/src/generated/PointerFactory.js +55 -0
- package/dist/src/generated/PointerFactory.js.map +1 -0
- package/dist/src/generated/PointerInterface.d.ts +48 -0
- package/dist/src/generated/PointerInterfaceFactory.d.ts +6 -0
- package/dist/src/generated/PointerInterfaceFactory.js +28 -0
- package/dist/src/generated/PointerInterfaceFactory.js.map +1 -0
- package/dist/src/generated/PublicResolver.d.ts +336 -0
- package/dist/src/generated/PublicResolverFactory.d.ts +12 -0
- package/dist/src/generated/PublicResolverFactory.js +476 -0
- package/dist/src/generated/PublicResolverFactory.js.map +1 -0
- package/dist/src/generated/UpdatableConsumer.d.ts +180 -0
- package/dist/src/generated/UpdatableConsumerFactory.d.ts +13 -0
- package/dist/src/generated/UpdatableConsumerFactory.js +220 -0
- package/dist/src/generated/UpdatableConsumerFactory.js.map +1 -0
- package/dist/src/generated/index.d.ts +44 -0
- package/dist/src/generated/index.js +41 -0
- package/dist/src/generated/index.js.map +1 -0
- package/dist/src/helpers.d.ts +144 -0
- package/dist/src/helpers.js +358 -0
- package/dist/src/helpers.js.map +1 -0
- package/dist/src/helpers.test.d.ts +1 -0
- package/dist/src/helpers.test.js +21 -0
- package/dist/src/helpers.test.js.map +1 -0
- package/dist/src/index.d.ts +8 -0
- package/dist/src/index.js +27 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/matchers.d.ts +2 -0
- package/dist/src/matchers.js +7 -0
- package/dist/src/matchers.js.map +1 -0
- package/dist/src/provider.d.ts +5 -0
- package/dist/src/provider.js +15 -0
- package/dist/src/provider.js.map +1 -0
- package/dist/src/wallet.d.ts +35 -0
- package/dist/src/wallet.js +64 -0
- package/dist/src/wallet.js.map +1 -0
- package/dist/test/Aggregator.test.d.ts +1 -0
- package/dist/test/Aggregator.test.js +581 -0
- package/dist/test/Aggregator.test.js.map +1 -0
- package/dist/test/AggregatorProxy.test.d.ts +1 -0
- package/dist/test/AggregatorProxy.test.js +179 -0
- package/dist/test/AggregatorProxy.test.js.map +1 -0
- package/dist/test/BasicConsumer.test.d.ts +1 -0
- package/dist/test/BasicConsumer.test.js +180 -0
- package/dist/test/BasicConsumer.test.js.map +1 -0
- package/dist/test/Chainlinked.test.d.ts +1 -0
- package/dist/test/Chainlinked.test.js +11 -0
- package/dist/test/Chainlinked.test.js.map +1 -0
- package/dist/test/ConcreteChainlink.test.d.ts +1 -0
- package/dist/test/ConcreteChainlink.test.js +163 -0
- package/dist/test/ConcreteChainlink.test.js.map +1 -0
- package/dist/test/ConcreteChainlinked.test.d.ts +1 -0
- package/dist/test/ConcreteChainlinked.test.js +182 -0
- package/dist/test/ConcreteChainlinked.test.js.map +1 -0
- package/dist/test/GetterSetter.test.d.ts +1 -0
- package/dist/test/GetterSetter.test.js +76 -0
- package/dist/test/GetterSetter.test.js.map +1 -0
- package/dist/test/Oracle.test.d.ts +1 -0
- package/dist/test/Oracle.test.js +669 -0
- package/dist/test/Oracle.test.js.map +1 -0
- package/dist/test/Pointer.test.d.ts +1 -0
- package/dist/test/Pointer.test.js +35 -0
- package/dist/test/Pointer.test.js.map +1 -0
- package/dist/test/SignedSafeMath.test.d.ts +1 -0
- package/dist/test/SignedSafeMath.test.js +75 -0
- package/dist/test/SignedSafeMath.test.js.map +1 -0
- package/dist/test/UpdatableConsumer.test.d.ts +1 -0
- package/dist/test/UpdatableConsumer.test.js +144 -0
- package/dist/test/UpdatableConsumer.test.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +7737 -0
- package/package.json +48 -4
- package/v0.5/contracts/Chainlink.sol +125 -0
- package/v0.5/contracts/ChainlinkClient.sol +263 -0
- package/v0.5/contracts/LinkTokenReceiver.sol +70 -0
- package/v0.5/contracts/Median.sol +108 -0
- package/v0.5/contracts/Migrations.sol +23 -0
- package/v0.5/contracts/Oracle.sol +273 -0
- package/v0.5/contracts/PreCoordinator.sol +305 -0
- package/v0.5/contracts/dev/AggregatorInterface.sol +12 -0
- package/v0.5/contracts/dev/Coordinator.sol +411 -0
- package/v0.5/contracts/dev/CoordinatorInterface.sol +14 -0
- package/v0.5/contracts/dev/OracleSignaturesDecoder.sol +24 -0
- package/v0.5/contracts/dev/Owned.sol +61 -0
- package/v0.5/contracts/dev/PrepaidAggregator.sol +621 -0
- package/v0.5/contracts/dev/SafeMath128.sol +110 -0
- package/v0.5/contracts/dev/SafeMath32.sol +110 -0
- package/v0.5/contracts/dev/SafeMath64.sol +110 -0
- package/v0.5/contracts/dev/SchnorrSECP256K1.sol +147 -0
- package/v0.5/contracts/dev/ServiceAgreementDecoder.sol +59 -0
- package/v0.5/contracts/dev/VRF.sol +382 -0
- package/v0.5/contracts/dev/Whitelisted.sol +41 -0
- package/v0.5/contracts/dev/WhitelistedAggregator.sol +80 -0
- package/v0.5/contracts/interfaces/ChainlinkRequestInterface.sol +21 -0
- package/v0.5/contracts/interfaces/ENSInterface.sol +26 -0
- package/v0.5/contracts/interfaces/LinkTokenInterface.sol +16 -0
- package/v0.5/contracts/interfaces/OracleInterface.sol +16 -0
- package/v0.5/contracts/interfaces/PointerInterface.sol +5 -0
- package/v0.5/contracts/interfaces/WithdrawalInterface.sol +16 -0
- package/v0.5/contracts/tests/BasicConsumer.sol +13 -0
- package/v0.5/contracts/tests/ChainlinkTestHelper.sol +75 -0
- package/v0.5/contracts/tests/Consumer.sol +55 -0
- package/v0.5/contracts/tests/EmptyAggregator.sol +34 -0
- package/v0.5/contracts/tests/GetterSetter.sol +45 -0
- package/v0.5/contracts/tests/MaliciousChainlink.sol +75 -0
- package/v0.5/contracts/tests/MaliciousChainlinkClient.sol +109 -0
- package/v0.5/contracts/tests/MaliciousConsumer.sol +54 -0
- package/v0.5/contracts/tests/MaliciousRequester.sol +52 -0
- package/v0.5/contracts/tests/MeanAggregator.sol +75 -0
- package/v0.5/contracts/tests/MedianTestHelper.sol +15 -0
- package/v0.5/contracts/tests/OwnedTestHelper.sol +16 -0
- package/v0.5/contracts/tests/ServiceAgreementConsumer.sol +30 -0
- package/v0.5/contracts/vendor/Buffer.sol +301 -0
- package/v0.5/contracts/vendor/CBOR.sol +71 -0
- package/v0.5/contracts/vendor/ENSResolver.sol +5 -0
- package/v0.5/contracts/vendor/Ownable.sol +65 -0
- package/v0.5/contracts/vendor/SafeMath.sol +107 -0
- package/v0.5/contracts/vendor/SignedSafeMath.sol +22 -0
- package/v0.6/contracts/Chainlink.sol +125 -0
- package/v0.6/contracts/ChainlinkClient.sol +263 -0
- package/v0.6/contracts/LinkTokenReceiver.sol +70 -0
- package/v0.6/contracts/Oracle.sol +276 -0
- package/v0.6/contracts/interfaces/ChainlinkRequestInterface.sol +21 -0
- package/v0.6/contracts/interfaces/ENSInterface.sol +26 -0
- package/v0.6/contracts/interfaces/LinkTokenInterface.sol +16 -0
- package/v0.6/contracts/interfaces/OracleInterface.sol +16 -0
- package/v0.6/contracts/interfaces/PointerInterface.sol +5 -0
- package/v0.6/contracts/interfaces/WithdrawalInterface.sol +16 -0
- package/v0.6/contracts/tests/BasicConsumer.sol +13 -0
- package/v0.6/contracts/tests/Consumer.sol +55 -0
- package/v0.6/contracts/vendor/Buffer.sol +301 -0
- package/v0.6/contracts/vendor/CBOR.sol +71 -0
- package/v0.6/contracts/vendor/ENSResolver.sol +5 -0
- package/v0.6/contracts/vendor/Ownable.sol +65 -0
- package/v0.6/contracts/vendor/SafeMath.sol +107 -0
- package/zos.json +8 -0
- package/zos.rinkeby.json +104 -0
- package/zos.ropsten.json +104 -0
@@ -0,0 +1,621 @@
|
|
1
|
+
pragma solidity 0.5.0;
|
2
|
+
|
3
|
+
import "../Median.sol";
|
4
|
+
import "../vendor/SafeMath.sol";
|
5
|
+
import "./SafeMath128.sol";
|
6
|
+
import "./SafeMath64.sol";
|
7
|
+
import "./SafeMath32.sol";
|
8
|
+
import "../interfaces/LinkTokenInterface.sol";
|
9
|
+
import "../interfaces/WithdrawalInterface.sol";
|
10
|
+
import "./AggregatorInterface.sol";
|
11
|
+
import "./Owned.sol";
|
12
|
+
|
13
|
+
/**
|
14
|
+
* @title The Prepaid Aggregator contract
|
15
|
+
* @notice Node handles aggregating data pushed in from off-chain, and unlocks
|
16
|
+
* payment for oracles as they report. Oracles' submissions are gathered in
|
17
|
+
* rounds, with each round aggregating the submissions for each oracle into a
|
18
|
+
* single answer. The latest aggregated answer is exposed as well as historical
|
19
|
+
* answers and their updated at timestamp.
|
20
|
+
*/
|
21
|
+
contract PrepaidAggregator is AggregatorInterface, Owned, WithdrawalInterface {
|
22
|
+
using SafeMath for uint256;
|
23
|
+
using SafeMath128 for uint128;
|
24
|
+
using SafeMath64 for uint64;
|
25
|
+
using SafeMath32 for uint32;
|
26
|
+
|
27
|
+
struct Round {
|
28
|
+
int256 answer;
|
29
|
+
uint64 startedAt;
|
30
|
+
uint64 updatedAt;
|
31
|
+
uint32 answeredInRound;
|
32
|
+
RoundDetails details;
|
33
|
+
}
|
34
|
+
|
35
|
+
struct RoundDetails {
|
36
|
+
int256[] answers;
|
37
|
+
uint32 maxAnswers;
|
38
|
+
uint32 minAnswers;
|
39
|
+
uint32 timeout;
|
40
|
+
uint128 paymentAmount;
|
41
|
+
}
|
42
|
+
|
43
|
+
struct OracleStatus {
|
44
|
+
uint128 withdrawable;
|
45
|
+
uint32 startingRound;
|
46
|
+
uint32 endingRound;
|
47
|
+
uint32 lastReportedRound;
|
48
|
+
uint32 lastStartedRound;
|
49
|
+
int256 latestAnswer;
|
50
|
+
}
|
51
|
+
|
52
|
+
uint128 public allocatedFunds;
|
53
|
+
uint128 public availableFunds;
|
54
|
+
|
55
|
+
// Round related params
|
56
|
+
uint128 public paymentAmount;
|
57
|
+
uint32 public oracleCount;
|
58
|
+
uint32 public maxAnswerCount;
|
59
|
+
uint32 public minAnswerCount;
|
60
|
+
uint32 public restartDelay;
|
61
|
+
uint32 public timeout;
|
62
|
+
uint8 public decimals;
|
63
|
+
bytes32 public description;
|
64
|
+
|
65
|
+
uint32 private reportingRoundId;
|
66
|
+
uint32 internal latestRoundId;
|
67
|
+
LinkTokenInterface private LINK;
|
68
|
+
mapping(address => OracleStatus) private oracles;
|
69
|
+
mapping(uint32 => Round) internal rounds;
|
70
|
+
|
71
|
+
event AvailableFundsUpdated(uint256 indexed amount);
|
72
|
+
event RoundDetailsUpdated(
|
73
|
+
uint128 indexed paymentAmount,
|
74
|
+
uint32 indexed minAnswerCount,
|
75
|
+
uint32 indexed maxAnswerCount,
|
76
|
+
uint32 restartDelay,
|
77
|
+
uint32 timeout // measured in seconds
|
78
|
+
);
|
79
|
+
event OracleAdded(address indexed oracle);
|
80
|
+
event OracleRemoved(address indexed oracle);
|
81
|
+
|
82
|
+
uint32 constant private ROUND_MAX = 2**32-1;
|
83
|
+
|
84
|
+
/**
|
85
|
+
* @notice Deploy with the address of the LINK token and initial payment amount
|
86
|
+
* @dev Sets the LinkToken address and amount of LINK paid
|
87
|
+
* @param _link The address of the LINK token
|
88
|
+
* @param _paymentAmount The amount paid of LINK paid to each oracle per response
|
89
|
+
* @param _timeout is the number of seconds after the previous round that are
|
90
|
+
* allowed to lapse before allowing an oracle to skip an unfinished round
|
91
|
+
*/
|
92
|
+
constructor(
|
93
|
+
address _link,
|
94
|
+
uint128 _paymentAmount,
|
95
|
+
uint32 _timeout,
|
96
|
+
uint8 _decimals,
|
97
|
+
bytes32 _description
|
98
|
+
) public {
|
99
|
+
LINK = LinkTokenInterface(_link);
|
100
|
+
paymentAmount = _paymentAmount;
|
101
|
+
timeout = _timeout;
|
102
|
+
decimals = _decimals;
|
103
|
+
description = _description;
|
104
|
+
}
|
105
|
+
|
106
|
+
/**
|
107
|
+
* @notice called by oracles when they have witnessed a need to update
|
108
|
+
* @param _round is the ID of the round this answer pertains to
|
109
|
+
* @param _answer is the updated data that the oracle is submitting
|
110
|
+
*/
|
111
|
+
function updateAnswer(uint256 _round, int256 _answer)
|
112
|
+
external
|
113
|
+
onlyValidRoundId(uint32(_round))
|
114
|
+
onlyValidOracleRound(uint32(_round))
|
115
|
+
{
|
116
|
+
startNewRound(uint32(_round));
|
117
|
+
recordSubmission(_answer, uint32(_round));
|
118
|
+
updateRoundAnswer(uint32(_round));
|
119
|
+
payOracle(uint32(_round));
|
120
|
+
deleteRound(uint32(_round));
|
121
|
+
}
|
122
|
+
|
123
|
+
/**
|
124
|
+
* @notice called by the owner to add a new Oracle and update the round
|
125
|
+
* related parameters
|
126
|
+
* @param _oracle is the address of the new Oracle being added
|
127
|
+
* @param _minAnswers is the new minimum answer count for each round
|
128
|
+
* @param _maxAnswers is the new maximum answer count for each round
|
129
|
+
* @param _restartDelay is the number of rounds an Oracle has to wait before
|
130
|
+
* they can initiate a round
|
131
|
+
*/
|
132
|
+
function addOracle(
|
133
|
+
address _oracle,
|
134
|
+
uint32 _minAnswers,
|
135
|
+
uint32 _maxAnswers,
|
136
|
+
uint32 _restartDelay
|
137
|
+
)
|
138
|
+
external
|
139
|
+
onlyOwner()
|
140
|
+
onlyUnenabledAddress(_oracle)
|
141
|
+
{
|
142
|
+
require(oracleCount < 42, "cannot add more than 42 oracles");
|
143
|
+
oracles[_oracle].startingRound = getStartingRound(_oracle);
|
144
|
+
oracles[_oracle].endingRound = ROUND_MAX;
|
145
|
+
oracleCount += 1;
|
146
|
+
|
147
|
+
emit OracleAdded(_oracle);
|
148
|
+
|
149
|
+
updateFutureRounds(paymentAmount, _minAnswers, _maxAnswers, _restartDelay, timeout);
|
150
|
+
}
|
151
|
+
|
152
|
+
/**
|
153
|
+
* @notice called by the owner to remove an Oracle and update the round
|
154
|
+
* related parameters
|
155
|
+
* @param _oracle is the address of the Oracle being removed
|
156
|
+
* @param _minAnswers is the new minimum answer count for each round
|
157
|
+
* @param _maxAnswers is the new maximum answer count for each round
|
158
|
+
* @param _restartDelay is the number of rounds an Oracle has to wait before
|
159
|
+
* they can initiate a round
|
160
|
+
*/
|
161
|
+
function removeOracle(
|
162
|
+
address _oracle,
|
163
|
+
uint32 _minAnswers,
|
164
|
+
uint32 _maxAnswers,
|
165
|
+
uint32 _restartDelay
|
166
|
+
)
|
167
|
+
external
|
168
|
+
onlyOwner()
|
169
|
+
onlyEnabledAddress(_oracle)
|
170
|
+
{
|
171
|
+
oracleCount -= 1;
|
172
|
+
oracles[_oracle].endingRound = reportingRoundId;
|
173
|
+
|
174
|
+
emit OracleRemoved(_oracle);
|
175
|
+
|
176
|
+
updateFutureRounds(paymentAmount, _minAnswers, _maxAnswers, _restartDelay, timeout);
|
177
|
+
}
|
178
|
+
|
179
|
+
/**
|
180
|
+
* @notice update the round and payment related parameters for subsequent
|
181
|
+
* rounds
|
182
|
+
* @param _newPaymentAmount is the payment amount for subsequent rounds
|
183
|
+
* @param _minAnswers is the new minimum answer count for each round
|
184
|
+
* @param _maxAnswers is the new maximum answer count for each round
|
185
|
+
* @param _restartDelay is the number of rounds an Oracle has to wait before
|
186
|
+
* they can initiate a round
|
187
|
+
*/
|
188
|
+
function updateFutureRounds(
|
189
|
+
uint128 _newPaymentAmount,
|
190
|
+
uint32 _minAnswers,
|
191
|
+
uint32 _maxAnswers,
|
192
|
+
uint32 _restartDelay,
|
193
|
+
uint32 _timeout
|
194
|
+
)
|
195
|
+
public
|
196
|
+
onlyOwner()
|
197
|
+
onlyValidRange(_minAnswers, _maxAnswers, _restartDelay)
|
198
|
+
{
|
199
|
+
paymentAmount = _newPaymentAmount;
|
200
|
+
minAnswerCount = _minAnswers;
|
201
|
+
maxAnswerCount = _maxAnswers;
|
202
|
+
restartDelay = _restartDelay;
|
203
|
+
timeout = _timeout;
|
204
|
+
|
205
|
+
emit RoundDetailsUpdated(
|
206
|
+
paymentAmount,
|
207
|
+
_minAnswers,
|
208
|
+
_maxAnswers,
|
209
|
+
_restartDelay,
|
210
|
+
_timeout
|
211
|
+
);
|
212
|
+
}
|
213
|
+
|
214
|
+
/**
|
215
|
+
* @notice recalculate the amount of LINK available for payouts
|
216
|
+
*/
|
217
|
+
function updateAvailableFunds()
|
218
|
+
public
|
219
|
+
{
|
220
|
+
uint128 pastAvailableFunds = availableFunds;
|
221
|
+
|
222
|
+
uint256 available = LINK.balanceOf(address(this)).sub(allocatedFunds);
|
223
|
+
availableFunds = uint128(available);
|
224
|
+
|
225
|
+
if (pastAvailableFunds != available) {
|
226
|
+
emit AvailableFundsUpdated(available);
|
227
|
+
}
|
228
|
+
}
|
229
|
+
|
230
|
+
/**
|
231
|
+
* @notice query the available amount of LINK for an oracle to withdraw
|
232
|
+
*/
|
233
|
+
function withdrawable()
|
234
|
+
external
|
235
|
+
view
|
236
|
+
returns (uint256)
|
237
|
+
{
|
238
|
+
return oracles[msg.sender].withdrawable;
|
239
|
+
}
|
240
|
+
|
241
|
+
/**
|
242
|
+
* @notice get the most recently reported answer
|
243
|
+
*/
|
244
|
+
function latestAnswer()
|
245
|
+
external
|
246
|
+
view
|
247
|
+
returns (int256)
|
248
|
+
{
|
249
|
+
return _latestAnswer();
|
250
|
+
}
|
251
|
+
|
252
|
+
/**
|
253
|
+
* @notice get the most recent updated at timestamp
|
254
|
+
*/
|
255
|
+
function latestTimestamp()
|
256
|
+
external
|
257
|
+
view
|
258
|
+
returns (uint256)
|
259
|
+
{
|
260
|
+
return _latestTimestamp();
|
261
|
+
}
|
262
|
+
|
263
|
+
/**
|
264
|
+
* @notice get the ID of the last updated round
|
265
|
+
*/
|
266
|
+
function latestRound()
|
267
|
+
external
|
268
|
+
view
|
269
|
+
returns (uint256)
|
270
|
+
{
|
271
|
+
return latestRoundId;
|
272
|
+
}
|
273
|
+
|
274
|
+
/**
|
275
|
+
* @notice get the ID of the round most recently reported on
|
276
|
+
*/
|
277
|
+
function reportingRound()
|
278
|
+
external
|
279
|
+
view
|
280
|
+
returns (uint256)
|
281
|
+
{
|
282
|
+
return reportingRoundId;
|
283
|
+
}
|
284
|
+
|
285
|
+
/**
|
286
|
+
* @notice get past rounds answers
|
287
|
+
* @param _roundId the round number to retrieve the answer for
|
288
|
+
*/
|
289
|
+
function getAnswer(uint256 _roundId)
|
290
|
+
external
|
291
|
+
view
|
292
|
+
returns (int256)
|
293
|
+
{
|
294
|
+
return _getAnswer(_roundId);
|
295
|
+
}
|
296
|
+
|
297
|
+
/**
|
298
|
+
* @notice get timestamp when an answer was last updated
|
299
|
+
* @param _roundId the round number to retrieve the updated timestamp for
|
300
|
+
*/
|
301
|
+
function getTimestamp(uint256 _roundId)
|
302
|
+
external
|
303
|
+
view
|
304
|
+
returns (uint256)
|
305
|
+
{
|
306
|
+
return _getTimestamp(_roundId);
|
307
|
+
}
|
308
|
+
|
309
|
+
/**
|
310
|
+
* @notice get the timed out status of a given round
|
311
|
+
* @param _roundId the round number to retrieve the timed out status for
|
312
|
+
*/
|
313
|
+
function getTimedOutStatus(uint256 _roundId)
|
314
|
+
external
|
315
|
+
view
|
316
|
+
returns (bool)
|
317
|
+
{
|
318
|
+
uint32 roundId = uint32(_roundId);
|
319
|
+
uint32 answeredIn = rounds[roundId].answeredInRound;
|
320
|
+
return answeredIn > 0 && answeredIn != roundId;
|
321
|
+
}
|
322
|
+
|
323
|
+
/**
|
324
|
+
* @notice get the round ID that an answer was originally reported in
|
325
|
+
* @param _roundId the round number to retrieve the answer for
|
326
|
+
*/
|
327
|
+
function getOriginatingRoundOfAnswer(uint256 _roundId)
|
328
|
+
external
|
329
|
+
view
|
330
|
+
returns (uint256)
|
331
|
+
{
|
332
|
+
return rounds[uint32(_roundId)].answeredInRound;
|
333
|
+
}
|
334
|
+
|
335
|
+
/**
|
336
|
+
* @notice transfers the oracle's LINK to another address
|
337
|
+
* @param _recipient is the address to send the LINK to
|
338
|
+
* @param _amount is the amount of LINK to send
|
339
|
+
*/
|
340
|
+
function withdraw(address _recipient, uint256 _amount)
|
341
|
+
external
|
342
|
+
{
|
343
|
+
uint128 amount = uint128(_amount);
|
344
|
+
uint128 available = oracles[msg.sender].withdrawable;
|
345
|
+
require(available >= amount, "Insufficient balance");
|
346
|
+
|
347
|
+
oracles[msg.sender].withdrawable = available.sub(amount);
|
348
|
+
allocatedFunds = allocatedFunds.sub(amount);
|
349
|
+
|
350
|
+
assert(LINK.transfer(_recipient, uint256(amount)));
|
351
|
+
}
|
352
|
+
|
353
|
+
/**
|
354
|
+
* @notice transfers the owner's LINK to another address
|
355
|
+
* @param _recipient is the address to send the LINK to
|
356
|
+
* @param _amount is the amount of LINK to send
|
357
|
+
*/
|
358
|
+
function withdrawFunds(address _recipient, uint256 _amount)
|
359
|
+
external
|
360
|
+
onlyOwner()
|
361
|
+
{
|
362
|
+
require(availableFunds >= _amount, "Insufficient funds");
|
363
|
+
require(LINK.transfer(_recipient, _amount), "LINK transfer failed");
|
364
|
+
updateAvailableFunds();
|
365
|
+
}
|
366
|
+
|
367
|
+
/**
|
368
|
+
* @notice get the latest submission for any oracle
|
369
|
+
* @param _oracle is the address to lookup the latest submission for
|
370
|
+
*/
|
371
|
+
function latestSubmission(address _oracle)
|
372
|
+
external
|
373
|
+
view
|
374
|
+
returns (int256, uint256)
|
375
|
+
{
|
376
|
+
return (oracles[_oracle].latestAnswer, oracles[_oracle].lastReportedRound);
|
377
|
+
}
|
378
|
+
|
379
|
+
/**
|
380
|
+
* @notice called through LINK's transferAndCall to update available funds
|
381
|
+
* in the same transaction as the funds were transfered to the aggregator
|
382
|
+
*/
|
383
|
+
function onTokenTransfer(address, uint256, bytes memory) public {
|
384
|
+
updateAvailableFunds();
|
385
|
+
}
|
386
|
+
|
387
|
+
/**
|
388
|
+
* Internal
|
389
|
+
*/
|
390
|
+
|
391
|
+
/**
|
392
|
+
* @dev Internal implementation for latestAnswer
|
393
|
+
*/
|
394
|
+
function _latestAnswer()
|
395
|
+
internal
|
396
|
+
view
|
397
|
+
returns (int256)
|
398
|
+
{
|
399
|
+
return rounds[latestRoundId].answer;
|
400
|
+
}
|
401
|
+
|
402
|
+
/**
|
403
|
+
* @dev Internal implementation of latestTimestamp
|
404
|
+
*/
|
405
|
+
function _latestTimestamp()
|
406
|
+
internal
|
407
|
+
view
|
408
|
+
returns (uint256)
|
409
|
+
{
|
410
|
+
return rounds[latestRoundId].updatedAt;
|
411
|
+
}
|
412
|
+
|
413
|
+
/**
|
414
|
+
* @dev Internal implementation of getAnswer
|
415
|
+
*/
|
416
|
+
function _getAnswer(uint256 _roundId)
|
417
|
+
internal
|
418
|
+
view
|
419
|
+
returns (int256)
|
420
|
+
{
|
421
|
+
return rounds[uint32(_roundId)].answer;
|
422
|
+
}
|
423
|
+
|
424
|
+
/**
|
425
|
+
* @dev Internal implementation of getTimestamp
|
426
|
+
*/
|
427
|
+
function _getTimestamp(uint256 _roundId)
|
428
|
+
internal
|
429
|
+
view
|
430
|
+
returns (uint256)
|
431
|
+
{
|
432
|
+
return rounds[uint32(_roundId)].updatedAt;
|
433
|
+
}
|
434
|
+
|
435
|
+
/**
|
436
|
+
* Private
|
437
|
+
*/
|
438
|
+
|
439
|
+
function startNewRound(uint32 _id)
|
440
|
+
private
|
441
|
+
ifNewRound(_id)
|
442
|
+
ifDelayed(_id)
|
443
|
+
{
|
444
|
+
updateTimedOutRoundInfo(_id.sub(1));
|
445
|
+
|
446
|
+
reportingRoundId = _id;
|
447
|
+
rounds[_id].details.maxAnswers = maxAnswerCount;
|
448
|
+
rounds[_id].details.minAnswers = minAnswerCount;
|
449
|
+
rounds[_id].details.paymentAmount = paymentAmount;
|
450
|
+
rounds[_id].details.timeout = timeout;
|
451
|
+
rounds[_id].startedAt = uint64(block.timestamp);
|
452
|
+
|
453
|
+
oracles[msg.sender].lastStartedRound = _id;
|
454
|
+
|
455
|
+
emit NewRound(_id, msg.sender);
|
456
|
+
}
|
457
|
+
|
458
|
+
function updateTimedOutRoundInfo(uint32 _id)
|
459
|
+
private
|
460
|
+
ifTimedOut(_id)
|
461
|
+
onlyWithPreviousAnswer(_id)
|
462
|
+
{
|
463
|
+
uint32 prevId = _id.sub(1);
|
464
|
+
rounds[_id].answer = rounds[prevId].answer;
|
465
|
+
rounds[_id].answeredInRound = rounds[prevId].answeredInRound;
|
466
|
+
rounds[_id].updatedAt = uint64(block.timestamp);
|
467
|
+
|
468
|
+
delete rounds[_id].details;
|
469
|
+
}
|
470
|
+
|
471
|
+
function updateRoundAnswer(uint32 _id)
|
472
|
+
private
|
473
|
+
ifMinAnswersReceived(_id)
|
474
|
+
{
|
475
|
+
int256 newAnswer = Median.calculate(rounds[_id].details.answers);
|
476
|
+
rounds[_id].answer = newAnswer;
|
477
|
+
rounds[_id].updatedAt = uint64(block.timestamp);
|
478
|
+
rounds[_id].answeredInRound = _id;
|
479
|
+
latestRoundId = _id;
|
480
|
+
|
481
|
+
emit AnswerUpdated(newAnswer, _id, now);
|
482
|
+
}
|
483
|
+
|
484
|
+
function payOracle(uint32 _id)
|
485
|
+
private
|
486
|
+
{
|
487
|
+
uint128 payment = rounds[_id].details.paymentAmount;
|
488
|
+
uint128 available = availableFunds.sub(payment);
|
489
|
+
|
490
|
+
availableFunds = available;
|
491
|
+
allocatedFunds = allocatedFunds.add(payment);
|
492
|
+
oracles[msg.sender].withdrawable = oracles[msg.sender].withdrawable.add(payment);
|
493
|
+
|
494
|
+
emit AvailableFundsUpdated(available);
|
495
|
+
}
|
496
|
+
|
497
|
+
function recordSubmission(int256 _answer, uint32 _id)
|
498
|
+
private
|
499
|
+
onlyWhenAcceptingAnswers(_id)
|
500
|
+
{
|
501
|
+
rounds[_id].details.answers.push(_answer);
|
502
|
+
oracles[msg.sender].lastReportedRound = _id;
|
503
|
+
oracles[msg.sender].latestAnswer = _answer;
|
504
|
+
}
|
505
|
+
|
506
|
+
function deleteRound(uint32 _id)
|
507
|
+
private
|
508
|
+
ifMaxAnswersReceived(_id)
|
509
|
+
{
|
510
|
+
delete rounds[_id].details;
|
511
|
+
}
|
512
|
+
|
513
|
+
function timedOut(uint32 _id)
|
514
|
+
private
|
515
|
+
view
|
516
|
+
returns (bool)
|
517
|
+
{
|
518
|
+
uint64 startedAt = rounds[_id].startedAt;
|
519
|
+
uint32 roundTimeout = rounds[_id].details.timeout;
|
520
|
+
return startedAt > 0 && roundTimeout > 0 && startedAt.add(roundTimeout) < block.timestamp;
|
521
|
+
}
|
522
|
+
|
523
|
+
function finished(uint32 _id)
|
524
|
+
private
|
525
|
+
view
|
526
|
+
returns (bool)
|
527
|
+
{
|
528
|
+
return rounds[_id].updatedAt > 0;
|
529
|
+
}
|
530
|
+
|
531
|
+
function getStartingRound(address _oracle)
|
532
|
+
private
|
533
|
+
view
|
534
|
+
returns (uint32)
|
535
|
+
{
|
536
|
+
uint32 currentRound = reportingRoundId;
|
537
|
+
if (currentRound != 0 && currentRound == oracles[_oracle].endingRound) {
|
538
|
+
return currentRound;
|
539
|
+
}
|
540
|
+
return currentRound.add(1);
|
541
|
+
}
|
542
|
+
|
543
|
+
/**
|
544
|
+
* Modifiers
|
545
|
+
*/
|
546
|
+
|
547
|
+
modifier onlyValidOracleRound(uint32 _id) {
|
548
|
+
uint32 startingRound = oracles[msg.sender].startingRound;
|
549
|
+
require(startingRound != 0, "Only updatable by whitelisted oracles");
|
550
|
+
require(startingRound <= _id, "New oracles cannot participate in in-progress rounds");
|
551
|
+
require(oracles[msg.sender].endingRound >= _id, "Oracle has been removed from whitelist");
|
552
|
+
require(oracles[msg.sender].lastReportedRound < _id, "Cannot update round reports");
|
553
|
+
_;
|
554
|
+
}
|
555
|
+
|
556
|
+
modifier ifMinAnswersReceived(uint32 _id) {
|
557
|
+
if (rounds[_id].details.answers.length >= rounds[_id].details.minAnswers) {
|
558
|
+
_;
|
559
|
+
}
|
560
|
+
}
|
561
|
+
|
562
|
+
modifier ifMaxAnswersReceived(uint32 _id) {
|
563
|
+
if (rounds[_id].details.answers.length == rounds[_id].details.maxAnswers) {
|
564
|
+
_;
|
565
|
+
}
|
566
|
+
}
|
567
|
+
|
568
|
+
modifier onlyWhenAcceptingAnswers(uint32 _id) {
|
569
|
+
require(rounds[_id].details.maxAnswers != 0, "Round not currently eligible for reporting");
|
570
|
+
_;
|
571
|
+
}
|
572
|
+
|
573
|
+
modifier ifNewRound(uint32 _id) {
|
574
|
+
if (_id == reportingRoundId.add(1)) {
|
575
|
+
_;
|
576
|
+
}
|
577
|
+
}
|
578
|
+
|
579
|
+
modifier ifDelayed(uint32 _id) {
|
580
|
+
uint256 lastStarted = oracles[msg.sender].lastStartedRound;
|
581
|
+
if (_id > lastStarted + restartDelay || lastStarted == 0) {
|
582
|
+
_;
|
583
|
+
}
|
584
|
+
}
|
585
|
+
|
586
|
+
modifier onlyValidRoundId(uint32 _id) {
|
587
|
+
require(_id == reportingRoundId || _id == reportingRoundId.add(1), "Must report on current round");
|
588
|
+
require(_id == 1 || finished(_id.sub(1)) || timedOut(_id.sub(1)), "Not eligible to bump round");
|
589
|
+
_;
|
590
|
+
}
|
591
|
+
|
592
|
+
modifier onlyValidRange(uint32 _min, uint32 _max, uint32 _restartDelay) {
|
593
|
+
uint32 oracleNum = oracleCount; // Save on storage reads
|
594
|
+
require(oracleNum >= _max, "Cannot have the answer max higher oracle count");
|
595
|
+
require(_max >= _min, "Cannot have the answer minimum higher the max");
|
596
|
+
require(oracleNum == 0 || oracleNum > _restartDelay, "Restart delay must be less than oracle count");
|
597
|
+
_;
|
598
|
+
}
|
599
|
+
|
600
|
+
modifier onlyUnenabledAddress(address _oracle) {
|
601
|
+
require(oracles[_oracle].endingRound != ROUND_MAX, "Address is already recorded as an oracle");
|
602
|
+
_;
|
603
|
+
}
|
604
|
+
|
605
|
+
modifier onlyEnabledAddress(address _oracle) {
|
606
|
+
require(oracles[_oracle].endingRound == ROUND_MAX, "Address is not a whitelisted oracle");
|
607
|
+
_;
|
608
|
+
}
|
609
|
+
|
610
|
+
modifier ifTimedOut(uint32 _id) {
|
611
|
+
if (timedOut(_id)) {
|
612
|
+
_;
|
613
|
+
}
|
614
|
+
}
|
615
|
+
|
616
|
+
modifier onlyWithPreviousAnswer(uint32 _id) {
|
617
|
+
require(rounds[_id.sub(1)].updatedAt != 0, "Must have a previous answer to pull from");
|
618
|
+
_;
|
619
|
+
}
|
620
|
+
|
621
|
+
}
|