Forkchoice Ethereum Mainnet

Address Contract Verified

Address 0x47Fb2585D2C56Fe188D0E6ec628a38b74fCeeeDf
Balance 0 ETH
Nonce 1
Code Size 13310 bytes
Indexed Transactions 0 (1 on-chain, 0.7% indexed)
External Etherscan · Sourcify

Contract Bytecode

13310 bytes
0x608060405234801561001057600080fd5b50600436106101da5760003560e01c8063a051538e11610104578063d2edb6dd116100a2578063f2fde38b11610071578063f2fde38b14610426578063fa820de914610439578063fc58749e1461044c578063ff0601c01461045f576101da565b8063d2edb6dd146103da578063d4c282a3146103ed578063ec62f44b14610400578063f08391d814610413576101da565b8063bcfd032d116100de578063bcfd032d14610380578063c1ce86fc14610393578063c639cd91146103b4578063d0188fc6146103c7576101da565b8063a051538e1461033a578063af34b03a1461034d578063b099d43b14610360576101da565b80635ad9d9df1161017c5780638da5cb5b1161014b5780638da5cb5b146102ec57806391624c95146102f45780639e3ff6fd146103075780639eed82b014610327576101da565b80635ad9d9df1461029a578063672ff44f146102ad57806379ba5097146102c05780638916524a146102c8576101da565b8063181f5a77116101b8578063181f5a7714610232578063303228181461024757806352dbeb8b1461026757806358e2d3a81461027a576101da565b8063045abf4b146101df57806315cd4ad2146101f457806316d6b5f61461021d575b600080fd5b6101f26101ed366004612cc5565b61047f565b005b610207610202366004612d54565b610585565b604051610214919061301f565b60405180910390f35b6102256107a1565b6040516102149190612f2e565b61023a6107bd565b6040516102149190613028565b61025a610255366004612c8d565b6107f4565b60405161021491906132c5565b610225610275366004612d0f565b610832565b61028d610288366004612c8d565b61088e565b6040516102149190613350565b6102256102a8366004612c8d565b610970565b6102076102bb366004612c8d565b61097c565b6101f2610b59565b6102db6102d6366004612d94565b610c5b565b604051610214959493929190613300565b610225610dac565b610207610302366004612d54565b610dc8565b61031a610315366004612d94565b610f7e565b60405161021491906132e9565b6101f2610335366004612cc5565b610fa3565b61031a610348366004612d94565b6110fc565b61020761035b366004612c8d565b611118565b61037361036e366004612c71565b6111ba565b6040516102149190613014565b6102db61038e366004612c8d565b6111e9565b6103a66103a1366004612d0f565b611435565b604051610214929190613333565b6102256103c2366004612d94565b6114f9565b6102db6103d5366004612c8d565b61156b565b6102256103e8366004612c8d565b61169e565b6102076103fb366004612c8d565b6116f9565b61020761040e366004612c8d565b61189e565b6101f2610421366004612c71565b611ad1565b6101f2610434366004612c71565b611bd5565b61023a610447366004612c8d565b611be9565b6102db61045a366004612d94565b611ce6565b61047261046d366004612d0f565b611f3d565b604051610214919061328e565b610487611f91565b600080610495858585612019565b73ffffffffffffffffffffffffffffffffffffffff80881660008181526004602090815260408083208b861680855290835281842080547fffffffffffffffffffffffff00000000000000000000000000000000000000001690558a8616808552600390935281842080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff009081166001179091559587168452928190208054909516909455925194965092945090927f27a180c70f2642f63d1694eb252b7df52e7ab2565e3f67adf7748acb7d82b9bc9061057690869088903390612fe0565b60405180910390a45050505050565b60025460009073ffffffffffffffffffffffffffffffffffffffff16158061065357506002546040517f6b14daf800000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911690636b14daf8906106039033906000903690600401612f4f565b60206040518083038186803b15801561061b57600080fd5b505afa15801561062f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106539190612dd3565b610692576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106899061311e565b60405180910390fd5b69ffffffffffffffffffff8211156106ac5750600061079a565b6000806106b884612337565b9150915060006106c987878561233f565b905073ffffffffffffffffffffffffffffffffffffffff81166106f2576000935050505061079a565b6040517fb5ab58dc00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82169063b5ab58dc906107449085906004016132d4565b60206040518083038186803b15801561075c57600080fd5b505afa158015610770573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107949190612df3565b93505050505b9392505050565b60025473ffffffffffffffffffffffffffffffffffffffff1690565b60408051808201909152601281527f46656564526567697374727920312e302e300000000000000000000000000000602082015290565b73ffffffffffffffffffffffffffffffffffffffff80831660009081526005602090815260408083209385168352929052205461ffff165b92915050565b600061083f84848461233f565b905073ffffffffffffffffffffffffffffffffffffffff811661079a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161068990613155565b60008061089b8484612386565b905073ffffffffffffffffffffffffffffffffffffffff81166108ea576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610689906131fa565b8073ffffffffffffffffffffffffffffffffffffffff1663313ce5676040518163ffffffff1660e01b815260040160206040518083038186803b15801561093057600080fd5b505afa158015610944573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109689190612f0d565b949350505050565b600061079a83836123ca565b60025460009073ffffffffffffffffffffffffffffffffffffffff161580610a4a57506002546040517f6b14daf800000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911690636b14daf8906109fa9033906000903690600401612f4f565b60206040518083038186803b158015610a1257600080fd5b505afa158015610a26573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a4a9190612dd3565b610a80576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106899061311e565b6000610a8c8484612386565b905073ffffffffffffffffffffffffffffffffffffffff8116610adb576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610689906131fa565b8073ffffffffffffffffffffffffffffffffffffffff16638205bf6a6040518163ffffffff1660e01b815260040160206040518083038186803b158015610b2157600080fd5b505afa158015610b35573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109689190612df3565b60015473ffffffffffffffffffffffffffffffffffffffff163314610bdf57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e657200000000000000000000604482015290519081900360640190fd5b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b73ffffffffffffffffffffffffffffffffffffffff80841660009081526004602090815260408083208487168452909152812054909182918291829182918991899116610cd4576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610689906130b0565b73ffffffffffffffffffffffffffffffffffffffff808b1660009081526004602081815260408084208e86168552909152918290205491517f9a6fc8f50000000000000000000000000000000000000000000000000000000081529190921691639a6fc8f591610d46918c91016132e9565b60a06040518083038186803b158015610d5e57600080fd5b505afa158015610d72573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d969190612eb6565b939e929d50909b50995090975095505050505050565b60005473ffffffffffffffffffffffffffffffffffffffff1690565b60025460009073ffffffffffffffffffffffffffffffffffffffff161580610e9657506002546040517f6b14daf800000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911690636b14daf890610e469033906000903690600401612f4f565b60206040518083038186803b158015610e5e57600080fd5b505afa158015610e72573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e969190612dd3565b610ecc576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106899061311e565b69ffffffffffffffffffff821115610ee65750600061079a565b600080610ef284612337565b915091506000610f0387878561233f565b905073ffffffffffffffffffffffffffffffffffffffff8116610f2c576000935050505061079a565b6040517fb633620c00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82169063b633620c906107449085906004016132d4565b600080610f8c858585612401565b9050610f9a8585838661261a565b95945050505050565b610fab611f91565b6000610fb78484612386565b90508073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16141561101f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161068990613231565b600061102b85856123ca565b90508273ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16146110f55773ffffffffffffffffffffffffffffffffffffffff858116600081815260046020908152604080832089861680855292529182902080547fffffffffffffffffffffffff000000000000000000000000000000000000000016948816948517905590519091907fb56c4f88c3e344891ef92e51f036d7116e886f4ea57f5ba93e28b5f44925b9ce906105769087903390612fb9565b5050505050565b60008061110a858585612401565b9050610f9a85858386612749565b6000806111258484612386565b905073ffffffffffffffffffffffffffffffffffffffff8116611174576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610689906131fa565b8073ffffffffffffffffffffffffffffffffffffffff166354fd4d506040518163ffffffff1660e01b815260040160206040518083038186803b158015610b2157600080fd5b73ffffffffffffffffffffffffffffffffffffffff811660009081526003602052604090205460ff165b919050565b600254600090819081908190819073ffffffffffffffffffffffffffffffffffffffff1615806112bf57506002546040517f6b14daf800000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911690636b14daf89061126f9033906000903690600401612f4f565b60206040518083038186803b15801561128757600080fd5b505afa15801561129b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112bf9190612dd3565b6112f5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106899061311e565b73ffffffffffffffffffffffffffffffffffffffff8088166000908152600560209081526040808320938a1683529290529081205461ffff16906113398989612386565b905073ffffffffffffffffffffffffffffffffffffffff8116611388576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610689906131fa565b8073ffffffffffffffffffffffffffffffffffffffff1663feaf968c6040518163ffffffff1660e01b815260040160a06040518083038186803b1580156113ce57600080fd5b505afa1580156113e2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114069190612eb6565b939a509198509650945092506114208787878787876128b7565b939d929c50909a509850909650945050505050565b60008060006114458686866128d6565b905061145081612968565b611486576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610689906131c3565b73ffffffffffffffffffffffffffffffffffffffff80871660009081526005602090815260408083209389168352929052205461ffff9081169085168114156114df576114d4878783612972565b9350935050506114f1565b6114ea878787612a1d565b9350935050505b935093915050565b600080611507858585612401565b905061151485858361233f565b915073ffffffffffffffffffffffffffffffffffffffff8216611563576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610689906130e7565b509392505050565b73ffffffffffffffffffffffffffffffffffffffff808316600090815260046020908152604080832084861684529091528120549091829182918291829188918891166115e4576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610689906130b0565b73ffffffffffffffffffffffffffffffffffffffff808a1660009081526004602081815260408084208d86168552909152918290205482517ffeaf968c000000000000000000000000000000000000000000000000000000008152925193169263feaf968c928083019260a09291829003018186803b15801561166657600080fd5b505afa15801561167a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114209190612eb6565b60006116aa8383612386565b905073ffffffffffffffffffffffffffffffffffffffff811661082c576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610689906131fa565b60025460009073ffffffffffffffffffffffffffffffffffffffff1615806117c757506002546040517f6b14daf800000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911690636b14daf8906117779033906000903690600401612f4f565b60206040518083038186803b15801561178f57600080fd5b505afa1580156117a3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117c79190612dd3565b6117fd576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106899061311e565b60006118098484612386565b905073ffffffffffffffffffffffffffffffffffffffff8116611858576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610689906131fa565b8073ffffffffffffffffffffffffffffffffffffffff166350d25bcd6040518163ffffffff1660e01b815260040160206040518083038186803b158015610b2157600080fd5b60025460009073ffffffffffffffffffffffffffffffffffffffff16158061196c57506002546040517f6b14daf800000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911690636b14daf89061191c9033906000903690600401612f4f565b60206040518083038186803b15801561193457600080fd5b505afa158015611948573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061196c9190612dd3565b6119a2576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106899061311e565b73ffffffffffffffffffffffffffffffffffffffff808416600090815260056020908152604080832093861683529290529081205461ffff16906119e68585612386565b905073ffffffffffffffffffffffffffffffffffffffff8116611a35576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610689906131fa565b611abc828273ffffffffffffffffffffffffffffffffffffffff1663668a0f026040518163ffffffff1660e01b815260040160206040518083038186803b158015611a7f57600080fd5b505afa158015611a93573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ab79190612df3565b612a43565b69ffffffffffffffffffff1695945050505050565b611ad9611f91565b60025473ffffffffffffffffffffffffffffffffffffffff82811691161415611b6357604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f41636365737320636f6e74726f6c6c657220697320616c726561647920736574604482015290519081900360640190fd5b600280547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83169081179091556040513391907f953e92b1a6442e9c3242531154a3f6f6eb00b4e9c719ba8118fa6235e4ce89b690600090a350565b611bdd611f91565b611be681612a63565b50565b60606000611bf78484612386565b905073ffffffffffffffffffffffffffffffffffffffff8116611c46576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610689906131fa565b8073ffffffffffffffffffffffffffffffffffffffff16637284e4166040518163ffffffff1660e01b815260040160006040518083038186803b158015611c8c57600080fd5b505afa158015611ca0573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682016040526109689190810190612e0b565b600254600090819081908190819073ffffffffffffffffffffffffffffffffffffffff161580611dbc57506002546040517f6b14daf800000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911690636b14daf890611d6c9033906000903690600401612f4f565b60206040518083038186803b158015611d8457600080fd5b505afa158015611d98573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611dbc9190612dd3565b611df2576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106899061311e565b600080611e0a8869ffffffffffffffffffff16612337565b915091506000611e1b8b8b8561233f565b905073ffffffffffffffffffffffffffffffffffffffff8116611e6a576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610689906131fa565b6040517f9a6fc8f500000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff821690639a6fc8f590611ebc9085906004016132d4565b60a06040518083038186803b158015611ed457600080fd5b505afa158015611ee8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f0c9190612eb6565b939b50919950975095509350611f268888888888886128b7565b939f929e50909c509a509098509650505050505050565b611f45612c51565b611f508484846128d6565b9050611f5b81612968565b61079a576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610689906131c3565b60005473ffffffffffffffffffffffffffffffffffffffff16331461201757604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e657200000000000000000000604482015290519081900360640190fd5b565b73ffffffffffffffffffffffffffffffffffffffff808416600090815260046020908152604080832086851684529091528120549091829184821691161461208d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106899061318c565b60006120998686612386565b905060006120a682612b5e565b73ffffffffffffffffffffffffffffffffffffffff88811660008181526005602090815260408083208c8616808552908352818420805486865260078552838620838752855283862061ffff91821680885290865284872080547fffffffffffffffffffff00000000000000000000ffffffffffffffffffffffff166c0100000000000000000000000069ffffffffffffffffffff8d160217905582547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000166001820192831690811790935596865260068552838620928652918452828520908552909252822080547fffffffffffffffffffffffff000000000000000000000000000000000000000016948b16949094179093559196509192506121ca87612b5e565b905060405180606001604052808761ffff1681526020018269ffffffffffffffffffff168152602001600069ffffffffffffffffffff16815250600760008b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008a73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008861ffff1661ffff16815260200190815260200160002060008201518160000160006101000a81548161ffff021916908361ffff16021790555060208201518160000160026101000a81548169ffffffffffffffffffff021916908369ffffffffffffffffffff160217905550604082015181600001600c6101000a81548169ffffffffffffffffffff021916908369ffffffffffffffffffff16021790555090505085849550955050505050935093915050565b604081901c91565b73ffffffffffffffffffffffffffffffffffffffff9283166000908152600660209081526040808320948616835293815283822061ffff9390931682529190915220541690565b73ffffffffffffffffffffffffffffffffffffffff808316600090815260056020908152604080832093851683529290529081205461ffff1661096884848361233f565b73ffffffffffffffffffffffffffffffffffffffff9182166000908152600460209081526040808320938516835292905220541690565b73ffffffffffffffffffffffffffffffffffffffff808416600090815260056020908152604080832093861683529290529081205461ffff168180612447878785612972565b915091508169ffffffffffffffffffff168569ffffffffffffffffffff161015801561248b57508069ffffffffffffffffffff168569ffffffffffffffffffff1611155b1561249b5782935050505061079a565b60008361ffff16116124d9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161068990613079565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83015b61ffff81161561260c5773ffffffffffffffffffffffffffffffffffffffff80891660009081526006602090815260408083208b85168452825280832061ffff86168452909152902054168061255357506125e5565b6000806125618b8b86612a1d565b915091508169ffffffffffffffffffff168969ffffffffffffffffffff16101580156125a557508069ffffffffffffffffffff168969ffffffffffffffffffff1611155b156125b9578397505050505050505061079a565b8069ffffffffffffffffffff168969ffffffffffffffffffff1611156125e15750505061260c565b5050505b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff016124fd565b506000979650505050505050565b6000825b61ffff81161561273d57600061263587878461233f565b9050600080612645898986612a1d565b909250905073ffffffffffffffffffffffffffffffffffffffff831661266d57505050612716565b8169ffffffffffffffffffff168669ffffffffffffffffffff161161269457505050612716565b8169ffffffffffffffffffff168669ffffffffffffffffffff161180156126d357508069ffffffffffffffffffff168669ffffffffffffffffffff1611155b156126e75760018603945050505050610968565b8069ffffffffffffffffffff168669ffffffffffffffffffff16111561271257935061096892505050565b5050505b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0161261e565b50600095945050505050565b73ffffffffffffffffffffffffffffffffffffffff808516600090815260056020908152604080832093871683529290529081205461ffff16835b8161ffff168161ffff16116128aa5760006127a088888461233f565b90506000808461ffff168461ffff16146127c4576127bf8a8a86612a1d565b6127cf565b6127cf8a8a86612972565b909250905073ffffffffffffffffffffffffffffffffffffffff83166127f7575050506128a2565b8069ffffffffffffffffffff168769ffffffffffffffffffff161061281e575050506128a2565b8169ffffffffffffffffffff168769ffffffffffffffffffff161015801561285d57508069ffffffffffffffffffff168769ffffffffffffffffffff16105b15612872578660010195505050505050610968565b8169ffffffffffffffffffff168769ffffffffffffffffffff16101561289e5750935061096892505050565b5050505b600101612784565b5060009695505050505050565b60008060008060006128c9868c612a43565b8a8a8a611f268a8c612a43565b6128de612c51565b5073ffffffffffffffffffffffffffffffffffffffff928316600090815260076020908152604080832094909516825292835283812061ffff9283168252835283902083516060810185529054918216815269ffffffffffffffffffff6201000083048116938201939093526c010000000000000000000000009091049091169181019190915290565b5161ffff16151590565b73ffffffffffffffffffffffffffffffffffffffff8084166000908152600760209081526040808320938616835292815282822061ffff808616845290825283832084516060810186529054918216815269ffffffffffffffffffff6201000083048116938201939093526c0100000000000000000000000090910490911692810192909252908190612a058482612c01565b612a10878787612c11565b9250925050935093915050565b6000806000612a2d8686866128d6565b9050612a398482612c01565b612a108583612c41565b67ffffffffffffffff1660409190911b69ffff0000000000000000161790565b73ffffffffffffffffffffffffffffffffffffffff8116331415612ae857604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015290519081900360640190fd5b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b600073ffffffffffffffffffffffffffffffffffffffff8216612b83575060006111e4565b8173ffffffffffffffffffffffffffffffffffffffff1663668a0f026040518163ffffffff1660e01b815260040160206040518083038186803b158015612bc957600080fd5b505afa158015612bdd573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061082c9190612df3565b600061079a838360200151612a43565b600080612c1e8585612386565b90506000612c2b82612b5e565b9050612c378482612a43565b9695505050505050565b600061079a838360400151612a43565b604080516060810182526000808252602082018190529181019190915290565b600060208284031215612c82578081fd5b813561079a8161338e565b60008060408385031215612c9f578081fd5b8235612caa8161338e565b91506020830135612cba8161338e565b809150509250929050565b600080600060608486031215612cd9578081fd5b8335612ce48161338e565b92506020840135612cf48161338e565b91506040840135612d048161338e565b809150509250925092565b600080600060608486031215612d23578283fd5b8335612d2e8161338e565b92506020840135612d3e8161338e565b9150604084013561ffff81168114612d04578182fd5b600080600060608486031215612d68578283fd5b8335612d738161338e565b92506020840135612d838161338e565b929592945050506040919091013590565b600080600060608486031215612da8578283fd5b8335612db38161338e565b92506020840135612dc38161338e565b91506040840135612d04816133b0565b600060208284031215612de4578081fd5b8151801515811461079a578182fd5b600060208284031215612e04578081fd5b5051919050565b600060208284031215612e1c578081fd5b815167ffffffffffffffff80821115612e33578283fd5b818401915084601f830112612e46578283fd5b815181811115612e5257fe5b60405160207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8401168201018181108482111715612e8e57fe5b604052818152838201602001871015612ea5578485fd5b612c3782602083016020870161335e565b600080600080600060a08688031215612ecd578081fd5b8551612ed8816133b0565b809550506020860151935060408601519250606086015191506080860151612eff816133b0565b809150509295509295909350565b600060208284031215612f1e578081fd5b815160ff8116811461079a578182fd5b73ffffffffffffffffffffffffffffffffffffffff91909116815260200190565b600073ffffffffffffffffffffffffffffffffffffffff851682526040602083015282604083015282846060840137818301606090810191909152601f9092017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016010192915050565b73ffffffffffffffffffffffffffffffffffffffff92831681529116602082015260400190565b73ffffffffffffffffffffffffffffffffffffffff938416815261ffff929092166020830152909116604082015260600190565b901515815260200190565b90815260200190565b600060208252825180602084015261304781604085016020870161335e565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169190910160400192915050565b6020808252600d908201527f496e76616c696420706861736500000000000000000000000000000000000000604082015260600190565b6020808252601e908201527f4e6f2070726f706f7365642061676772656761746f722070726573656e740000604082015260600190565b60208082526018908201527f46656564206e6f7420666f756e6420666f7220726f756e640000000000000000604082015260600190565b60208082526009908201527f4e6f206163636573730000000000000000000000000000000000000000000000604082015260600190565b60208082526018908201527f46656564206e6f7420666f756e6420666f722070686173650000000000000000604082015260600190565b6020808252601b908201527f496e76616c69642070726f706f7365642061676772656761746f720000000000604082015260600190565b60208082526014908201527f506861736520646f6573206e6f74206578697374000000000000000000000000604082015260600190565b6020808252600e908201527f46656564206e6f7420666f756e64000000000000000000000000000000000000604082015260600190565b60208082526021908201527f43616e6e6f742070726f706f73652063757272656e742061676772656761746f60408201527f7200000000000000000000000000000000000000000000000000000000000000606082015260800190565b815161ffff16815260208083015169ffffffffffffffffffff90811691830191909152604092830151169181019190915260600190565b61ffff91909116815260200190565b67ffffffffffffffff91909116815260200190565b69ffffffffffffffffffff91909116815260200190565b69ffffffffffffffffffff9586168152602081019490945260408401929092526060830152909116608082015260a00190565b69ffffffffffffffffffff92831681529116602082015260400190565b60ff91909116815260200190565b60005b83811015613379578181015183820152602001613361565b83811115613388576000848401525b50505050565b73ffffffffffffffffffffffffffffffffffffffff81168114611be657600080fd5b69ffffffffffffffffffff81168114611be657600080fdfea264697066735822122027953606a21f192d41af98298de12dd74dcea88de0e834d8a593ef86cbba4f1d64736f6c63430007060033

