Address Contract Verified
Address
0x47Fb2585D2C56Fe188D0E6ec628a38b74fCeeeDf
Balance
0 ETH
Nonce
1
Code Size
13310 bytes
Creator
0x71C05a4e...Bdc8 at tx 0x9acce3b0...8e5354
Indexed Transactions
0 (1 on-chain, 0.7% indexed)
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
Token Balances (1)
View Transfers →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 →