Transaction Hash:
Block:
12111246 at Mar-25-2021 11:59:57 PM +UTC
Transaction Fee:
0.176188582 ETH
$446.15
Gas Used:
1,061,377 Gas / 166 Gwei
Emitted Events:
177 |
Aggregator.ChainlinkRequested( id=848DD4FE2DD61126FA27D2F0EC68A328EC211F1B1D23D4F85CAAB9928D7919C7 )
|
178 |
LinkToken.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x00000000000000000000000025fa978ea1a7dc9bdc33a2959b9053eae57169b5, 0x000000000000000000000000180486507e7e20490fc0228efd182989e0bd61cc, 0000000000000000000000000000000000000000000000000a688906bd8b0000 )
|
179 |
LinkToken.Transfer( from=[Receiver] Aggregator, to=Oracle, value=750000000000000000, data=0x4042994600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000A2EF5FD990294D1FBD928D2CB351B7570000000000000000000000000000000000000000000000000000000025FA978EA1A7DC9BDC33A2959B9053EAE57169B56A9705B4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010AEC000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000 )
|
180 |
Oracle.OracleRequest( specId=A2EF5FD990294D1FBD928D2CB351B75700000000000000000000000000000000, requester=[Receiver] Aggregator, requestId=848DD4FE2DD61126FA27D2F0EC68A328EC211F1B1D23D4F85CAAB9928D7919C7, payment=750000000000000000, callbackAddr=[Receiver] Aggregator, callbackFunctionId=System.Byte[], cancelExpiration=1616717097, dataVersion=1, data=0x )
|
181 |
Aggregator.ChainlinkRequested( id=008B459587E4505978ADE4268350DC2A0AB9263F7D9369D4A8CB6CEA2C58CD8B )
|
182 |
LinkToken.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x00000000000000000000000025fa978ea1a7dc9bdc33a2959b9053eae57169b5, 0x000000000000000000000000d9a815095266584cbcc8357a1ac1d7007ff2e847, 0000000000000000000000000000000000000000000000000a688906bd8b0000 )
|
183 |
LinkToken.Transfer( from=[Receiver] Aggregator, to=Oracle, value=750000000000000000, data=0x404299460000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000046BA54D90A6445A2A7478EC4CC95F8C50000000000000000000000000000000000000000000000000000000025FA978EA1A7DC9BDC33A2959B9053EAE57169B56A9705B4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010AED000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000 )
|
184 |
Oracle.OracleRequest( specId=46BA54D90A6445A2A7478EC4CC95F8C500000000000000000000000000000000, requester=[Receiver] Aggregator, requestId=008B459587E4505978ADE4268350DC2A0AB9263F7D9369D4A8CB6CEA2C58CD8B, payment=750000000000000000, callbackAddr=[Receiver] Aggregator, callbackFunctionId=System.Byte[], cancelExpiration=1616717097, dataVersion=1, data=0x )
|
185 |
Aggregator.ChainlinkRequested( id=7EB3FEB80C987626926184AFCAB487B97C48DAA0CCD11062EA1AEC014FD3CECC )
|
186 |
LinkToken.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x00000000000000000000000025fa978ea1a7dc9bdc33a2959b9053eae57169b5, 0x000000000000000000000000049bd8c3adc3fe7d3fc2a44541d955a537c2a484, 0000000000000000000000000000000000000000000000000a688906bd8b0000 )
|
187 |
LinkToken.Transfer( from=[Receiver] Aggregator, to=Oracle, value=750000000000000000, data=0x4042994600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000660E3AA4F520460BAADAFE565B8C57400000000000000000000000000000000000000000000000000000000025FA978EA1A7DC9BDC33A2959B9053EAE57169B56A9705B4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010AEE000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000 )
|
188 |
Oracle.OracleRequest( specId=660E3AA4F520460BAADAFE565B8C574000000000000000000000000000000000, requester=[Receiver] Aggregator, requestId=7EB3FEB80C987626926184AFCAB487B97C48DAA0CCD11062EA1AEC014FD3CECC, payment=750000000000000000, callbackAddr=[Receiver] Aggregator, callbackFunctionId=System.Byte[], cancelExpiration=1616717097, dataVersion=1, data=0x )
|
189 |
Aggregator.ChainlinkRequested( id=BBD9D1209504BFB8BFE5A348E23442B9D7BACD261AA95BEC7CF950133496FDE9 )
|
190 |
LinkToken.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x00000000000000000000000025fa978ea1a7dc9bdc33a2959b9053eae57169b5, 0x00000000000000000000000026d5a7517017b76d99ce6db6db172ffeced09f54, 0000000000000000000000000000000000000000000000000a688906bd8b0000 )
|
191 |
LinkToken.Transfer( from=[Receiver] Aggregator, to=Oracle, value=750000000000000000, data=0x4042994600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000511A0B92908B42639B1C547A17E62F2E0000000000000000000000000000000000000000000000000000000025FA978EA1A7DC9BDC33A2959B9053EAE57169B56A9705B4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010AEF000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000 )
|
192 |
Oracle.OracleRequest( specId=511A0B92908B42639B1C547A17E62F2E00000000000000000000000000000000, requester=[Receiver] Aggregator, requestId=BBD9D1209504BFB8BFE5A348E23442B9D7BACD261AA95BEC7CF950133496FDE9, payment=750000000000000000, callbackAddr=[Receiver] Aggregator, callbackFunctionId=System.Byte[], cancelExpiration=1616717097, dataVersion=1, data=0x )
|
193 |
Aggregator.ChainlinkRequested( id=D71A0F12ED4B28229A8BF34908E6E279F09C7D1F5DFA2C609D94F8EB44AC0039 )
|
194 |
LinkToken.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x00000000000000000000000025fa978ea1a7dc9bdc33a2959b9053eae57169b5, 0x0000000000000000000000004565300c576431e5228e8aa32642d5739cf9247d, 0000000000000000000000000000000000000000000000000a688906bd8b0000 )
|
195 |
LinkToken.Transfer( from=[Receiver] Aggregator, to=0x4565300c576431e5228e8aa32642d5739cf9247d, value=750000000000000000, data=0x4042994600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000D385652A23574164ABB72053B6AE489C0000000000000000000000000000000000000000000000000000000025FA978EA1A7DC9BDC33A2959B9053EAE57169B56A9705B4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010AF0000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000 )
|
196 |
0x4565300c576431e5228e8aa32642d5739cf9247d.0xd8d7ecc4800d25fa53ce0372f13a416d98907a7ef3d8d3bdd79cf4fe75529c65( 0xd8d7ecc4800d25fa53ce0372f13a416d98907a7ef3d8d3bdd79cf4fe75529c65, 0xd385652a23574164abb72053b6ae489c00000000000000000000000000000000, 00000000000000000000000025fa978ea1a7dc9bdc33a2959b9053eae57169b5, d71a0f12ed4b28229a8bf34908e6e279f09c7d1f5dfa2c609d94f8eb44ac0039, 0000000000000000000000000000000000000000000000000a688906bd8b0000, 00000000000000000000000025fa978ea1a7dc9bdc33a2959b9053eae57169b5, 6a9705b400000000000000000000000000000000000000000000000000000000, 00000000000000000000000000000000000000000000000000000000605d2529, 0000000000000000000000000000000000000000000000000000000000000001, 0000000000000000000000000000000000000000000000000000000000000100, 0000000000000000000000000000000000000000000000000000000000000000 )
|
197 |
Aggregator.ChainlinkRequested( id=8F1ED2E76916E196EE92993A27D728BD34AEF3E54A37DE692CE84CD80FF8A058 )
|
198 |
LinkToken.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x00000000000000000000000025fa978ea1a7dc9bdc33a2959b9053eae57169b5, 0x000000000000000000000000b92ec7d213a28e21b426d79ede3c9bbcf6917c09, 0000000000000000000000000000000000000000000000000a688906bd8b0000 )
|
199 |
LinkToken.Transfer( from=[Receiver] Aggregator, to=Oracle, value=750000000000000000, data=0x4042994600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C6AEFDC316F545EAB179DE1A29D25F320000000000000000000000000000000000000000000000000000000025FA978EA1A7DC9BDC33A2959B9053EAE57169B56A9705B4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010AF1000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000 )
|
200 |
Oracle.OracleRequest( specId=C6AEFDC316F545EAB179DE1A29D25F3200000000000000000000000000000000, requester=[Receiver] Aggregator, requestId=8F1ED2E76916E196EE92993A27D728BD34AEF3E54A37DE692CE84CD80FF8A058, payment=750000000000000000, callbackAddr=[Receiver] Aggregator, callbackFunctionId=System.Byte[], cancelExpiration=1616717097, dataVersion=1, data=0x )
|
201 |
Aggregator.ChainlinkRequested( id=B03FB3E4C3CA896110029A96226D1E3A05BE6B49E0BF7C3D3CF627F91BA287EA )
|
202 |
LinkToken.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x00000000000000000000000025fa978ea1a7dc9bdc33a2959b9053eae57169b5, 0x0000000000000000000000002ed7e9fcd3c0568dc6167f0b8aee06a02cd9ebd8, 0000000000000000000000000000000000000000000000000a688906bd8b0000 )
|
203 |
LinkToken.Transfer( from=[Receiver] Aggregator, to=Oracle, value=750000000000000000, data=0x40429946000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009258D955ACE44DA79F5C2BA2877E3AD90000000000000000000000000000000000000000000000000000000025FA978EA1A7DC9BDC33A2959B9053EAE57169B56A9705B4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010AF2000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000 )
|
204 |
Oracle.OracleRequest( specId=9258D955ACE44DA79F5C2BA2877E3AD900000000000000000000000000000000, requester=[Receiver] Aggregator, requestId=B03FB3E4C3CA896110029A96226D1E3A05BE6B49E0BF7C3D3CF627F91BA287EA, payment=750000000000000000, callbackAddr=[Receiver] Aggregator, callbackFunctionId=System.Byte[], cancelExpiration=1616717097, dataVersion=1, data=0x )
|
205 |
Aggregator.ChainlinkRequested( id=981DAA6BE81A6966745346D4817C30D1F9611CDB2CB2A414B8B3AD502CFE4D25 )
|
206 |
LinkToken.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x00000000000000000000000025fa978ea1a7dc9bdc33a2959b9053eae57169b5, 0x00000000000000000000000072f3dff4cd17816604dd2df6c2741e739484ca62, 0000000000000000000000000000000000000000000000000a688906bd8b0000 )
|
207 |
LinkToken.Transfer( from=[Receiver] Aggregator, to=Oracle, value=750000000000000000, data=0x4042994600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000EC8CE80540874FDAAAA067503017051D0000000000000000000000000000000000000000000000000000000025FA978EA1A7DC9BDC33A2959B9053EAE57169B56A9705B4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010AF3000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000 )
|
208 |
Oracle.OracleRequest( specId=EC8CE80540874FDAAAA067503017051D00000000000000000000000000000000, requester=[Receiver] Aggregator, requestId=981DAA6BE81A6966745346D4817C30D1F9611CDB2CB2A414B8B3AD502CFE4D25, payment=750000000000000000, callbackAddr=[Receiver] Aggregator, callbackFunctionId=System.Byte[], cancelExpiration=1616717097, dataVersion=1, data=0x )
|
209 |
Aggregator.ChainlinkRequested( id=5CDCBBA15D2F6BEF89582ACCAAFD56C67D6A8B12748DA3A5127F060D7A74E853 )
|
210 |
LinkToken.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x00000000000000000000000025fa978ea1a7dc9bdc33a2959b9053eae57169b5, 0x00000000000000000000000029e3b3c76e7ae0d681bf1a6bcee1c0e7d17dbaa9, 0000000000000000000000000000000000000000000000000a688906bd8b0000 )
|
211 |
LinkToken.Transfer( from=[Receiver] Aggregator, to=0x29e3b3c76e7ae0d681bf1a6bcee1c0e7d17dbaa9, value=750000000000000000, data=0x4042994600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000AFCC2D75F0494906BB644D3EB5CFEE9C0000000000000000000000000000000000000000000000000000000025FA978EA1A7DC9BDC33A2959B9053EAE57169B56A9705B4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010AF4000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000 )
|
212 |
0x29e3b3c76e7ae0d681bf1a6bcee1c0e7d17dbaa9.0xd8d7ecc4800d25fa53ce0372f13a416d98907a7ef3d8d3bdd79cf4fe75529c65( 0xd8d7ecc4800d25fa53ce0372f13a416d98907a7ef3d8d3bdd79cf4fe75529c65, 0xafcc2d75f0494906bb644d3eb5cfee9c00000000000000000000000000000000, 00000000000000000000000025fa978ea1a7dc9bdc33a2959b9053eae57169b5, 5cdcbba15d2f6bef89582accaafd56c67d6a8b12748da3a5127f060d7a74e853, 0000000000000000000000000000000000000000000000000a688906bd8b0000, 00000000000000000000000025fa978ea1a7dc9bdc33a2959b9053eae57169b5, 6a9705b400000000000000000000000000000000000000000000000000000000, 00000000000000000000000000000000000000000000000000000000605d2529, 0000000000000000000000000000000000000000000000000000000000000001, 0000000000000000000000000000000000000000000000000000000000000100, 0000000000000000000000000000000000000000000000000000000000000000 )
|
213 |
Aggregator.NewRound( roundId=7611, startedBy=[Sender] 0xd8aa8f3be2fb0c790d3579dcf68a04701c1e33db )
|
Account State Difference:
Address | Before | After | State Difference | ||
---|---|---|---|---|---|
0x049Bd8C3...537c2A484 | |||||
0x18048650...9E0BD61Cc | |||||
0x25Fa978e...aE57169B5 | (Chainlink: EUR / USD Aggregator) | ||||
0x26d5A751...EcEd09f54 | |||||
0x29e3b3c7...7d17DBAA9 | |||||
0x2Ed7E9fC...02CD9ebd8 | |||||
0x4565300C...39CF9247d | |||||
0x51491077...4EcF986CA | |||||
0x72f3dFf4...39484CA62 | |||||
0x829BD824...93333A830
Miner
| (F2Pool Old) | 7,020.055653126159360481 Eth | 7,020.231841708159360481 Eth | 0.176188582 | |
0xB92ec7D2...cf6917c09 | |||||
0xd8Aa8F3b...01C1e33DB |
495.39080545940528543 Eth
Nonce: 277637
|
495.21461687740528543 Eth
Nonce: 277638
| 0.176188582 | ||
0xd9A81509...07ff2E847 |
Execution Trace
Aggregator.requestRateUpdate( )
LinkToken.transferAndCall( _to=0x180486507E7e20490FC0228efd182989E0BD61Cc, _value=750000000000000000, _data=0x4042994600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000A2EF5FD990294D1FBD928D2CB351B7570000000000000000000000000000000000000000000000000000000025FA978EA1A7DC9BDC33A2959B9053EAE57169B56A9705B4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010AEC000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000 ) => ( success=True )
Oracle.onTokenTransfer( _sender=0x25Fa978ea1a7dc9bDc33a2959B9053EaE57169B5, _amount=750000000000000000, _data=0x4042994600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000A2EF5FD990294D1FBD928D2CB351B7570000000000000000000000000000000000000000000000000000000025FA978EA1A7DC9BDC33A2959B9053EAE57169B56A9705B4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010AEC000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000 )
-
Oracle.oracleRequest( _sender=0x25Fa978ea1a7dc9bDc33a2959B9053EaE57169B5, _payment=750000000000000000, _specId=A2EF5FD990294D1FBD928D2CB351B75700000000000000000000000000000000, _callbackAddress=0x25Fa978ea1a7dc9bDc33a2959B9053EaE57169B5, _callbackFunctionId=System.Byte[], _nonce=68332, _dataVersion=1, _data=0x )
-
LinkToken.transferAndCall( _to=0xd9A815095266584cBcC8357a1aC1D7007ff2E847, _value=750000000000000000, _data=0x404299460000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000046BA54D90A6445A2A7478EC4CC95F8C50000000000000000000000000000000000000000000000000000000025FA978EA1A7DC9BDC33A2959B9053EAE57169B56A9705B4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010AED000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000 ) => ( success=True )
Oracle.onTokenTransfer( _sender=0x25Fa978ea1a7dc9bDc33a2959B9053EaE57169B5, _amount=750000000000000000, _data=0x404299460000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000046BA54D90A6445A2A7478EC4CC95F8C50000000000000000000000000000000000000000000000000000000025FA978EA1A7DC9BDC33A2959B9053EAE57169B56A9705B4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010AED000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000 )
-
Oracle.oracleRequest( _sender=0x25Fa978ea1a7dc9bDc33a2959B9053EaE57169B5, _payment=750000000000000000, _specId=46BA54D90A6445A2A7478EC4CC95F8C500000000000000000000000000000000, _callbackAddress=0x25Fa978ea1a7dc9bDc33a2959B9053EaE57169B5, _callbackFunctionId=System.Byte[], _nonce=68333, _dataVersion=1, _data=0x )
-
LinkToken.transferAndCall( _to=0x049Bd8C3adC3fE7d3Fc2a44541d955A537c2A484, _value=750000000000000000, _data=0x4042994600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000660E3AA4F520460BAADAFE565B8C57400000000000000000000000000000000000000000000000000000000025FA978EA1A7DC9BDC33A2959B9053EAE57169B56A9705B4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010AEE000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000 ) => ( success=True )
Oracle.onTokenTransfer( _sender=0x25Fa978ea1a7dc9bDc33a2959B9053EaE57169B5, _amount=750000000000000000, _data=0x4042994600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000660E3AA4F520460BAADAFE565B8C57400000000000000000000000000000000000000000000000000000000025FA978EA1A7DC9BDC33A2959B9053EAE57169B56A9705B4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010AEE000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000 )
-
Oracle.oracleRequest( _sender=0x25Fa978ea1a7dc9bDc33a2959B9053EaE57169B5, _payment=750000000000000000, _specId=660E3AA4F520460BAADAFE565B8C574000000000000000000000000000000000, _callbackAddress=0x25Fa978ea1a7dc9bDc33a2959B9053EaE57169B5, _callbackFunctionId=System.Byte[], _nonce=68334, _dataVersion=1, _data=0x )
-
LinkToken.transferAndCall( _to=0x26d5A7517017b76D99ce6dB6dB172ffEcEd09f54, _value=750000000000000000, _data=0x4042994600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000511A0B92908B42639B1C547A17E62F2E0000000000000000000000000000000000000000000000000000000025FA978EA1A7DC9BDC33A2959B9053EAE57169B56A9705B4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010AEF000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000 ) => ( success=True )
Oracle.onTokenTransfer( _sender=0x25Fa978ea1a7dc9bDc33a2959B9053EaE57169B5, _amount=750000000000000000, _data=0x4042994600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000511A0B92908B42639B1C547A17E62F2E0000000000000000000000000000000000000000000000000000000025FA978EA1A7DC9BDC33A2959B9053EAE57169B56A9705B4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010AEF000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000 )
-
Oracle.oracleRequest( _sender=0x25Fa978ea1a7dc9bDc33a2959B9053EaE57169B5, _payment=750000000000000000, _specId=511A0B92908B42639B1C547A17E62F2E00000000000000000000000000000000, _callbackAddress=0x25Fa978ea1a7dc9bDc33a2959B9053EaE57169B5, _callbackFunctionId=System.Byte[], _nonce=68335, _dataVersion=1, _data=0x )
-
LinkToken.transferAndCall( _to=0x4565300C576431e5228e8aA32642D5739CF9247d, _value=750000000000000000, _data=0x4042994600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000D385652A23574164ABB72053B6AE489C0000000000000000000000000000000000000000000000000000000025FA978EA1A7DC9BDC33A2959B9053EAE57169B56A9705B4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010AF0000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000 ) => ( success=True )
0x4565300c576431e5228e8aa32642d5739cf9247d.a4c0ed36( )
-
0x4565300c576431e5228e8aa32642d5739cf9247d.40429946( )
-
LinkToken.transferAndCall( _to=0xB92ec7D213a28e21b426D79EDe3c9BBcf6917c09, _value=750000000000000000, _data=0x4042994600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C6AEFDC316F545EAB179DE1A29D25F320000000000000000000000000000000000000000000000000000000025FA978EA1A7DC9BDC33A2959B9053EAE57169B56A9705B4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010AF1000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000 ) => ( success=True )
Oracle.onTokenTransfer( _sender=0x25Fa978ea1a7dc9bDc33a2959B9053EaE57169B5, _amount=750000000000000000, _data=0x4042994600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C6AEFDC316F545EAB179DE1A29D25F320000000000000000000000000000000000000000000000000000000025FA978EA1A7DC9BDC33A2959B9053EAE57169B56A9705B4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010AF1000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000 )
-
Oracle.oracleRequest( _sender=0x25Fa978ea1a7dc9bDc33a2959B9053EaE57169B5, _payment=750000000000000000, _specId=C6AEFDC316F545EAB179DE1A29D25F3200000000000000000000000000000000, _callbackAddress=0x25Fa978ea1a7dc9bDc33a2959B9053EaE57169B5, _callbackFunctionId=System.Byte[], _nonce=68337, _dataVersion=1, _data=0x )
-
LinkToken.transferAndCall( _to=0x2Ed7E9fCd3c0568dC6167F0b8aEe06A02CD9ebd8, _value=750000000000000000, _data=0x40429946000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009258D955ACE44DA79F5C2BA2877E3AD90000000000000000000000000000000000000000000000000000000025FA978EA1A7DC9BDC33A2959B9053EAE57169B56A9705B4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010AF2000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000 ) => ( success=True )
Oracle.onTokenTransfer( _sender=0x25Fa978ea1a7dc9bDc33a2959B9053EaE57169B5, _amount=750000000000000000, _data=0x40429946000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009258D955ACE44DA79F5C2BA2877E3AD90000000000000000000000000000000000000000000000000000000025FA978EA1A7DC9BDC33A2959B9053EAE57169B56A9705B4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010AF2000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000 )
-
Oracle.oracleRequest( _sender=0x25Fa978ea1a7dc9bDc33a2959B9053EaE57169B5, _payment=750000000000000000, _specId=9258D955ACE44DA79F5C2BA2877E3AD900000000000000000000000000000000, _callbackAddress=0x25Fa978ea1a7dc9bDc33a2959B9053EaE57169B5, _callbackFunctionId=System.Byte[], _nonce=68338, _dataVersion=1, _data=0x )
-
LinkToken.transferAndCall( _to=0x72f3dFf4CD17816604dd2df6C2741e739484CA62, _value=750000000000000000, _data=0x4042994600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000EC8CE80540874FDAAAA067503017051D0000000000000000000000000000000000000000000000000000000025FA978EA1A7DC9BDC33A2959B9053EAE57169B56A9705B4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010AF3000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000 ) => ( success=True )
Oracle.onTokenTransfer( _sender=0x25Fa978ea1a7dc9bDc33a2959B9053EaE57169B5, _amount=750000000000000000, _data=0x4042994600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000EC8CE80540874FDAAAA067503017051D0000000000000000000000000000000000000000000000000000000025FA978EA1A7DC9BDC33A2959B9053EAE57169B56A9705B4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010AF3000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000 )
-
Oracle.oracleRequest( _sender=0x25Fa978ea1a7dc9bDc33a2959B9053EaE57169B5, _payment=750000000000000000, _specId=EC8CE80540874FDAAAA067503017051D00000000000000000000000000000000, _callbackAddress=0x25Fa978ea1a7dc9bDc33a2959B9053EaE57169B5, _callbackFunctionId=System.Byte[], _nonce=68339, _dataVersion=1, _data=0x )
-
LinkToken.transferAndCall( _to=0x29e3b3c76e7ae0d681bf1a6BceE1c0E7d17DBAA9, _value=750000000000000000, _data=0x4042994600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000AFCC2D75F0494906BB644D3EB5CFEE9C0000000000000000000000000000000000000000000000000000000025FA978EA1A7DC9BDC33A2959B9053EAE57169B56A9705B4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010AF4000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000 ) => ( success=True )
0x29e3b3c76e7ae0d681bf1a6bcee1c0e7d17dbaa9.a4c0ed36( )
-
0x29e3b3c76e7ae0d681bf1a6bcee1c0e7d17dbaa9.40429946( )
-
requestRateUpdate[Aggregator (ln:1022)]
buildChainlinkRequest[Aggregator (ln:1031)]
sendChainlinkRequestTo[Aggregator (ln:1032)]
add[Aggregator (ln:1037)]
NewRound[Aggregator (ln:1039)]
File 1 of 9: Aggregator
File 2 of 9: LinkToken
File 3 of 9: Oracle
File 4 of 9: Oracle
File 5 of 9: Oracle
File 6 of 9: Oracle
File 7 of 9: Oracle
File 8 of 9: Oracle
File 9 of 9: Oracle
pragma solidity 0.4.24; /** * @dev A library for working with mutable byte buffers in Solidity. * * Byte buffers are mutable and expandable, and provide a variety of primitives * for writing to them. At any time you can fetch a bytes object containing the * current contents of the buffer. The bytes object should not be stored between * operations, as it may change due to resizing of the buffer. */ library Buffer { /** * @dev Represents a mutable buffer. Buffers have a current value (buf) and * a capacity. The capacity may be longer than the current value, in * which case it can be extended without the need to allocate more memory. */ struct buffer { bytes buf; uint capacity; } /** * @dev Initializes a buffer with an initial capacity. * @param buf The buffer to initialize. * @param capacity The number of bytes of space to allocate the buffer. * @return The buffer, for chaining. */ function init(buffer memory buf, uint capacity) internal pure returns(buffer memory) { if (capacity % 32 != 0) { capacity += 32 - (capacity % 32); } // Allocate space for the buffer data buf.capacity = capacity; assembly { let ptr := mload(0x40) mstore(buf, ptr) mstore(ptr, 0) mstore(0x40, add(32, add(ptr, capacity))) } return buf; } /** * @dev Initializes a new buffer from an existing bytes object. * Changes to the buffer may mutate the original value. * @param b The bytes object to initialize the buffer with. * @return A new buffer. */ function fromBytes(bytes memory b) internal pure returns(buffer memory) { buffer memory buf; buf.buf = b; buf.capacity = b.length; return buf; } function resize(buffer memory buf, uint capacity) private pure { bytes memory oldbuf = buf.buf; init(buf, capacity); append(buf, oldbuf); } function max(uint a, uint b) private pure returns(uint) { if (a > b) { return a; } return b; } /** * @dev Sets buffer length to 0. * @param buf The buffer to truncate. * @return The original buffer, for chaining.. */ function truncate(buffer memory buf) internal pure returns (buffer memory) { assembly { let bufptr := mload(buf) mstore(bufptr, 0) } return buf; } /** * @dev Writes a byte string to a buffer. Resizes if doing so would exceed * the capacity of the buffer. * @param buf The buffer to append to. * @param off The start offset to write to. * @param data The data to append. * @param len The number of bytes to copy. * @return The original buffer, for chaining. */ function write(buffer memory buf, uint off, bytes memory data, uint len) internal pure returns(buffer memory) { require(len <= data.length); if (off + len > buf.capacity) { resize(buf, max(buf.capacity, len + off) * 2); } uint dest; uint src; assembly { // Memory address of the buffer data let bufptr := mload(buf) // Length of existing buffer data let buflen := mload(bufptr) // Start address = buffer address + offset + sizeof(buffer length) dest := add(add(bufptr, 32), off) // Update buffer length if we're extending it if gt(add(len, off), buflen) { mstore(bufptr, add(len, off)) } src := add(data, 32) } // Copy word-length chunks while possible for (; len >= 32; len -= 32) { assembly { mstore(dest, mload(src)) } dest += 32; src += 32; } // Copy remaining bytes uint mask = 256 ** (32 - len) - 1; assembly { let srcpart := and(mload(src), not(mask)) let destpart := and(mload(dest), mask) mstore(dest, or(destpart, srcpart)) } return buf; } /** * @dev Appends a byte string to a buffer. Resizes if doing so would exceed * the capacity of the buffer. * @param buf The buffer to append to. * @param data The data to append. * @param len The number of bytes to copy. * @return The original buffer, for chaining. */ function append(buffer memory buf, bytes memory data, uint len) internal pure returns (buffer memory) { return write(buf, buf.buf.length, data, len); } /** * @dev Appends a byte string to a buffer. Resizes if doing so would exceed * the capacity of the buffer. * @param buf The buffer to append to. * @param data The data to append. * @return The original buffer, for chaining. */ function append(buffer memory buf, bytes memory data) internal pure returns (buffer memory) { return write(buf, buf.buf.length, data, data.length); } /** * @dev Writes a byte to the buffer. Resizes if doing so would exceed the * capacity of the buffer. * @param buf The buffer to append to. * @param off The offset to write the byte at. * @param data The data to append. * @return The original buffer, for chaining. */ function writeUint8(buffer memory buf, uint off, uint8 data) internal pure returns(buffer memory) { if (off >= buf.capacity) { resize(buf, buf.capacity * 2); } assembly { // Memory address of the buffer data let bufptr := mload(buf) // Length of existing buffer data let buflen := mload(bufptr) // Address = buffer address + sizeof(buffer length) + off let dest := add(add(bufptr, off), 32) mstore8(dest, data) // Update buffer length if we extended it if eq(off, buflen) { mstore(bufptr, add(buflen, 1)) } } return buf; } /** * @dev Appends a byte to the buffer. Resizes if doing so would exceed the * capacity of the buffer. * @param buf The buffer to append to. * @param data The data to append. * @return The original buffer, for chaining. */ function appendUint8(buffer memory buf, uint8 data) internal pure returns(buffer memory) { return writeUint8(buf, buf.buf.length, data); } /** * @dev Writes up to 32 bytes to the buffer. Resizes if doing so would * exceed the capacity of the buffer. * @param buf The buffer to append to. * @param off The offset to write at. * @param data The data to append. * @param len The number of bytes to write (left-aligned). * @return The original buffer, for chaining. */ function write(buffer memory buf, uint off, bytes32 data, uint len) private pure returns(buffer memory) { if (len + off > buf.capacity) { resize(buf, (len + off) * 2); } uint mask = 256 ** len - 1; // Right-align data data = data >> (8 * (32 - len)); assembly { // Memory address of the buffer data let bufptr := mload(buf) // Address = buffer address + sizeof(buffer length) + off + len let dest := add(add(bufptr, off), len) mstore(dest, or(and(mload(dest), not(mask)), data)) // Update buffer length if we extended it if gt(add(off, len), mload(bufptr)) { mstore(bufptr, add(off, len)) } } return buf; } /** * @dev Writes a bytes20 to the buffer. Resizes if doing so would exceed the * capacity of the buffer. * @param buf The buffer to append to. * @param off The offset to write at. * @param data The data to append. * @return The original buffer, for chaining. */ function writeBytes20(buffer memory buf, uint off, bytes20 data) internal pure returns (buffer memory) { return write(buf, off, bytes32(data), 20); } /** * @dev Appends a bytes20 to the buffer. Resizes if doing so would exceed * the capacity of the buffer. * @param buf The buffer to append to. * @param data The data to append. * @return The original buffer, for chhaining. */ function appendBytes20(buffer memory buf, bytes20 data) internal pure returns (buffer memory) { return write(buf, buf.buf.length, bytes32(data), 20); } /** * @dev Appends a bytes32 to the buffer. Resizes if doing so would exceed * the capacity of the buffer. * @param buf The buffer to append to. * @param data The data to append. * @return The original buffer, for chaining. */ function appendBytes32(buffer memory buf, bytes32 data) internal pure returns (buffer memory) { return write(buf, buf.buf.length, data, 32); } /** * @dev Writes an integer to the buffer. Resizes if doing so would exceed * the capacity of the buffer. * @param buf The buffer to append to. * @param off The offset to write at. * @param data The data to append. * @param len The number of bytes to write (right-aligned). * @return The original buffer, for chaining. */ function writeInt(buffer memory buf, uint off, uint data, uint len) private pure returns(buffer memory) { if (len + off > buf.capacity) { resize(buf, (len + off) * 2); } uint mask = 256 ** len - 1; assembly { // Memory address of the buffer data let bufptr := mload(buf) // Address = buffer address + off + sizeof(buffer length) + len let dest := add(add(bufptr, off), len) mstore(dest, or(and(mload(dest), not(mask)), data)) // Update buffer length if we extended it if gt(add(off, len), mload(bufptr)) { mstore(bufptr, add(off, len)) } } return buf; } /** * @dev Appends a byte to the end of the buffer. Resizes if doing so would * exceed the capacity of the buffer. * @param buf The buffer to append to. * @param data The data to append. * @return The original buffer. */ function appendInt(buffer memory buf, uint data, uint len) internal pure returns(buffer memory) { return writeInt(buf, buf.buf.length, data, len); } } library CBOR { using Buffer for Buffer.buffer; uint8 private constant MAJOR_TYPE_INT = 0; uint8 private constant MAJOR_TYPE_NEGATIVE_INT = 1; uint8 private constant MAJOR_TYPE_BYTES = 2; uint8 private constant MAJOR_TYPE_STRING = 3; uint8 private constant MAJOR_TYPE_ARRAY = 4; uint8 private constant MAJOR_TYPE_MAP = 5; uint8 private constant MAJOR_TYPE_CONTENT_FREE = 7; function encodeType(Buffer.buffer memory buf, uint8 major, uint value) private pure { if(value <= 23) { buf.appendUint8(uint8((major << 5) | value)); } else if(value <= 0xFF) { buf.appendUint8(uint8((major << 5) | 24)); buf.appendInt(value, 1); } else if(value <= 0xFFFF) { buf.appendUint8(uint8((major << 5) | 25)); buf.appendInt(value, 2); } else if(value <= 0xFFFFFFFF) { buf.appendUint8(uint8((major << 5) | 26)); buf.appendInt(value, 4); } else if(value <= 0xFFFFFFFFFFFFFFFF) { buf.appendUint8(uint8((major << 5) | 27)); buf.appendInt(value, 8); } } function encodeIndefiniteLengthType(Buffer.buffer memory buf, uint8 major) private pure { buf.appendUint8(uint8((major << 5) | 31)); } function encodeUInt(Buffer.buffer memory buf, uint value) internal pure { encodeType(buf, MAJOR_TYPE_INT, value); } function encodeInt(Buffer.buffer memory buf, int value) internal pure { if(value >= 0) { encodeType(buf, MAJOR_TYPE_INT, uint(value)); } else { encodeType(buf, MAJOR_TYPE_NEGATIVE_INT, uint(-1 - value)); } } function encodeBytes(Buffer.buffer memory buf, bytes value) internal pure { encodeType(buf, MAJOR_TYPE_BYTES, value.length); buf.append(value); } function encodeString(Buffer.buffer memory buf, string value) internal pure { encodeType(buf, MAJOR_TYPE_STRING, bytes(value).length); buf.append(bytes(value)); } function startArray(Buffer.buffer memory buf) internal pure { encodeIndefiniteLengthType(buf, MAJOR_TYPE_ARRAY); } function startMap(Buffer.buffer memory buf) internal pure { encodeIndefiniteLengthType(buf, MAJOR_TYPE_MAP); } function endSequence(Buffer.buffer memory buf) internal pure { encodeIndefiniteLengthType(buf, MAJOR_TYPE_CONTENT_FREE); } } /** * @title Library for common Chainlink functions * @dev Uses imported CBOR library for encoding to buffer */ library Chainlink { uint256 internal constant defaultBufferSize = 256; // solhint-disable-line const-name-snakecase using CBOR for Buffer.buffer; struct Request { bytes32 id; address callbackAddress; bytes4 callbackFunctionId; uint256 nonce; Buffer.buffer buf; } /** * @notice Initializes a Chainlink request * @dev Sets the ID, callback address, and callback function signature on the request * @param self The uninitialized request * @param _id The Job Specification ID * @param _callbackAddress The callback address * @param _callbackFunction The callback function signature * @return The initialized request */ function initialize( Request memory self, bytes32 _id, address _callbackAddress, bytes4 _callbackFunction ) internal pure returns (Chainlink.Request memory) { Buffer.init(self.buf, defaultBufferSize); self.id = _id; self.callbackAddress = _callbackAddress; self.callbackFunctionId = _callbackFunction; return self; } /** * @notice Sets the data for the buffer without encoding CBOR on-chain * @dev CBOR can be closed with curly-brackets {} or they can be left off * @param self The initialized request * @param _data The CBOR data */ function setBuffer(Request memory self, bytes _data) internal pure { Buffer.init(self.buf, _data.length); Buffer.append(self.buf, _data); } /** * @notice Adds a string value to the request with a given key name * @param self The initialized request * @param _key The name of the key * @param _value The string value to add */ function add(Request memory self, string _key, string _value) internal pure { self.buf.encodeString(_key); self.buf.encodeString(_value); } /** * @notice Adds a bytes value to the request with a given key name * @param self The initialized request * @param _key The name of the key * @param _value The bytes value to add */ function addBytes(Request memory self, string _key, bytes _value) internal pure { self.buf.encodeString(_key); self.buf.encodeBytes(_value); } /** * @notice Adds a int256 value to the request with a given key name * @param self The initialized request * @param _key The name of the key * @param _value The int256 value to add */ function addInt(Request memory self, string _key, int256 _value) internal pure { self.buf.encodeString(_key); self.buf.encodeInt(_value); } /** * @notice Adds a uint256 value to the request with a given key name * @param self The initialized request * @param _key The name of the key * @param _value The uint256 value to add */ function addUint(Request memory self, string _key, uint256 _value) internal pure { self.buf.encodeString(_key); self.buf.encodeUInt(_value); } /** * @notice Adds an array of strings to the request with a given key name * @param self The initialized request * @param _key The name of the key * @param _values The array of string values to add */ function addStringArray(Request memory self, string _key, string[] memory _values) internal pure { self.buf.encodeString(_key); self.buf.startArray(); for (uint256 i = 0; i < _values.length; i++) { self.buf.encodeString(_values[i]); } self.buf.endSequence(); } } interface ENSInterface { // Logged when the owner of a node assigns a new owner to a subnode. event NewOwner(bytes32 indexed node, bytes32 indexed label, address owner); // Logged when the owner of a node transfers ownership to a new account. event Transfer(bytes32 indexed node, address owner); // Logged when the resolver for a node changes. event NewResolver(bytes32 indexed node, address resolver); // Logged when the TTL of a node changes event NewTTL(bytes32 indexed node, uint64 ttl); function setSubnodeOwner(bytes32 node, bytes32 label, address owner) external; function setResolver(bytes32 node, address resolver) external; function setOwner(bytes32 node, address owner) external; function setTTL(bytes32 node, uint64 ttl) external; function owner(bytes32 node) external view returns (address); function resolver(bytes32 node) external view returns (address); function ttl(bytes32 node) external view returns (uint64); } interface LinkTokenInterface { function allowance(address owner, address spender) external returns (uint256 remaining); function approve(address spender, uint256 value) external returns (bool success); function balanceOf(address owner) external returns (uint256 balance); function decimals() external returns (uint8 decimalPlaces); function decreaseApproval(address spender, uint256 addedValue) external returns (bool success); function increaseApproval(address spender, uint256 subtractedValue) external; function name() external returns (string tokenName); function symbol() external returns (string tokenSymbol); function totalSupply() external returns (uint256 totalTokensIssued); function transfer(address to, uint256 value) external returns (bool success); function transferAndCall(address to, uint256 value, bytes data) external returns (bool success); function transferFrom(address from, address to, uint256 value) external returns (bool success); } interface ChainlinkRequestInterface { function oracleRequest( address sender, uint256 payment, bytes32 id, address callbackAddress, bytes4 callbackFunctionId, uint256 nonce, uint256 version, bytes data ) external; function cancelOracleRequest( bytes32 requestId, uint256 payment, bytes4 callbackFunctionId, uint256 expiration ) external; } interface PointerInterface { function getAddress() external view returns (address); } contract ENSResolver { function addr(bytes32 node) public view returns (address); } /** * @title SafeMath * @dev Math operations with safety checks that throw on error */ library SafeMath { /** * @dev Multiplies two numbers, throws on overflow. */ function mul(uint256 _a, uint256 _b) internal pure returns (uint256 c) { // Gas optimization: this is cheaper than asserting 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522 if (_a == 0) { return 0; } c = _a * _b; assert(c / _a == _b); return c; } /** * @dev Integer division of two numbers, truncating the quotient. */ function div(uint256 _a, uint256 _b) internal pure returns (uint256) { // assert(_b > 0); // Solidity automatically throws when dividing by 0 // uint256 c = _a / _b; // assert(_a == _b * c + _a % _b); // There is no case in which this doesn't hold return _a / _b; } /** * @dev Subtracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend). */ function sub(uint256 _a, uint256 _b) internal pure returns (uint256) { assert(_b <= _a); return _a - _b; } /** * @dev Adds two numbers, throws on overflow. */ function add(uint256 _a, uint256 _b) internal pure returns (uint256 c) { c = _a + _b; assert(c >= _a); return c; } } /** * @title The ChainlinkClient contract * @notice Contract writers can inherit this contract in order to create requests for the * Chainlink network */ contract ChainlinkClient { using Chainlink for Chainlink.Request; using SafeMath for uint256; uint256 constant internal LINK = 10**18; uint256 constant private AMOUNT_OVERRIDE = 0; address constant private SENDER_OVERRIDE = 0x0; uint256 constant private ARGS_VERSION = 1; bytes32 constant private ENS_TOKEN_SUBNAME = keccak256("link"); bytes32 constant private ENS_ORACLE_SUBNAME = keccak256("oracle"); address constant private LINK_TOKEN_POINTER = 0xC89bD4E1632D3A43CB03AAAd5262cbe4038Bc571; ENSInterface private ens; bytes32 private ensNode; LinkTokenInterface private link; ChainlinkRequestInterface private oracle; uint256 private requests = 1; mapping(bytes32 => address) private pendingRequests; event ChainlinkRequested(bytes32 indexed id); event ChainlinkFulfilled(bytes32 indexed id); event ChainlinkCancelled(bytes32 indexed id); /** * @notice Creates a request that can hold additional parameters * @param _specId The Job Specification ID that the request will be created for * @param _callbackAddress The callback address that the response will be sent to * @param _callbackFunctionSignature The callback function signature to use for the callback address * @return A Chainlink Request struct in memory */ function buildChainlinkRequest( bytes32 _specId, address _callbackAddress, bytes4 _callbackFunctionSignature ) internal pure returns (Chainlink.Request memory) { Chainlink.Request memory req; return req.initialize(_specId, _callbackAddress, _callbackFunctionSignature); } /** * @notice Creates a Chainlink request to the stored oracle address * @dev Calls `chainlinkRequestTo` with the stored oracle address * @param _req The initialized Chainlink Request * @param _payment The amount of LINK to send for the request * @return The request ID */ function sendChainlinkRequest(Chainlink.Request memory _req, uint256 _payment) internal returns (bytes32) { return sendChainlinkRequestTo(oracle, _req, _payment); } /** * @notice Creates a Chainlink request to the specified oracle address * @dev Generates and stores a request ID, increments the local nonce, and uses `transferAndCall` to * send LINK which creates a request on the target oracle contract. * Emits ChainlinkRequested event. * @param _oracle The address of the oracle for the request * @param _req The initialized Chainlink Request * @param _payment The amount of LINK to send for the request * @return The request ID */ function sendChainlinkRequestTo(address _oracle, Chainlink.Request memory _req, uint256 _payment) internal returns (bytes32 requestId) { requestId = keccak256(abi.encodePacked(this, requests)); _req.nonce = requests; pendingRequests[requestId] = _oracle; emit ChainlinkRequested(requestId); require(link.transferAndCall(_oracle, _payment, encodeRequest(_req)), "unable to transferAndCall to oracle"); requests += 1; return requestId; } /** * @notice Allows a request to be cancelled if it has not been fulfilled * @dev Requires keeping track of the expiration value emitted from the oracle contract. * Deletes the request from the `pendingRequests` mapping. * Emits ChainlinkCancelled event. * @param _requestId The request ID * @param _payment The amount of LINK sent for the request * @param _callbackFunc The callback function specified for the request * @param _expiration The time of the expiration for the request */ function cancelChainlinkRequest( bytes32 _requestId, uint256 _payment, bytes4 _callbackFunc, uint256 _expiration ) internal { ChainlinkRequestInterface requested = ChainlinkRequestInterface(pendingRequests[_requestId]); delete pendingRequests[_requestId]; emit ChainlinkCancelled(_requestId); requested.cancelOracleRequest(_requestId, _payment, _callbackFunc, _expiration); } /** * @notice Sets the stored oracle address * @param _oracle The address of the oracle contract */ function setChainlinkOracle(address _oracle) internal { oracle = ChainlinkRequestInterface(_oracle); } /** * @notice Sets the LINK token address * @param _link The address of the LINK token contract */ function setChainlinkToken(address _link) internal { link = LinkTokenInterface(_link); } /** * @notice Sets the Chainlink token address for the public * network as given by the Pointer contract */ function setPublicChainlinkToken() internal { setChainlinkToken(PointerInterface(LINK_TOKEN_POINTER).getAddress()); } /** * @notice Retrieves the stored address of the LINK token * @return The address of the LINK token */ function chainlinkTokenAddress() internal view returns (address) { return address(link); } /** * @notice Retrieves the stored address of the oracle contract * @return The address of the oracle contract */ function chainlinkOracleAddress() internal view returns (address) { return address(oracle); } /** * @notice Allows for a request which was created on another contract to be fulfilled * on this contract * @param _oracle The address of the oracle contract that will fulfill the request * @param _requestId The request ID used for the response */ function addChainlinkExternalRequest(address _oracle, bytes32 _requestId) internal notPendingRequest(_requestId) { pendingRequests[_requestId] = _oracle; } /** * @notice Sets the stored oracle and LINK token contracts with the addresses resolved by ENS * @dev Accounts for subnodes having different resolvers * @param _ens The address of the ENS contract * @param _node The ENS node hash */ function useChainlinkWithENS(address _ens, bytes32 _node) internal { ens = ENSInterface(_ens); ensNode = _node; bytes32 linkSubnode = keccak256(abi.encodePacked(ensNode, ENS_TOKEN_SUBNAME)); ENSResolver resolver = ENSResolver(ens.resolver(linkSubnode)); setChainlinkToken(resolver.addr(linkSubnode)); updateChainlinkOracleWithENS(); } /** * @notice Sets the stored oracle contract with the address resolved by ENS * @dev This may be called on its own as long as `useChainlinkWithENS` has been called previously */ function updateChainlinkOracleWithENS() internal { bytes32 oracleSubnode = keccak256(abi.encodePacked(ensNode, ENS_ORACLE_SUBNAME)); ENSResolver resolver = ENSResolver(ens.resolver(oracleSubnode)); setChainlinkOracle(resolver.addr(oracleSubnode)); } /** * @notice Encodes the request to be sent to the oracle contract * @dev The Chainlink node expects values to be in order for the request to be picked up. Order of types * will be validated in the oracle contract. * @param _req The initialized Chainlink Request * @return The bytes payload for the `transferAndCall` method */ function encodeRequest(Chainlink.Request memory _req) private view returns (bytes memory) { return abi.encodeWithSelector( oracle.oracleRequest.selector, SENDER_OVERRIDE, // Sender value - overridden by onTokenTransfer by the requesting contract's address AMOUNT_OVERRIDE, // Amount value - overridden by onTokenTransfer by the actual amount of LINK sent _req.id, _req.callbackAddress, _req.callbackFunctionId, _req.nonce, ARGS_VERSION, _req.buf.buf); } /** * @notice Ensures that the fulfillment is valid for this contract * @dev Use if the contract developer prefers methods instead of modifiers for validation * @param _requestId The request ID for fulfillment */ function validateChainlinkCallback(bytes32 _requestId) internal recordChainlinkFulfillment(_requestId) // solhint-disable-next-line no-empty-blocks {} /** * @dev Reverts if the sender is not the oracle of the request. * Emits ChainlinkFulfilled event. * @param _requestId The request ID for fulfillment */ modifier recordChainlinkFulfillment(bytes32 _requestId) { require(msg.sender == pendingRequests[_requestId], "Source must be the oracle of the request"); delete pendingRequests[_requestId]; emit ChainlinkFulfilled(_requestId); _; } /** * @dev Reverts if the request is already pending * @param _requestId The request ID for fulfillment */ modifier notPendingRequest(bytes32 _requestId) { require(pendingRequests[_requestId] == address(0), "Request is already pending"); _; } } interface AggregatorInterface { function latestAnswer() external view returns (int256); function latestTimestamp() external view returns (uint256); function latestRound() external view returns (uint256); function getAnswer(uint256 roundId) external view returns (int256); function getTimestamp(uint256 roundId) external view returns (uint256); event AnswerUpdated(int256 indexed current, uint256 indexed roundId, uint256 timestamp); event NewRound(uint256 indexed roundId, address indexed startedBy); } library SignedSafeMath { /** * @dev Adds two int256s and makes sure the result doesn't overflow. Signed * integers aren't supported by the SafeMath library, thus this method * @param _a The first number to be added * @param _a The second number to be added */ function add(int256 _a, int256 _b) internal pure returns (int256) { int256 c = _a + _b; require((_b >= 0 && c >= _a) || (_b < 0 && c < _a), "SignedSafeMath: addition overflow"); return c; } } /** * @title Ownable * @dev The Ownable contract has an owner address, and provides basic authorization control * functions, this simplifies the implementation of "user permissions". */ contract Ownable { address public owner; event OwnershipRenounced(address indexed previousOwner); event OwnershipTransferred( address indexed previousOwner, address indexed newOwner ); /** * @dev The Ownable constructor sets the original `owner` of the contract to the sender * account. */ constructor() public { owner = msg.sender; } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { require(msg.sender == owner); _; } /** * @dev Allows the current owner to relinquish control of the contract. * @notice Renouncing to ownership will leave the contract without an owner. * It will not be possible to call the functions with the `onlyOwner` * modifier anymore. */ function renounceOwnership() public onlyOwner { emit OwnershipRenounced(owner); owner = address(0); } /** * @dev Allows the current owner to transfer control of the contract to a newOwner. * @param _newOwner The address to transfer ownership to. */ function transferOwnership(address _newOwner) public onlyOwner { _transferOwnership(_newOwner); } /** * @dev Transfers control of the contract to a newOwner. * @param _newOwner The address to transfer ownership to. */ function _transferOwnership(address _newOwner) internal { require(_newOwner != address(0)); emit OwnershipTransferred(owner, _newOwner); owner = _newOwner; } } /** * @title An example Chainlink contract with aggregation * @notice Requesters can use this contract as a framework for creating * requests to multiple Chainlink nodes and running aggregation * as the contract receives answers. */ contract Aggregator is AggregatorInterface, ChainlinkClient, Ownable { using SignedSafeMath for int256; struct Answer { uint128 minimumResponses; uint128 maxResponses; int256[] responses; } event ResponseReceived(int256 indexed response, uint256 indexed answerId, address indexed sender); int256 private currentAnswerValue; uint256 private updatedTimestampValue; uint256 private latestCompletedAnswer; uint128 public paymentAmount; uint128 public minimumResponses; bytes32[] public jobIds; address[] public oracles; uint256 private answerCounter = 1; mapping(address => bool) public authorizedRequesters; mapping(bytes32 => uint256) private requestAnswers; mapping(uint256 => Answer) private answers; mapping(uint256 => int256) private currentAnswers; mapping(uint256 => uint256) private updatedTimestamps; uint256 constant private MAX_ORACLE_COUNT = 45; /** * @notice Deploy with the address of the LINK token and arrays of matching * length containing the addresses of the oracles and their corresponding * Job IDs. * @dev Sets the LinkToken address for the network, addresses of the oracles, * and jobIds in storage. * @param _link The address of the LINK token * @param _paymentAmount the amount of LINK to be sent to each oracle for each request * @param _minimumResponses the minimum number of responses * before an answer will be calculated * @param _oracles An array of oracle addresses * @param _jobIds An array of Job IDs */ constructor( address _link, uint128 _paymentAmount, uint128 _minimumResponses, address[] _oracles, bytes32[] _jobIds ) public Ownable() { setChainlinkToken(_link); updateRequestDetails(_paymentAmount, _minimumResponses, _oracles, _jobIds); } /** * @notice Creates a Chainlink request for each oracle in the oracles array. * @dev This example does not include request parameters. Reference any documentation * associated with the Job IDs used to determine the required parameters per-request. */ function requestRateUpdate() external ensureAuthorizedRequester() { Chainlink.Request memory request; bytes32 requestId; uint256 oraclePayment = paymentAmount; for (uint i = 0; i < oracles.length; i++) { request = buildChainlinkRequest(jobIds[i], this, this.chainlinkCallback.selector); requestId = sendChainlinkRequestTo(oracles[i], request, oraclePayment); requestAnswers[requestId] = answerCounter; } answers[answerCounter].minimumResponses = minimumResponses; answers[answerCounter].maxResponses = uint128(oracles.length); answerCounter = answerCounter.add(1); emit NewRound(answerCounter, msg.sender); } /** * @notice Receives the answer from the Chainlink node. * @dev This function can only be called by the oracle that received the request. * @param _clRequestId The Chainlink request ID associated with the answer * @param _response The answer provided by the Chainlink node */ function chainlinkCallback(bytes32 _clRequestId, int256 _response) external { validateChainlinkCallback(_clRequestId); uint256 answerId = requestAnswers[_clRequestId]; delete requestAnswers[_clRequestId]; answers[answerId].responses.push(_response); emit ResponseReceived(_response, answerId, msg.sender); updateLatestAnswer(answerId); deleteAnswer(answerId); } /** * @notice Updates the arrays of oracles and jobIds with new values, * overwriting the old values. * @dev Arrays are validated to be equal length. * @param _paymentAmount the amount of LINK to be sent to each oracle for each request * @param _minimumResponses the minimum number of responses * before an answer will be calculated * @param _oracles An array of oracle addresses * @param _jobIds An array of Job IDs */ function updateRequestDetails( uint128 _paymentAmount, uint128 _minimumResponses, address[] _oracles, bytes32[] _jobIds ) public onlyOwner() validateAnswerRequirements(_minimumResponses, _oracles, _jobIds) { paymentAmount = _paymentAmount; minimumResponses = _minimumResponses; jobIds = _jobIds; oracles = _oracles; } /** * @notice Allows the owner of the contract to withdraw any LINK balance * available on the contract. * @dev The contract will need to have a LINK balance in order to create requests. * @param _recipient The address to receive the LINK tokens * @param _amount The amount of LINK to send from the contract */ function transferLINK(address _recipient, uint256 _amount) public onlyOwner() { LinkTokenInterface linkToken = LinkTokenInterface(chainlinkTokenAddress()); require(linkToken.transfer(_recipient, _amount), "LINK transfer failed"); } /** * @notice Called by the owner to permission other addresses to generate new * requests to oracles. * @param _requester the address whose permissions are being set * @param _allowed boolean that determines whether the requester is * permissioned or not */ function setAuthorization(address _requester, bool _allowed) external onlyOwner() { authorizedRequesters[_requester] = _allowed; } /** * @notice Cancels an outstanding Chainlink request. * The oracle contract requires the request ID and additional metadata to * validate the cancellation. Only old answers can be cancelled. * @param _requestId is the identifier for the chainlink request being cancelled * @param _payment is the amount of LINK paid to the oracle for the request * @param _expiration is the time when the request expires */ function cancelRequest( bytes32 _requestId, uint256 _payment, uint256 _expiration ) external ensureAuthorizedRequester() { uint256 answerId = requestAnswers[_requestId]; require(answerId < latestCompletedAnswer, "Cannot modify an in-progress answer"); delete requestAnswers[_requestId]; answers[answerId].responses.push(0); deleteAnswer(answerId); cancelChainlinkRequest( _requestId, _payment, this.chainlinkCallback.selector, _expiration ); } /** * @notice Called by the owner to kill the contract. This transfers all LINK * balance and ETH balance (if there is any) to the owner. */ function destroy() external onlyOwner() { LinkTokenInterface linkToken = LinkTokenInterface(chainlinkTokenAddress()); transferLINK(owner, linkToken.balanceOf(address(this))); selfdestruct(owner); } /** * @dev Performs aggregation of the answers received from the Chainlink nodes. * Assumes that at least half the oracles are honest and so can't contol the * middle of the ordered responses. * @param _answerId The answer ID associated with the group of requests */ function updateLatestAnswer(uint256 _answerId) private ensureMinResponsesReceived(_answerId) ensureOnlyLatestAnswer(_answerId) { uint256 responseLength = answers[_answerId].responses.length; uint256 middleIndex = responseLength.div(2); int256 currentAnswerTemp; if (responseLength % 2 == 0) { int256 median1 = quickselect(answers[_answerId].responses, middleIndex); int256 median2 = quickselect(answers[_answerId].responses, middleIndex.add(1)); // quickselect is 1 indexed currentAnswerTemp = median1.add(median2) / 2; // signed integers are not supported by SafeMath } else { currentAnswerTemp = quickselect(answers[_answerId].responses, middleIndex.add(1)); // quickselect is 1 indexed } currentAnswerValue = currentAnswerTemp; latestCompletedAnswer = _answerId; updatedTimestampValue = now; updatedTimestamps[_answerId] = now; currentAnswers[_answerId] = currentAnswerTemp; emit AnswerUpdated(currentAnswerTemp, _answerId, now); } /** * @notice get the most recently reported answer */ function latestAnswer() external view returns (int256) { return currentAnswers[latestCompletedAnswer]; } /** * @notice get the last updated at block timestamp */ function latestTimestamp() external view returns (uint256) { return updatedTimestamps[latestCompletedAnswer]; } /** * @notice get past rounds answers * @param _roundId the answer number to retrieve the answer for */ function getAnswer(uint256 _roundId) external view returns (int256) { return currentAnswers[_roundId]; } /** * @notice get block timestamp when an answer was last updated * @param _roundId the answer number to retrieve the updated timestamp for */ function getTimestamp(uint256 _roundId) external view returns (uint256) { return updatedTimestamps[_roundId]; } /** * @notice get the latest completed round where the answer was updated */ function latestRound() external view returns (uint256) { return latestCompletedAnswer; } /** * @dev Returns the kth value of the ordered array * See: http://www.cs.yale.edu/homes/aspnes/pinewiki/QuickSelect.html * @param _a The list of elements to pull from * @param _k The index, 1 based, of the elements you want to pull from when ordered */ function quickselect(int256[] memory _a, uint256 _k) private pure returns (int256) { int256[] memory a = _a; uint256 k = _k; uint256 aLen = a.length; int256[] memory a1 = new int256[](aLen); int256[] memory a2 = new int256[](aLen); uint256 a1Len; uint256 a2Len; int256 pivot; uint256 i; while (true) { pivot = a[aLen.div(2)]; a1Len = 0; a2Len = 0; for (i = 0; i < aLen; i++) { if (a[i] < pivot) { a1[a1Len] = a[i]; a1Len++; } else if (a[i] > pivot) { a2[a2Len] = a[i]; a2Len++; } } if (k <= a1Len) { aLen = a1Len; (a, a1) = swap(a, a1); } else if (k > (aLen.sub(a2Len))) { k = k.sub(aLen.sub(a2Len)); aLen = a2Len; (a, a2) = swap(a, a2); } else { return pivot; } } } /** * @dev Swaps the pointers to two uint256 arrays in memory * @param _a The pointer to the first in memory array * @param _b The pointer to the second in memory array */ function swap(int256[] memory _a, int256[] memory _b) private pure returns(int256[] memory, int256[] memory) { return (_b, _a); } /** * @dev Cleans up the answer record if all responses have been received. * @param _answerId The identifier of the answer to be deleted */ function deleteAnswer(uint256 _answerId) private ensureAllResponsesReceived(_answerId) { delete answers[_answerId]; } /** * @dev Prevents taking an action if the minimum number of responses has not * been received for an answer. * @param _answerId The the identifier of the answer that keeps track of the responses. */ modifier ensureMinResponsesReceived(uint256 _answerId) { if (answers[_answerId].responses.length >= answers[_answerId].minimumResponses) { _; } } /** * @dev Prevents taking an action if not all responses are received for an answer. * @param _answerId The the identifier of the answer that keeps track of the responses. */ modifier ensureAllResponsesReceived(uint256 _answerId) { if (answers[_answerId].responses.length == answers[_answerId].maxResponses) { _; } } /** * @dev Prevents taking an action if a newer answer has been recorded. * @param _answerId The current answer's identifier. * Answer IDs are in ascending order. */ modifier ensureOnlyLatestAnswer(uint256 _answerId) { if (latestCompletedAnswer <= _answerId) { _; } } /** * @dev Ensures corresponding number of oracles and jobs. * @param _oracles The list of oracles. * @param _jobIds The list of jobs. */ modifier validateAnswerRequirements( uint256 _minimumResponses, address[] _oracles, bytes32[] _jobIds ) { require(_oracles.length <= MAX_ORACLE_COUNT, "cannot have more than 45 oracles"); require(_oracles.length >= _minimumResponses, "must have at least as many oracles as responses"); require(_oracles.length == _jobIds.length, "must have exactly as many oracles as job IDs"); _; } /** * @dev Reverts if `msg.sender` is not authorized to make requests. */ modifier ensureAuthorizedRequester() { require(authorizedRequesters[msg.sender] || msg.sender == owner, "Not an authorized address for creating requests"); _; } }
File 2 of 9: LinkToken
pragma solidity ^0.4.16; /** * @title SafeMath * @dev Math operations with safety checks that throw on error */ library SafeMath { function mul(uint256 a, uint256 b) internal constant returns (uint256) { uint256 c = a * b; assert(a == 0 || c / a == b); return c; } function div(uint256 a, uint256 b) internal constant returns (uint256) { // assert(b > 0); // Solidity automatically throws when dividing by 0 uint256 c = a / b; // assert(a == b * c + a % b); // There is no case in which this doesn't hold return c; } function sub(uint256 a, uint256 b) internal constant returns (uint256) { assert(b <= a); return a - b; } function add(uint256 a, uint256 b) internal constant returns (uint256) { uint256 c = a + b; assert(c >= a); return c; } } /** * @title ERC20Basic * @dev Simpler version of ERC20 interface * @dev see https://github.com/ethereum/EIPs/issues/179 */ contract ERC20Basic { uint256 public totalSupply; function balanceOf(address who) constant returns (uint256); function transfer(address to, uint256 value) returns (bool); event Transfer(address indexed from, address indexed to, uint256 value); } /** * @title ERC20 interface * @dev see https://github.com/ethereum/EIPs/issues/20 */ contract ERC20 is ERC20Basic { function allowance(address owner, address spender) constant returns (uint256); function transferFrom(address from, address to, uint256 value) returns (bool); function approve(address spender, uint256 value) returns (bool); event Approval(address indexed owner, address indexed spender, uint256 value); } contract ERC677 is ERC20 { function transferAndCall(address to, uint value, bytes data) returns (bool success); event Transfer(address indexed from, address indexed to, uint value, bytes data); } contract ERC677Receiver { function onTokenTransfer(address _sender, uint _value, bytes _data); } /** * @title Basic token * @dev Basic version of StandardToken, with no allowances. */ contract BasicToken is ERC20Basic { using SafeMath for uint256; mapping(address => uint256) balances; /** * @dev transfer token for a specified address * @param _to The address to transfer to. * @param _value The amount to be transferred. */ function transfer(address _to, uint256 _value) returns (bool) { balances[msg.sender] = balances[msg.sender].sub(_value); balances[_to] = balances[_to].add(_value); Transfer(msg.sender, _to, _value); return true; } /** * @dev Gets the balance of the specified address. * @param _owner The address to query the the balance of. * @return An uint256 representing the amount owned by the passed address. */ function balanceOf(address _owner) constant returns (uint256 balance) { return balances[_owner]; } } /** * @title Standard ERC20 token * * @dev Implementation of the basic standard token. * @dev https://github.com/ethereum/EIPs/issues/20 * @dev Based on code by FirstBlood: https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol */ contract StandardToken is ERC20, BasicToken { mapping (address => mapping (address => uint256)) allowed; /** * @dev Transfer tokens from one address to another * @param _from address The address which you want to send tokens from * @param _to address The address which you want to transfer to * @param _value uint256 the amount of tokens to be transferred */ function transferFrom(address _from, address _to, uint256 _value) returns (bool) { var _allowance = allowed[_from][msg.sender]; // Check is not needed because sub(_allowance, _value) will already throw if this condition is not met // require (_value <= _allowance); balances[_from] = balances[_from].sub(_value); balances[_to] = balances[_to].add(_value); allowed[_from][msg.sender] = _allowance.sub(_value); Transfer(_from, _to, _value); return true; } /** * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender. * @param _spender The address which will spend the funds. * @param _value The amount of tokens to be spent. */ function approve(address _spender, uint256 _value) returns (bool) { allowed[msg.sender][_spender] = _value; Approval(msg.sender, _spender, _value); return true; } /** * @dev Function to check the amount of tokens that an owner allowed to a spender. * @param _owner address The address which owns the funds. * @param _spender address The address which will spend the funds. * @return A uint256 specifying the amount of tokens still available for the spender. */ function allowance(address _owner, address _spender) constant returns (uint256 remaining) { return allowed[_owner][_spender]; } /* * approve should be called when allowed[_spender] == 0. To increment * allowed value is better to use this function to avoid 2 calls (and wait until * the first transaction is mined) * From MonolithDAO Token.sol */ function increaseApproval (address _spender, uint _addedValue) returns (bool success) { allowed[msg.sender][_spender] = allowed[msg.sender][_spender].add(_addedValue); Approval(msg.sender, _spender, allowed[msg.sender][_spender]); return true; } function decreaseApproval (address _spender, uint _subtractedValue) returns (bool success) { uint oldValue = allowed[msg.sender][_spender]; if (_subtractedValue > oldValue) { allowed[msg.sender][_spender] = 0; } else { allowed[msg.sender][_spender] = oldValue.sub(_subtractedValue); } Approval(msg.sender, _spender, allowed[msg.sender][_spender]); return true; } } contract ERC677Token is ERC677 { /** * @dev transfer token to a contract address with additional data if the recipient is a contact. * @param _to The address to transfer to. * @param _value The amount to be transferred. * @param _data The extra data to be passed to the receiving contract. */ function transferAndCall(address _to, uint _value, bytes _data) public returns (bool success) { super.transfer(_to, _value); Transfer(msg.sender, _to, _value, _data); if (isContract(_to)) { contractFallback(_to, _value, _data); } return true; } // PRIVATE function contractFallback(address _to, uint _value, bytes _data) private { ERC677Receiver receiver = ERC677Receiver(_to); receiver.onTokenTransfer(msg.sender, _value, _data); } function isContract(address _addr) private returns (bool hasCode) { uint length; assembly { length := extcodesize(_addr) } return length > 0; } } contract LinkToken is StandardToken, ERC677Token { uint public constant totalSupply = 10**27; string public constant name = 'ChainLink Token'; uint8 public constant decimals = 18; string public constant symbol = 'LINK'; function LinkToken() public { balances[msg.sender] = totalSupply; } /** * @dev transfer token to a specified address with additional data if the recipient is a contract. * @param _to The address to transfer to. * @param _value The amount to be transferred. * @param _data The extra data to be passed to the receiving contract. */ function transferAndCall(address _to, uint _value, bytes _data) public validRecipient(_to) returns (bool success) { return super.transferAndCall(_to, _value, _data); } /** * @dev transfer token to a specified address. * @param _to The address to transfer to. * @param _value The amount to be transferred. */ function transfer(address _to, uint _value) public validRecipient(_to) returns (bool success) { return super.transfer(_to, _value); } /** * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender. * @param _spender The address which will spend the funds. * @param _value The amount of tokens to be spent. */ function approve(address _spender, uint256 _value) public validRecipient(_spender) returns (bool) { return super.approve(_spender, _value); } /** * @dev Transfer tokens from one address to another * @param _from address The address which you want to send tokens from * @param _to address The address which you want to transfer to * @param _value uint256 the amount of tokens to be transferred */ function transferFrom(address _from, address _to, uint256 _value) public validRecipient(_to) returns (bool) { return super.transferFrom(_from, _to, _value); } // MODIFIERS modifier validRecipient(address _recipient) { require(_recipient != address(0) && _recipient != address(this)); _; } }
File 3 of 9: Oracle
/* * ___ ___ ___ ___ * `MM `MMb dMM' `MM * MM MMM. ,PMM MM * ____MM M`Mb d'MM ___ MM __ ____ ___ __ ____ _____ ___ __ __ * 6MMMMMM M YM. ,P MM 6MMMMb MM d' 6MMMMb `MM 6MM 6MMMMb\ 6MMMMMb `MM 6MM 6MMbMMM * 6M' `MM M `Mb d' MM 8M' `Mb MM d' 6M' `Mb MM69 " MM' ` 6M' `Mb MM69 " 6M'`Mb * MM MM M YM.P MM ,oMM MM d' MM MM MM' YM. MM MM MM' MM MM * MM MM M `Mb' MM ,6MM9'MM MMdM. MMMMMMMM MM YMMMMb MM MM MM YM.,M9 * MM MM M YP MM MM' MM MMPYM. MM MM `Mb MM MM MM YMM9 * YM. ,MM M `' MM MM. ,MM MM YM. YM d9 MM L ,MM 68b YM. ,M9 MM (M * YMMMMMM__M_ _MM_`YMMM9'Yb_MM_ YM._YMMMM9 _MM_ MYMMMM9 Y89 YMMMMM9 _MM_ YMMMMb. * 6M Yb * YM. d9 * YMMMM9 */ pragma solidity 0.4.24; /** * @title Ownable * @dev The Ownable contract has an owner address, and provides basic authorization control * functions, this simplifies the implementation of "user permissions". */ contract Ownable { address public owner; event OwnershipRenounced(address indexed previousOwner); event OwnershipTransferred( address indexed previousOwner, address indexed newOwner ); /** * @dev The Ownable constructor sets the original `owner` of the contract to the sender * account. */ constructor() public { owner = msg.sender; } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { require(msg.sender == owner); _; } /** * @dev Allows the current owner to relinquish control of the contract. * @notice Renouncing to ownership will leave the contract without an owner. * It will not be possible to call the functions with the `onlyOwner` * modifier anymore. */ function renounceOwnership() public onlyOwner { emit OwnershipRenounced(owner); owner = address(0); } /** * @dev Allows the current owner to transfer control of the contract to a newOwner. * @param _newOwner The address to transfer ownership to. */ function transferOwnership(address _newOwner) public onlyOwner { _transferOwnership(_newOwner); } /** * @dev Transfers control of the contract to a newOwner. * @param _newOwner The address to transfer ownership to. */ function _transferOwnership(address _newOwner) internal { require(_newOwner != address(0)); emit OwnershipTransferred(owner, _newOwner); owner = _newOwner; } } // File: openzeppelin-solidity/contracts/math/SafeMath.sol pragma solidity ^0.4.24; /** * @title SafeMath * @dev Math operations with safety checks that throw on error */ library SafeMath { /** * @dev Multiplies two numbers, throws on overflow. */ function mul(uint256 _a, uint256 _b) internal pure returns (uint256 c) { // Gas optimization: this is cheaper than asserting 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522 if (_a == 0) { return 0; } c = _a * _b; assert(c / _a == _b); return c; } /** * @dev Integer division of two numbers, truncating the quotient. */ function div(uint256 _a, uint256 _b) internal pure returns (uint256) { // assert(_b > 0); // Solidity automatically throws when dividing by 0 // uint256 c = _a / _b; // assert(_a == _b * c + _a % _b); // There is no case in which this doesn't hold return _a / _b; } /** * @dev Subtracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend). */ function sub(uint256 _a, uint256 _b) internal pure returns (uint256) { assert(_b <= _a); return _a - _b; } /** * @dev Adds two numbers, throws on overflow. */ function add(uint256 _a, uint256 _b) internal pure returns (uint256 c) { c = _a + _b; assert(c >= _a); return c; } } // File: contracts/interfaces/ChainlinkRequestInterface.sol pragma solidity 0.4.24; interface ChainlinkRequestInterface { function oracleRequest( address sender, uint256 payment, bytes32 id, address callbackAddress, bytes4 callbackFunctionId, uint256 nonce, uint256 version, bytes data ) external; function cancelOracleRequest( bytes32 requestId, uint256 payment, bytes4 callbackFunctionId, uint256 expiration ) external; } // File: contracts/interfaces/OracleInterface.sol pragma solidity 0.4.24; interface OracleInterface { function fulfillOracleRequest( bytes32 requestId, uint256 payment, address callbackAddress, bytes4 callbackFunctionId, uint256 expiration, bytes32 data ) external returns (bool); function getAuthorizationStatus(address node) external view returns (bool); function setFulfillmentPermission(address node, bool allowed) external; function withdraw(address recipient, uint256 amount) external; function withdrawable() external view returns (uint256); } // File: contracts/interfaces/LinkTokenInterface.sol pragma solidity 0.4.24; interface LinkTokenInterface { function allowance(address owner, address spender) external returns (bool success); function approve(address spender, uint256 value) external returns (bool success); function balanceOf(address owner) external returns (uint256 balance); function decimals() external returns (uint8 decimalPlaces); function decreaseApproval(address spender, uint256 addedValue) external returns (bool success); function increaseApproval(address spender, uint256 subtractedValue) external; function name() external returns (string tokenName); function symbol() external returns (string tokenSymbol); function totalSupply() external returns (uint256 totalTokensIssued); function transfer(address to, uint256 value) external returns (bool success); function transferAndCall(address to, uint256 value, bytes data) external returns (bool success); function transferFrom(address from, address to, uint256 value) external returns (bool success); } // File: contracts/Oracle.sol pragma solidity 0.4.24; /** * @title The Chainlink Oracle contract * @notice Node operators can deploy this contract to fulfill requests sent to them */ contract Oracle is ChainlinkRequestInterface, OracleInterface, Ownable { using SafeMath for uint256; uint256 constant public EXPIRY_TIME = 5 minutes; uint256 constant private MINIMUM_CONSUMER_GAS_LIMIT = 400000; // We initialize fields to 1 instead of 0 so that the first invocation // does not cost more gas. uint256 constant private ONE_FOR_CONSISTENT_GAS_COST = 1; uint256 constant private SELECTOR_LENGTH = 4; uint256 constant private EXPECTED_REQUEST_WORDS = 2; // solium-disable-next-line zeppelin/no-arithmetic-operations uint256 constant private MINIMUM_REQUEST_LENGTH = SELECTOR_LENGTH + (32 * EXPECTED_REQUEST_WORDS); LinkTokenInterface internal LinkToken; mapping(bytes32 => bytes32) private commitments; mapping(address => bool) private authorizedNodes; uint256 private withdrawableTokens = ONE_FOR_CONSISTENT_GAS_COST; event OracleRequest( bytes32 indexed specId, address requester, bytes32 requestId, uint256 payment, address callbackAddr, bytes4 callbackFunctionId, uint256 cancelExpiration, uint256 dataVersion, bytes data ); event CancelOracleRequest( bytes32 indexed requestId ); /** * @notice Deploy with the address of the LINK token * @dev Sets the LinkToken address for the imported LinkTokenInterface * @param _link The address of the LINK token */ constructor(address _link) Ownable() public { LinkToken = LinkTokenInterface(_link); } /** * @notice Called when LINK is sent to the contract via `transferAndCall` * @dev The data payload's first 2 words will be overwritten by the `_sender` and `_amount` * values to ensure correctness. Calls oracleRequest. * @param _sender Address of the sender * @param _amount Amount of LINK sent (specified in wei) * @param _data Payload of the transaction */ function onTokenTransfer( address _sender, uint256 _amount, bytes _data ) public onlyLINK validRequestLength(_data) permittedFunctionsForLINK(_data) { assembly { // solium-disable-next-line security/no-low-level-calls mstore(add(_data, 36), _sender) // ensure correct sender is passed // solium-disable-next-line security/no-low-level-calls mstore(add(_data, 68), _amount) // ensure correct amount is passed } // solium-disable-next-line security/no-low-level-calls require(address(this).delegatecall(_data), "Unable to create request"); // calls oracleRequest } /** * @notice Creates the Chainlink request * @dev Stores the hash of the params as the on-chain commitment for the request. * Emits OracleRequest event for the Chainlink node to detect. * @param _sender The sender of the request * @param _payment The amount of payment given (specified in wei) * @param _specId The Job Specification ID * @param _callbackAddress The callback address for the response * @param _callbackFunctionId The callback function ID for the response * @param _nonce The nonce sent by the requester * @param _dataVersion The specified data version * @param _data The CBOR payload of the request */ function oracleRequest( address _sender, uint256 _payment, bytes32 _specId, address _callbackAddress, bytes4 _callbackFunctionId, uint256 _nonce, uint256 _dataVersion, bytes _data ) external onlyLINK checkCallbackAddress(_callbackAddress) { bytes32 requestId = keccak256(abi.encodePacked(_sender, _nonce)); require(commitments[requestId] == 0, "Must use a unique ID"); uint256 expiration = now.add(EXPIRY_TIME); commitments[requestId] = keccak256( abi.encodePacked( _payment, _callbackAddress, _callbackFunctionId, expiration ) ); emit OracleRequest( _specId, _sender, requestId, _payment, _callbackAddress, _callbackFunctionId, expiration, _dataVersion, _data); } /** * @notice Called by the Chainlink node to fulfill requests * @dev Given params must hash back to the commitment stored from `oracleRequest`. * Will call the callback address' callback function without bubbling up error * checking in a `require` so that the node can get paid. * @param _requestId The fulfillment request ID that must match the requester's * @param _payment The payment amount that will be released for the oracle (specified in wei) * @param _callbackAddress The callback address to call for fulfillment * @param _callbackFunctionId The callback function ID to use for fulfillment * @param _expiration The expiration that the node should respond by before the requester can cancel * @param _data The data to return to the consuming contract * @return Status if the external call was successful */ function fulfillOracleRequest( bytes32 _requestId, uint256 _payment, address _callbackAddress, bytes4 _callbackFunctionId, uint256 _expiration, bytes32 _data ) external onlyAuthorizedNode isValidRequest(_requestId) returns (bool) { bytes32 paramsHash = keccak256( abi.encodePacked( _payment, _callbackAddress, _callbackFunctionId, _expiration ) ); require(commitments[_requestId] == paramsHash, "Params do not match request ID"); withdrawableTokens = withdrawableTokens.add(_payment); delete commitments[_requestId]; require(gasleft() >= MINIMUM_CONSUMER_GAS_LIMIT, "Must provide consumer enough gas"); // All updates to the oracle's fulfillment should come before calling the // callback(addr+functionId) as it is untrusted. // See: https://solidity.readthedocs.io/en/develop/security-considerations.html#use-the-checks-effects-interactions-pattern return _callbackAddress.call(_callbackFunctionId, _requestId, _data); // solium-disable-line security/no-low-level-calls } /** * @notice Use this to check if a node is authorized for fulfilling requests * @param _node The address of the Chainlink node * @return The authorization status of the node */ function getAuthorizationStatus(address _node) external view returns (bool) { return authorizedNodes[_node]; } /** * @notice Sets the fulfillment permission for a given node. Use `true` to allow, `false` to disallow. * @param _node The address of the Chainlink node * @param _allowed Bool value to determine if the node can fulfill requests */ function setFulfillmentPermission(address _node, bool _allowed) external onlyOwner { authorizedNodes[_node] = _allowed; } /** * @notice Allows the node operator to withdraw earned LINK to a given address * @dev The owner of the contract can be another wallet and does not have to be a Chainlink node * @param _recipient The address to send the LINK token to * @param _amount The amount to send (specified in wei) */ function withdraw(address _recipient, uint256 _amount) external onlyOwner hasAvailableFunds(_amount) { withdrawableTokens = withdrawableTokens.sub(_amount); assert(LinkToken.transfer(_recipient, _amount)); } /** * @notice Displays the amount of LINK that is available for the node operator to withdraw * @dev We use `ONE_FOR_CONSISTENT_GAS_COST` in place of 0 in storage * @return The amount of withdrawable LINK on the contract */ function withdrawable() external view onlyOwner returns (uint256) { return withdrawableTokens.sub(ONE_FOR_CONSISTENT_GAS_COST); } /** * @notice Allows requesters to cancel requests sent to this oracle contract. Will transfer the LINK * sent for the request back to the requester's address. * @dev Given params must hash to a commitment stored on the contract in order for the request to be valid * Emits CancelOracleRequest event. * @param _requestId The request ID * @param _payment The amount of payment given (specified in wei) * @param _callbackFunc The requester's specified callback address * @param _expiration The time of the expiration for the request */ function cancelOracleRequest( bytes32 _requestId, uint256 _payment, bytes4 _callbackFunc, uint256 _expiration ) external { bytes32 paramsHash = keccak256( abi.encodePacked( _payment, msg.sender, _callbackFunc, _expiration) ); require(paramsHash == commitments[_requestId], "Params do not match request ID"); require(_expiration <= now, "Request is not expired"); delete commitments[_requestId]; emit CancelOracleRequest(_requestId); assert(LinkToken.transfer(msg.sender, _payment)); } // MODIFIERS /** * @dev Reverts if amount requested is greater than withdrawable balance * @param _amount The given amount to compare to `withdrawableTokens` */ modifier hasAvailableFunds(uint256 _amount) { require(withdrawableTokens >= _amount.add(ONE_FOR_CONSISTENT_GAS_COST), "Amount requested is greater than withdrawable balance"); _; } /** * @dev Reverts if request ID does not exist * @param _requestId The given request ID to check in stored `commitments` */ modifier isValidRequest(bytes32 _requestId) { require(commitments[_requestId] != 0, "Must have a valid requestId"); _; } /** * @dev Reverts if `msg.sender` is not authorized to fulfill requests */ modifier onlyAuthorizedNode() { require(authorizedNodes[msg.sender] || msg.sender == owner, "Not an authorized node to fulfill requests"); _; } /** * @dev Reverts if not sent from the LINK token */ modifier onlyLINK() { require(msg.sender == address(LinkToken), "Must use LINK token"); _; } /** * @dev Reverts if the given data does not begin with the `oracleRequest` function selector * @param _data The data payload of the request */ modifier permittedFunctionsForLINK(bytes _data) { bytes4 funcSelector; assembly { // solium-disable-next-line security/no-low-level-calls funcSelector := mload(add(_data, 32)) } require(funcSelector == this.oracleRequest.selector, "Must use whitelisted functions"); _; } /** * @dev Reverts if the callback address is the LINK token * @param _to The callback address */ modifier checkCallbackAddress(address _to) { require(_to != address(LinkToken), "Cannot callback to LINK"); _; } /** * @dev Reverts if the given payload is less than needed to create a request * @param _data The request payload */ modifier validRequestLength(bytes _data) { require(_data.length >= MINIMUM_REQUEST_LENGTH, "Invalid request length"); _; } }
File 4 of 9: Oracle
/* * ___ ___ ___ ___ * `MM `MMb dMM' `MM * MM MMM. ,PMM MM * ____MM M`Mb d'MM ___ MM __ ____ ___ __ ____ _____ ___ __ __ * 6MMMMMM M YM. ,P MM 6MMMMb MM d' 6MMMMb `MM 6MM 6MMMMb\ 6MMMMMb `MM 6MM 6MMbMMM * 6M' `MM M `Mb d' MM 8M' `Mb MM d' 6M' `Mb MM69 " MM' ` 6M' `Mb MM69 " 6M'`Mb * MM MM M YM.P MM ,oMM MM d' MM MM MM' YM. MM MM MM' MM MM * MM MM M `Mb' MM ,6MM9'MM MMdM. MMMMMMMM MM YMMMMb MM MM MM YM.,M9 * MM MM M YP MM MM' MM MMPYM. MM MM `Mb MM MM MM YMM9 * YM. ,MM M `' MM MM. ,MM MM YM. YM d9 MM L ,MM 68b YM. ,M9 MM (M * YMMMMMM__M_ _MM_`YMMM9'Yb_MM_ YM._YMMMM9 _MM_ MYMMMM9 Y89 YMMMMM9 _MM_ YMMMMb. * 6M Yb * YM. d9 * YMMMM9 */ pragma solidity 0.4.24; /** * @title Ownable * @dev The Ownable contract has an owner address, and provides basic authorization control * functions, this simplifies the implementation of "user permissions". */ contract Ownable { address public owner; event OwnershipRenounced(address indexed previousOwner); event OwnershipTransferred( address indexed previousOwner, address indexed newOwner ); /** * @dev The Ownable constructor sets the original `owner` of the contract to the sender * account. */ constructor() public { owner = msg.sender; } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { require(msg.sender == owner); _; } /** * @dev Allows the current owner to relinquish control of the contract. * @notice Renouncing to ownership will leave the contract without an owner. * It will not be possible to call the functions with the `onlyOwner` * modifier anymore. */ function renounceOwnership() public onlyOwner { emit OwnershipRenounced(owner); owner = address(0); } /** * @dev Allows the current owner to transfer control of the contract to a newOwner. * @param _newOwner The address to transfer ownership to. */ function transferOwnership(address _newOwner) public onlyOwner { _transferOwnership(_newOwner); } /** * @dev Transfers control of the contract to a newOwner. * @param _newOwner The address to transfer ownership to. */ function _transferOwnership(address _newOwner) internal { require(_newOwner != address(0)); emit OwnershipTransferred(owner, _newOwner); owner = _newOwner; } } // File: openzeppelin-solidity/contracts/math/SafeMath.sol pragma solidity ^0.4.24; /** * @title SafeMath * @dev Math operations with safety checks that throw on error */ library SafeMath { /** * @dev Multiplies two numbers, throws on overflow. */ function mul(uint256 _a, uint256 _b) internal pure returns (uint256 c) { // Gas optimization: this is cheaper than asserting 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522 if (_a == 0) { return 0; } c = _a * _b; assert(c / _a == _b); return c; } /** * @dev Integer division of two numbers, truncating the quotient. */ function div(uint256 _a, uint256 _b) internal pure returns (uint256) { // assert(_b > 0); // Solidity automatically throws when dividing by 0 // uint256 c = _a / _b; // assert(_a == _b * c + _a % _b); // There is no case in which this doesn't hold return _a / _b; } /** * @dev Subtracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend). */ function sub(uint256 _a, uint256 _b) internal pure returns (uint256) { assert(_b <= _a); return _a - _b; } /** * @dev Adds two numbers, throws on overflow. */ function add(uint256 _a, uint256 _b) internal pure returns (uint256 c) { c = _a + _b; assert(c >= _a); return c; } } // File: contracts/interfaces/ChainlinkRequestInterface.sol pragma solidity 0.4.24; interface ChainlinkRequestInterface { function oracleRequest( address sender, uint256 payment, bytes32 id, address callbackAddress, bytes4 callbackFunctionId, uint256 nonce, uint256 version, bytes data ) external; function cancelOracleRequest( bytes32 requestId, uint256 payment, bytes4 callbackFunctionId, uint256 expiration ) external; } // File: contracts/interfaces/OracleInterface.sol pragma solidity 0.4.24; interface OracleInterface { function fulfillOracleRequest( bytes32 requestId, uint256 payment, address callbackAddress, bytes4 callbackFunctionId, uint256 expiration, bytes32 data ) external returns (bool); function getAuthorizationStatus(address node) external view returns (bool); function setFulfillmentPermission(address node, bool allowed) external; function withdraw(address recipient, uint256 amount) external; function withdrawable() external view returns (uint256); } // File: contracts/interfaces/LinkTokenInterface.sol pragma solidity 0.4.24; interface LinkTokenInterface { function allowance(address owner, address spender) external returns (bool success); function approve(address spender, uint256 value) external returns (bool success); function balanceOf(address owner) external returns (uint256 balance); function decimals() external returns (uint8 decimalPlaces); function decreaseApproval(address spender, uint256 addedValue) external returns (bool success); function increaseApproval(address spender, uint256 subtractedValue) external; function name() external returns (string tokenName); function symbol() external returns (string tokenSymbol); function totalSupply() external returns (uint256 totalTokensIssued); function transfer(address to, uint256 value) external returns (bool success); function transferAndCall(address to, uint256 value, bytes data) external returns (bool success); function transferFrom(address from, address to, uint256 value) external returns (bool success); } // File: contracts/Oracle.sol pragma solidity 0.4.24; /** * @title The Chainlink Oracle contract * @notice Node operators can deploy this contract to fulfill requests sent to them */ contract Oracle is ChainlinkRequestInterface, OracleInterface, Ownable { using SafeMath for uint256; uint256 constant public EXPIRY_TIME = 5 minutes; uint256 constant private MINIMUM_CONSUMER_GAS_LIMIT = 400000; // We initialize fields to 1 instead of 0 so that the first invocation // does not cost more gas. uint256 constant private ONE_FOR_CONSISTENT_GAS_COST = 1; uint256 constant private SELECTOR_LENGTH = 4; uint256 constant private EXPECTED_REQUEST_WORDS = 2; // solium-disable-next-line zeppelin/no-arithmetic-operations uint256 constant private MINIMUM_REQUEST_LENGTH = SELECTOR_LENGTH + (32 * EXPECTED_REQUEST_WORDS); LinkTokenInterface internal LinkToken; mapping(bytes32 => bytes32) private commitments; mapping(address => bool) private authorizedNodes; uint256 private withdrawableTokens = ONE_FOR_CONSISTENT_GAS_COST; event OracleRequest( bytes32 indexed specId, address requester, bytes32 requestId, uint256 payment, address callbackAddr, bytes4 callbackFunctionId, uint256 cancelExpiration, uint256 dataVersion, bytes data ); event CancelOracleRequest( bytes32 indexed requestId ); /** * @notice Deploy with the address of the LINK token * @dev Sets the LinkToken address for the imported LinkTokenInterface * @param _link The address of the LINK token */ constructor(address _link) Ownable() public { LinkToken = LinkTokenInterface(_link); } /** * @notice Called when LINK is sent to the contract via `transferAndCall` * @dev The data payload's first 2 words will be overwritten by the `_sender` and `_amount` * values to ensure correctness. Calls oracleRequest. * @param _sender Address of the sender * @param _amount Amount of LINK sent (specified in wei) * @param _data Payload of the transaction */ function onTokenTransfer( address _sender, uint256 _amount, bytes _data ) public onlyLINK validRequestLength(_data) permittedFunctionsForLINK(_data) { assembly { // solium-disable-next-line security/no-low-level-calls mstore(add(_data, 36), _sender) // ensure correct sender is passed // solium-disable-next-line security/no-low-level-calls mstore(add(_data, 68), _amount) // ensure correct amount is passed } // solium-disable-next-line security/no-low-level-calls require(address(this).delegatecall(_data), "Unable to create request"); // calls oracleRequest } /** * @notice Creates the Chainlink request * @dev Stores the hash of the params as the on-chain commitment for the request. * Emits OracleRequest event for the Chainlink node to detect. * @param _sender The sender of the request * @param _payment The amount of payment given (specified in wei) * @param _specId The Job Specification ID * @param _callbackAddress The callback address for the response * @param _callbackFunctionId The callback function ID for the response * @param _nonce The nonce sent by the requester * @param _dataVersion The specified data version * @param _data The CBOR payload of the request */ function oracleRequest( address _sender, uint256 _payment, bytes32 _specId, address _callbackAddress, bytes4 _callbackFunctionId, uint256 _nonce, uint256 _dataVersion, bytes _data ) external onlyLINK checkCallbackAddress(_callbackAddress) { bytes32 requestId = keccak256(abi.encodePacked(_sender, _nonce)); require(commitments[requestId] == 0, "Must use a unique ID"); uint256 expiration = now.add(EXPIRY_TIME); commitments[requestId] = keccak256( abi.encodePacked( _payment, _callbackAddress, _callbackFunctionId, expiration ) ); emit OracleRequest( _specId, _sender, requestId, _payment, _callbackAddress, _callbackFunctionId, expiration, _dataVersion, _data); } /** * @notice Called by the Chainlink node to fulfill requests * @dev Given params must hash back to the commitment stored from `oracleRequest`. * Will call the callback address' callback function without bubbling up error * checking in a `require` so that the node can get paid. * @param _requestId The fulfillment request ID that must match the requester's * @param _payment The payment amount that will be released for the oracle (specified in wei) * @param _callbackAddress The callback address to call for fulfillment * @param _callbackFunctionId The callback function ID to use for fulfillment * @param _expiration The expiration that the node should respond by before the requester can cancel * @param _data The data to return to the consuming contract * @return Status if the external call was successful */ function fulfillOracleRequest( bytes32 _requestId, uint256 _payment, address _callbackAddress, bytes4 _callbackFunctionId, uint256 _expiration, bytes32 _data ) external onlyAuthorizedNode isValidRequest(_requestId) returns (bool) { bytes32 paramsHash = keccak256( abi.encodePacked( _payment, _callbackAddress, _callbackFunctionId, _expiration ) ); require(commitments[_requestId] == paramsHash, "Params do not match request ID"); withdrawableTokens = withdrawableTokens.add(_payment); delete commitments[_requestId]; require(gasleft() >= MINIMUM_CONSUMER_GAS_LIMIT, "Must provide consumer enough gas"); // All updates to the oracle's fulfillment should come before calling the // callback(addr+functionId) as it is untrusted. // See: https://solidity.readthedocs.io/en/develop/security-considerations.html#use-the-checks-effects-interactions-pattern return _callbackAddress.call(_callbackFunctionId, _requestId, _data); // solium-disable-line security/no-low-level-calls } /** * @notice Use this to check if a node is authorized for fulfilling requests * @param _node The address of the Chainlink node * @return The authorization status of the node */ function getAuthorizationStatus(address _node) external view returns (bool) { return authorizedNodes[_node]; } /** * @notice Sets the fulfillment permission for a given node. Use `true` to allow, `false` to disallow. * @param _node The address of the Chainlink node * @param _allowed Bool value to determine if the node can fulfill requests */ function setFulfillmentPermission(address _node, bool _allowed) external onlyOwner { authorizedNodes[_node] = _allowed; } /** * @notice Allows the node operator to withdraw earned LINK to a given address * @dev The owner of the contract can be another wallet and does not have to be a Chainlink node * @param _recipient The address to send the LINK token to * @param _amount The amount to send (specified in wei) */ function withdraw(address _recipient, uint256 _amount) external onlyOwner hasAvailableFunds(_amount) { withdrawableTokens = withdrawableTokens.sub(_amount); assert(LinkToken.transfer(_recipient, _amount)); } /** * @notice Displays the amount of LINK that is available for the node operator to withdraw * @dev We use `ONE_FOR_CONSISTENT_GAS_COST` in place of 0 in storage * @return The amount of withdrawable LINK on the contract */ function withdrawable() external view onlyOwner returns (uint256) { return withdrawableTokens.sub(ONE_FOR_CONSISTENT_GAS_COST); } /** * @notice Allows requesters to cancel requests sent to this oracle contract. Will transfer the LINK * sent for the request back to the requester's address. * @dev Given params must hash to a commitment stored on the contract in order for the request to be valid * Emits CancelOracleRequest event. * @param _requestId The request ID * @param _payment The amount of payment given (specified in wei) * @param _callbackFunc The requester's specified callback address * @param _expiration The time of the expiration for the request */ function cancelOracleRequest( bytes32 _requestId, uint256 _payment, bytes4 _callbackFunc, uint256 _expiration ) external { bytes32 paramsHash = keccak256( abi.encodePacked( _payment, msg.sender, _callbackFunc, _expiration) ); require(paramsHash == commitments[_requestId], "Params do not match request ID"); require(_expiration <= now, "Request is not expired"); delete commitments[_requestId]; emit CancelOracleRequest(_requestId); assert(LinkToken.transfer(msg.sender, _payment)); } // MODIFIERS /** * @dev Reverts if amount requested is greater than withdrawable balance * @param _amount The given amount to compare to `withdrawableTokens` */ modifier hasAvailableFunds(uint256 _amount) { require(withdrawableTokens >= _amount.add(ONE_FOR_CONSISTENT_GAS_COST), "Amount requested is greater than withdrawable balance"); _; } /** * @dev Reverts if request ID does not exist * @param _requestId The given request ID to check in stored `commitments` */ modifier isValidRequest(bytes32 _requestId) { require(commitments[_requestId] != 0, "Must have a valid requestId"); _; } /** * @dev Reverts if `msg.sender` is not authorized to fulfill requests */ modifier onlyAuthorizedNode() { require(authorizedNodes[msg.sender] || msg.sender == owner, "Not an authorized node to fulfill requests"); _; } /** * @dev Reverts if not sent from the LINK token */ modifier onlyLINK() { require(msg.sender == address(LinkToken), "Must use LINK token"); _; } /** * @dev Reverts if the given data does not begin with the `oracleRequest` function selector * @param _data The data payload of the request */ modifier permittedFunctionsForLINK(bytes _data) { bytes4 funcSelector; assembly { // solium-disable-next-line security/no-low-level-calls funcSelector := mload(add(_data, 32)) } require(funcSelector == this.oracleRequest.selector, "Must use whitelisted functions"); _; } /** * @dev Reverts if the callback address is the LINK token * @param _to The callback address */ modifier checkCallbackAddress(address _to) { require(_to != address(LinkToken), "Cannot callback to LINK"); _; } /** * @dev Reverts if the given payload is less than needed to create a request * @param _data The request payload */ modifier validRequestLength(bytes _data) { require(_data.length >= MINIMUM_REQUEST_LENGTH, "Invalid request length"); _; } }
File 5 of 9: Oracle
// File: openzeppelin-solidity/contracts/ownership/Ownable.sol pragma solidity ^0.4.24; /** * @title Ownable * @dev The Ownable contract has an owner address, and provides basic authorization control * functions, this simplifies the implementation of "user permissions". */ contract Ownable { address public owner; event OwnershipRenounced(address indexed previousOwner); event OwnershipTransferred( address indexed previousOwner, address indexed newOwner ); /** * @dev The Ownable constructor sets the original `owner` of the contract to the sender * account. */ constructor() public { owner = msg.sender; } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { require(msg.sender == owner); _; } /** * @dev Allows the current owner to relinquish control of the contract. * @notice Renouncing to ownership will leave the contract without an owner. * It will not be possible to call the functions with the `onlyOwner` * modifier anymore. */ function renounceOwnership() public onlyOwner { emit OwnershipRenounced(owner); owner = address(0); } /** * @dev Allows the current owner to transfer control of the contract to a newOwner. * @param _newOwner The address to transfer ownership to. */ function transferOwnership(address _newOwner) public onlyOwner { _transferOwnership(_newOwner); } /** * @dev Transfers control of the contract to a newOwner. * @param _newOwner The address to transfer ownership to. */ function _transferOwnership(address _newOwner) internal { require(_newOwner != address(0)); emit OwnershipTransferred(owner, _newOwner); owner = _newOwner; } } // File: openzeppelin-solidity/contracts/math/SafeMath.sol pragma solidity ^0.4.24; /** * @title SafeMath * @dev Math operations with safety checks that throw on error */ library SafeMath { /** * @dev Multiplies two numbers, throws on overflow. */ function mul(uint256 _a, uint256 _b) internal pure returns (uint256 c) { // Gas optimization: this is cheaper than asserting 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522 if (_a == 0) { return 0; } c = _a * _b; assert(c / _a == _b); return c; } /** * @dev Integer division of two numbers, truncating the quotient. */ function div(uint256 _a, uint256 _b) internal pure returns (uint256) { // assert(_b > 0); // Solidity automatically throws when dividing by 0 // uint256 c = _a / _b; // assert(_a == _b * c + _a % _b); // There is no case in which this doesn't hold return _a / _b; } /** * @dev Subtracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend). */ function sub(uint256 _a, uint256 _b) internal pure returns (uint256) { assert(_b <= _a); return _a - _b; } /** * @dev Adds two numbers, throws on overflow. */ function add(uint256 _a, uint256 _b) internal pure returns (uint256 c) { c = _a + _b; assert(c >= _a); return c; } } // File: contracts/interfaces/ChainlinkRequestInterface.sol pragma solidity 0.4.24; interface ChainlinkRequestInterface { function oracleRequest( address sender, uint256 payment, bytes32 id, address callbackAddress, bytes4 callbackFunctionId, uint256 nonce, uint256 version, bytes data ) external; function cancelOracleRequest( bytes32 requestId, uint256 payment, bytes4 callbackFunctionId, uint256 expiration ) external; } // File: contracts/interfaces/OracleInterface.sol pragma solidity 0.4.24; interface OracleInterface { function fulfillOracleRequest( bytes32 requestId, uint256 payment, address callbackAddress, bytes4 callbackFunctionId, uint256 expiration, bytes32 data ) external returns (bool); function getAuthorizationStatus(address node) external view returns (bool); function setFulfillmentPermission(address node, bool allowed) external; function withdraw(address recipient, uint256 amount) external; function withdrawable() external view returns (uint256); } // File: contracts/interfaces/LinkTokenInterface.sol pragma solidity 0.4.24; interface LinkTokenInterface { function allowance(address owner, address spender) external returns (bool success); function approve(address spender, uint256 value) external returns (bool success); function balanceOf(address owner) external returns (uint256 balance); function decimals() external returns (uint8 decimalPlaces); function decreaseApproval(address spender, uint256 addedValue) external returns (bool success); function increaseApproval(address spender, uint256 subtractedValue) external; function name() external returns (string tokenName); function symbol() external returns (string tokenSymbol); function totalSupply() external returns (uint256 totalTokensIssued); function transfer(address to, uint256 value) external returns (bool success); function transferAndCall(address to, uint256 value, bytes data) external returns (bool success); function transferFrom(address from, address to, uint256 value) external returns (bool success); } // File: contracts/Oracle.sol pragma solidity 0.4.24; /** * @title The Chainlink Oracle contract * @notice Node operators can deploy this contract to fulfill requests sent to them */ contract Oracle is ChainlinkRequestInterface, OracleInterface, Ownable { using SafeMath for uint256; uint256 constant public EXPIRY_TIME = 5 minutes; uint256 constant private MINIMUM_CONSUMER_GAS_LIMIT = 400000; // We initialize fields to 1 instead of 0 so that the first invocation // does not cost more gas. uint256 constant private ONE_FOR_CONSISTENT_GAS_COST = 1; uint256 constant private SELECTOR_LENGTH = 4; uint256 constant private EXPECTED_REQUEST_WORDS = 2; // solium-disable-next-line zeppelin/no-arithmetic-operations uint256 constant private MINIMUM_REQUEST_LENGTH = SELECTOR_LENGTH + (32 * EXPECTED_REQUEST_WORDS); LinkTokenInterface internal LinkToken; mapping(bytes32 => bytes32) private commitments; mapping(address => bool) private authorizedNodes; uint256 private withdrawableTokens = ONE_FOR_CONSISTENT_GAS_COST; event OracleRequest( bytes32 indexed specId, address requester, bytes32 requestId, uint256 payment, address callbackAddr, bytes4 callbackFunctionId, uint256 cancelExpiration, uint256 dataVersion, bytes data ); event CancelOracleRequest( bytes32 indexed requestId ); /** * @notice Deploy with the address of the LINK token * @dev Sets the LinkToken address for the imported LinkTokenInterface * @param _link The address of the LINK token */ constructor(address _link) Ownable() public { LinkToken = LinkTokenInterface(_link); } /** * @notice Called when LINK is sent to the contract via `transferAndCall` * @dev The data payload's first 2 words will be overwritten by the `_sender` and `_amount` * values to ensure correctness. Calls oracleRequest. * @param _sender Address of the sender * @param _amount Amount of LINK sent (specified in wei) * @param _data Payload of the transaction */ function onTokenTransfer( address _sender, uint256 _amount, bytes _data ) public onlyLINK validRequestLength(_data) permittedFunctionsForLINK(_data) { assembly { // solium-disable-next-line security/no-low-level-calls mstore(add(_data, 36), _sender) // ensure correct sender is passed // solium-disable-next-line security/no-low-level-calls mstore(add(_data, 68), _amount) // ensure correct amount is passed } // solium-disable-next-line security/no-low-level-calls require(address(this).delegatecall(_data), "Unable to create request"); // calls oracleRequest } /** * @notice Creates the Chainlink request * @dev Stores the hash of the params as the on-chain commitment for the request. * Emits OracleRequest event for the Chainlink node to detect. * @param _sender The sender of the request * @param _payment The amount of payment given (specified in wei) * @param _specId The Job Specification ID * @param _callbackAddress The callback address for the response * @param _callbackFunctionId The callback function ID for the response * @param _nonce The nonce sent by the requester * @param _dataVersion The specified data version * @param _data The CBOR payload of the request */ function oracleRequest( address _sender, uint256 _payment, bytes32 _specId, address _callbackAddress, bytes4 _callbackFunctionId, uint256 _nonce, uint256 _dataVersion, bytes _data ) external onlyLINK checkCallbackAddress(_callbackAddress) { bytes32 requestId = keccak256(abi.encodePacked(_sender, _nonce)); require(commitments[requestId] == 0, "Must use a unique ID"); uint256 expiration = now.add(EXPIRY_TIME); commitments[requestId] = keccak256( abi.encodePacked( _payment, _callbackAddress, _callbackFunctionId, expiration ) ); emit OracleRequest( _specId, _sender, requestId, _payment, _callbackAddress, _callbackFunctionId, expiration, _dataVersion, _data); } /** * @notice Called by the Chainlink node to fulfill requests * @dev Given params must hash back to the commitment stored from `oracleRequest`. * Will call the callback address' callback function without bubbling up error * checking in a `require` so that the node can get paid. * @param _requestId The fulfillment request ID that must match the requester's * @param _payment The payment amount that will be released for the oracle (specified in wei) * @param _callbackAddress The callback address to call for fulfillment * @param _callbackFunctionId The callback function ID to use for fulfillment * @param _expiration The expiration that the node should respond by before the requester can cancel * @param _data The data to return to the consuming contract * @return Status if the external call was successful */ function fulfillOracleRequest( bytes32 _requestId, uint256 _payment, address _callbackAddress, bytes4 _callbackFunctionId, uint256 _expiration, bytes32 _data ) external onlyAuthorizedNode isValidRequest(_requestId) returns (bool) { bytes32 paramsHash = keccak256( abi.encodePacked( _payment, _callbackAddress, _callbackFunctionId, _expiration ) ); require(commitments[_requestId] == paramsHash, "Params do not match request ID"); withdrawableTokens = withdrawableTokens.add(_payment); delete commitments[_requestId]; require(gasleft() >= MINIMUM_CONSUMER_GAS_LIMIT, "Must provide consumer enough gas"); // All updates to the oracle's fulfillment should come before calling the // callback(addr+functionId) as it is untrusted. // See: https://solidity.readthedocs.io/en/develop/security-considerations.html#use-the-checks-effects-interactions-pattern return _callbackAddress.call(_callbackFunctionId, _requestId, _data); // solium-disable-line security/no-low-level-calls } /** * @notice Use this to check if a node is authorized for fulfilling requests * @param _node The address of the Chainlink node * @return The authorization status of the node */ function getAuthorizationStatus(address _node) external view returns (bool) { return authorizedNodes[_node]; } /** * @notice Sets the fulfillment permission for a given node. Use `true` to allow, `false` to disallow. * @param _node The address of the Chainlink node * @param _allowed Bool value to determine if the node can fulfill requests */ function setFulfillmentPermission(address _node, bool _allowed) external onlyOwner { authorizedNodes[_node] = _allowed; } /** * @notice Allows the node operator to withdraw earned LINK to a given address * @dev The owner of the contract can be another wallet and does not have to be a Chainlink node * @param _recipient The address to send the LINK token to * @param _amount The amount to send (specified in wei) */ function withdraw(address _recipient, uint256 _amount) external onlyOwner hasAvailableFunds(_amount) { withdrawableTokens = withdrawableTokens.sub(_amount); assert(LinkToken.transfer(_recipient, _amount)); } /** * @notice Displays the amount of LINK that is available for the node operator to withdraw * @dev We use `ONE_FOR_CONSISTENT_GAS_COST` in place of 0 in storage * @return The amount of withdrawable LINK on the contract */ function withdrawable() external view onlyOwner returns (uint256) { return withdrawableTokens.sub(ONE_FOR_CONSISTENT_GAS_COST); } /** * @notice Allows requesters to cancel requests sent to this oracle contract. Will transfer the LINK * sent for the request back to the requester's address. * @dev Given params must hash to a commitment stored on the contract in order for the request to be valid * Emits CancelOracleRequest event. * @param _requestId The request ID * @param _payment The amount of payment given (specified in wei) * @param _callbackFunc The requester's specified callback address * @param _expiration The time of the expiration for the request */ function cancelOracleRequest( bytes32 _requestId, uint256 _payment, bytes4 _callbackFunc, uint256 _expiration ) external { bytes32 paramsHash = keccak256( abi.encodePacked( _payment, msg.sender, _callbackFunc, _expiration) ); require(paramsHash == commitments[_requestId], "Params do not match request ID"); require(_expiration <= now, "Request is not expired"); delete commitments[_requestId]; emit CancelOracleRequest(_requestId); assert(LinkToken.transfer(msg.sender, _payment)); } // MODIFIERS /** * @dev Reverts if amount requested is greater than withdrawable balance * @param _amount The given amount to compare to `withdrawableTokens` */ modifier hasAvailableFunds(uint256 _amount) { require(withdrawableTokens >= _amount.add(ONE_FOR_CONSISTENT_GAS_COST), "Amount requested is greater than withdrawable balance"); _; } /** * @dev Reverts if request ID does not exist * @param _requestId The given request ID to check in stored `commitments` */ modifier isValidRequest(bytes32 _requestId) { require(commitments[_requestId] != 0, "Must have a valid requestId"); _; } /** * @dev Reverts if `msg.sender` is not authorized to fulfill requests */ modifier onlyAuthorizedNode() { require(authorizedNodes[msg.sender] || msg.sender == owner, "Not an authorized node to fulfill requests"); _; } /** * @dev Reverts if not sent from the LINK token */ modifier onlyLINK() { require(msg.sender == address(LinkToken), "Must use LINK token"); _; } /** * @dev Reverts if the given data does not begin with the `oracleRequest` function selector * @param _data The data payload of the request */ modifier permittedFunctionsForLINK(bytes _data) { bytes4 funcSelector; assembly { // solium-disable-next-line security/no-low-level-calls funcSelector := mload(add(_data, 32)) } require(funcSelector == this.oracleRequest.selector, "Must use whitelisted functions"); _; } /** * @dev Reverts if the callback address is the LINK token * @param _to The callback address */ modifier checkCallbackAddress(address _to) { require(_to != address(LinkToken), "Cannot callback to LINK"); _; } /** * @dev Reverts if the given payload is less than needed to create a request * @param _data The request payload */ modifier validRequestLength(bytes _data) { require(_data.length >= MINIMUM_REQUEST_LENGTH, "Invalid request length"); _; } }
File 6 of 9: Oracle
/* * ___ ___ ___ ___ * `MM `MMb dMM' `MM * MM MMM. ,PMM MM * ____MM M`Mb d'MM ___ MM __ ____ ___ __ ____ _____ ___ __ __ * 6MMMMMM M YM. ,P MM 6MMMMb MM d' 6MMMMb `MM 6MM 6MMMMb\ 6MMMMMb `MM 6MM 6MMbMMM * 6M' `MM M `Mb d' MM 8M' `Mb MM d' 6M' `Mb MM69 " MM' ` 6M' `Mb MM69 " 6M'`Mb * MM MM M YM.P MM ,oMM MM d' MM MM MM' YM. MM MM MM' MM MM * MM MM M `Mb' MM ,6MM9'MM MMdM. MMMMMMMM MM YMMMMb MM MM MM YM.,M9 * MM MM M YP MM MM' MM MMPYM. MM MM `Mb MM MM MM YMM9 * YM. ,MM M `' MM MM. ,MM MM YM. YM d9 MM L ,MM 68b YM. ,M9 MM (M * YMMMMMM__M_ _MM_`YMMM9'Yb_MM_ YM._YMMMM9 _MM_ MYMMMM9 Y89 YMMMMM9 _MM_ YMMMMb. * 6M Yb * YM. d9 * YMMMM9 */ pragma solidity 0.4.24; /** * @title Ownable * @dev The Ownable contract has an owner address, and provides basic authorization control * functions, this simplifies the implementation of "user permissions". */ contract Ownable { address public owner; event OwnershipRenounced(address indexed previousOwner); event OwnershipTransferred( address indexed previousOwner, address indexed newOwner ); /** * @dev The Ownable constructor sets the original `owner` of the contract to the sender * account. */ constructor() public { owner = msg.sender; } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { require(msg.sender == owner); _; } /** * @dev Allows the current owner to relinquish control of the contract. * @notice Renouncing to ownership will leave the contract without an owner. * It will not be possible to call the functions with the `onlyOwner` * modifier anymore. */ function renounceOwnership() public onlyOwner { emit OwnershipRenounced(owner); owner = address(0); } /** * @dev Allows the current owner to transfer control of the contract to a newOwner. * @param _newOwner The address to transfer ownership to. */ function transferOwnership(address _newOwner) public onlyOwner { _transferOwnership(_newOwner); } /** * @dev Transfers control of the contract to a newOwner. * @param _newOwner The address to transfer ownership to. */ function _transferOwnership(address _newOwner) internal { require(_newOwner != address(0)); emit OwnershipTransferred(owner, _newOwner); owner = _newOwner; } } // File: openzeppelin-solidity/contracts/math/SafeMath.sol pragma solidity ^0.4.24; /** * @title SafeMath * @dev Math operations with safety checks that throw on error */ library SafeMath { /** * @dev Multiplies two numbers, throws on overflow. */ function mul(uint256 _a, uint256 _b) internal pure returns (uint256 c) { // Gas optimization: this is cheaper than asserting 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522 if (_a == 0) { return 0; } c = _a * _b; assert(c / _a == _b); return c; } /** * @dev Integer division of two numbers, truncating the quotient. */ function div(uint256 _a, uint256 _b) internal pure returns (uint256) { // assert(_b > 0); // Solidity automatically throws when dividing by 0 // uint256 c = _a / _b; // assert(_a == _b * c + _a % _b); // There is no case in which this doesn't hold return _a / _b; } /** * @dev Subtracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend). */ function sub(uint256 _a, uint256 _b) internal pure returns (uint256) { assert(_b <= _a); return _a - _b; } /** * @dev Adds two numbers, throws on overflow. */ function add(uint256 _a, uint256 _b) internal pure returns (uint256 c) { c = _a + _b; assert(c >= _a); return c; } } // File: contracts/interfaces/ChainlinkRequestInterface.sol pragma solidity 0.4.24; interface ChainlinkRequestInterface { function oracleRequest( address sender, uint256 payment, bytes32 id, address callbackAddress, bytes4 callbackFunctionId, uint256 nonce, uint256 version, bytes data ) external; function cancelOracleRequest( bytes32 requestId, uint256 payment, bytes4 callbackFunctionId, uint256 expiration ) external; } // File: contracts/interfaces/OracleInterface.sol pragma solidity 0.4.24; interface OracleInterface { function fulfillOracleRequest( bytes32 requestId, uint256 payment, address callbackAddress, bytes4 callbackFunctionId, uint256 expiration, bytes32 data ) external returns (bool); function getAuthorizationStatus(address node) external view returns (bool); function setFulfillmentPermission(address node, bool allowed) external; function withdraw(address recipient, uint256 amount) external; function withdrawable() external view returns (uint256); } // File: contracts/interfaces/LinkTokenInterface.sol pragma solidity 0.4.24; interface LinkTokenInterface { function allowance(address owner, address spender) external returns (bool success); function approve(address spender, uint256 value) external returns (bool success); function balanceOf(address owner) external returns (uint256 balance); function decimals() external returns (uint8 decimalPlaces); function decreaseApproval(address spender, uint256 addedValue) external returns (bool success); function increaseApproval(address spender, uint256 subtractedValue) external; function name() external returns (string tokenName); function symbol() external returns (string tokenSymbol); function totalSupply() external returns (uint256 totalTokensIssued); function transfer(address to, uint256 value) external returns (bool success); function transferAndCall(address to, uint256 value, bytes data) external returns (bool success); function transferFrom(address from, address to, uint256 value) external returns (bool success); } // File: contracts/Oracle.sol pragma solidity 0.4.24; /** * @title The Chainlink Oracle contract * @notice Node operators can deploy this contract to fulfill requests sent to them */ contract Oracle is ChainlinkRequestInterface, OracleInterface, Ownable { using SafeMath for uint256; uint256 constant public EXPIRY_TIME = 5 minutes; uint256 constant private MINIMUM_CONSUMER_GAS_LIMIT = 400000; // We initialize fields to 1 instead of 0 so that the first invocation // does not cost more gas. uint256 constant private ONE_FOR_CONSISTENT_GAS_COST = 1; uint256 constant private SELECTOR_LENGTH = 4; uint256 constant private EXPECTED_REQUEST_WORDS = 2; // solium-disable-next-line zeppelin/no-arithmetic-operations uint256 constant private MINIMUM_REQUEST_LENGTH = SELECTOR_LENGTH + (32 * EXPECTED_REQUEST_WORDS); LinkTokenInterface internal LinkToken; mapping(bytes32 => bytes32) private commitments; mapping(address => bool) private authorizedNodes; uint256 private withdrawableTokens = ONE_FOR_CONSISTENT_GAS_COST; event OracleRequest( bytes32 indexed specId, address requester, bytes32 requestId, uint256 payment, address callbackAddr, bytes4 callbackFunctionId, uint256 cancelExpiration, uint256 dataVersion, bytes data ); event CancelOracleRequest( bytes32 indexed requestId ); /** * @notice Deploy with the address of the LINK token * @dev Sets the LinkToken address for the imported LinkTokenInterface * @param _link The address of the LINK token */ constructor(address _link) Ownable() public { LinkToken = LinkTokenInterface(_link); } /** * @notice Called when LINK is sent to the contract via `transferAndCall` * @dev The data payload's first 2 words will be overwritten by the `_sender` and `_amount` * values to ensure correctness. Calls oracleRequest. * @param _sender Address of the sender * @param _amount Amount of LINK sent (specified in wei) * @param _data Payload of the transaction */ function onTokenTransfer( address _sender, uint256 _amount, bytes _data ) public onlyLINK validRequestLength(_data) permittedFunctionsForLINK(_data) { assembly { // solium-disable-next-line security/no-low-level-calls mstore(add(_data, 36), _sender) // ensure correct sender is passed // solium-disable-next-line security/no-low-level-calls mstore(add(_data, 68), _amount) // ensure correct amount is passed } // solium-disable-next-line security/no-low-level-calls require(address(this).delegatecall(_data), "Unable to create request"); // calls oracleRequest } /** * @notice Creates the Chainlink request * @dev Stores the hash of the params as the on-chain commitment for the request. * Emits OracleRequest event for the Chainlink node to detect. * @param _sender The sender of the request * @param _payment The amount of payment given (specified in wei) * @param _specId The Job Specification ID * @param _callbackAddress The callback address for the response * @param _callbackFunctionId The callback function ID for the response * @param _nonce The nonce sent by the requester * @param _dataVersion The specified data version * @param _data The CBOR payload of the request */ function oracleRequest( address _sender, uint256 _payment, bytes32 _specId, address _callbackAddress, bytes4 _callbackFunctionId, uint256 _nonce, uint256 _dataVersion, bytes _data ) external onlyLINK checkCallbackAddress(_callbackAddress) { bytes32 requestId = keccak256(abi.encodePacked(_sender, _nonce)); require(commitments[requestId] == 0, "Must use a unique ID"); uint256 expiration = now.add(EXPIRY_TIME); commitments[requestId] = keccak256( abi.encodePacked( _payment, _callbackAddress, _callbackFunctionId, expiration ) ); emit OracleRequest( _specId, _sender, requestId, _payment, _callbackAddress, _callbackFunctionId, expiration, _dataVersion, _data); } /** * @notice Called by the Chainlink node to fulfill requests * @dev Given params must hash back to the commitment stored from `oracleRequest`. * Will call the callback address' callback function without bubbling up error * checking in a `require` so that the node can get paid. * @param _requestId The fulfillment request ID that must match the requester's * @param _payment The payment amount that will be released for the oracle (specified in wei) * @param _callbackAddress The callback address to call for fulfillment * @param _callbackFunctionId The callback function ID to use for fulfillment * @param _expiration The expiration that the node should respond by before the requester can cancel * @param _data The data to return to the consuming contract * @return Status if the external call was successful */ function fulfillOracleRequest( bytes32 _requestId, uint256 _payment, address _callbackAddress, bytes4 _callbackFunctionId, uint256 _expiration, bytes32 _data ) external onlyAuthorizedNode isValidRequest(_requestId) returns (bool) { bytes32 paramsHash = keccak256( abi.encodePacked( _payment, _callbackAddress, _callbackFunctionId, _expiration ) ); require(commitments[_requestId] == paramsHash, "Params do not match request ID"); withdrawableTokens = withdrawableTokens.add(_payment); delete commitments[_requestId]; require(gasleft() >= MINIMUM_CONSUMER_GAS_LIMIT, "Must provide consumer enough gas"); // All updates to the oracle's fulfillment should come before calling the // callback(addr+functionId) as it is untrusted. // See: https://solidity.readthedocs.io/en/develop/security-considerations.html#use-the-checks-effects-interactions-pattern return _callbackAddress.call(_callbackFunctionId, _requestId, _data); // solium-disable-line security/no-low-level-calls } /** * @notice Use this to check if a node is authorized for fulfilling requests * @param _node The address of the Chainlink node * @return The authorization status of the node */ function getAuthorizationStatus(address _node) external view returns (bool) { return authorizedNodes[_node]; } /** * @notice Sets the fulfillment permission for a given node. Use `true` to allow, `false` to disallow. * @param _node The address of the Chainlink node * @param _allowed Bool value to determine if the node can fulfill requests */ function setFulfillmentPermission(address _node, bool _allowed) external onlyOwner { authorizedNodes[_node] = _allowed; } /** * @notice Allows the node operator to withdraw earned LINK to a given address * @dev The owner of the contract can be another wallet and does not have to be a Chainlink node * @param _recipient The address to send the LINK token to * @param _amount The amount to send (specified in wei) */ function withdraw(address _recipient, uint256 _amount) external onlyOwner hasAvailableFunds(_amount) { withdrawableTokens = withdrawableTokens.sub(_amount); assert(LinkToken.transfer(_recipient, _amount)); } /** * @notice Displays the amount of LINK that is available for the node operator to withdraw * @dev We use `ONE_FOR_CONSISTENT_GAS_COST` in place of 0 in storage * @return The amount of withdrawable LINK on the contract */ function withdrawable() external view onlyOwner returns (uint256) { return withdrawableTokens.sub(ONE_FOR_CONSISTENT_GAS_COST); } /** * @notice Allows requesters to cancel requests sent to this oracle contract. Will transfer the LINK * sent for the request back to the requester's address. * @dev Given params must hash to a commitment stored on the contract in order for the request to be valid * Emits CancelOracleRequest event. * @param _requestId The request ID * @param _payment The amount of payment given (specified in wei) * @param _callbackFunc The requester's specified callback address * @param _expiration The time of the expiration for the request */ function cancelOracleRequest( bytes32 _requestId, uint256 _payment, bytes4 _callbackFunc, uint256 _expiration ) external { bytes32 paramsHash = keccak256( abi.encodePacked( _payment, msg.sender, _callbackFunc, _expiration) ); require(paramsHash == commitments[_requestId], "Params do not match request ID"); require(_expiration <= now, "Request is not expired"); delete commitments[_requestId]; emit CancelOracleRequest(_requestId); assert(LinkToken.transfer(msg.sender, _payment)); } // MODIFIERS /** * @dev Reverts if amount requested is greater than withdrawable balance * @param _amount The given amount to compare to `withdrawableTokens` */ modifier hasAvailableFunds(uint256 _amount) { require(withdrawableTokens >= _amount.add(ONE_FOR_CONSISTENT_GAS_COST), "Amount requested is greater than withdrawable balance"); _; } /** * @dev Reverts if request ID does not exist * @param _requestId The given request ID to check in stored `commitments` */ modifier isValidRequest(bytes32 _requestId) { require(commitments[_requestId] != 0, "Must have a valid requestId"); _; } /** * @dev Reverts if `msg.sender` is not authorized to fulfill requests */ modifier onlyAuthorizedNode() { require(authorizedNodes[msg.sender] || msg.sender == owner, "Not an authorized node to fulfill requests"); _; } /** * @dev Reverts if not sent from the LINK token */ modifier onlyLINK() { require(msg.sender == address(LinkToken), "Must use LINK token"); _; } /** * @dev Reverts if the given data does not begin with the `oracleRequest` function selector * @param _data The data payload of the request */ modifier permittedFunctionsForLINK(bytes _data) { bytes4 funcSelector; assembly { // solium-disable-next-line security/no-low-level-calls funcSelector := mload(add(_data, 32)) } require(funcSelector == this.oracleRequest.selector, "Must use whitelisted functions"); _; } /** * @dev Reverts if the callback address is the LINK token * @param _to The callback address */ modifier checkCallbackAddress(address _to) { require(_to != address(LinkToken), "Cannot callback to LINK"); _; } /** * @dev Reverts if the given payload is less than needed to create a request * @param _data The request payload */ modifier validRequestLength(bytes _data) { require(_data.length >= MINIMUM_REQUEST_LENGTH, "Invalid request length"); _; } }
File 7 of 9: Oracle
// File: openzeppelin-solidity/contracts/ownership/Ownable.sol pragma solidity ^0.4.24; /** * @title Ownable * @dev The Ownable contract has an owner address, and provides basic authorization control * functions, this simplifies the implementation of "user permissions". */ contract Ownable { address public owner; event OwnershipRenounced(address indexed previousOwner); event OwnershipTransferred( address indexed previousOwner, address indexed newOwner ); /** * @dev The Ownable constructor sets the original `owner` of the contract to the sender * account. */ constructor() public { owner = msg.sender; } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { require(msg.sender == owner); _; } /** * @dev Allows the current owner to relinquish control of the contract. * @notice Renouncing to ownership will leave the contract without an owner. * It will not be possible to call the functions with the `onlyOwner` * modifier anymore. */ function renounceOwnership() public onlyOwner { emit OwnershipRenounced(owner); owner = address(0); } /** * @dev Allows the current owner to transfer control of the contract to a newOwner. * @param _newOwner The address to transfer ownership to. */ function transferOwnership(address _newOwner) public onlyOwner { _transferOwnership(_newOwner); } /** * @dev Transfers control of the contract to a newOwner. * @param _newOwner The address to transfer ownership to. */ function _transferOwnership(address _newOwner) internal { require(_newOwner != address(0)); emit OwnershipTransferred(owner, _newOwner); owner = _newOwner; } } // File: openzeppelin-solidity/contracts/math/SafeMath.sol pragma solidity ^0.4.24; /** * @title SafeMath * @dev Math operations with safety checks that throw on error */ library SafeMath { /** * @dev Multiplies two numbers, throws on overflow. */ function mul(uint256 _a, uint256 _b) internal pure returns (uint256 c) { // Gas optimization: this is cheaper than asserting 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522 if (_a == 0) { return 0; } c = _a * _b; assert(c / _a == _b); return c; } /** * @dev Integer division of two numbers, truncating the quotient. */ function div(uint256 _a, uint256 _b) internal pure returns (uint256) { // assert(_b > 0); // Solidity automatically throws when dividing by 0 // uint256 c = _a / _b; // assert(_a == _b * c + _a % _b); // There is no case in which this doesn't hold return _a / _b; } /** * @dev Subtracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend). */ function sub(uint256 _a, uint256 _b) internal pure returns (uint256) { assert(_b <= _a); return _a - _b; } /** * @dev Adds two numbers, throws on overflow. */ function add(uint256 _a, uint256 _b) internal pure returns (uint256 c) { c = _a + _b; assert(c >= _a); return c; } } // File: contracts/interfaces/ChainlinkRequestInterface.sol pragma solidity 0.4.24; interface ChainlinkRequestInterface { function oracleRequest( address sender, uint256 payment, bytes32 id, address callbackAddress, bytes4 callbackFunctionId, uint256 nonce, uint256 version, bytes data ) external; function cancelOracleRequest( bytes32 requestId, uint256 payment, bytes4 callbackFunctionId, uint256 expiration ) external; } // File: contracts/interfaces/OracleInterface.sol pragma solidity 0.4.24; interface OracleInterface { function fulfillOracleRequest( bytes32 requestId, uint256 payment, address callbackAddress, bytes4 callbackFunctionId, uint256 expiration, bytes32 data ) external returns (bool); function getAuthorizationStatus(address node) external view returns (bool); function setFulfillmentPermission(address node, bool allowed) external; function withdraw(address recipient, uint256 amount) external; function withdrawable() external view returns (uint256); } // File: contracts/interfaces/LinkTokenInterface.sol pragma solidity 0.4.24; interface LinkTokenInterface { function allowance(address owner, address spender) external returns (bool success); function approve(address spender, uint256 value) external returns (bool success); function balanceOf(address owner) external returns (uint256 balance); function decimals() external returns (uint8 decimalPlaces); function decreaseApproval(address spender, uint256 addedValue) external returns (bool success); function increaseApproval(address spender, uint256 subtractedValue) external; function name() external returns (string tokenName); function symbol() external returns (string tokenSymbol); function totalSupply() external returns (uint256 totalTokensIssued); function transfer(address to, uint256 value) external returns (bool success); function transferAndCall(address to, uint256 value, bytes data) external returns (bool success); function transferFrom(address from, address to, uint256 value) external returns (bool success); } // File: contracts/Oracle.sol pragma solidity 0.4.24; /** * @title The Chainlink Oracle contract * @notice Node operators can deploy this contract to fulfill requests sent to them */ contract Oracle is ChainlinkRequestInterface, OracleInterface, Ownable { using SafeMath for uint256; uint256 constant public EXPIRY_TIME = 5 minutes; uint256 constant private MINIMUM_CONSUMER_GAS_LIMIT = 400000; // We initialize fields to 1 instead of 0 so that the first invocation // does not cost more gas. uint256 constant private ONE_FOR_CONSISTENT_GAS_COST = 1; uint256 constant private SELECTOR_LENGTH = 4; uint256 constant private EXPECTED_REQUEST_WORDS = 2; // solium-disable-next-line zeppelin/no-arithmetic-operations uint256 constant private MINIMUM_REQUEST_LENGTH = SELECTOR_LENGTH + (32 * EXPECTED_REQUEST_WORDS); LinkTokenInterface internal LinkToken; mapping(bytes32 => bytes32) private commitments; mapping(address => bool) private authorizedNodes; uint256 private withdrawableTokens = ONE_FOR_CONSISTENT_GAS_COST; event OracleRequest( bytes32 indexed specId, address requester, bytes32 requestId, uint256 payment, address callbackAddr, bytes4 callbackFunctionId, uint256 cancelExpiration, uint256 dataVersion, bytes data ); event CancelOracleRequest( bytes32 indexed requestId ); /** * @notice Deploy with the address of the LINK token * @dev Sets the LinkToken address for the imported LinkTokenInterface * @param _link The address of the LINK token */ constructor(address _link) Ownable() public { LinkToken = LinkTokenInterface(_link); } /** * @notice Called when LINK is sent to the contract via `transferAndCall` * @dev The data payload's first 2 words will be overwritten by the `_sender` and `_amount` * values to ensure correctness. Calls oracleRequest. * @param _sender Address of the sender * @param _amount Amount of LINK sent (specified in wei) * @param _data Payload of the transaction */ function onTokenTransfer( address _sender, uint256 _amount, bytes _data ) public onlyLINK validRequestLength(_data) permittedFunctionsForLINK(_data) { assembly { // solium-disable-next-line security/no-low-level-calls mstore(add(_data, 36), _sender) // ensure correct sender is passed // solium-disable-next-line security/no-low-level-calls mstore(add(_data, 68), _amount) // ensure correct amount is passed } // solium-disable-next-line security/no-low-level-calls require(address(this).delegatecall(_data), "Unable to create request"); // calls oracleRequest } /** * @notice Creates the Chainlink request * @dev Stores the hash of the params as the on-chain commitment for the request. * Emits OracleRequest event for the Chainlink node to detect. * @param _sender The sender of the request * @param _payment The amount of payment given (specified in wei) * @param _specId The Job Specification ID * @param _callbackAddress The callback address for the response * @param _callbackFunctionId The callback function ID for the response * @param _nonce The nonce sent by the requester * @param _dataVersion The specified data version * @param _data The CBOR payload of the request */ function oracleRequest( address _sender, uint256 _payment, bytes32 _specId, address _callbackAddress, bytes4 _callbackFunctionId, uint256 _nonce, uint256 _dataVersion, bytes _data ) external onlyLINK checkCallbackAddress(_callbackAddress) { bytes32 requestId = keccak256(abi.encodePacked(_sender, _nonce)); require(commitments[requestId] == 0, "Must use a unique ID"); uint256 expiration = now.add(EXPIRY_TIME); commitments[requestId] = keccak256( abi.encodePacked( _payment, _callbackAddress, _callbackFunctionId, expiration ) ); emit OracleRequest( _specId, _sender, requestId, _payment, _callbackAddress, _callbackFunctionId, expiration, _dataVersion, _data); } /** * @notice Called by the Chainlink node to fulfill requests * @dev Given params must hash back to the commitment stored from `oracleRequest`. * Will call the callback address' callback function without bubbling up error * checking in a `require` so that the node can get paid. * @param _requestId The fulfillment request ID that must match the requester's * @param _payment The payment amount that will be released for the oracle (specified in wei) * @param _callbackAddress The callback address to call for fulfillment * @param _callbackFunctionId The callback function ID to use for fulfillment * @param _expiration The expiration that the node should respond by before the requester can cancel * @param _data The data to return to the consuming contract * @return Status if the external call was successful */ function fulfillOracleRequest( bytes32 _requestId, uint256 _payment, address _callbackAddress, bytes4 _callbackFunctionId, uint256 _expiration, bytes32 _data ) external onlyAuthorizedNode isValidRequest(_requestId) returns (bool) { bytes32 paramsHash = keccak256( abi.encodePacked( _payment, _callbackAddress, _callbackFunctionId, _expiration ) ); require(commitments[_requestId] == paramsHash, "Params do not match request ID"); withdrawableTokens = withdrawableTokens.add(_payment); delete commitments[_requestId]; require(gasleft() >= MINIMUM_CONSUMER_GAS_LIMIT, "Must provide consumer enough gas"); // All updates to the oracle's fulfillment should come before calling the // callback(addr+functionId) as it is untrusted. // See: https://solidity.readthedocs.io/en/develop/security-considerations.html#use-the-checks-effects-interactions-pattern return _callbackAddress.call(_callbackFunctionId, _requestId, _data); // solium-disable-line security/no-low-level-calls } /** * @notice Use this to check if a node is authorized for fulfilling requests * @param _node The address of the Chainlink node * @return The authorization status of the node */ function getAuthorizationStatus(address _node) external view returns (bool) { return authorizedNodes[_node]; } /** * @notice Sets the fulfillment permission for a given node. Use `true` to allow, `false` to disallow. * @param _node The address of the Chainlink node * @param _allowed Bool value to determine if the node can fulfill requests */ function setFulfillmentPermission(address _node, bool _allowed) external onlyOwner { authorizedNodes[_node] = _allowed; } /** * @notice Allows the node operator to withdraw earned LINK to a given address * @dev The owner of the contract can be another wallet and does not have to be a Chainlink node * @param _recipient The address to send the LINK token to * @param _amount The amount to send (specified in wei) */ function withdraw(address _recipient, uint256 _amount) external onlyOwner hasAvailableFunds(_amount) { withdrawableTokens = withdrawableTokens.sub(_amount); assert(LinkToken.transfer(_recipient, _amount)); } /** * @notice Displays the amount of LINK that is available for the node operator to withdraw * @dev We use `ONE_FOR_CONSISTENT_GAS_COST` in place of 0 in storage * @return The amount of withdrawable LINK on the contract */ function withdrawable() external view onlyOwner returns (uint256) { return withdrawableTokens.sub(ONE_FOR_CONSISTENT_GAS_COST); } /** * @notice Allows requesters to cancel requests sent to this oracle contract. Will transfer the LINK * sent for the request back to the requester's address. * @dev Given params must hash to a commitment stored on the contract in order for the request to be valid * Emits CancelOracleRequest event. * @param _requestId The request ID * @param _payment The amount of payment given (specified in wei) * @param _callbackFunc The requester's specified callback address * @param _expiration The time of the expiration for the request */ function cancelOracleRequest( bytes32 _requestId, uint256 _payment, bytes4 _callbackFunc, uint256 _expiration ) external { bytes32 paramsHash = keccak256( abi.encodePacked( _payment, msg.sender, _callbackFunc, _expiration) ); require(paramsHash == commitments[_requestId], "Params do not match request ID"); require(_expiration <= now, "Request is not expired"); delete commitments[_requestId]; emit CancelOracleRequest(_requestId); assert(LinkToken.transfer(msg.sender, _payment)); } // MODIFIERS /** * @dev Reverts if amount requested is greater than withdrawable balance * @param _amount The given amount to compare to `withdrawableTokens` */ modifier hasAvailableFunds(uint256 _amount) { require(withdrawableTokens >= _amount.add(ONE_FOR_CONSISTENT_GAS_COST), "Amount requested is greater than withdrawable balance"); _; } /** * @dev Reverts if request ID does not exist * @param _requestId The given request ID to check in stored `commitments` */ modifier isValidRequest(bytes32 _requestId) { require(commitments[_requestId] != 0, "Must have a valid requestId"); _; } /** * @dev Reverts if `msg.sender` is not authorized to fulfill requests */ modifier onlyAuthorizedNode() { require(authorizedNodes[msg.sender] || msg.sender == owner, "Not an authorized node to fulfill requests"); _; } /** * @dev Reverts if not sent from the LINK token */ modifier onlyLINK() { require(msg.sender == address(LinkToken), "Must use LINK token"); _; } /** * @dev Reverts if the given data does not begin with the `oracleRequest` function selector * @param _data The data payload of the request */ modifier permittedFunctionsForLINK(bytes _data) { bytes4 funcSelector; assembly { // solium-disable-next-line security/no-low-level-calls funcSelector := mload(add(_data, 32)) } require(funcSelector == this.oracleRequest.selector, "Must use whitelisted functions"); _; } /** * @dev Reverts if the callback address is the LINK token * @param _to The callback address */ modifier checkCallbackAddress(address _to) { require(_to != address(LinkToken), "Cannot callback to LINK"); _; } /** * @dev Reverts if the given payload is less than needed to create a request * @param _data The request payload */ modifier validRequestLength(bytes _data) { require(_data.length >= MINIMUM_REQUEST_LENGTH, "Invalid request length"); _; } }
File 8 of 9: Oracle
// File: openzeppelin-solidity/contracts/ownership/Ownable.sol pragma solidity ^0.4.24; /** * @title Ownable * @dev The Ownable contract has an owner address, and provides basic authorization control * functions, this simplifies the implementation of "user permissions". */ contract Ownable { address public owner; event OwnershipRenounced(address indexed previousOwner); event OwnershipTransferred( address indexed previousOwner, address indexed newOwner ); /** * @dev The Ownable constructor sets the original `owner` of the contract to the sender * account. */ constructor() public { owner = msg.sender; } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { require(msg.sender == owner); _; } /** * @dev Allows the current owner to relinquish control of the contract. * @notice Renouncing to ownership will leave the contract without an owner. * It will not be possible to call the functions with the `onlyOwner` * modifier anymore. */ function renounceOwnership() public onlyOwner { emit OwnershipRenounced(owner); owner = address(0); } /** * @dev Allows the current owner to transfer control of the contract to a newOwner. * @param _newOwner The address to transfer ownership to. */ function transferOwnership(address _newOwner) public onlyOwner { _transferOwnership(_newOwner); } /** * @dev Transfers control of the contract to a newOwner. * @param _newOwner The address to transfer ownership to. */ function _transferOwnership(address _newOwner) internal { require(_newOwner != address(0)); emit OwnershipTransferred(owner, _newOwner); owner = _newOwner; } } // File: openzeppelin-solidity/contracts/math/SafeMath.sol pragma solidity ^0.4.24; /** * @title SafeMath * @dev Math operations with safety checks that throw on error */ library SafeMath { /** * @dev Multiplies two numbers, throws on overflow. */ function mul(uint256 _a, uint256 _b) internal pure returns (uint256 c) { // Gas optimization: this is cheaper than asserting 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522 if (_a == 0) { return 0; } c = _a * _b; assert(c / _a == _b); return c; } /** * @dev Integer division of two numbers, truncating the quotient. */ function div(uint256 _a, uint256 _b) internal pure returns (uint256) { // assert(_b > 0); // Solidity automatically throws when dividing by 0 // uint256 c = _a / _b; // assert(_a == _b * c + _a % _b); // There is no case in which this doesn't hold return _a / _b; } /** * @dev Subtracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend). */ function sub(uint256 _a, uint256 _b) internal pure returns (uint256) { assert(_b <= _a); return _a - _b; } /** * @dev Adds two numbers, throws on overflow. */ function add(uint256 _a, uint256 _b) internal pure returns (uint256 c) { c = _a + _b; assert(c >= _a); return c; } } // File: contracts/interfaces/ChainlinkRequestInterface.sol pragma solidity 0.4.24; interface ChainlinkRequestInterface { function oracleRequest( address sender, uint256 payment, bytes32 id, address callbackAddress, bytes4 callbackFunctionId, uint256 nonce, uint256 version, bytes data ) external; function cancelOracleRequest( bytes32 requestId, uint256 payment, bytes4 callbackFunctionId, uint256 expiration ) external; } // File: contracts/interfaces/OracleInterface.sol pragma solidity 0.4.24; interface OracleInterface { function fulfillOracleRequest( bytes32 requestId, uint256 payment, address callbackAddress, bytes4 callbackFunctionId, uint256 expiration, bytes32 data ) external returns (bool); function getAuthorizationStatus(address node) external view returns (bool); function setFulfillmentPermission(address node, bool allowed) external; function withdraw(address recipient, uint256 amount) external; function withdrawable() external view returns (uint256); } // File: contracts/interfaces/LinkTokenInterface.sol pragma solidity 0.4.24; interface LinkTokenInterface { function allowance(address owner, address spender) external returns (bool success); function approve(address spender, uint256 value) external returns (bool success); function balanceOf(address owner) external returns (uint256 balance); function decimals() external returns (uint8 decimalPlaces); function decreaseApproval(address spender, uint256 addedValue) external returns (bool success); function increaseApproval(address spender, uint256 subtractedValue) external; function name() external returns (string tokenName); function symbol() external returns (string tokenSymbol); function totalSupply() external returns (uint256 totalTokensIssued); function transfer(address to, uint256 value) external returns (bool success); function transferAndCall(address to, uint256 value, bytes data) external returns (bool success); function transferFrom(address from, address to, uint256 value) external returns (bool success); } // File: contracts/Oracle.sol pragma solidity 0.4.24; /** * @title The Chainlink Oracle contract * @notice Node operators can deploy this contract to fulfill requests sent to them */ contract Oracle is ChainlinkRequestInterface, OracleInterface, Ownable { using SafeMath for uint256; uint256 constant public EXPIRY_TIME = 5 minutes; uint256 constant private MINIMUM_CONSUMER_GAS_LIMIT = 400000; // We initialize fields to 1 instead of 0 so that the first invocation // does not cost more gas. uint256 constant private ONE_FOR_CONSISTENT_GAS_COST = 1; uint256 constant private SELECTOR_LENGTH = 4; uint256 constant private EXPECTED_REQUEST_WORDS = 2; // solium-disable-next-line zeppelin/no-arithmetic-operations uint256 constant private MINIMUM_REQUEST_LENGTH = SELECTOR_LENGTH + (32 * EXPECTED_REQUEST_WORDS); LinkTokenInterface internal LinkToken; mapping(bytes32 => bytes32) private commitments; mapping(address => bool) private authorizedNodes; uint256 private withdrawableTokens = ONE_FOR_CONSISTENT_GAS_COST; event OracleRequest( bytes32 indexed specId, address requester, bytes32 requestId, uint256 payment, address callbackAddr, bytes4 callbackFunctionId, uint256 cancelExpiration, uint256 dataVersion, bytes data ); event CancelOracleRequest( bytes32 indexed requestId ); /** * @notice Deploy with the address of the LINK token * @dev Sets the LinkToken address for the imported LinkTokenInterface * @param _link The address of the LINK token */ constructor(address _link) Ownable() public { LinkToken = LinkTokenInterface(_link); } /** * @notice Called when LINK is sent to the contract via `transferAndCall` * @dev The data payload's first 2 words will be overwritten by the `_sender` and `_amount` * values to ensure correctness. Calls oracleRequest. * @param _sender Address of the sender * @param _amount Amount of LINK sent (specified in wei) * @param _data Payload of the transaction */ function onTokenTransfer( address _sender, uint256 _amount, bytes _data ) public onlyLINK validRequestLength(_data) permittedFunctionsForLINK(_data) { assembly { // solium-disable-next-line security/no-low-level-calls mstore(add(_data, 36), _sender) // ensure correct sender is passed // solium-disable-next-line security/no-low-level-calls mstore(add(_data, 68), _amount) // ensure correct amount is passed } // solium-disable-next-line security/no-low-level-calls require(address(this).delegatecall(_data), "Unable to create request"); // calls oracleRequest } /** * @notice Creates the Chainlink request * @dev Stores the hash of the params as the on-chain commitment for the request. * Emits OracleRequest event for the Chainlink node to detect. * @param _sender The sender of the request * @param _payment The amount of payment given (specified in wei) * @param _specId The Job Specification ID * @param _callbackAddress The callback address for the response * @param _callbackFunctionId The callback function ID for the response * @param _nonce The nonce sent by the requester * @param _dataVersion The specified data version * @param _data The CBOR payload of the request */ function oracleRequest( address _sender, uint256 _payment, bytes32 _specId, address _callbackAddress, bytes4 _callbackFunctionId, uint256 _nonce, uint256 _dataVersion, bytes _data ) external onlyLINK checkCallbackAddress(_callbackAddress) { bytes32 requestId = keccak256(abi.encodePacked(_sender, _nonce)); require(commitments[requestId] == 0, "Must use a unique ID"); uint256 expiration = now.add(EXPIRY_TIME); commitments[requestId] = keccak256( abi.encodePacked( _payment, _callbackAddress, _callbackFunctionId, expiration ) ); emit OracleRequest( _specId, _sender, requestId, _payment, _callbackAddress, _callbackFunctionId, expiration, _dataVersion, _data); } /** * @notice Called by the Chainlink node to fulfill requests * @dev Given params must hash back to the commitment stored from `oracleRequest`. * Will call the callback address' callback function without bubbling up error * checking in a `require` so that the node can get paid. * @param _requestId The fulfillment request ID that must match the requester's * @param _payment The payment amount that will be released for the oracle (specified in wei) * @param _callbackAddress The callback address to call for fulfillment * @param _callbackFunctionId The callback function ID to use for fulfillment * @param _expiration The expiration that the node should respond by before the requester can cancel * @param _data The data to return to the consuming contract * @return Status if the external call was successful */ function fulfillOracleRequest( bytes32 _requestId, uint256 _payment, address _callbackAddress, bytes4 _callbackFunctionId, uint256 _expiration, bytes32 _data ) external onlyAuthorizedNode isValidRequest(_requestId) returns (bool) { bytes32 paramsHash = keccak256( abi.encodePacked( _payment, _callbackAddress, _callbackFunctionId, _expiration ) ); require(commitments[_requestId] == paramsHash, "Params do not match request ID"); withdrawableTokens = withdrawableTokens.add(_payment); delete commitments[_requestId]; require(gasleft() >= MINIMUM_CONSUMER_GAS_LIMIT, "Must provide consumer enough gas"); // All updates to the oracle's fulfillment should come before calling the // callback(addr+functionId) as it is untrusted. // See: https://solidity.readthedocs.io/en/develop/security-considerations.html#use-the-checks-effects-interactions-pattern return _callbackAddress.call(_callbackFunctionId, _requestId, _data); // solium-disable-line security/no-low-level-calls } /** * @notice Use this to check if a node is authorized for fulfilling requests * @param _node The address of the Chainlink node * @return The authorization status of the node */ function getAuthorizationStatus(address _node) external view returns (bool) { return authorizedNodes[_node]; } /** * @notice Sets the fulfillment permission for a given node. Use `true` to allow, `false` to disallow. * @param _node The address of the Chainlink node * @param _allowed Bool value to determine if the node can fulfill requests */ function setFulfillmentPermission(address _node, bool _allowed) external onlyOwner { authorizedNodes[_node] = _allowed; } /** * @notice Allows the node operator to withdraw earned LINK to a given address * @dev The owner of the contract can be another wallet and does not have to be a Chainlink node * @param _recipient The address to send the LINK token to * @param _amount The amount to send (specified in wei) */ function withdraw(address _recipient, uint256 _amount) external onlyOwner hasAvailableFunds(_amount) { withdrawableTokens = withdrawableTokens.sub(_amount); assert(LinkToken.transfer(_recipient, _amount)); } /** * @notice Displays the amount of LINK that is available for the node operator to withdraw * @dev We use `ONE_FOR_CONSISTENT_GAS_COST` in place of 0 in storage * @return The amount of withdrawable LINK on the contract */ function withdrawable() external view onlyOwner returns (uint256) { return withdrawableTokens.sub(ONE_FOR_CONSISTENT_GAS_COST); } /** * @notice Allows requesters to cancel requests sent to this oracle contract. Will transfer the LINK * sent for the request back to the requester's address. * @dev Given params must hash to a commitment stored on the contract in order for the request to be valid * Emits CancelOracleRequest event. * @param _requestId The request ID * @param _payment The amount of payment given (specified in wei) * @param _callbackFunc The requester's specified callback address * @param _expiration The time of the expiration for the request */ function cancelOracleRequest( bytes32 _requestId, uint256 _payment, bytes4 _callbackFunc, uint256 _expiration ) external { bytes32 paramsHash = keccak256( abi.encodePacked( _payment, msg.sender, _callbackFunc, _expiration) ); require(paramsHash == commitments[_requestId], "Params do not match request ID"); require(_expiration <= now, "Request is not expired"); delete commitments[_requestId]; emit CancelOracleRequest(_requestId); assert(LinkToken.transfer(msg.sender, _payment)); } // MODIFIERS /** * @dev Reverts if amount requested is greater than withdrawable balance * @param _amount The given amount to compare to `withdrawableTokens` */ modifier hasAvailableFunds(uint256 _amount) { require(withdrawableTokens >= _amount.add(ONE_FOR_CONSISTENT_GAS_COST), "Amount requested is greater than withdrawable balance"); _; } /** * @dev Reverts if request ID does not exist * @param _requestId The given request ID to check in stored `commitments` */ modifier isValidRequest(bytes32 _requestId) { require(commitments[_requestId] != 0, "Must have a valid requestId"); _; } /** * @dev Reverts if `msg.sender` is not authorized to fulfill requests */ modifier onlyAuthorizedNode() { require(authorizedNodes[msg.sender] || msg.sender == owner, "Not an authorized node to fulfill requests"); _; } /** * @dev Reverts if not sent from the LINK token */ modifier onlyLINK() { require(msg.sender == address(LinkToken), "Must use LINK token"); _; } /** * @dev Reverts if the given data does not begin with the `oracleRequest` function selector * @param _data The data payload of the request */ modifier permittedFunctionsForLINK(bytes _data) { bytes4 funcSelector; assembly { // solium-disable-next-line security/no-low-level-calls funcSelector := mload(add(_data, 32)) } require(funcSelector == this.oracleRequest.selector, "Must use whitelisted functions"); _; } /** * @dev Reverts if the callback address is the LINK token * @param _to The callback address */ modifier checkCallbackAddress(address _to) { require(_to != address(LinkToken), "Cannot callback to LINK"); _; } /** * @dev Reverts if the given payload is less than needed to create a request * @param _data The request payload */ modifier validRequestLength(bytes _data) { require(_data.length >= MINIMUM_REQUEST_LENGTH, "Invalid request length"); _; } }
File 9 of 9: Oracle
// File: openzeppelin-solidity/contracts/ownership/Ownable.sol pragma solidity ^0.4.24; /** * @title Ownable * @dev The Ownable contract has an owner address, and provides basic authorization control * functions, this simplifies the implementation of "user permissions". */ contract Ownable { address public owner; event OwnershipRenounced(address indexed previousOwner); event OwnershipTransferred( address indexed previousOwner, address indexed newOwner ); /** * @dev The Ownable constructor sets the original `owner` of the contract to the sender * account. */ constructor() public { owner = msg.sender; } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { require(msg.sender == owner); _; } /** * @dev Allows the current owner to relinquish control of the contract. * @notice Renouncing to ownership will leave the contract without an owner. * It will not be possible to call the functions with the `onlyOwner` * modifier anymore. */ function renounceOwnership() public onlyOwner { emit OwnershipRenounced(owner); owner = address(0); } /** * @dev Allows the current owner to transfer control of the contract to a newOwner. * @param _newOwner The address to transfer ownership to. */ function transferOwnership(address _newOwner) public onlyOwner { _transferOwnership(_newOwner); } /** * @dev Transfers control of the contract to a newOwner. * @param _newOwner The address to transfer ownership to. */ function _transferOwnership(address _newOwner) internal { require(_newOwner != address(0)); emit OwnershipTransferred(owner, _newOwner); owner = _newOwner; } } // File: openzeppelin-solidity/contracts/math/SafeMath.sol pragma solidity ^0.4.24; /** * @title SafeMath * @dev Math operations with safety checks that throw on error */ library SafeMath { /** * @dev Multiplies two numbers, throws on overflow. */ function mul(uint256 _a, uint256 _b) internal pure returns (uint256 c) { // Gas optimization: this is cheaper than asserting 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522 if (_a == 0) { return 0; } c = _a * _b; assert(c / _a == _b); return c; } /** * @dev Integer division of two numbers, truncating the quotient. */ function div(uint256 _a, uint256 _b) internal pure returns (uint256) { // assert(_b > 0); // Solidity automatically throws when dividing by 0 // uint256 c = _a / _b; // assert(_a == _b * c + _a % _b); // There is no case in which this doesn't hold return _a / _b; } /** * @dev Subtracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend). */ function sub(uint256 _a, uint256 _b) internal pure returns (uint256) { assert(_b <= _a); return _a - _b; } /** * @dev Adds two numbers, throws on overflow. */ function add(uint256 _a, uint256 _b) internal pure returns (uint256 c) { c = _a + _b; assert(c >= _a); return c; } } // File: contracts/interfaces/ChainlinkRequestInterface.sol pragma solidity 0.4.24; interface ChainlinkRequestInterface { function oracleRequest( address sender, uint256 payment, bytes32 id, address callbackAddress, bytes4 callbackFunctionId, uint256 nonce, uint256 version, bytes data ) external; function cancelOracleRequest( bytes32 requestId, uint256 payment, bytes4 callbackFunctionId, uint256 expiration ) external; } // File: contracts/interfaces/OracleInterface.sol pragma solidity 0.4.24; interface OracleInterface { function fulfillOracleRequest( bytes32 requestId, uint256 payment, address callbackAddress, bytes4 callbackFunctionId, uint256 expiration, bytes32 data ) external returns (bool); function getAuthorizationStatus(address node) external view returns (bool); function setFulfillmentPermission(address node, bool allowed) external; function withdraw(address recipient, uint256 amount) external; function withdrawable() external view returns (uint256); } // File: contracts/interfaces/LinkTokenInterface.sol pragma solidity 0.4.24; interface LinkTokenInterface { function allowance(address owner, address spender) external returns (bool success); function approve(address spender, uint256 value) external returns (bool success); function balanceOf(address owner) external returns (uint256 balance); function decimals() external returns (uint8 decimalPlaces); function decreaseApproval(address spender, uint256 addedValue) external returns (bool success); function increaseApproval(address spender, uint256 subtractedValue) external; function name() external returns (string tokenName); function symbol() external returns (string tokenSymbol); function totalSupply() external returns (uint256 totalTokensIssued); function transfer(address to, uint256 value) external returns (bool success); function transferAndCall(address to, uint256 value, bytes data) external returns (bool success); function transferFrom(address from, address to, uint256 value) external returns (bool success); } // File: contracts/Oracle.sol pragma solidity 0.4.24; /** * @title The Chainlink Oracle contract * @notice Node operators can deploy this contract to fulfill requests sent to them */ contract Oracle is ChainlinkRequestInterface, OracleInterface, Ownable { using SafeMath for uint256; uint256 constant public EXPIRY_TIME = 5 minutes; uint256 constant private MINIMUM_CONSUMER_GAS_LIMIT = 400000; // We initialize fields to 1 instead of 0 so that the first invocation // does not cost more gas. uint256 constant private ONE_FOR_CONSISTENT_GAS_COST = 1; uint256 constant private SELECTOR_LENGTH = 4; uint256 constant private EXPECTED_REQUEST_WORDS = 2; // solium-disable-next-line zeppelin/no-arithmetic-operations uint256 constant private MINIMUM_REQUEST_LENGTH = SELECTOR_LENGTH + (32 * EXPECTED_REQUEST_WORDS); LinkTokenInterface internal LinkToken; mapping(bytes32 => bytes32) private commitments; mapping(address => bool) private authorizedNodes; uint256 private withdrawableTokens = ONE_FOR_CONSISTENT_GAS_COST; event OracleRequest( bytes32 indexed specId, address requester, bytes32 requestId, uint256 payment, address callbackAddr, bytes4 callbackFunctionId, uint256 cancelExpiration, uint256 dataVersion, bytes data ); event CancelOracleRequest( bytes32 indexed requestId ); /** * @notice Deploy with the address of the LINK token * @dev Sets the LinkToken address for the imported LinkTokenInterface * @param _link The address of the LINK token */ constructor(address _link) Ownable() public { LinkToken = LinkTokenInterface(_link); } /** * @notice Called when LINK is sent to the contract via `transferAndCall` * @dev The data payload's first 2 words will be overwritten by the `_sender` and `_amount` * values to ensure correctness. Calls oracleRequest. * @param _sender Address of the sender * @param _amount Amount of LINK sent (specified in wei) * @param _data Payload of the transaction */ function onTokenTransfer( address _sender, uint256 _amount, bytes _data ) public onlyLINK validRequestLength(_data) permittedFunctionsForLINK(_data) { assembly { // solium-disable-next-line security/no-low-level-calls mstore(add(_data, 36), _sender) // ensure correct sender is passed // solium-disable-next-line security/no-low-level-calls mstore(add(_data, 68), _amount) // ensure correct amount is passed } // solium-disable-next-line security/no-low-level-calls require(address(this).delegatecall(_data), "Unable to create request"); // calls oracleRequest } /** * @notice Creates the Chainlink request * @dev Stores the hash of the params as the on-chain commitment for the request. * Emits OracleRequest event for the Chainlink node to detect. * @param _sender The sender of the request * @param _payment The amount of payment given (specified in wei) * @param _specId The Job Specification ID * @param _callbackAddress The callback address for the response * @param _callbackFunctionId The callback function ID for the response * @param _nonce The nonce sent by the requester * @param _dataVersion The specified data version * @param _data The CBOR payload of the request */ function oracleRequest( address _sender, uint256 _payment, bytes32 _specId, address _callbackAddress, bytes4 _callbackFunctionId, uint256 _nonce, uint256 _dataVersion, bytes _data ) external onlyLINK checkCallbackAddress(_callbackAddress) { bytes32 requestId = keccak256(abi.encodePacked(_sender, _nonce)); require(commitments[requestId] == 0, "Must use a unique ID"); uint256 expiration = now.add(EXPIRY_TIME); commitments[requestId] = keccak256( abi.encodePacked( _payment, _callbackAddress, _callbackFunctionId, expiration ) ); emit OracleRequest( _specId, _sender, requestId, _payment, _callbackAddress, _callbackFunctionId, expiration, _dataVersion, _data); } /** * @notice Called by the Chainlink node to fulfill requests * @dev Given params must hash back to the commitment stored from `oracleRequest`. * Will call the callback address' callback function without bubbling up error * checking in a `require` so that the node can get paid. * @param _requestId The fulfillment request ID that must match the requester's * @param _payment The payment amount that will be released for the oracle (specified in wei) * @param _callbackAddress The callback address to call for fulfillment * @param _callbackFunctionId The callback function ID to use for fulfillment * @param _expiration The expiration that the node should respond by before the requester can cancel * @param _data The data to return to the consuming contract * @return Status if the external call was successful */ function fulfillOracleRequest( bytes32 _requestId, uint256 _payment, address _callbackAddress, bytes4 _callbackFunctionId, uint256 _expiration, bytes32 _data ) external onlyAuthorizedNode isValidRequest(_requestId) returns (bool) { bytes32 paramsHash = keccak256( abi.encodePacked( _payment, _callbackAddress, _callbackFunctionId, _expiration ) ); require(commitments[_requestId] == paramsHash, "Params do not match request ID"); withdrawableTokens = withdrawableTokens.add(_payment); delete commitments[_requestId]; require(gasleft() >= MINIMUM_CONSUMER_GAS_LIMIT, "Must provide consumer enough gas"); // All updates to the oracle's fulfillment should come before calling the // callback(addr+functionId) as it is untrusted. // See: https://solidity.readthedocs.io/en/develop/security-considerations.html#use-the-checks-effects-interactions-pattern return _callbackAddress.call(_callbackFunctionId, _requestId, _data); // solium-disable-line security/no-low-level-calls } /** * @notice Use this to check if a node is authorized for fulfilling requests * @param _node The address of the Chainlink node * @return The authorization status of the node */ function getAuthorizationStatus(address _node) external view returns (bool) { return authorizedNodes[_node]; } /** * @notice Sets the fulfillment permission for a given node. Use `true` to allow, `false` to disallow. * @param _node The address of the Chainlink node * @param _allowed Bool value to determine if the node can fulfill requests */ function setFulfillmentPermission(address _node, bool _allowed) external onlyOwner { authorizedNodes[_node] = _allowed; } /** * @notice Allows the node operator to withdraw earned LINK to a given address * @dev The owner of the contract can be another wallet and does not have to be a Chainlink node * @param _recipient The address to send the LINK token to * @param _amount The amount to send (specified in wei) */ function withdraw(address _recipient, uint256 _amount) external onlyOwner hasAvailableFunds(_amount) { withdrawableTokens = withdrawableTokens.sub(_amount); assert(LinkToken.transfer(_recipient, _amount)); } /** * @notice Displays the amount of LINK that is available for the node operator to withdraw * @dev We use `ONE_FOR_CONSISTENT_GAS_COST` in place of 0 in storage * @return The amount of withdrawable LINK on the contract */ function withdrawable() external view onlyOwner returns (uint256) { return withdrawableTokens.sub(ONE_FOR_CONSISTENT_GAS_COST); } /** * @notice Allows requesters to cancel requests sent to this oracle contract. Will transfer the LINK * sent for the request back to the requester's address. * @dev Given params must hash to a commitment stored on the contract in order for the request to be valid * Emits CancelOracleRequest event. * @param _requestId The request ID * @param _payment The amount of payment given (specified in wei) * @param _callbackFunc The requester's specified callback address * @param _expiration The time of the expiration for the request */ function cancelOracleRequest( bytes32 _requestId, uint256 _payment, bytes4 _callbackFunc, uint256 _expiration ) external { bytes32 paramsHash = keccak256( abi.encodePacked( _payment, msg.sender, _callbackFunc, _expiration) ); require(paramsHash == commitments[_requestId], "Params do not match request ID"); require(_expiration <= now, "Request is not expired"); delete commitments[_requestId]; emit CancelOracleRequest(_requestId); assert(LinkToken.transfer(msg.sender, _payment)); } // MODIFIERS /** * @dev Reverts if amount requested is greater than withdrawable balance * @param _amount The given amount to compare to `withdrawableTokens` */ modifier hasAvailableFunds(uint256 _amount) { require(withdrawableTokens >= _amount.add(ONE_FOR_CONSISTENT_GAS_COST), "Amount requested is greater than withdrawable balance"); _; } /** * @dev Reverts if request ID does not exist * @param _requestId The given request ID to check in stored `commitments` */ modifier isValidRequest(bytes32 _requestId) { require(commitments[_requestId] != 0, "Must have a valid requestId"); _; } /** * @dev Reverts if `msg.sender` is not authorized to fulfill requests */ modifier onlyAuthorizedNode() { require(authorizedNodes[msg.sender] || msg.sender == owner, "Not an authorized node to fulfill requests"); _; } /** * @dev Reverts if not sent from the LINK token */ modifier onlyLINK() { require(msg.sender == address(LinkToken), "Must use LINK token"); _; } /** * @dev Reverts if the given data does not begin with the `oracleRequest` function selector * @param _data The data payload of the request */ modifier permittedFunctionsForLINK(bytes _data) { bytes4 funcSelector; assembly { // solium-disable-next-line security/no-low-level-calls funcSelector := mload(add(_data, 32)) } require(funcSelector == this.oracleRequest.selector, "Must use whitelisted functions"); _; } /** * @dev Reverts if the callback address is the LINK token * @param _to The callback address */ modifier checkCallbackAddress(address _to) { require(_to != address(LinkToken), "Cannot callback to LINK"); _; } /** * @dev Reverts if the given payload is less than needed to create a request * @param _data The request payload */ modifier validRequestLength(bytes _data) { require(_data.length >= MINIMUM_REQUEST_LENGTH, "Invalid request length"); _; } }