Verified Source Code Full Match

Compiler: v0.7.6+commit.7338295f EVM: istanbul Optimization: Yes (1000000 runs)
FeedRegistry.sol 1033 lines
// SPDX-License-Identifier: MIT

pragma solidity 0.7.6;
pragma abicoder v2; // solhint-disable compiler-version

import "@chainlink/contracts/src/v0.7/interfaces/AggregatorV2V3Interface.sol";
import "./access/AccessControlled.sol";
import "./interfaces/FeedRegistryInterface.sol";
import "./interfaces/TypeAndVersionInterface.sol";

/**
  * @notice An on-chain registry of assets to aggregators.
  * @notice This contract provides a consistent address for consumers but delegates where it reads from to the owner, who is
  * trusted to update it. This registry contract works for multiple feeds, not just a single aggregator.
  * @notice Only access enabled addresses are allowed to access getters for answers and round data
  */
contract FeedRegistry is FeedRegistryInterface, TypeAndVersionInterface, AccessControlled {
  uint256 constant private PHASE_OFFSET = 64;
  uint256 constant private PHASE_SIZE = 16;
  uint256 constant private MAX_ID = 2**(PHASE_OFFSET+PHASE_SIZE) - 1;

  mapping(address => bool) private s_isAggregatorEnabled;
  mapping(address => mapping(address => AggregatorV2V3Interface)) private s_proposedAggregators;
  mapping(address => mapping(address => uint16)) private s_currentPhaseId;
  mapping(address => mapping(address => mapping(uint16 => AggregatorV2V3Interface))) private s_phaseAggregators;
  mapping(address => mapping(address => mapping(uint16 => Phase))) private s_phases;

  /*
   * @notice Versioning
   */
  function typeAndVersion()
    external
    override
    pure
    virtual
    returns (
      string memory
    )
  {
    return "FeedRegistry 1.0.0";
  }

  /**
   * @notice represents the number of decimals the aggregator responses represent.
   */
  function decimals(
    address base,
    address quote
  )
    external
    view
    override
    returns (
      uint8
    )
  {
    AggregatorV2V3Interface aggregator = _getFeed(base, quote);
    require(address(aggregator) != address(0), "Feed not found");
    return aggregator.decimals();
  }

  /**
   * @notice returns the description of the aggregator the proxy points to.
   */
  function description(
    address base,
    address quote
  )
    external
    view
    override
    returns (
      string memory
    )
  {
    AggregatorV2V3Interface aggregator = _getFeed(base, quote);
    require(address(aggregator) != address(0), "Feed not found");
    return aggregator.description();
  }

  /**
   * @notice the version number representing the type of aggregator the proxy
   * points to.
   */
  function version(
    address base,
    address quote
  )
    external
    view
    override
    returns (
      uint256
    )
  {
    AggregatorV2V3Interface aggregator = _getFeed(base, quote);
    require(address(aggregator) != address(0), "Feed not found");
    return aggregator.version();
  }

  /**
   * @notice get data about the latest round. Consumers are encouraged to check
   * that they're receiving fresh data by inspecting the updatedAt and
   * answeredInRound return values.
   * Note that different underlying implementations of AggregatorV3Interface
   * have slightly different semantics for some of the return values. Consumers
   * should determine what implementations they expect to receive
   * data from and validate that they can properly handle return data from all
   * of them.
   * @param base base asset address
   * @param quote quote asset address
   * @return roundId is the round ID from the aggregator for which the data was
   * retrieved combined with a phase to ensure that round IDs get larger as
   * time moves forward.
   * @return answer is the answer for the given round
   * @return startedAt is the timestamp when the round was started.
   * (Only some AggregatorV3Interface implementations return meaningful values)
   * @return updatedAt is the timestamp when the round last was updated (i.e.
   * answer was last computed)
   * @return answeredInRound is the round ID of the round in which the answer
   * was computed.
   * (Only some AggregatorV3Interface implementations return meaningful values)
   * @dev Note that answer and updatedAt may change between queries.
   */
  function latestRoundData(
    address base,
    address quote
  )
    external
    view
    override
    checkPairAccess()
    returns (
      uint80 roundId,
      int256 answer,
      uint256 startedAt,
      uint256 updatedAt,
      uint80 answeredInRound
    )
  {
    uint16 currentPhaseId = s_currentPhaseId[base][quote];
    AggregatorV2V3Interface aggregator = _getFeed(base, quote);
    require(address(aggregator) != address(0), "Feed not found");
    (
      roundId,
      answer,
      startedAt,
      updatedAt,
      answeredInRound
    ) = aggregator.latestRoundData();
    return _addPhaseIds(roundId, answer, startedAt, updatedAt, answeredInRound, currentPhaseId);
  }

  /**
   * @notice get data about a round. Consumers are encouraged to check
   * that they're receiving fresh data by inspecting the updatedAt and
   * answeredInRound return values.
   * Note that different underlying implementations of AggregatorV3Interface
   * have slightly different semantics for some of the return values. Consumers
   * should determine what implementations they expect to receive
   * data from and validate that they can properly handle return data from all
   * of them.
   * @param base base asset address
   * @param quote quote asset address
   * @param _roundId the proxy round id number to retrieve the round data for
   * @return roundId is the round ID from the aggregator for which the data was
   * retrieved combined with a phase to ensure that round IDs get larger as
   * time moves forward.
   * @return answer is the answer for the given round
   * @return startedAt is the timestamp when the round was started.
   * (Only some AggregatorV3Interface implementations return meaningful values)
   * @return updatedAt is the timestamp when the round last was updated (i.e.
   * answer was last computed)
   * @return answeredInRound is the round ID of the round in which the answer
   * was computed.
   * (Only some AggregatorV3Interface implementations return meaningful values)
   * @dev Note that answer and updatedAt may change between queries.
   */
  function getRoundData(
    address base,
    address quote,
    uint80 _roundId
  )
    external
    view
    override
    checkPairAccess()
    returns (
      uint80 roundId,
      int256 answer,
      uint256 startedAt,
      uint256 updatedAt,
      uint80 answeredInRound
    )
  {
    (uint16 phaseId, uint64 aggregatorRoundId) = _parseIds(_roundId);
    AggregatorV2V3Interface aggregator = _getPhaseFeed(base, quote, phaseId);
    require(address(aggregator) != address(0), "Feed not found");
    (
      roundId,
      answer,
      startedAt,
      updatedAt,
      answeredInRound
    ) = aggregator.getRoundData(aggregatorRoundId);
    return _addPhaseIds(roundId, answer, startedAt, updatedAt, answeredInRound, phaseId);
  }


  /**
   * @notice Reads the current answer for an base / quote pair's aggregator.
   * @param base base asset address
   * @param quote quote asset address
   * @notice We advise to use latestRoundData() instead because it returns more in-depth information.
   * @dev This does not error if no answer has been reached, it will simply return 0. Either wait to point to
   * an already answered Aggregator or use the recommended latestRoundData
   * instead which includes better verification information.
   */
  function latestAnswer(
    address base,
    address quote
  )
    external
    view
    override
    checkPairAccess()
    returns (
      int256 answer
    )
  {
    AggregatorV2V3Interface aggregator = _getFeed(base, quote);
    require(address(aggregator) != address(0), "Feed not found");
    return aggregator.latestAnswer();
  }

  /**
   * @notice get the latest completed timestamp where the answer was updated.
   * @param base base asset address
   * @param quote quote asset address
   *
   * @notice We advise to use latestRoundData() instead because it returns more in-depth information.
   * @dev This does not error if no answer has been reached, it will simply return 0. Either wait to point to
   * an already answered Aggregator or use the recommended latestRoundData
   * instead which includes better verification information.
   */
  function latestTimestamp(
    address base,
    address quote
  )
    external
    view
    override
    checkPairAccess()
    returns (
      uint256 timestamp
    )
  {
    AggregatorV2V3Interface aggregator = _getFeed(base, quote);
    require(address(aggregator) != address(0), "Feed not found");
    return aggregator.latestTimestamp();
  }

  /**
   * @notice get the latest completed round where the answer was updated
   * @param base base asset address
   * @param quote quote asset address
   * @dev overridden function to add the checkPairAccess() modifier
   *
   * @notice We advise to use latestRoundData() instead because it returns more in-depth information.
   * @dev Use latestRoundData instead. This does not error if no
   * answer has been reached, it will simply return 0. Either wait to point to
   * an already answered Aggregator or use the recommended latestRoundData
   * instead which includes better verification information.
   */
  function latestRound(
    address base,
    address quote
  )
    external
    view
    override
    checkPairAccess()
    returns (
      uint256 roundId
    )
  {
    uint16 currentPhaseId = s_currentPhaseId[base][quote];
    AggregatorV2V3Interface aggregator = _getFeed(base, quote);
    require(address(aggregator) != address(0), "Feed not found");
    return _addPhase(currentPhaseId, uint64(aggregator.latestRound()));
  }

  /**
   * @notice get past rounds answers
   * @param base base asset address
   * @param quote quote asset address
   * @param roundId the proxy round id number to retrieve the answer for
   * @dev overridden function to add the checkPairAccess() modifier
   *
   * @notice We advise to use getRoundData() instead because it returns more in-depth information.
   * @dev This does not error if no answer has been reached, it will simply return 0. Either wait to point to
   * an already answered Aggregator or use the recommended getRoundData
   * instead which includes better verification information.
   */
  function getAnswer(
    address base,
    address quote,
    uint256 roundId
  )
    external
    view
    override
    checkPairAccess()
    returns (
      int256 answer
    )
  {
    if (roundId > MAX_ID) return 0;
    (uint16 phaseId, uint64 aggregatorRoundId) = _parseIds(roundId);
    AggregatorV2V3Interface aggregator = _getPhaseFeed(base, quote, phaseId);
    if (address(aggregator) == address(0)) return 0;
    return aggregator.getAnswer(aggregatorRoundId);
  }

  /**
   * @notice get block timestamp when an answer was last updated
   * @param base base asset address
   * @param quote quote asset address
   * @param roundId the proxy round id number to retrieve the updated timestamp for
   * @dev overridden function to add the checkPairAccess() modifier
   *
   * @notice We advise to use getRoundData() instead because it returns more in-depth information.
   * @dev This does not error if no answer has been reached, it will simply return 0. Either wait to point to
   * an already answered Aggregator or use the recommended getRoundData
   * instead which includes better verification information.
   */
  function getTimestamp(
    address base,
    address quote,
    uint256 roundId
  )
    external
    view
    override
    checkPairAccess()
    returns (
      uint256 timestamp
    )
  {
    if (roundId > MAX_ID) return 0;
    (uint16 phaseId, uint64 aggregatorRoundId) = _parseIds(roundId);
    AggregatorV2V3Interface aggregator = _getPhaseFeed(base, quote, phaseId);
    if (address(aggregator) == address(0)) return 0;
    return aggregator.getTimestamp(aggregatorRoundId);
  }


  /**
   * @notice Retrieve the aggregator of an base / quote pair in the current phase
   * @param base base asset address
   * @param quote quote asset address
   * @return aggregator
   */
  function getFeed(
    address base,
    address quote
  )
    external
    view
    override
    returns (
      AggregatorV2V3Interface aggregator
    )
  {
    aggregator = _getFeed(base, quote);
    require(address(aggregator) != address(0), "Feed not found");
  }

  /**
   * @notice retrieve the aggregator of an base / quote pair at a specific phase
   * @param base base asset address
   * @param quote quote asset address
   * @param phaseId phase ID
   * @return aggregator
   */
  function getPhaseFeed(
    address base,
    address quote,
    uint16 phaseId
  )
    external
    view
    override
    returns (
      AggregatorV2V3Interface aggregator
    )
  {
    aggregator = _getPhaseFeed(base, quote, phaseId);
    require(address(aggregator) != address(0), "Feed not found for phase");
  }

  /**
   * @notice returns true if a aggregator is enabled for any pair
   * @param aggregator aggregator address
   */
  function isFeedEnabled(
    address aggregator
  )
    external
    view
    override
    returns (
      bool
    )
  {
    return s_isAggregatorEnabled[aggregator];
  }

  /**
   * @notice returns a phase by id. A Phase contains the starting and ending aggregator round ids.
   * endingAggregatorRoundId will be 0 if the phase is the current phase
   * @dev reverts if the phase does not exist
   * @param base base asset address
   * @param quote quote asset address
   * @param phaseId phase id
   * @return phase
   */
  function getPhase(
    address base,
    address quote,
    uint16 phaseId
  )
    external
    view
    override
    returns (
      Phase memory phase
    )
  {
    phase = _getPhase(base, quote, phaseId);
    require(_phaseExists(phase), "Phase does not exist");
  }

  /**
   * @notice retrieve the aggregator of an base / quote pair at a specific round id
   * @param base base asset address
   * @param quote quote asset address
   * @param roundId the proxy round id
   */
  function getRoundFeed(
    address base,
    address quote,
    uint80 roundId
  )
    external
    view
    override
    returns (
      AggregatorV2V3Interface aggregator
    )
  {
    uint16 phaseId = _getPhaseIdByRoundId(base, quote, roundId);
    aggregator = _getPhaseFeed(base, quote, phaseId);
    require(address(aggregator) != address(0), "Feed not found for round");
  }

  /**
   * @notice returns the range of proxy round ids of a phase
   * @param base base asset address
   * @param quote quote asset address
   * @param phaseId phase id
   * @return startingRoundId
   * @return endingRoundId
   */
  function getPhaseRange(
    address base,
    address quote,
    uint16 phaseId
  )
    external
    view
    override
    returns (
      uint80 startingRoundId,
      uint80 endingRoundId
    )
  {
    Phase memory phase = _getPhase(base, quote, phaseId);
    require(_phaseExists(phase), "Phase does not exist");

    uint16 currentPhaseId = s_currentPhaseId[base][quote];
    if (phaseId == currentPhaseId) return _getLatestRoundRange(base, quote, currentPhaseId);
    return _getPhaseRange(base, quote, phaseId);
  }

  /**
   * @notice return the previous round id of a given round
   * @param base base asset address
   * @param quote quote asset address
   * @param roundId the round id number to retrieve the updated timestamp for
   * @dev Note that this is not the aggregator round id, but the proxy round id
   * To get full ranges of round ids of different phases, use getPhaseRange()
   * @return previousRoundId
   */
  function getPreviousRoundId(
    address base,
    address quote,
    uint80 roundId
  ) external
    view
    override
    returns (
      uint80 previousRoundId
    )
  {
    uint16 phaseId = _getPhaseIdByRoundId(base, quote, roundId);
    return _getPreviousRoundId(base, quote, phaseId, roundId);
  }

  /**
   * @notice return the next round id of a given round
   * @param base base asset address
   * @param quote quote asset address
   * @param roundId the round id number to retrieve the updated timestamp for
   * @dev Note that this is not the aggregator round id, but the proxy round id
   * To get full ranges of round ids of different phases, use getPhaseRange()
   * @return nextRoundId
   */
  function getNextRoundId(
    address base,
    address quote,
    uint80 roundId
  ) external
    view
    override
    returns (
      uint80 nextRoundId
    )
  {
    uint16 phaseId = _getPhaseIdByRoundId(base, quote, roundId);
    return _getNextRoundId(base, quote, phaseId, roundId);
  }

  /**
   * @notice Allows the owner to propose a new address for the aggregator
   * @param base base asset address
   * @param quote quote asset address
   * @param aggregator The new aggregator contract address
   */
  function proposeFeed(
    address base,
    address quote,
    address aggregator
  )
    external
    override
    onlyOwner()
  {
    AggregatorV2V3Interface currentPhaseAggregator = _getFeed(base, quote);
    require(aggregator != address(currentPhaseAggregator), "Cannot propose current aggregator");
    address proposedAggregator = address(_getProposedFeed(base, quote));
    if (proposedAggregator != aggregator) {
      s_proposedAggregators[base][quote] = AggregatorV2V3Interface(aggregator);
      emit FeedProposed(base, quote, aggregator, address(currentPhaseAggregator), msg.sender);
    }
  }

  /**
   * @notice Allows the owner to confirm and change the address
   * to the proposed aggregator
   * @dev Reverts if the given address doesn't match what was previously
   * proposed
   * @param base base asset address
   * @param quote quote asset address
   * @param aggregator The new aggregator contract address
   */
  function confirmFeed(
    address base,
    address quote,
    address aggregator
  )
    external
    override
    onlyOwner()
  {
    (uint16 nextPhaseId, address previousAggregator) = _setFeed(base, quote, aggregator);
    delete s_proposedAggregators[base][quote];
    s_isAggregatorEnabled[aggregator] = true;
    s_isAggregatorEnabled[previousAggregator] = false;
    emit FeedConfirmed(base, quote, aggregator, previousAggregator, nextPhaseId, msg.sender);
  }

  /**
   * @notice Returns the proposed aggregator for an base / quote pair
   * returns a zero address if there is no proposed aggregator for the pair
   * @param base base asset address
   * @param quote quote asset address
   * @return proposedAggregator
  */
  function getProposedFeed(
    address base,
    address quote
  )
    external
    view
    override
    returns (
      AggregatorV2V3Interface proposedAggregator
    )
  {
    return _getProposedFeed(base, quote);
  }

  /**
   * @notice Used if an aggregator contract has been proposed.
   * @param base base asset address
   * @param quote quote asset address
   * @param roundId the round ID to retrieve the round data for
   * @return id is the round ID for which data was retrieved
   * @return answer is the answer for the given round
   * @return startedAt is the timestamp when the round was started.
   * (Only some AggregatorV3Interface implementations return meaningful values)
   * @return updatedAt is the timestamp when the round last was updated (i.e.
   * answer was last computed)
   * @return answeredInRound is the round ID of the round in which the answer
   * was computed.
  */
  function proposedGetRoundData(
    address base,
    address quote,
    uint80 roundId
  )
    external
    view
    virtual
    override
    hasProposal(base, quote)
    returns (
      uint80 id,
      int256 answer,
      uint256 startedAt,
      uint256 updatedAt,
      uint80 answeredInRound
    )
  {
    return s_proposedAggregators[base][quote].getRoundData(roundId);
  }

  /**
   * @notice Used if an aggregator contract has been proposed.
   * @param base base asset address
   * @param quote quote asset address
   * @return id is the round ID for which data was retrieved
   * @return answer is the answer for the given round
   * @return startedAt is the timestamp when the round was started.
   * (Only some AggregatorV3Interface implementations return meaningful values)
   * @return updatedAt is the timestamp when the round last was updated (i.e.
   * answer was last computed)
   * @return answeredInRound is the round ID of the round in which the answer
   * was computed.
  */
  function proposedLatestRoundData(
    address base,
    address quote
  )
    external
    view
    virtual
    override
    hasProposal(base, quote)
    returns (
      uint80 id,
      int256 answer,
      uint256 startedAt,
      uint256 updatedAt,
      uint80 answeredInRound
    )
  {
    return s_proposedAggregators[base][quote].latestRoundData();
  }

  function getCurrentPhaseId(
    address base,
    address quote
  )
    external
    view
    override
    returns (
      uint16 currentPhaseId
    )
  {
    return s_currentPhaseId[base][quote];
  }

  function _addPhase(
    uint16 phase,
    uint64 roundId
  )
    internal
    pure
    returns (
      uint80
    )
  {
    return uint80(uint256(phase) << PHASE_OFFSET | roundId);
  }

  function _parseIds(
    uint256 roundId
  )
    internal
    pure
    returns (
      uint16,
      uint64
    )
  {
    uint16 phaseId = uint16(roundId >> PHASE_OFFSET);
    uint64 aggregatorRoundId = uint64(roundId);

    return (phaseId, aggregatorRoundId);
  }

  function _addPhaseIds(
      uint80 roundId,
      int256 answer,
      uint256 startedAt,
      uint256 updatedAt,
      uint80 answeredInRound,
      uint16 phaseId
  )
    internal
    pure
    returns (
      uint80,
      int256,
      uint256,
      uint256,
      uint80
    )
  {
    return (
      _addPhase(phaseId, uint64(roundId)),
      answer,
      startedAt,
      updatedAt,
      _addPhase(phaseId, uint64(answeredInRound))
    );
  }

  function _getPhase(
    address base,
    address quote,
    uint16 phaseId
  )
    internal
    view
    returns (
      Phase memory phase
    )
  {
    return s_phases[base][quote][phaseId];
  }

  function _phaseExists(
    Phase memory phase
  )
    internal
    pure
    returns (
      bool
    )
  {
    return phase.phaseId > 0;
  }

  function _getProposedFeed(
    address base,
    address quote
  )
    internal
    view
    returns (
      AggregatorV2V3Interface proposedAggregator
    )
  {
    return s_proposedAggregators[base][quote];
  }

  function _getPhaseFeed(
    address base,
    address quote,
    uint16 phaseId
  )
    internal
    view
    returns (
      AggregatorV2V3Interface aggregator
    )
  {
    return s_phaseAggregators[base][quote][phaseId];
  }

  function _getFeed(
    address base,
    address quote
  )
    internal
    view
    returns (
      AggregatorV2V3Interface aggregator
    )
  {
    uint16 currentPhaseId = s_currentPhaseId[base][quote];
    return _getPhaseFeed(base, quote, currentPhaseId);
  }

  function _setFeed(
    address base,
    address quote,
    address newAggregator
  )
    internal
    returns (
      uint16 nextPhaseId,
      address previousAggregator
    )
  {
    require(newAggregator == address(s_proposedAggregators[base][quote]), "Invalid proposed aggregator");
    AggregatorV2V3Interface currentAggregator = _getFeed(base, quote);
    uint80 previousAggregatorEndingRoundId = _getLatestAggregatorRoundId(currentAggregator);
    uint16 currentPhaseId = s_currentPhaseId[base][quote];
    s_phases[base][quote][currentPhaseId].endingAggregatorRoundId = previousAggregatorEndingRoundId;

    nextPhaseId = currentPhaseId + 1;
    s_currentPhaseId[base][quote] = nextPhaseId;
    s_phaseAggregators[base][quote][nextPhaseId] = AggregatorV2V3Interface(newAggregator);
    uint80 startingRoundId = _getLatestAggregatorRoundId(AggregatorV2V3Interface(newAggregator));
    s_phases[base][quote][nextPhaseId] = Phase(nextPhaseId, startingRoundId, 0);

    return (nextPhaseId, address(currentAggregator));
  }

  function _getPreviousRoundId(
    address base,
    address quote,
    uint16 phaseId,
    uint80 roundId
  )
    internal
    view
    returns (
      uint80
    )
  {
    for (uint16 pid = phaseId; pid > 0; pid--) {
      AggregatorV2V3Interface phaseAggregator = _getPhaseFeed(base, quote, pid);
      (uint80 startingRoundId, uint80 endingRoundId) = _getPhaseRange(base, quote, pid);
      if (address(phaseAggregator) == address(0)) continue;
      if (roundId <= startingRoundId) continue;
      if (roundId > startingRoundId && roundId <= endingRoundId) return roundId - 1;
      if (roundId > endingRoundId) return endingRoundId;
    }
    return 0; // Round not found
  }

  function _getNextRoundId(
    address base,
    address quote,
    uint16 phaseId,
    uint80 roundId
  )
    internal
    view
    returns (
      uint80
    )
  {
    uint16 currentPhaseId = s_currentPhaseId[base][quote];
    for (uint16 pid = phaseId; pid <= currentPhaseId; pid++) {
      AggregatorV2V3Interface phaseAggregator = _getPhaseFeed(base, quote, pid);
      (uint80 startingRoundId, uint80 endingRoundId) =
        (pid == currentPhaseId) ? _getLatestRoundRange(base, quote, pid) : _getPhaseRange(base, quote, pid);
      if (address(phaseAggregator) == address(0)) continue;
      if (roundId >= endingRoundId) continue;
      if (roundId >= startingRoundId && roundId < endingRoundId) return roundId + 1;
      if (roundId < startingRoundId) return startingRoundId;
    }
    return 0; // Round not found
  }

  function _getPhaseRange(
    address base,
    address quote,
    uint16 phaseId
  )
    internal
    view
    returns (
      uint80 startingRoundId,
      uint80 endingRoundId
    )
  {
    Phase memory phase = _getPhase(base, quote, phaseId);
    return (
      _getStartingRoundId(phaseId, phase),
      _getEndingRoundId(phaseId, phase)
    );
  }

  function _getLatestRoundRange(
    address base,
    address quote,
    uint16 currentPhaseId
  )
    internal
    view
    returns (
      uint80 startingRoundId,
      uint80 endingRoundId
    )
  {
    Phase memory phase = s_phases[base][quote][currentPhaseId];
    return (
      _getStartingRoundId(currentPhaseId, phase),
      _getLatestRoundId(base, quote, currentPhaseId)
    );
  }

  function _getStartingRoundId(
    uint16 phaseId,
    Phase memory phase
  )
    internal
    pure
    returns (
      uint80 startingRoundId
    )
  {
    return _addPhase(phaseId, uint64(phase.startingAggregatorRoundId));
  }

  function _getEndingRoundId(
    uint16 phaseId,
    Phase memory phase
  )
    internal
    pure
    returns (
      uint80 startingRoundId
    )
  {
    return _addPhase(phaseId, uint64(phase.endingAggregatorRoundId));
  }

  function _getLatestRoundId(
    address base,
    address quote,
    uint16 phaseId
  )
    internal
    view
    returns (
      uint80 startingRoundId
    )
  {
    AggregatorV2V3Interface currentPhaseAggregator = _getFeed(base, quote);
    uint80 latestAggregatorRoundId = _getLatestAggregatorRoundId(currentPhaseAggregator);
    return _addPhase(phaseId, uint64(latestAggregatorRoundId));
  }

  function _getLatestAggregatorRoundId(
    AggregatorV2V3Interface aggregator
  )
    internal
    view
    returns (
      uint80 roundId
    )
  {
    if (address(aggregator) == address(0)) return uint80(0);
    return uint80(aggregator.latestRound());
  }

  function _getPhaseIdByRoundId(
    address base,
    address quote,
    uint80 roundId
  )
    internal
    view
    returns (
      uint16 phaseId
    )
  {
    // Handle case where the round is in current phase
    uint16 currentPhaseId = s_currentPhaseId[base][quote];
    (uint80 startingCurrentRoundId, uint80 endingCurrentRoundId) = _getLatestRoundRange(base, quote, currentPhaseId);
    if (roundId >= startingCurrentRoundId && roundId <= endingCurrentRoundId) return currentPhaseId;

    // Handle case where the round is in past phases
    require(currentPhaseId > 0, "Invalid phase");
    for (uint16 pid = currentPhaseId - 1; pid > 0; pid--) {
      AggregatorV2V3Interface phaseAggregator = s_phaseAggregators[base][quote][pid];
      if (address(phaseAggregator) == address(0)) continue;
      (uint80 startingRoundId, uint80 endingRoundId) = _getPhaseRange(base, quote, pid);
      if (roundId >= startingRoundId && roundId <= endingRoundId) return pid;
      if (roundId > endingRoundId) break;
    }
    return 0;
  }

  /**
   * @dev reverts if the caller does not have access granted by the accessController contract
   * to the base / quote pair or is the contract itself.
   */
  modifier checkPairAccess() {
    require(address(s_accessController) == address(0) || s_accessController.hasAccess(msg.sender, msg.data), "No access");
    _;
  }

  /**
   * @dev reverts if no proposed aggregator was set
   */
  modifier hasProposal(
    address base,
    address quote
  ) {
    require(address(s_proposedAggregators[base][quote]) != address(0), "No proposed aggregator present");
    _;
  }
}
AccessControlled.sol 33 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.7.6;

import "@chainlink/contracts/src/v0.7/dev/ConfirmedOwner.sol";
import "../interfaces/AccessControlledInterface.sol";
import "../interfaces/AccessControllerInterface.sol";

contract AccessControlled is AccessControlledInterface, ConfirmedOwner(msg.sender) {
  AccessControllerInterface internal s_accessController;

  function setAccessController(
    AccessControllerInterface _accessController
  )
    public
    override
    onlyOwner()
  {
    require(address(_accessController) != address(s_accessController), "Access controller is already set");
    s_accessController = _accessController;
    emit AccessControllerSet(address(_accessController), msg.sender);
  }

  function getAccessController()
    public
    view
    override
    returns (
      AccessControllerInterface
    )
  {
    return s_accessController;
  }
}
FeedRegistryInterface.sol 298 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.7.6;
pragma abicoder v2; // solhint-disable compiler-version

import "@chainlink/contracts/src/v0.7/interfaces/AggregatorV2V3Interface.sol";

interface FeedRegistryInterface {
  struct Phase {
    uint16 phaseId;
    uint80 startingAggregatorRoundId;
    uint80 endingAggregatorRoundId;
  }

  event FeedProposed(
    address indexed asset,
    address indexed denomination,
    address indexed proposedAggregator,
    address currentAggregator,
    address sender
  );
  event FeedConfirmed(
    address indexed asset,
    address indexed denomination,
    address indexed latestAggregator,
    address previousAggregator,
    uint16 nextPhaseId,
    address sender
  );

  // V3 AggregatorV3Interface

  function decimals(
    address base,
    address quote
  )
    external
    view
    returns (
      uint8
    );

  function description(
    address base,
    address quote
  )
    external
    view
    returns (
      string memory
    );

  function version(
    address base,
    address quote
  )
    external
    view
    returns (
      uint256
    );

  function latestRoundData(
    address base,
    address quote
  )
    external
    view
    returns (
      uint80 roundId,
      int256 answer,
      uint256 startedAt,
      uint256 updatedAt,
      uint80 answeredInRound
    );

  function getRoundData(
    address base,
    address quote,
    uint80 _roundId
  )
    external
    view
    returns (
      uint80 roundId,
      int256 answer,
      uint256 startedAt,
      uint256 updatedAt,
      uint80 answeredInRound
    );

  // V2 AggregatorInterface

  function latestAnswer(
    address base,
    address quote
  )
    external
    view
    returns (
      int256 answer
    );

  function latestTimestamp(
    address base,
    address quote
  )
    external
    view
    returns (
      uint256 timestamp
    );

  function latestRound(
    address base,
    address quote
  )
    external
    view
    returns (
      uint256 roundId
    );

  function getAnswer(
    address base,
    address quote,
    uint256 roundId
  )
    external
    view
    returns (
      int256 answer
    );

  function getTimestamp(
    address base,
    address quote,
    uint256 roundId
  )
    external
    view
    returns (
      uint256 timestamp
    );

  // Registry getters

  function getFeed(
    address base,
    address quote
  )
    external
    view
    returns (
      AggregatorV2V3Interface aggregator
    );

  function getPhaseFeed(
    address base,
    address quote,
    uint16 phaseId
  )
    external
    view
    returns (
      AggregatorV2V3Interface aggregator
    );

  function isFeedEnabled(
    address aggregator
  )
    external
    view
    returns (
      bool
    );

  function getPhase(
    address base,
    address quote,
    uint16 phaseId
  )
    external
    view
    returns (
      Phase memory phase
    );

  // Round helpers

  function getRoundFeed(
    address base,
    address quote,
    uint80 roundId
  )
    external
    view
    returns (
      AggregatorV2V3Interface aggregator
    );

  function getPhaseRange(
    address base,
    address quote,
    uint16 phaseId
  )
    external
    view
    returns (
      uint80 startingRoundId,
      uint80 endingRoundId
    );

  function getPreviousRoundId(
    address base,
    address quote,
    uint80 roundId
  ) external
    view
    returns (
      uint80 previousRoundId
    );

  function getNextRoundId(
    address base,
    address quote,
    uint80 roundId
  ) external
    view
    returns (
      uint80 nextRoundId
    );

  // Feed management

  function proposeFeed(
    address base,
    address quote,
    address aggregator
  ) external;

  function confirmFeed(
    address base,
    address quote,
    address aggregator
  ) external;

  // Proposed aggregator

  function getProposedFeed(
    address base,
    address quote
  )
    external
    view
    returns (
      AggregatorV2V3Interface proposedAggregator
    );

  function proposedGetRoundData(
    address base,
    address quote,
    uint80 roundId
  )
    external
    view
    returns (
      uint80 id,
      int256 answer,
      uint256 startedAt,
      uint256 updatedAt,
      uint80 answeredInRound
    );

  function proposedLatestRoundData(
    address base,
    address quote
  )
    external
    view
    returns (
      uint80 id,
      int256 answer,
      uint256 startedAt,
      uint256 updatedAt,
      uint80 answeredInRound
    );

  // Phases
  function getCurrentPhaseId(
    address base,
    address quote
  )
    external
    view
    returns (
      uint16 currentPhaseId
    );
}
TypeAndVersionInterface.sol 11 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;

interface TypeAndVersionInterface{
  function typeAndVersion()
    external
    pure
    returns (
      string memory
    );
}
AccessControlledInterface.sol 23 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.7.6;

import "./AccessControllerInterface.sol";

interface AccessControlledInterface {
  event AccessControllerSet(
    address indexed accessController,
    address indexed sender
  );

  function setAccessController(
    AccessControllerInterface _accessController
  )
    external;

  function getAccessController()
    external
    view
    returns (
      AccessControllerInterface
    );
}
AccessControllerInterface.sol 6 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;

interface AccessControllerInterface {
  function hasAccess(address user, bytes calldata data) external view returns (bool);
}
ConfirmedOwner.sol 22 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;

import "./ConfirmedOwnerWithProposal.sol";

/**
 * @title The ConfirmedOwner contract
 * @notice A contract with helpers for basic contract ownership.
 */
contract ConfirmedOwner is ConfirmedOwnerWithProposal {

  constructor(
    address newOwner
  )
    ConfirmedOwnerWithProposal(
      newOwner,
      address(0)
    )
  {
  }

}
OwnableInterface.sol 18 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;

interface OwnableInterface {
  function owner()
    external
    returns (
      address
    );

  function transferOwnership(
    address recipient
  )
    external;

  function acceptOwnership()
    external;
}
ConfirmedOwnerWithProposal.sol 113 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;

import "../interfaces/OwnableInterface.sol";

/**
 * @title The ConfirmedOwner contract
 * @notice A contract with helpers for basic contract ownership.
 */
contract ConfirmedOwnerWithProposal is OwnableInterface {

  address private s_owner;
  address private s_pendingOwner;

  event OwnershipTransferRequested(
    address indexed from,
    address indexed to
  );
  event OwnershipTransferred(
    address indexed from,
    address indexed to
  );

  constructor(
    address owner,
    address pendingOwner
  ) {
    require(owner != address(0), "Cannot set owner to zero");

    s_owner = owner;
    if (pendingOwner != address(0)) {
      _transferOwnership(pendingOwner);
    }
  }

  /**
   * @notice Allows an owner to begin transferring ownership to a new address,
   * pending.
   */
  function transferOwnership(
    address to
  )
    public
    override
    onlyOwner()
  {
    _transferOwnership(to);
  }

  /**
   * @notice Allows an ownership transfer to be completed by the recipient.
   */
  function acceptOwnership()
    external
    override
  {
    require(msg.sender == s_pendingOwner, "Must be proposed owner");

    address oldOwner = s_owner;
    s_owner = msg.sender;
    s_pendingOwner = address(0);

    emit OwnershipTransferred(oldOwner, msg.sender);
  }

  /**
   * @notice Get the current owner
   */
  function owner()
    public
    view
    override
    returns (
      address
    )
  {
    return s_owner;
  }

  /**
   * @notice validate, transfer ownership, and emit relevant events
   */
  function _transferOwnership(
    address to
  )
    private
  {
    require(to != msg.sender, "Cannot transfer to self");

    s_pendingOwner = to;

    emit OwnershipTransferRequested(s_owner, to);
  }

  /**
   * @notice validate access
   */
  function _validateOwnership()
    internal
    view
  {
    require(msg.sender == s_owner, "Only callable by owner");
  }

  /**
   * @notice Reverts if called by anyone other than the contract owner.
   */
  modifier onlyOwner() {
    _validateOwnership();
    _;
  }

}
AggregatorInterface.sol 55 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;

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 updatedAt
  );

  event NewRound(
    uint256 indexed roundId,
    address indexed startedBy,
    uint256 startedAt
  );
}
AggregatorV3Interface.sol 54 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;

interface AggregatorV3Interface {

  function decimals()
    external
    view
    returns (
      uint8
    );

  function description()
    external
    view
    returns (
      string memory
    );

  function version()
    external
    view
    returns (
      uint256
    );

  // getRoundData and latestRoundData should both raise "No data present"
  // if they do not have data to report, instead of returning unset values
  // which could be misinterpreted as actual reported values.
  function getRoundData(
    uint80 _roundId
  )
    external
    view
    returns (
      uint80 roundId,
      int256 answer,
      uint256 startedAt,
      uint256 updatedAt,
      uint80 answeredInRound
    );

  function latestRoundData()
    external
    view
    returns (
      uint80 roundId,
      int256 answer,
      uint256 startedAt,
      uint256 updatedAt,
      uint80 answeredInRound
    );

}
AggregatorV2V3Interface.sol 9 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;

import "./AggregatorInterface.sol";
import "./AggregatorV3Interface.sol";

interface AggregatorV2V3Interface is AggregatorInterface, AggregatorV3Interface
{
}
AggregatorProxy.sol 542 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;

import "./ConfirmedOwner.sol";
import "../interfaces/AggregatorProxyInterface.sol";

/**
 * @title A trusted proxy for updating where current answers are read from
 * @notice This contract provides a consistent address for the
 * CurrentAnwerInterface but delegates where it reads from to the owner, who is
 * trusted to update it.
 */
contract AggregatorProxy is AggregatorProxyInterface, ConfirmedOwner {

  struct Phase {
    uint16 id;
    AggregatorProxyInterface aggregator;
  }
  AggregatorProxyInterface private s_proposedAggregator;
  mapping(uint16 => AggregatorProxyInterface) private s_phaseAggregators;
  Phase private s_currentPhase;
  
  uint256 constant private PHASE_OFFSET = 64;
  uint256 constant private PHASE_SIZE = 16;
  uint256 constant private MAX_ID = 2**(PHASE_OFFSET+PHASE_SIZE) - 1;

  event AggregatorProposed(
    address indexed current,
    address indexed proposed
  );
  event AggregatorConfirmed(
    address indexed previous,
    address indexed latest
  );

  constructor(
    address aggregatorAddress
  )
    ConfirmedOwner(msg.sender)
  {
    setAggregator(aggregatorAddress);
  }

  /**
   * @notice Reads the current answer from aggregator delegated to.
   *
   * @dev #[deprecated] Use latestRoundData instead. This does not error if no
   * answer has been reached, it will simply return 0. Either wait to point to
   * an already answered Aggregator or use the recommended latestRoundData
   * instead which includes better verification information.
   */
  function latestAnswer()
    public
    view
    virtual
    override
    returns (
      int256 answer
    )
  {
    return s_currentPhase.aggregator.latestAnswer();
  }

  /**
   * @notice Reads the last updated height from aggregator delegated to.
   *
   * @dev #[deprecated] Use latestRoundData instead. This does not error if no
   * answer has been reached, it will simply return 0. Either wait to point to
   * an already answered Aggregator or use the recommended latestRoundData
   * instead which includes better verification information.
   */
  function latestTimestamp()
    public
    view
    virtual
    override
    returns (
      uint256 updatedAt
    )
  {
    return s_currentPhase.aggregator.latestTimestamp();
  }

  /**
   * @notice get past rounds answers
   * @param roundId the answer number to retrieve the answer for
   *
   * @dev #[deprecated] Use getRoundData instead. This does not error if no
   * answer has been reached, it will simply return 0. Either wait to point to
   * an already answered Aggregator or use the recommended getRoundData
   * instead which includes better verification information.
   */
  function getAnswer(
    uint256 roundId
  )
    public
    view
    virtual
    override
    returns (
      int256 answer
    )
  {
    if (roundId > MAX_ID) return 0;

    (uint16 phaseId, uint64 aggregatorRoundId) = parseIds(roundId);
    AggregatorProxyInterface aggregator = s_phaseAggregators[phaseId];
    if (address(aggregator) == address(0)) return 0;

    return aggregator.getAnswer(aggregatorRoundId);
  }

  /**
   * @notice get block timestamp when an answer was last updated
   * @param roundId the answer number to retrieve the updated timestamp for
   *
   * @dev #[deprecated] Use getRoundData instead. This does not error if no
   * answer has been reached, it will simply return 0. Either wait to point to
   * an already answered Aggregator or use the recommended getRoundData
   * instead which includes better verification information.
   */
  function getTimestamp(
    uint256 roundId
  )
    public
    view
    virtual
    override
    returns (
      uint256 updatedAt
    )
  {
    if (roundId > MAX_ID) return 0;

    (uint16 phaseId, uint64 aggregatorRoundId) = parseIds(roundId);
    AggregatorProxyInterface aggregator = s_phaseAggregators[phaseId];
    if (address(aggregator) == address(0)) return 0;

    return aggregator.getTimestamp(aggregatorRoundId);
  }

  /**
   * @notice get the latest completed round where the answer was updated. This
   * ID includes the proxy's phase, to make sure round IDs increase even when
   * switching to a newly deployed aggregator.
   *
   * @dev #[deprecated] Use latestRoundData instead. This does not error if no
   * answer has been reached, it will simply return 0. Either wait to point to
   * an already answered Aggregator or use the recommended latestRoundData
   * instead which includes better verification information.
   */
  function latestRound()
    public
    view
    virtual
    override
    returns (
      uint256 roundId
    )
  {
    Phase memory phase = s_currentPhase; // cache storage reads
    return addPhase(phase.id, uint64(phase.aggregator.latestRound()));
  }

  /**
   * @notice get data about a round. Consumers are encouraged to check
   * that they're receiving fresh data by inspecting the updatedAt and
   * answeredInRound return values.
   * Note that different underlying implementations of AggregatorV3Interface
   * have slightly different semantics for some of the return values. Consumers
   * should determine what implementations they expect to receive
   * data from and validate that they can properly handle return data from all
   * of them.
   * @param roundId the requested round ID as presented through the proxy, this
   * is made up of the aggregator's round ID with the phase ID encoded in the
   * two highest order bytes
   * @return id is the round ID from the aggregator for which the data was
   * retrieved combined with an phase to ensure that round IDs get larger as
   * time moves forward.
   * @return answer is the answer for the given round
   * @return startedAt is the timestamp when the round was started.
   * (Only some AggregatorV3Interface implementations return meaningful values)
   * @return updatedAt is the timestamp when the round last was updated (i.e.
   * answer was last computed)
   * @return answeredInRound is the round ID of the round in which the answer
   * was computed.
   * (Only some AggregatorV3Interface implementations return meaningful values)
   * @dev Note that answer and updatedAt may change between queries.
   */
  function getRoundData(
    uint80 roundId
  )
    public
    view
    virtual
    override
    returns (
      uint80 id,
      int256 answer,
      uint256 startedAt,
      uint256 updatedAt,
      uint80 answeredInRound
    )
  {
    (uint16 phaseId, uint64 aggregatorRoundId) = parseIds(roundId);

    (
      id,
      answer,
      startedAt,
      updatedAt,
      answeredInRound
    ) = s_phaseAggregators[phaseId].getRoundData(aggregatorRoundId);

    return addPhaseIds(id, answer, startedAt, updatedAt, answeredInRound, phaseId);
  }

  /**
   * @notice get data about the latest round. Consumers are encouraged to check
   * that they're receiving fresh data by inspecting the updatedAt and
   * answeredInRound return values.
   * Note that different underlying implementations of AggregatorV3Interface
   * have slightly different semantics for some of the return values. Consumers
   * should determine what implementations they expect to receive
   * data from and validate that they can properly handle return data from all
   * of them.
   * @return id is the round ID from the aggregator for which the data was
   * retrieved combined with an phase to ensure that round IDs get larger as
   * time moves forward.
   * @return answer is the answer for the given round
   * @return startedAt is the timestamp when the round was started.
   * (Only some AggregatorV3Interface implementations return meaningful values)
   * @return updatedAt is the timestamp when the round last was updated (i.e.
   * answer was last computed)
   * @return answeredInRound is the round ID of the round in which the answer
   * was computed.
   * (Only some AggregatorV3Interface implementations return meaningful values)
   * @dev Note that answer and updatedAt may change between queries.
   */
  function latestRoundData()
    public
    view
    virtual
    override
    returns (
      uint80 id,
      int256 answer,
      uint256 startedAt,
      uint256 updatedAt,
      uint80 answeredInRound
    )
  {
    Phase memory current = s_currentPhase; // cache storage reads

    (
      id,
      answer,
      startedAt,
      updatedAt,
      answeredInRound
    ) = current.aggregator.latestRoundData();

    return addPhaseIds(id, answer, startedAt, updatedAt, answeredInRound, current.id);
  }

  /**
   * @notice Used if an aggregator contract has been proposed.
   * @param roundId the round ID to retrieve the round data for
   * @return id is the round ID for which data was retrieved
   * @return answer is the answer for the given round
   * @return startedAt is the timestamp when the round was started.
   * (Only some AggregatorV3Interface implementations return meaningful values)
   * @return updatedAt is the timestamp when the round last was updated (i.e.
   * answer was last computed)
   * @return answeredInRound is the round ID of the round in which the answer
   * was computed.
  */
  function proposedGetRoundData(
    uint80 roundId
  )
    external
    view
    virtual
    override
    hasProposal()
    returns (
      uint80 id,
      int256 answer,
      uint256 startedAt,
      uint256 updatedAt,
      uint80 answeredInRound
    )
  {
    return s_proposedAggregator.getRoundData(roundId);
  }

  /**
   * @notice Used if an aggregator contract has been proposed.
   * @return id is the round ID for which data was retrieved
   * @return answer is the answer for the given round
   * @return startedAt is the timestamp when the round was started.
   * (Only some AggregatorV3Interface implementations return meaningful values)
   * @return updatedAt is the timestamp when the round last was updated (i.e.
   * answer was last computed)
   * @return answeredInRound is the round ID of the round in which the answer
   * was computed.
  */
  function proposedLatestRoundData()
    external
    view
    virtual
    override
    hasProposal()
    returns (
      uint80 id,
      int256 answer,
      uint256 startedAt,
      uint256 updatedAt,
      uint80 answeredInRound
    )
  {
    return s_proposedAggregator.latestRoundData();
  }

  /**
   * @notice returns the current phase's aggregator address.
   */
  function aggregator()
    external
    view
    override
    returns (
      address
    )
  {
    return address(s_currentPhase.aggregator);
  }

  /**
   * @notice returns the current phase's ID.
   */
  function phaseId()
    external
    view
    override
    returns (
      uint16
    )
  {
    return s_currentPhase.id;
  }

  /**
   * @notice represents the number of decimals the aggregator responses represent.
   */
  function decimals()
    external
    view
    override
    returns (
      uint8
    )
  {
    return s_currentPhase.aggregator.decimals();
  }

  /**
   * @notice the version number representing the type of aggregator the proxy
   * points to.
   */
  function version()
    external
    view
    override
    returns (
      uint256
    )
  {
    return s_currentPhase.aggregator.version();
  }

  /**
   * @notice returns the description of the aggregator the proxy points to.
   */
  function description()
    external
    view
    override
    returns (
      string memory
    )
  {
    return s_currentPhase.aggregator.description();
  }

  /**
   * @notice returns the current proposed aggregator
   */
  function proposedAggregator()
    external
    view
    override
    returns (
      address
    )
  {
    return address(s_proposedAggregator);
  }

  /**
   * @notice return a phase aggregator using the phaseId
   *
   * @param phaseId uint16
   */
  function phaseAggregators(
    uint16 phaseId
  )
    external
    view
    override
    returns (
      address
    )
  {
    return address(s_phaseAggregators[phaseId]);
  }

  /**
   * @notice Allows the owner to propose a new address for the aggregator
   * @param aggregatorAddress The new address for the aggregator contract
   */
  function proposeAggregator(
    address aggregatorAddress
  )
    external
    onlyOwner()
  {
    s_proposedAggregator = AggregatorProxyInterface(aggregatorAddress);
    emit AggregatorProposed(address(s_currentPhase.aggregator), aggregatorAddress);
  }

  /**
   * @notice Allows the owner to confirm and change the address
   * to the proposed aggregator
   * @dev Reverts if the given address doesn't match what was previously
   * proposed
   * @param aggregatorAddress The new address for the aggregator contract
   */
  function confirmAggregator(
    address aggregatorAddress
  )
    external
    onlyOwner()
  {
    require(aggregatorAddress == address(s_proposedAggregator), "Invalid proposed aggregator");
    address previousAggregator = address(s_currentPhase.aggregator);
    delete s_proposedAggregator;
    setAggregator(aggregatorAddress);
    emit AggregatorConfirmed(previousAggregator, aggregatorAddress);
  }


  /*
   * Internal
   */

  function setAggregator(
    address aggregatorAddress
  )
    internal
  {
    uint16 id = s_currentPhase.id + 1;
    s_currentPhase = Phase(id, AggregatorProxyInterface(aggregatorAddress));
    s_phaseAggregators[id] = AggregatorProxyInterface(aggregatorAddress);
  }

  function addPhase(
    uint16 phase,
    uint64 originalId
  )
    internal
    pure
    returns (
      uint80
    )
  {
    return uint80(uint256(phase) << PHASE_OFFSET | originalId);
  }

  function parseIds(
    uint256 roundId
  )
    internal
    pure
    returns (
      uint16,
      uint64
    )
  {
    uint16 phaseId = uint16(roundId >> PHASE_OFFSET);
    uint64 aggregatorRoundId = uint64(roundId);

    return (phaseId, aggregatorRoundId);
  }

  function addPhaseIds(
      uint80 roundId,
      int256 answer,
      uint256 startedAt,
      uint256 updatedAt,
      uint80 answeredInRound,
      uint16 phaseId
  )
    internal
    pure
    returns (
      uint80,
      int256,
      uint256,
      uint256,
      uint80
    )
  {
    return (
      addPhase(phaseId, uint64(roundId)),
      answer,
      startedAt,
      updatedAt,
      addPhase(phaseId, uint64(answeredInRound))
    );
  }

  /*
   * Modifiers
   */

  modifier hasProposal() {
    require(address(s_proposedAggregator) != address(0), "No proposed aggregator present");
    _;
  }

}
MockConsumer.sol 37 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;

import "../interfaces/FeedRegistryInterface.sol";

contract MockConsumer {
  FeedRegistryInterface private s_FeedRegistry;

  constructor(
    FeedRegistryInterface FeedRegistry
  ) {
    s_FeedRegistry = FeedRegistry;
  }

  function getFeedRegistry()
    public
    view
    returns (
      FeedRegistryInterface
    )
  {
    return s_FeedRegistry;
  }

  function read(
    address base,
    address quote
  )
    public
    view
    returns (
      int256
    )
  {
    return s_FeedRegistry.latestAnswer(base, quote);
  }
}
ReadAccessController.sol 36 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.7.6;

import "./WriteAccessController.sol";
import "../utils/EOAContext.sol";

/**
 * @title ReadAccessController
 * @notice Gives access to:
 * - any externally owned account (note that offchain actors can always read
 * any contract storage regardless of onchain access control measures, so this
 * does not weaken the access control while improving usability)
 * - accounts explicitly added to an access list
 * @dev ReadAccessController is not suitable for access controlling writes
 * since it grants any externally owned account access! See
 * WriteAccessController for that.
 */
contract ReadAccessController is WriteAccessController, EOAContext {
  /**
   * @notice Returns the access of an address
   * @param account The address to query
   * @param data The calldata to query
   */
  function hasAccess(
    address account,
    bytes memory data
  )
    public
    view
    virtual
    override
    returns (bool)
  {
    return super.hasAccess(account, data) || _isEOA(account);
  }
}
AggregatorProxyInterface.sol 61 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;

import "./AggregatorV2V3Interface.sol";

interface AggregatorProxyInterface is AggregatorV2V3Interface {
  
	function phaseAggregators(
    uint16 phaseId
  )
    external
    view
    returns (
      address
    );

	function phaseId()
    external
    view
    returns (
      uint16
    );

	function proposedAggregator()
    external
    view
    returns (
      address
    );

	function proposedGetRoundData(
    uint80 roundId
  )
    external
    view
    returns (
      uint80 id,
      int256 answer,
      uint256 startedAt,
      uint256 updatedAt,
      uint80 answeredInRound
    );

	function proposedLatestRoundData()
    external
    view
    returns (
      uint80 id,
      int256 answer,
      uint256 startedAt,
      uint256 updatedAt,
      uint80 answeredInRound
    );

	function aggregator()
    external
    view
    returns (
      address
    );
}
EOAContext.sol 14 lines
// SPDX-License-Identifier: MIT

pragma solidity 0.7.6;

/*
 * @dev Provides information about the current execution context, specifically on if an account is an EOA on that chain.
 * Different chains have different account abstractions, so this contract helps to switch behaviour between chains.
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract EOAContext {
  function _isEOA(address account) internal view virtual returns (bool) {
      return account == tx.origin; // solhint-disable-line avoid-tx-origin
  }
}
MockAggregatorProxy.sol 10 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;

import "@chainlink/contracts/src/v0.7/dev/AggregatorProxy.sol";

contract MockAggregatorProxy is AggregatorProxy {
    constructor(
        address aggregatorAddress
    ) AggregatorProxy(aggregatorAddress) {} // solhint-disable-line
}
WriteAccessController.sol 178 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.7.6;

import "@chainlink/contracts/src/v0.7/dev/ConfirmedOwner.sol";
import "../interfaces/AccessControllerInterface.sol";

/**
 * @title WriteAccessController
 * @notice Has two access lists: a global list and a data-specific list.
 * @dev does not make any special permissions for EOAs, see
 * ReadAccessController for that.
 */
contract WriteAccessController is AccessControllerInterface, ConfirmedOwner(msg.sender) {
  bool private s_checkEnabled = true;
  mapping(address => bool) internal s_globalAccessList;
  mapping(address => mapping(bytes => bool)) internal s_localAccessList;

  event AccessAdded(address user, bytes data, address sender);
  event AccessRemoved(address user, bytes data, address sender);
  event CheckAccessEnabled();
  event CheckAccessDisabled();

  function checkEnabled()
    public
    view
    returns (
      bool
    )
  {
    return s_checkEnabled;
  }

  /**
   * @notice Returns the access of an address
   * @param user The address to query
   * @param data The calldata to query
   */
  function hasAccess(
    address user,
    bytes memory data
  )
    public
    view
    virtual
    override
    returns (bool)
  {
    return !s_checkEnabled || s_globalAccessList[user] || s_localAccessList[user][data];
  }

/**
   * @notice Adds an address to the global access list
   * @param user The address to add
   */
  function addGlobalAccess(
    address user
  )
    external
    onlyOwner()
  {
    _addGlobalAccess(user);
  }

  /**
   * @notice Adds an address+data to the local access list
   * @param user The address to add
   * @param data The calldata to add
   */
  function addLocalAccess(
    address user,
    bytes memory data
  )
    external
    onlyOwner()
  {
    _addLocalAccess(user, data);
  }

  /**
   * @notice Removes an address from the global access list
   * @param user The address to remove
   */
  function removeGlobalAccess(
    address user
  )
    external
    onlyOwner()
  {
    _removeGlobalAccess(user);
  }

  /**
   * @notice Removes an address+data from the local access list
   * @param user The address to remove
   * @param data The calldata to remove
   */
  function removeLocalAccess(
    address user,
    bytes memory data
  )
    external
    onlyOwner()
  {
    _removeLocalAccess(user, data);
  }

  /**
   * @notice makes the access check enforced
   */
  function enableAccessCheck()
    external
    onlyOwner()
  {
    _enableAccessCheck();
  }

  /**
   * @notice makes the access check unenforced
   */
  function disableAccessCheck()
    external
    onlyOwner()
  {
    _disableAccessCheck();
  }

  /**
   * @dev reverts if the caller does not have access
   */
  modifier checkAccess() {
    if (s_checkEnabled) {
      require(hasAccess(msg.sender, msg.data), "No access");
    }
    _;
  }

  function _enableAccessCheck() internal {
    if (!s_checkEnabled) {
      s_checkEnabled = true;
      emit CheckAccessEnabled();
    }
  }

  function _disableAccessCheck() internal {
    if (s_checkEnabled) {
      s_checkEnabled = false;
      emit CheckAccessDisabled();
    }
  }

  function _addGlobalAccess(address user) internal {
    if (!s_globalAccessList[user]) {
      s_globalAccessList[user] = true;
      emit AccessAdded(user, "", msg.sender);
    }
  }

  function _removeGlobalAccess(address user) internal {
    if (s_globalAccessList[user]) {
      s_globalAccessList[user] = false;
      emit AccessRemoved(user, "", msg.sender);
    }
  }

  function _addLocalAccess(address user, bytes memory data) internal {
    if (!s_localAccessList[user][data]) {
      s_localAccessList[user][data] = true;
      emit AccessAdded(user, data, msg.sender);
    }
  }

  function _removeLocalAccess(address user, bytes memory data) internal {
    if (s_localAccessList[user][data]) {
      s_localAccessList[user][data] = false;
      emit AccessRemoved(user, data, msg.sender);
    }
  }
}
PairReadAccessController.sol 42 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.7.6;

import "./WriteAccessController.sol";
import "../utils/EOAContext.sol";

/**
 * @title PairReadAccessController
 * @notice Extends WriteAccessController. Decodes the (base, quote) pair values of msg.data.
 * @notice Gives access to:
 * - any externally owned account (note that offchain actors can always read
 * any contract storage regardless of onchain access control measures, so this
 * does not weaken the access control while improving usability)
 * - accounts explicitly added to an access list
 * @dev PairReadAccessController is not suitable for access controlling writes
 * since it grants any externally owned account access! See
 * WriteAccessController for that.
 */
contract PairReadAccessController is WriteAccessController, EOAContext {
  /**
   * @notice Returns the access of an address to an base / quote pair
   * @param account The address to query
   * @param data The calldata to query
   */
  function hasAccess(
    address account,
    bytes calldata data
  )
    public
    view
    virtual
    override
    returns (bool)
  {
    (
      address base,
      address quote
    ) = abi.decode(data[4:], (address, address));
    bytes memory pairData = abi.encode(base, quote); // Check access to pair (TKN / ETH)
    return super.hasAccess(account, pairData) || _isEOA(account);
  }
}

Read Contract

decimals 0x58e2d3a8 → uint8
description 0xfa820de9 → string
getAccessController 0x16d6b5f6 → address
getAnswer 0x15cd4ad2 → int256
getCurrentPhaseId 0x30322818 → uint16
getFeed 0xd2edb6dd → address
getNextRoundId 0xa051538e → uint80
getPhase 0xff0601c0 → tuple
getPhaseFeed 0x52dbeb8b → address
getPhaseRange 0xc1ce86fc → uint80, uint80
getPreviousRoundId 0x9e3ff6fd → uint80
getProposedFeed 0x5ad9d9df → address
getRoundData 0xfc58749e → uint80, int256, uint256, uint256, uint80
getRoundFeed 0xc639cd91 → address
getTimestamp 0x91624c95 → uint256
isFeedEnabled 0xb099d43b → bool
latestAnswer 0xd4c282a3 → int256
latestRound 0xec62f44b → uint256
latestRoundData 0xbcfd032d → uint80, int256, uint256, uint256, uint80
latestTimestamp 0x672ff44f → uint256
owner 0x8da5cb5b → address
proposedGetRoundData 0x8916524a → uint80, int256, uint256, uint256, uint80
proposedLatestRoundData 0xd0188fc6 → uint80, int256, uint256, uint256, uint80
typeAndVersion 0x181f5a77 → string
version 0xaf34b03a → uint256

Write Contract 5 functions

These functions modify contract state and require a wallet transaction to execute.

acceptOwnership 0x79ba5097
No parameters
confirmFeed 0x045abf4b
address base
address quote
address aggregator
proposeFeed 0x9eed82b0
address base
address quote
address aggregator
setAccessController 0xf08391d8
address _accessController
transferOwnership 0xf2fde38b
address to

Recent Transactions

This address has 1 on-chain transactions, but only 0.7% of the chain is indexed. Transactions will appear as indexing progresses. View on Etherscan →