Cryo Explorer Ethereum Mainnet

Address Contract Partially Verified

Address 0x9df238BE059572d7211F1a1a5fEe609F979AAD2d
Balance 0 ETH
Nonce 1
Code Size 23186 bytes
Indexed Transactions 0
External Etherscan · Sourcify

Contract Bytecode

23186 bytes
0x608060405234801561000f575f80fd5b506004361061031a575f3560e01c80639bd2c0b1116101a8578063c4c92b37116100f3578063e5fe45771161009e578063eb5dcd6c11610079578063eb5dcd6c146108b8578063f2fde38b146108cb578063fbffd2c1146108de578063feaf968c146108f1575f80fd5b8063e5fe45771461083d578063e76d516814610887578063eb457163146108a5575f80fd5b8063dc7f0124116100ce578063dc7f0124146107dd578063e3d0e71214610802578063e4902f8214610815575f80fd5b8063c4c92b3714610799578063d09dc339146107b7578063daffc4b5146107bf575f80fd5b8063b17f2a6b11610153578063b633620c1161012e578063b633620c14610760578063ba0cb29e14610773578063c107532914610786575f80fd5b8063b17f2a6b14610727578063b1dc65a41461073a578063b5ab58dc1461074d575f80fd5b8063a118f24911610183578063a118f249146106d1578063afcb95d7146106e4578063b121e14714610714575f80fd5b80639bd2c0b11461064c5780639c849b30146106ab5780639e3ceeab146106be575f80fd5b80636b14daf8116102685780638205bf6a116102135780638da5cb5b116101ee5780638da5cb5b146105a157806398e5b12a146105df5780639a6fc8f514610602575f80fd5b80638205bf6a146105735780638823da6c1461057b5780638ac28d5a1461058e575f80fd5b806379ba50971161024357806379ba5097146105335780638038e4a11461053b57806381ff704814610543575f80fd5b80636b14daf8146104df57806370da2f67146105025780637284e4161461052b575f80fd5b80634fb17470116102c8578063643dc105116102a3578063643dc105146104af578063666cab8d146104c2578063668a0f02146104d7575f80fd5b80634fb174701461048d57806350d25bcd146104a057806354fd4d50146104a8575f80fd5b806322adbc78116102f857806322adbc781461039757806329937268146103c0578063313ce5671461045c575f80fd5b80630a7569831461031e5780630eafb25b14610328578063181f5a771461034e575b5f80fd5b6103266108f9565b005b61033b610336366004614e46565b610977565b6040519081526020015b60405180910390f35b61038a6040518060400160405280601481526020017f4475616c41676772656761746f7220312e302e3000000000000000000000000081525081565b6040516103459190614ec2565b7f000000000000000000000000000000000000000000000000000000000000000160170b61033b565b600d54600c546040805163ffffffff6e010000000000000000000000000000850481168252720100000000000000000000000000000000000085048116602083015276010000000000000000000000000000000000000000000085048116928201929092527a01000000000000000000000000000000000000000000000000000090930416606083015262ffffff16608082015260a001610345565b60405160ff7f0000000000000000000000000000000000000000000000000000000000000008168152602001610345565b61032661049b366004614ed4565b610aa6565b61033b610d43565b600661033b565b6103266104bd366004614f1c565b610d6d565b6104ca611001565b6040516103459190614fe0565b61033b61106e565b6104f26104ed3660046150f8565b611082565b6040519015158152602001610345565b7f00000000000000000000ffffffffffffffffffffffffffffffffffffffffffff60170b61033b565b61038a6110b6565b61032661113d565b61032661123e565b600e54600b546040805163ffffffff80851682526401000000009094049093166020840152820152606001610345565b61033b6112d1565b610326610589366004614e46565b61131b565b61032661059c366004614e46565b6113d3565b5f5473ffffffffffffffffffffffffffffffffffffffff165b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610345565b6105e761143b565b60405169ffffffffffffffffffff9091168152602001610345565b610615610610366004615145565b6115b7565b6040805169ffffffffffffffffffff968716815260208101959095528401929092526060830152909116608082015260a001610345565b604080518082018252600f5473ffffffffffffffffffffffffffffffffffffffff81168083527401000000000000000000000000000000000000000090910463ffffffff16602092830181905283519182529181019190915201610345565b6103266106b93660046151b6565b61167b565b6103266106cc366004614e46565b611896565b6103266106df366004614e46565b611947565b600b54600d54604080515f8152602081019390935261010090910460081c63ffffffff1690820152606001610345565b610326610722366004614e46565b6119f9565b61032661073536600461521d565b611aef565b610326610748366004615238565b611b5a565b61033b61075b366004615311565b611b75565b61033b61076e366004615311565b611bad565b610326610781366004615238565b611c05565b610326610794366004615328565b611c17565b60155473ffffffffffffffffffffffffffffffffffffffff166105ba565b61033b611ee4565b60105473ffffffffffffffffffffffffffffffffffffffff166105ba565b6001546104f29074010000000000000000000000000000000000000000900460ff1681565b610326610810366004615416565b611f96565b610828610823366004614e46565b6127bc565b60405163ffffffff9091168152602001610345565b610845612885565b6040805195865263ffffffff909416602086015260ff9092169284019290925260179190910b606083015267ffffffffffffffff16608082015260a001610345565b60145473ffffffffffffffffffffffffffffffffffffffff166105ba565b6103266108b33660046154db565b612932565b6103266108c6366004614ed4565b612a69565b6103266108d9366004614e46565b612bbe565b6103266108ec366004614e46565b612bcf565b610615612be0565b610901612c78565b60015474010000000000000000000000000000000000000000900460ff161561097557600180547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff1690556040517f3be8a977a014527b50ae38adda80b56911c267328965c98ddc385d248f539638905f90a15b565b73ffffffffffffffffffffffffffffffffffffffff81165f9081526003602090815260408083208151606081018352905460ff80821615158084526101008304909116948301949094526201000090046bffffffffffffffffffffffff1691810191909152906109e957505f92915050565b600d5460208201515f91760100000000000000000000000000000000000000000000900463ffffffff169060079060ff16601f8110610a2a57610a2a615507565b600881049190910154600d54610a60926007166004026101000a90910463ffffffff908116916601000000000000900416615561565b63ffffffff16610a70919061557e565b610a7e90633b9aca0061557e565b905081604001516bffffffffffffffffffffffff1681610a9e9190615595565b949350505050565b610aae612c78565b60145473ffffffffffffffffffffffffffffffffffffffff908116908316819003610ad857505050565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff8416906370a0823190602401602060405180830381865afa158015610b40573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610b6491906155a8565b50610b6d612cf8565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201525f9073ffffffffffffffffffffffffffffffffffffffff8316906370a0823190602401602060405180830381865afa158015610bd7573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610bfb91906155a8565b6040517fa9059cbb00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8581166004830152602482018390529192509083169063a9059cbb906044016020604051808303815f875af1158015610c71573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610c9591906155bf565b610ccb576040517f7725087a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b601480547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff86811691821790925560405190918416907f4966a50c93f855342ccf6c5c0d358b85b91335b2acedc7da0932f691f351711a905f90a350505b5050565b5f60115f610d4f613138565b63ffffffff16815260208101919091526040015f205460170b919050565b5f5473ffffffffffffffffffffffffffffffffffffffff16331480610e2657506015546040517f6b14daf800000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911690636b14daf890610de79033905f9036906004016155de565b602060405180830381865afa158015610e02573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610e2691906155bf565b610e5c576040517f91ed77c500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610e64612cf8565b600d80547fffffffffffffffffffff0000000000000000ffffffffffffffffffffffffffff166e01000000000000000000000000000063ffffffff8881169182027fffffffffffffffffffff00000000ffffffffffffffffffffffffffffffffffff16929092177201000000000000000000000000000000000000888416908102919091177fffff0000000000000000ffffffffffffffffffffffffffffffffffffffffffff167601000000000000000000000000000000000000000000008885169081027fffff00000000ffffffffffffffffffffffffffffffffffffffffffffffffffff16919091177a01000000000000000000000000000000000000000000000000000094881694850217909455600c80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000001662ffffff871690811790915560408051938452602084019290925290820193909352606081019190915260808101919091527f0bf184bf1bba9699114bdceddaf338a1b364252c5e497cc01918dde92031713f9060a00160405180910390a15050505050565b6060600680548060200260200160405190810160405280929190818152602001828054801561106457602002820191905f5260205f20905b815473ffffffffffffffffffffffffffffffffffffffff168152600190910190602001808311611039575b5050505050905090565b5f611077613138565b63ffffffff16905090565b5f61108d838361329d565b806110ad575073ffffffffffffffffffffffffffffffffffffffff831632145b90505b92915050565b6060601380546110c590615647565b80601f01602080910402602001604051908101604052809291908181526020018280546110f190615647565b80156110645780601f1061111357610100808354040283529160200191611064565b820191905f5260205f20905b81548152906001019060200180831161111f57509395945050505050565b60015473ffffffffffffffffffffffffffffffffffffffff1633146111c3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e65720000000000000000000060448201526064015b60405180910390fd5b5f8054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b611246612c78565b60015474010000000000000000000000000000000000000000900460ff1661097557600180547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff16740100000000000000000000000000000000000000001790556040517faebf329500988c6488a0074e5a0a9ff304561fc5c6fc877aeb1d59c8282c3480905f90a1565b5f60115f6112dd613138565b63ffffffff908116825260208201929092526040015f20547c0100000000000000000000000000000000000000000000000000000000900416919050565b611323612c78565b73ffffffffffffffffffffffffffffffffffffffff81165f9081526002602052604090205460ff16156113d05773ffffffffffffffffffffffffffffffffffffffff81165f8181526002602090815260409182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016905590519182527f3d68a6fce901d20453d1a7aa06bf3950302a735948037deb182a8db66df2a0d191015b60405180910390a15b50565b73ffffffffffffffffffffffffffffffffffffffff8181165f90815260166020526040902054163314611432576040517f2ab4a3db00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6113d0816132f1565b5f805473ffffffffffffffffffffffffffffffffffffffff1633148015906114f957506010546040517f6b14daf800000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911690636b14daf8906114b89033905f9036906004016155de565b602060405180830381865afa1580156114d3573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906114f791906155bf565b155b15611530576040517f4cdc445800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600d54600b546040805191825263ffffffff6101008404600881901c8216602085015260ff811684840152915164ffffffffff9092169366010000000000009004169133917f41e3990591fd372502daa15842da15bc7f41c75309ab3ff4f56f1848c178825c9181900360600190a26115aa816001615692565b63ffffffff169250505090565b5f805f805f6115c4613138565b63ffffffff168669ffffffffffffffffffff1611156115f057505f935083925082915081905080611672565b5050505063ffffffff8281165f9081526011602090815260409182902082516060810184529054601781900b8083527801000000000000000000000000000000000000000000000000820486169383018490527c0100000000000000000000000000000000000000000000000000000000909104909416920182905284935090835b91939590929450565b611683612c78565b8281146116bc576040517f3d2f942900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f5b8381101561188f575f8585838181106116d9576116d9615507565b90506020020160208101906116ee9190614e46565b90505f84848481811061170357611703615507565b90506020020160208101906117189190614e46565b73ffffffffffffffffffffffffffffffffffffffff8084165f9081526016602052604090205491925016801580158161177d57508273ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614155b156117b4576040517faeae062800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8481165f90815260166020526040902080547fffffffffffffffffffffffff00000000000000000000000000000000000000001685831690811790915590831614611880578273ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff167f78af32efdcad432315431e9b03d27e6cd98fb79c405fdc5af7c1714d9c0f75b360405160405180910390a45b505050508060010190506116be565b5050505050565b61189e612c78565b60105473ffffffffffffffffffffffffffffffffffffffff9081169082168114610d3f57601080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff84811691821790925560408051928416835260208301919091527f27b89aede8b560578baaa25ee5ce3852c5eecad1e114b941bbd89e1eb4bae63491015b60405180910390a15050565b61194f612c78565b73ffffffffffffffffffffffffffffffffffffffff81165f9081526002602052604090205460ff166113d05773ffffffffffffffffffffffffffffffffffffffff81165f8181526002602090815260409182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117905590519182527f87286ad1f399c8e82bf0c4ef4fcdc570ea2e1e92176e5c848b6413545b885db491016113c7565b73ffffffffffffffffffffffffffffffffffffffff8181165f90815260176020526040902054163314611a58576040517f6599cbbe00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8181165f8181526016602090815260408083208054337fffffffffffffffffffffffff000000000000000000000000000000000000000080831682179093556017909452828520805490921690915590519416939092849290917f78af32efdcad432315431e9b03d27e6cd98fb79c405fdc5af7c1714d9c0f75b39190a45050565b611af7612c78565b601280547fffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000001663ffffffff83169081179091556040519081527fb24a681ce3399a408a89fd0c2b59dfc24bdad592b1c7ec7671cf060596c1c4d1906020016113c7565b611b6b88888888888888885f613546565b5050505050505050565b5f611b7e613138565b63ffffffff16821115611b9257505f919050565b5063ffffffff165f9081526011602052604090205460170b90565b5f611bb6613138565b63ffffffff16821115611bca57505f919050565b5063ffffffff9081165f908152601160205260409020547c010000000000000000000000000000000000000000000000000000000090041690565b611b6b88888888888888886001613546565b5f5473ffffffffffffffffffffffffffffffffffffffff163314801590611cd457506015546040517f6b14daf800000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911690636b14daf890611c939033905f9036906004016155de565b602060405180830381865afa158015611cae573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611cd291906155bf565b155b15611d0b576040517f91ed77c500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f611d146138c3565b6014546040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201529192505f9173ffffffffffffffffffffffffffffffffffffffff909116906370a0823190602401602060405180830381865afa158015611d85573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611da991906155a8565b905081811015611de5576040517ff4d678b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60145473ffffffffffffffffffffffffffffffffffffffff1663a9059cbb85611e17611e1186866156af565b87613aa6565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e085901b16815273ffffffffffffffffffffffffffffffffffffffff909216600483015260248201526044016020604051808303815f875af1158015611e84573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611ea891906155bf565b611ede576040517f356680b700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50505050565b6014546040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201525f91829173ffffffffffffffffffffffffffffffffffffffff909116906370a0823190602401602060405180830381865afa158015611f54573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611f7891906155a8565b90505f611f836138c3565b9050611f8f81836156c2565b9250505090565b611f9e612c78565b601f86511115611fda576040517f25d0209c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8451865114612015576040517f250a65b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b85516120228560036156e1565b60ff161061205c576040517f20c9729a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6120688460ff16613abc565b604080517f010000000000000000000000000000000000000000000000000000000000000060208201527f0000000000000000000000000000000000000000000000000000000000000001821b60218201527f00000000000000000000ffffffffffffffffffffffffffffffffffffffffffff90911b60398201526051016040516020818303038152906040528051906020012083805190602001201461213b576040517fa8811dc600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040805160c08101825267ffffffffffffffff8416815260ff86166020820152908101849052606081018290526080810187905260a08101869052600d80547fffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000ff1690556121a6612cf8565b6005545f5b81811015612291575f600582815481106121c7576121c7615507565b5f9182526020822001546006805473ffffffffffffffffffffffffffffffffffffffff9092169350908490811061220057612200615507565b5f91825260208083209091015473ffffffffffffffffffffffffffffffffffffffff948516835260048252604080842080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000016905594168252600390529190912080547fffffffffffffffffffffffffffffffffffff0000000000000000000000000000169055506001016121ab565b5061229d60055f614cdc565b6122a860065f614cdc565b5f5b8260800151518110156125b55760045f846080015183815181106122d0576122d0615507565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff1682528101919091526040015f205460ff161561233a576040517f16c6131500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60405180604001604052806001151581526020018260ff1681525060045f8560800151848151811061236e5761236e615507565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff168252818101929092526040015f90812083518154949093015160ff16610100027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff931515939093167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000909416939093179190911790915560a0840151805160039291908490811061242357612423615507565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff1682528101919091526040015f205460ff161561248d576040517fd63d347400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60405180606001604052806001151581526020018260ff1681526020015f6bffffffffffffffffffffffff1681525060035f8560a0015184815181106124d5576124d5615507565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff1682528181019290925260409081015f20835181549385015194909201516bffffffffffffffffffffffff1662010000027fffffffffffffffffffffffffffffffffffff000000000000000000000000ffff60ff95909516610100027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff931515939093167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00009094169390931791909117929092161790556001016122aa565b50608082015180516125cf91600591602090910190614cf7565b5060a082015180516125e991600691602090910190614cf7565b506020820151600d80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660ff909216919091179055600e80547fffffffffffffffffffffffffffffffffffffffffffffffff00000000ffffffff811664010000000063ffffffff4381168202928317855590830481169360019390925f9261267a928692908216911617615692565b92506101000a81548163ffffffff021916908363ffffffff1602179055506126d74630600e5f9054906101000a900463ffffffff1663ffffffff1686608001518760a00151886020015189604001518a5f01518b60600151613af5565b600b819055600e54608085015160a08601516020870151604080890151895160608b015192517f1591690b8638f5fb2dbec82ac741805ac5da8b45dc5263f4875b0496fdce4e059861273d988b98919763ffffffff9091169691959094919391926156fd565b60405180910390a1600d546601000000000000900463ffffffff165f5b8460800151518110156127af5781600782601f811061277b5761277b615507565b600891828204019190066004026101000a81548163ffffffff021916908363ffffffff16021790555080600101905061275a565b5050505050505050505050565b73ffffffffffffffffffffffffffffffffffffffff81165f9081526003602090815260408083208151606081018352905460ff80821615158084526101008304909116948301949094526201000090046bffffffffffffffffffffffff16918101919091529061282e57505f92915050565b6007816020015160ff16601f811061284857612848615507565b600881049190910154600d5461287e926007166004026101000a90910463ffffffff908116916601000000000000900416615561565b9392505050565b5f808080803332146128c3576040517f74e2cd5100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050600b54600d5463ffffffff6601000000000000820481165f908152601160205260409020549296610100909204600881901c8216965064ffffffffff169450601783900b93507c010000000000000000000000000000000000000000000000000000000090920490911690565b61293a612c78565b60408051808201909152600f5473ffffffffffffffffffffffffffffffffffffffff8082168084527401000000000000000000000000000000000000000090920463ffffffff16602084015284161415806129a557508163ffffffff16816020015163ffffffff1614155b15612a645760408051808201825273ffffffffffffffffffffffffffffffffffffffff85811680835263ffffffff8681166020948501819052600f80547fffffffffffffffff00000000000000000000000000000000000000000000000016841774010000000000000000000000000000000000000000830217905586518786015187519316835294820152909392909116917fb04e3a37abe9c0fcdfebdeae019a8e2b12ddf53f5d55ffb0caccc1bedaca1541910160405180910390a35b505050565b73ffffffffffffffffffffffffffffffffffffffff8281165f90815260166020526040902054163314612ac8576040517fb97d016a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff81163303612b17576040517f79df0c6600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8083165f90815260176020526040902080548383167fffffffffffffffffffffffff000000000000000000000000000000000000000082168117909255909116908114612a645760405173ffffffffffffffffffffffffffffffffffffffff8084169133918616907f84f7c7c80bb8ed2279b4aab5f61cd05e6374073d38f46d7f32de8c30e9e38367905f90a4505050565b612bc6612c78565b6113d081613ba0565b612bd7612c78565b6113d081613c94565b5f805f805f80612bee613138565b63ffffffff9081165f8181526011602090815260409182902082516060810184529054601781900b8083527801000000000000000000000000000000000000000000000000820487169383018490527c0100000000000000000000000000000000000000000000000000000000909104909516920182905291999298509096509450879350915050565b5f5473ffffffffffffffffffffffffffffffffffffffff163314610975576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e65720000000000000000000060448201526064016111ba565b601454600d54604080516103e081019182905273ffffffffffffffffffffffffffffffffffffffff90931692660100000000000090920463ffffffff16915f91600790601f908285855b82829054906101000a900463ffffffff1663ffffffff1681526020019060040190602082600301049283019260010382029150808411612d42579050505050505090505f6006805480602002602001604051908101604052809291908181526020018280548015612de757602002820191905f5260205f20905b815473ffffffffffffffffffffffffffffffffffffffff168152600190910190602001808311612dbc575b505050505090505f5b815181101561312a575f60035f848481518110612e0f57612e0f615507565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f0160029054906101000a90046bffffffffffffffffffffffff166bffffffffffffffffffffffff1690505f60035f858581518110612e9157612e91615507565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f0160026101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff1602179055505f8483601f8110612f1557612f15615507565b6020020151600d5490870363ffffffff9081169250760100000000000000000000000000000000000000000000909104168102633b9aca00028201801561311f575f60165f878781518110612f6c57612f6c615507565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff90811683529082019290925260409081015f205490517fa9059cbb00000000000000000000000000000000000000000000000000000000815290821660048201819052602482018590529250908a169063a9059cbb906044016020604051808303815f875af1158015613002573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061302691906155bf565b61305c576040517f356680b700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b878786601f811061306f5761306f615507565b602002019063ffffffff16908163ffffffff16815250508873ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff168787815181106130c6576130c6615507565b602002602001015173ffffffffffffffffffffffffffffffffffffffff167fd0b1dac935d85bd54cf0a33b0d41d39f8cf53a968465fc7ea2377526b8ac712c8560405161311591815260200190565b60405180910390a4505b505050600101612df0565b5061188f600783601f614d7f565b600d545f9063ffffffff660100000000000082048116916a010000000000000000000081049091169060ff7e010000000000000000000000000000000000000000000000000000000000009091041673ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000062c2ab773b7324ad9e030d777989b3b5d5c54c0a1633036132305760125463ffffffff8381165f90815260116020526040902054429261320d92908116917c0100000000000000000000000000000000000000000000000000000000900416615692565b63ffffffff16101561322957613221613d35565b935050505090565b5092915050565b8163ffffffff168363ffffffff160361329557808015613285575063ffffffff8084165f908152601160205260409020547c010000000000000000000000000000000000000000000000000000000090041642145b1561329557613221600184615561565b509092915050565b73ffffffffffffffffffffffffffffffffffffffff82165f9081526002602052604081205460ff16806110ad57505060015474010000000000000000000000000000000000000000900460ff161592915050565b73ffffffffffffffffffffffffffffffffffffffff81165f908152600360209081526040918290208251606081018452905460ff80821615158084526101008304909116938301939093526201000090046bffffffffffffffffffffffff169281019290925261335f575050565b5f61336983610977565b90508015612a645773ffffffffffffffffffffffffffffffffffffffff8381165f90815260166020526040908190205460145491517fa9059cbb000000000000000000000000000000000000000000000000000000008152908316600482018190526024820185905292919091169063a9059cbb906044016020604051808303815f875af11580156133fd573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061342191906155bf565b613457576040517f356680b700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600d5f0160069054906101000a900463ffffffff166007846020015160ff16601f811061348657613486615507565b6008810491909101805460079092166004026101000a63ffffffff81810219909316939092169190910291909117905573ffffffffffffffffffffffffffffffffffffffff8481165f8181526003602090815260409182902080547fffffffffffffffffffffffffffffffffffff000000000000000000000000ffff169055601454915186815291841693851692917fd0b1dac935d85bd54cf0a33b0d41d39f8cf53a968465fc7ea2377526b8ac712c910160405180910390a450505050565b5f5a90506135568a898887613e1d565b5f6135958a8a8080601f0160208091040260200160405190810160405280939291908181526020018383808284375f92019190915250613f3192505050565b6040805161012081018252600d5460ff808216835261010080830464ffffffffff1660208501526601000000000000830463ffffffff908116958501959095526a01000000000000000000008304851660608501526e01000000000000000000000000000083048516608085015272010000000000000000000000000000000000008304851660a08501527601000000000000000000000000000000000000000000008304851660c08501527a010000000000000000000000000000000000000000000000000000830490941660e08401527e01000000000000000000000000000000000000000000000000000000000000909104161515918101919091529091508315613781575f806136a884613fc7565b91509150811561377e578063ffffffff16836060015163ffffffff16106136fb576040517ff803a2ca00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600d80547fffffffffffffffffffffffffffffffffffff00000000ffffffffffffffffffff166a010000000000000000000063ffffffff8416908102919091179091556040517f8d530b9ddc4b318d28fdd4c3a21fcfecece54c1a72a824f262985b99afef009b905f90a261377483855f015187614170565b50505050506138b8565b50505b602081810151908d01359064ffffffffff80831691161415806137a75750816101000151155b1561381a57816020015164ffffffffff168164ffffffffff16116137f7576040517ff803a2ca00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6138078d8d8d8d8d8d8d8d6142ab565b613815828e358386896144f8565b613858565b600d54604051660100000000000090910463ffffffff16907fda2435684a37fba6f7841e49b59e6ad975e462bbebd28ec9da4ed9746a6992be905f90a25b600d80547fff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167e010000000000000000000000000000000000000000000000000000000000008715150217905582516138b390839086614170565b505050505b505050505050505050565b5f80600680548060200260200160405190810160405280929190818152602001828054801561392657602002820191905f5260205f20905b815473ffffffffffffffffffffffffffffffffffffffff1681526001909101906020018083116138fb575b50508351600d54604080516103e08101918290529697509195660100000000000090910463ffffffff1694505f93509150600790601f908285855b82829054906101000a900463ffffffff1663ffffffff1681526020019060040190602082600301049283019260010382029150808411613961579050505050505090505f5b838110156139e9578181601f81106139c0576139c0615507565b60200201516139cf9084615561565b6139df9063ffffffff1687615595565b95506001016139a6565b50600d54613a1b90760100000000000000000000000000000000000000000000900463ffffffff16633b9aca0061557e565b613a25908661557e565b94505f5b83811015613a9e5760035f868381518110613a4657613a46615507565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff1682528101919091526040015f2054613a94906201000090046bffffffffffffffffffffffff1687615595565b9550600101613a29565b505050505090565b5f81831015613ab65750816110b0565b50919050565b5f81116113d0576040517f39d1a4d000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f808a8a8a8a8a8a8a8a8a604051602001613b1899989796959493929190615792565b604080518083037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe001815291905280516020909101207dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167e01000000000000000000000000000000000000000000000000000000000000179150505b9998505050505050505050565b3373ffffffffffffffffffffffffffffffffffffffff821603613c1f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c6600000000000000000060448201526064016111ba565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff8381169182179092555f8054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b60155473ffffffffffffffffffffffffffffffffffffffff9081169082168114610d3f57601580547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff84811691821790925560408051928416835260208301919091527f793cb73064f3c8cde7e187ae515511e6e56d1ee89bf08b82fa60fb70f8d48912910161193b565b600d545f906601000000000000900463ffffffff16805b63ffffffff811615613dff5763ffffffff7f000000000000000000000000000000000000000000000000000000000000001416613d898284615561565b63ffffffff1614613dff5760125463ffffffff8281165f908152601160205260409020544292613ddd92908116917c0100000000000000000000000000000000000000000000000000000000900416615692565b63ffffffff161015613def5792915050565b613df881615826565b9050613d4c565b5050600d546a0100000000000000000000900463ffffffff16919050565b335f9081526003602052604090205460ff16613e65576040517fda0f08e800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600b54843514613ea1576040517fdfdcf8e700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b613eac8383836149eb565b600d54613ebd9060ff166001615863565b60ff168214613ef8576040517f71253a2500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b808214611ede576040517fa75d88af00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b604080516080810182525f80825260208201526060918101829052818101919091525f805f8085806020019051810190613f6b919061588d565b9350935093509350613f7d8683614a67565b815160408051602081018690525f910160408051918152928152825160808101845260179490940b845263ffffffff90961660208401525081019390935260608301525092915050565b600d545f9081906601000000000000900463ffffffff16805b63ffffffff8116156141645763ffffffff7f00000000000000000000000000000000000000000000000000000000000000141661401d8284615561565b63ffffffff160361405a576040517fc3d2e25400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b63ffffffff8082165f9081526011602090815260409182902082516060810184529054601781900b82527801000000000000000000000000000000000000000000000000810485168284018190527c01000000000000000000000000000000000000000000000000000000009091048516938201939093529088015190921611156140ec57505f958695509350505050565b856020015163ffffffff16816020015163ffffffff1614801561414157506060860151805161411d90600290615950565b8151811061412d5761412d615507565b602002602001015160170b815f015160170b145b15614153575060019590945092505050565b5061415d81615826565b9050613fe0565b505f9485945092505050565b5f8260170b121561418057505050565b5f6141a6633b9aca003a048560a0015163ffffffff16866080015163ffffffff16614ac7565b9050601036025f5a600c549091505f906141d19063ffffffff8716908690869062ffffff1686614aec565b90505f670de0b6b3a764000077ffffffffffffffffffffffffffffffffffffffffffffffff88168302335f9081526003602052604090205460e08b01519290910492506201000090046bffffffffffffffffffffffff9081169163ffffffff16633b9aca0002828401019081168211156142515750505050505050505050565b335f90815260036020526040902080546bffffffffffffffffffffffff90921662010000027fffffffffffffffffffffffffffffffffffff000000000000000000000000ffff909216919091179055505050505050505050565b5f87876040516142bc929190615988565b6040519081900381206142d3918b90602001615997565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815282825280516020918201208383019092525f808452908301819052909250905f5b8781101561449d575f60018587846020811061433d5761433d615507565b61434a91901a601b615863565b8c8c8681811061435c5761435c615507565b905060200201358b8b8781811061437557614375615507565b905060200201356040515f81526020016040526040516143b1949392919093845260ff9290921660208401526040830152606082015260800190565b6020604051602081039080840390855afa1580156143d1573d5f803e3d5ffd5b5050604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081015173ffffffffffffffffffffffffffffffffffffffff81165f9081526004602090815290849020838501909452925460ff808216151580855261010090920416938301939093529095509250905061447e576040517fcd2467c600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b826020015160080260ff166001901b840193505080600101905061431f565b5081827e0101010101010101010101010101010101010101010101010101010101010116146127af576040517f8044bb3300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b601f8260600151511115614538576040517fff6c220500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b845f015160ff168260600151511161457c576040517f5765bdd700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b64ffffffffff83166020860152606082015180515f919061459f90600290615950565b815181106145af576145af615507565b602002602001015190508060170b7f000000000000000000000000000000000000000000000000000000000000000160170b138061461257507f00000000000000000000ffffffffffffffffffffffffffffffffffffffffffff60170b8160170b135b15614649576040517fca191b2600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60408601805190614659826159ad565b63ffffffff90811690915260408051606081018252601785900b815260208781015184168183019081524285168385019081528c85015186165f908152601190935293909120915182549151935185167c0100000000000000000000000000000000000000000000000000000000027bffffffffffffffffffffffffffffffffffffffffffffffffffffffff949095167801000000000000000000000000000000000000000000000000027fffffffff0000000000000000000000000000000000000000000000000000000090921677ffffffffffffffffffffffffffffffffffffffffffffffff9091161717919091169190911790555081156147925760408087015163ffffffff166060880181905290517f8d530b9ddc4b318d28fdd4c3a21fcfecece54c1a72a824f262985b99afef009b905f90a25b85600d5f820151815f015f6101000a81548160ff021916908360ff1602179055506020820151815f0160016101000a81548164ffffffffff021916908364ffffffffff1602179055506040820151815f0160066101000a81548163ffffffff021916908363ffffffff1602179055506060820151815f01600a6101000a81548163ffffffff021916908363ffffffff1602179055506080820151815f01600e6101000a81548163ffffffff021916908363ffffffff16021790555060a0820151815f0160126101000a81548163ffffffff021916908363ffffffff16021790555060c0820151815f0160166101000a81548163ffffffff021916908363ffffffff16021790555060e0820151815f01601a6101000a81548163ffffffff021916908363ffffffff160217905550610100820151815f01601e6101000a81548160ff021916908315150217905550905050856040015163ffffffff167fc797025feeeaf2cd924c99e9205acb8ec04d5cad21c41ce637a38fb6dee6016a8233866020015187606001518860400151895f01518c8c6040516149399897969594939291906159cf565b60405180910390a2604080870151602080860151925163ffffffff93841681525f93909216917f0109fc6f55cf40689f02fbaad7af7fe7bbac8a3d2186600afc7d3e10cac60271910160405180910390a3856040015163ffffffff168160170b7f0559884fd3a460db3073b7fc896cc77986f16e378210ded43186175bf646fc5f426040516149ca91815260200190565b60405180910390a36149e386604001518260170b614b39565b505050505050565b5f6149f782602061557e565b614a0284602061557e565b614a0e86610144615595565b614a189190615595565b614a229190615595565b614a2c905f615595565b9050368114611ede576040517fb4d895d500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f81516020614a76919061557e565b614a819060a0615595565b614a8b905f615595565b905080835114612a64576040517fd4e1416000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f8383811015614ad957600285850304015b614ae38184613aa6565b95945050505050565b5f81861015614b27576040517ffbf484ab00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50633b9aca0094039190910101020290565b60408051808201909152600f5473ffffffffffffffffffffffffffffffffffffffff81168083527401000000000000000000000000000000000000000090910463ffffffff166020830152614b8d57505050565b5f614b99600185615561565b63ffffffff8181165f81815260116020526040808220549051602481019390935260170b60448301819052928816606483015260848201879052929350909190614c6b9060a401604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152919052602080820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fbeed9b510000000000000000000000000000000000000000000000000000000017905286519087015163ffffffff16611388614ca5565b915050806149e3576040517f1c26714c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f805a838110614cd257839003604081048103851015614cd2575f80885160208a015f8a8af19250600191505b5094509492505050565b5080545f8255905f5260205f20908101906113d09190614e11565b828054828255905f5260205f20908101928215614d6f579160200282015b82811115614d6f57825182547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff909116178255602090920191600190910190614d15565b50614d7b929150614e11565b5090565b600483019183908215614d6f579160200282015f5b83821115614dd857835183826101000a81548163ffffffff021916908363ffffffff1602179055509260200192600401602081600301049283019260010302614d94565b8015614e085782816101000a81549063ffffffff0219169055600401602081600301049283019260010302614dd8565b5050614d7b9291505b5b80821115614d7b575f8155600101614e12565b73ffffffffffffffffffffffffffffffffffffffff811681146113d0575f80fd5b5f60208284031215614e56575f80fd5b813561287e81614e25565b5f81518084525f5b81811015614e8557602081850181015186830182015201614e69565b505f6020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b602081525f6110ad6020830184614e61565b5f8060408385031215614ee5575f80fd5b8235614ef081614e25565b91506020830135614f0081614e25565b809150509250929050565b63ffffffff811681146113d0575f80fd5b5f805f805f60a08688031215614f30575f80fd5b8535614f3b81614f0b565b94506020860135614f4b81614f0b565b93506040860135614f5b81614f0b565b92506060860135614f6b81614f0b565b9150608086013562ffffff81168114614f82575f80fd5b809150509295509295909350565b5f815180845260208085019450602084015f5b83811015614fd557815173ffffffffffffffffffffffffffffffffffffffff1687529582019590820190600101614fa3565b509495945050505050565b602081525f6110ad6020830184614f90565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff8111828210171561506657615066614ff2565b604052919050565b5f82601f83011261507d575f80fd5b813567ffffffffffffffff81111561509757615097614ff2565b6150c860207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8401160161501f565b8181528460208386010111156150dc575f80fd5b816020850160208301375f918101602001919091529392505050565b5f8060408385031215615109575f80fd5b823561511481614e25565b9150602083013567ffffffffffffffff81111561512f575f80fd5b61513b8582860161506e565b9150509250929050565b5f60208284031215615155575f80fd5b813569ffffffffffffffffffff8116811461287e575f80fd5b5f8083601f84011261517e575f80fd5b50813567ffffffffffffffff811115615195575f80fd5b6020830191508360208260051b85010111156151af575f80fd5b9250929050565b5f805f80604085870312156151c9575f80fd5b843567ffffffffffffffff808211156151e0575f80fd5b6151ec8883890161516e565b90965094506020870135915080821115615204575f80fd5b506152118782880161516e565b95989497509550505050565b5f6020828403121561522d575f80fd5b813561287e81614f0b565b5f805f805f805f8060e0898b03121561524f575f80fd5b606089018a81111561525f575f80fd5b8998503567ffffffffffffffff80821115615278575f80fd5b818b0191508b601f83011261528b575f80fd5b813581811115615299575f80fd5b8c60208285010111156152aa575f80fd5b6020830199508098505060808b01359150808211156152c7575f80fd5b6152d38c838d0161516e565b909750955060a08b01359150808211156152eb575f80fd5b506152f88b828c0161516e565b999c989b50969995989497949560c00135949350505050565b5f60208284031215615321575f80fd5b5035919050565b5f8060408385031215615339575f80fd5b823561534481614e25565b946020939093013593505050565b5f67ffffffffffffffff82111561536b5761536b614ff2565b5060051b60200190565b5f82601f830112615384575f80fd5b8135602061539961539483615352565b61501f565b8083825260208201915060208460051b8701019350868411156153ba575f80fd5b602086015b848110156153df5780356153d281614e25565b83529183019183016153bf565b509695505050505050565b803560ff811681146153fa575f80fd5b919050565b803567ffffffffffffffff811681146153fa575f80fd5b5f805f805f8060c0878903121561542b575f80fd5b863567ffffffffffffffff80821115615442575f80fd5b61544e8a838b01615375565b97506020890135915080821115615463575f80fd5b61546f8a838b01615375565b965061547d60408a016153ea565b95506060890135915080821115615492575f80fd5b61549e8a838b0161506e565b94506154ac60808a016153ff565b935060a08901359150808211156154c1575f80fd5b506154ce89828a0161506e565b9150509295509295509295565b5f80604083850312156154ec575f80fd5b82356154f781614e25565b91506020830135614f0081614f0b565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b63ffffffff82811682821603908082111561322957613229615534565b80820281158282048414176110b0576110b0615534565b808201808211156110b0576110b0615534565b5f602082840312156155b8575f80fd5b5051919050565b5f602082840312156155cf575f80fd5b8151801515811461287e575f80fd5b73ffffffffffffffffffffffffffffffffffffffff8416815260406020820152816040820152818360608301375f818301606090810191909152601f9092017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016010192915050565b600181811c9082168061565b57607f821691505b602082108103613ab6577f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b63ffffffff81811683821601908082111561322957613229615534565b818103818111156110b0576110b0615534565b8181035f83128015838313168383128216171561322957613229615534565b60ff818116838216029081169081811461322957613229615534565b5f61012063ffffffff808d1684528b6020850152808b1660408501525080606084015261572c8184018a614f90565b905082810360808401526157408189614f90565b905060ff871660a084015282810360c084015261575d8187614e61565b905067ffffffffffffffff851660e08401528281036101008401526157828185614e61565b9c9b505050505050505050505050565b5f6101208b835273ffffffffffffffffffffffffffffffffffffffff8b16602084015267ffffffffffffffff808b1660408501528160608501526157d88285018b614f90565b915083820360808501526157ec828a614f90565b915060ff881660a085015283820360c08501526158098288614e61565b90861660e085015283810361010085015290506157828185614e61565b5f63ffffffff82168061583b5761583b615534565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0192915050565b60ff81811683821601908111156110b0576110b0615534565b8051601781900b81146153fa575f80fd5b5f805f80608085870312156158a0575f80fd5b84516158ab81614f0b565b809450506020808601519350604086015167ffffffffffffffff8111156158d0575f80fd5b8601601f810188136158e0575f80fd5b80516158ee61539482615352565b81815260059190911b8201830190838101908a83111561590c575f80fd5b928401925b82841015615931576159228461587c565b82529284019290840190615911565b80965050505050506159456060860161587c565b905092959194509250565b5f82615983577f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b500490565b818382375f9101908152919050565b8281526080810160608360208401379392505050565b5f63ffffffff8083168181036159c5576159c5615534565b6001019392505050565b5f61010080830160178c60170b8552602073ffffffffffffffffffffffffffffffffffffffff8d16602087015263ffffffff8c1660408701528360608701528293508a518084526101208701945060208c0193505f5b81811015615a43578451840b86529482019493820193600101615a25565b50505050508281036080840152615a5a8188614e61565b915050615a6c60a083018660170b9052565b8360c0830152613b9360e083018464ffffffffff16905256fea164736f6c6343000818000a

Verified Source Code Partial Match

Compiler: v0.8.24+commit.e11b9ed9 EVM: cancun Optimization: Yes (1000000 runs)
DualAggregator.sol 1632 lines
// SPDX-License-Identifier: BUSL 1.1
pragma solidity 0.8.24;

import {AccessControllerInterface} from "@chainlink/contracts/src/v0.8/shared/interfaces/AccessControllerInterface.sol";
import {AggregatorV2V3Interface} from "@chainlink/contracts/src/v0.8/shared/interfaces/AggregatorV2V3Interface.sol";
import {AggregatorValidatorInterface} from
  "@chainlink/contracts/src/v0.8/shared/interfaces/AggregatorValidatorInterface.sol";
import {LinkTokenInterface} from "@chainlink/contracts/src/v0.8/shared/interfaces/LinkTokenInterface.sol";

import {SimpleReadAccessController} from "@chainlink/contracts/src/v0.8/shared/access/SimpleReadAccessController.sol";
import {CallWithExactGas} from "@chainlink/contracts/src/v0.8/shared/call/CallWithExactGas.sol";
import {OCR2Abstract} from "@chainlink/contracts/src/v0.8/shared/ocr2/OCR2Abstract.sol";

// This contract is a port of OCR2Aggregator from `libocr` it is being used
// for a new feeds based project that is ongoing, there will be some modernization
// that happens to this contract as the project progresses.
// solhint-disable max-states-count
contract DualAggregator is OCR2Abstract, AggregatorV2V3Interface, SimpleReadAccessController {
  string public constant override typeAndVersion = "DualAggregator 1.0.0";

  // This contract is divided into sections. Each section defines a set of
  // variables, events, and functions that belong together.

  // ================================================================
  // │            Variables used in multiple other sections         │
  // ================================================================

  // Transmitter information.
  struct Transmitter {
    bool active; // ─────────╮ True if active.
    uint8 index; //          │ Index in `s_transmittersList`.
    //                       │
    //                       │ Juels-denominated payment for transmitters, covering gas costs incurred
    //                       │ by the transmitter plus additional rewards. The entire LINK supply (1e9
    uint96 paymentJuels; // ─╯ LINK = 1e27 Juels) will always fit into a uint96.
  }

  // Signer information.
  struct Signer {
    bool active; // ─╮ True if active.
    uint8 index; // ─╯ Index of oracle in `s_signersList`.
  }

  // Storing these fields used on the hot path in a HotVars variable reduces the
  // retrieval of all of them to one SLOAD.
  struct HotVars {
    uint8 f; //  ─────────────────────────╮ Maximum number of faulty oracles.
    //                                    │
    uint40 latestEpochAndRound; //        │ Epoch and round from OCR protocol,
    //                                    │ 32 most significant bits for epoch, 8 least sig bits for round.
    //                                    │
    uint32 latestAggregatorRoundId; //    │ Chainlink Aggregators expose a roundId to consumers. The offchain protocol
    //                                    │ does not use this id anywhere. We increment it whenever a new transmission
    //                                    │ is made to provide callers with contiguous ids for successive reports.
    uint32 latestSecondaryRoundId; //     │ Latest transmission round arrived from the Secondary Proxy.
    uint32 maximumGasPriceGwei; //        │ Highest compensated gas price, in gwei uints.
    uint32 reasonableGasPriceGwei; //     │ If gas price is less (in gwei units), transmitter gets half the savings.
    uint32 observationPaymentGjuels; //   │ Fixed LINK reward for each observer.
    uint32 transmissionPaymentGjuels; //  │ Fixed reward for transmitter.
    bool isLatestSecondary; // ───────────╯ Whether the latest report was secondary or not
  }

  /// @notice mapping containing the transmitter information of each transmitter address.
  mapping(address transmitterAddress => Transmitter transmitter) internal s_transmitters;

  /// @notice mapping containing the signer information of each signer address.
  mapping(address signerAddress => Signer signer) internal s_signers;

  /// @notice s_signersList contains the signing address of each oracle.
  address[] internal s_signersList;

  /// @notice s_transmittersList contains the transmission address of each oracle,
  /// i.e. the address the oracle actually sends transactions to the contract from.
  address[] internal s_transmittersList;

  /// @notice We assume that all oracles contribute observations to all rounds. This
  /// variable tracks (per-oracle) from what round an oracle should be rewarded,
  /// i.e. the oracle gets (latestAggregatorRoundId - rewardFromAggregatorRoundId) * reward.
  uint32[MAX_NUM_ORACLES] internal s_rewardFromAggregatorRoundId;

  /// @notice latest setted config.
  bytes32 internal s_latestConfigDigest;

  /// @notice overhead incurred by accounting logic.
  uint24 internal s_accountingGas;

  /// @notice most common fields used on the hot path.
  HotVars internal s_hotVars;

  /// @notice lowest answer the system is allowed to report in response to transmissions.
  int192 internal immutable i_minAnswer;

  /// @notice highest answer the system is allowed to report in response to transmissions.
  int192 internal immutable i_maxAnswer;

  /// @param link address of the LINK contract.
  /// @param minAnswer_ lowest answer the median of a report is allowed to be.
  /// @param maxAnswer_ highest answer the median of a report is allowed to be.
  /// @param billingAccessController access controller for managing the billing.
  /// @param requesterAccessController access controller for requesting new rounds.
  /// @param decimals_ answers are stored in fixed-point format, with this many digits of precision.
  /// @param description_ short human-readable description of observable this contract's answers pertain to.
  /// @param secondaryProxy_ proxy address to manage the secondary reports.
  /// @param cutoffTime_ timetamp to define the window in which a secondary report is valid.
  /// @param maxSyncIterations_ max iterations the secondary proxy will be able to loop to sync with the primary rounds.
  constructor(
    LinkTokenInterface link,
    int192 minAnswer_,
    int192 maxAnswer_,
    AccessControllerInterface billingAccessController,
    AccessControllerInterface requesterAccessController,
    uint8 decimals_,
    string memory description_,
    address secondaryProxy_,
    uint32 cutoffTime_,
    uint32 maxSyncIterations_
  ) {
    i_decimals = decimals_;
    i_minAnswer = minAnswer_;
    i_maxAnswer = maxAnswer_;
    i_secondaryProxy = secondaryProxy_;
    i_maxSyncIterations = maxSyncIterations_;

    s_linkToken = link;
    emit LinkTokenSet(LinkTokenInterface(address(0)), link);

    _setBillingAccessController(billingAccessController);
    setRequesterAccessController(requesterAccessController);
    setValidatorConfig(AggregatorValidatorInterface(address(0x0)), 0);

    s_cutoffTime = cutoffTime_;
    emit CutoffTimeSet(cutoffTime_);

    s_description = description_;
  }

  // ================================================================
  // │                  OCR2Abstract Configuration                  │
  // ================================================================

  // SetConfig information
  struct SetConfigArgs {
    uint64 offchainConfigVersion; // ─╮ OffchainConfig version.
    uint8 f; // ──────────────────────╯ Faulty Oracles amount.
    bytes onchainConfig; //             Onchain configuration.
    bytes offchainConfig; //            Offchain configuration.
    address[] signers; //               Signing addresses of each oracle.
    address[] transmitters; //          Transmitting addresses of each oracle.
  }

  error FMustBePositive();
  error TooManyOracles();
  error OracleLengthMismatch();
  error FaultyOracleFTooHigh();
  error InvalidOnChainConfig();
  error RepeatedSignerAddress();
  error RepeatedTransmitterAddress();

  /// @notice incremented each time a new config is posted. This count is incorporated
  /// into the config digest to prevent replay attacks.
  uint32 internal s_configCount;

  /// @notice makes it easier for offchain systems to extract config from logs.
  uint32 internal s_latestConfigBlockNumber;

  /// @notice check if `f` is a positive number.
  /// @dev left as a function so this check can be disabled in derived contracts.
  /// @param f amount of faulty oracles to check.
  function _requirePositiveF(
    uint256 f
  ) internal pure virtual {
    if (f <= 0) {
      revert FMustBePositive();
    }
  }

  /// @inheritdoc OCR2Abstract
  function setConfig(
    address[] memory signers,
    address[] memory transmitters,
    uint8 f,
    bytes memory onchainConfig,
    uint64 offchainConfigVersion,
    bytes memory offchainConfig
  ) external override onlyOwner {
    if (signers.length > MAX_NUM_ORACLES) {
      revert TooManyOracles();
    }
    if (signers.length != transmitters.length) {
      revert OracleLengthMismatch();
    }
    if (3 * f >= signers.length) {
      revert FaultyOracleFTooHigh();
    }
    _requirePositiveF(f);
    if (keccak256(onchainConfig) != keccak256(abi.encodePacked(uint8(1), /*version*/ i_minAnswer, i_maxAnswer))) {
      revert InvalidOnChainConfig();
    }

    SetConfigArgs memory args = SetConfigArgs({
      signers: signers,
      transmitters: transmitters,
      f: f,
      onchainConfig: onchainConfig,
      offchainConfigVersion: offchainConfigVersion,
      offchainConfig: offchainConfig
    });

    s_hotVars.latestEpochAndRound = 0;
    _payOracles();

    // Remove any old signer/transmitter addresses.
    uint256 oldLength = s_signersList.length;
    for (uint256 i = 0; i < oldLength; ++i) {
      address signer = s_signersList[i];
      address transmitter = s_transmittersList[i];
      delete s_signers[signer];
      delete s_transmitters[transmitter];
    }
    delete s_signersList;
    delete s_transmittersList;

    // Add new signer/transmitter addresses.
    for (uint256 i = 0; i < args.signers.length; ++i) {
      if (s_signers[args.signers[i]].active) {
        revert RepeatedSignerAddress();
      }
      s_signers[args.signers[i]] = Signer({active: true, index: uint8(i)});
      if (s_transmitters[args.transmitters[i]].active) {
        revert RepeatedTransmitterAddress();
      }
      s_transmitters[args.transmitters[i]] = Transmitter({active: true, index: uint8(i), paymentJuels: 0});
    }
    s_signersList = args.signers;
    s_transmittersList = args.transmitters;

    s_hotVars.f = args.f;
    uint32 previousConfigBlockNumber = s_latestConfigBlockNumber;
    s_latestConfigBlockNumber = uint32(block.number);
    s_configCount += 1;
    s_latestConfigDigest = _configDigestFromConfigData(
      block.chainid,
      address(this),
      s_configCount,
      args.signers,
      args.transmitters,
      args.f,
      args.onchainConfig,
      args.offchainConfigVersion,
      args.offchainConfig
    );

    emit ConfigSet(
      previousConfigBlockNumber,
      s_latestConfigDigest,
      s_configCount,
      args.signers,
      args.transmitters,
      args.f,
      args.onchainConfig,
      args.offchainConfigVersion,
      args.offchainConfig
    );

    uint32 latestAggregatorRoundId = s_hotVars.latestAggregatorRoundId;
    for (uint256 i = 0; i < args.signers.length; ++i) {
      s_rewardFromAggregatorRoundId[i] = latestAggregatorRoundId;
    }
  }

  /// @inheritdoc OCR2Abstract
  function latestConfigDetails()
    external
    view
    override
    returns (uint32 configCount, uint32 blockNumber, bytes32 configDigest)
  {
    return (s_configCount, s_latestConfigBlockNumber, s_latestConfigDigest);
  }

  /// @notice get the transmitters list.
  /// @dev The list will match the order used to specify the transmitter during setConfig.
  /// @return s_transmittersList list of addresses permitted to transmit reports to this contract.
  function getTransmitters() external view returns (address[] memory) {
    return s_transmittersList;
  }

  /// @notice Get the mininum answer value.
  /// @return minAnswer the lowest answer the system is allowed to report in a transmission.
  function minAnswer() public view returns (int256) {
    return i_minAnswer;
  }

  /// @notice Get the maximum answer value.
  /// @return maxAnswer the highest answer the system is allowed to report in a transmission.
  function maxAnswer() public view returns (int256) {
    return i_maxAnswer;
  }

  // ================================================================
  // │                      Onchain Validation                      │
  // ================================================================

  // Configuration for validator.
  struct ValidatorConfig {
    AggregatorValidatorInterface validator; // ─╮ Validator contract interface.
    uint32 gasLimit; // ────────────────────────╯ Gas limit defined for the validation call.
  }

  /// @notice indicates that the validator configuration has been set.
  /// @param previousValidator previous validator contract.
  /// @param previousGasLimit previous gas limit for validate calls.
  /// @param currentValidator current validator contract.
  /// @param currentGasLimit current gas limit for validate calls.
  event ValidatorConfigSet(
    AggregatorValidatorInterface indexed previousValidator,
    uint32 previousGasLimit,
    AggregatorValidatorInterface indexed currentValidator,
    uint32 currentGasLimit
  );

  error InsufficientGas();

  /// @notice contstant exact gas cushion defined to do a call.
  uint16 private constant CALL_WITH_EXACT_GAS_CUSHION = 5_000;

  /// @notice validator configuration.
  ValidatorConfig private s_validatorConfig;

  /// @notice get the validator configuration.
  /// @return validator validator contract.
  /// @return gasLimit gas limit for validate calls.
  function getValidatorConfig() external view returns (AggregatorValidatorInterface validator, uint32 gasLimit) {
    ValidatorConfig memory vc = s_validatorConfig;
    return (vc.validator, vc.gasLimit);
  }

  /// @notice sets validator configuration.
  /// @dev set newValidator to 0x0 to disable validate calls.
  /// @param newValidator address of the new validator contract.
  /// @param newGasLimit new gas limit for validate calls.
  function setValidatorConfig(AggregatorValidatorInterface newValidator, uint32 newGasLimit) public onlyOwner {
    ValidatorConfig memory previous = s_validatorConfig;

    if (previous.validator != newValidator || previous.gasLimit != newGasLimit) {
      s_validatorConfig = ValidatorConfig({validator: newValidator, gasLimit: newGasLimit});

      emit ValidatorConfigSet(previous.validator, previous.gasLimit, newValidator, newGasLimit);
    }
  }

  /// @notice validate the answer against the validator configuration.
  /// @param aggregatorRoundId report round id to validate.
  /// @param answer report answer to validate.
  function _validateAnswer(uint32 aggregatorRoundId, int256 answer) private {
    ValidatorConfig memory vc = s_validatorConfig;

    if (address(vc.validator) == address(0)) {
      return;
    }

    uint32 prevAggregatorRoundId = aggregatorRoundId - 1;
    int256 prevAggregatorRoundAnswer = s_transmissions[prevAggregatorRoundId].answer;

    (, bool sufficientGas) = CallWithExactGas._callWithExactGasEvenIfTargetIsNoContract(
      abi.encodeCall(
        AggregatorValidatorInterface.validate,
        (uint256(prevAggregatorRoundId), prevAggregatorRoundAnswer, uint256(aggregatorRoundId), answer)
      ),
      address(vc.validator),
      vc.gasLimit,
      CALL_WITH_EXACT_GAS_CUSHION
    );

    if (!sufficientGas) {
      revert InsufficientGas();
    }
  }

  // ================================================================
  // │                       RequestNewRound                        │
  // ================================================================

  /// @notice contract address with AccessController Interface.
  AccessControllerInterface internal s_requesterAccessController;

  /// @notice emitted when a new requester access controller contract is set.
  /// @param old the address prior to the current setting.
  /// @param current the address of the new access controller contract.
  event RequesterAccessControllerSet(AccessControllerInterface old, AccessControllerInterface current);

  /// @notice emitted to immediately request a new round.
  /// @param requester the address of the requester.
  /// @param configDigest the latest transmission's configDigest.
  /// @param epoch the latest transmission's epoch.
  /// @param round the latest transmission's round.
  event RoundRequested(address indexed requester, bytes32 configDigest, uint32 epoch, uint8 round);

  error OnlyOwnerAndRequesterCanCall();

  /// @notice address of the requester access controller contract.
  /// @return s_requesterAccessController requester access controller address.
  function getRequesterAccessController() external view returns (AccessControllerInterface) {
    return s_requesterAccessController;
  }

  /// @notice sets the new requester access controller.
  /// @param requesterAccessController designates the address of the new requester access controller.
  function setRequesterAccessController(
    AccessControllerInterface requesterAccessController
  ) public onlyOwner {
    AccessControllerInterface oldController = s_requesterAccessController;

    if (requesterAccessController != oldController) {
      s_requesterAccessController = AccessControllerInterface(requesterAccessController);
      emit RequesterAccessControllerSet(oldController, requesterAccessController);
    }
  }

  /// @notice immediately requests a new round.
  /// @dev access control provided by requesterAccessController.
  /// @return aggregatorRoundId round id of the next round. Note: The report for this round may have been
  /// transmitted (but not yet mined) *before* requestNewRound() was even called. There is *no*
  /// guarantee of causality between the request and the report at aggregatorRoundId.
  function requestNewRound() external returns (uint80) {
    if (msg.sender != owner() && !s_requesterAccessController.hasAccess(msg.sender, msg.data)) {
      revert OnlyOwnerAndRequesterCanCall();
    }

    uint40 latestEpochAndRound = s_hotVars.latestEpochAndRound;
    uint32 latestAggregatorRoundId = s_hotVars.latestAggregatorRoundId;

    emit RoundRequested(msg.sender, s_latestConfigDigest, uint32(latestEpochAndRound >> 8), uint8(latestEpochAndRound));
    return latestAggregatorRoundId + 1;
  }

  // ================================================================
  // │                       Secondary Proxy                        │
  // ================================================================

  // Used to relieve stack pressure in transmit.
  struct Report {
    int192 juelsPerFeeCoin; // ───────╮ Exchange rate between feeCoin (e.g. ETH on Ethereum) and LINK, denominated in juels.
    uint32 observationsTimestamp; // ─╯ Timestamp when the observations were made offchain.
    bytes observers; //                 i-th element is the index of the ith observer.
    int192[] observations; //           i-th element is the ith observation.
  }

  // Transmission records the median answer from the transmit transaction at
  // time timestamp.
  struct Transmission {
    int192 answer; // ───────────────╮ 192 bits ought to be enough for anyone.
    uint32 observationsTimestamp; // │ When were observations made offchain.
    uint32 recordedTimestamp; // ────╯ When was report received onchain.
  }

  /// @notice indicates that a new report arrived from the secondary feed and the round id was updated.
  /// @param secondaryRoundId the new secondary round id.
  event SecondaryRoundIdUpdated(uint32 indexed secondaryRoundId);

  /// @notice indicates that a new report arrived from the primary feed and the report had already been stored .
  /// @param primaryRoundId the new primary round id (if we're at the next block since the report it should be the same).
  event PrimaryFeedUnlocked(uint32 indexed primaryRoundId);

  /// @notice emitted when a new cutoff time is set.
  /// @param cutoffTime the new defined cutoff time.
  event CutoffTimeSet(uint32 cutoffTime);

  /// @notice revert when the loop reaches the max sync iterations amount.
  error MaxSyncIterationsReached();

  /// @notice mapping containing the Transmission records of each round id.
  mapping(uint32 aggregatorRoundId => Transmission transmission) internal s_transmissions;

  /// @notice secondary proxy address, used to detect who's calling the contract methods.
  address internal immutable i_secondaryProxy;

  /// @notice cutoff time defines the time window in which a secondary report is valid.
  uint32 internal s_cutoffTime;

  /// @notice max iterations the secondary proxy will be able to loop to sync with the primary rounds.
  uint32 internal immutable i_maxSyncIterations;

  /// @notice sets the max time cutoff.
  /// @param _cutoffTime new max cutoff timestamp.
  function setCutoffTime(
    uint32 _cutoffTime
  ) external onlyOwner {
    s_cutoffTime = _cutoffTime;
    emit CutoffTimeSet(_cutoffTime);
  }

  /// @notice check if a report has already been transmitted.
  /// @param report the report to check.
  /// @return exist whether the report exist or not.
  /// @return roundId the round id where the report was found.
  function _doesReportExist(
    Report memory report
  ) internal view returns (bool exist, uint32 roundId) {
    // Get the latest round id.
    uint32 latestAggregatorRoundId = s_hotVars.latestAggregatorRoundId;

    for (uint32 round_ = latestAggregatorRoundId; round_ > 0; --round_) {
      // In case the loop reaches the max iterations revert it, the
      // function is not able to check if the report exists or not
      if (latestAggregatorRoundId - round_ == i_maxSyncIterations) {
        revert MaxSyncIterationsReached();
      }

      Transmission memory transmission = s_transmissions[round_];

      if (transmission.observationsTimestamp < report.observationsTimestamp) {
        return (false, 0);
      }

      if (
        transmission.observationsTimestamp == report.observationsTimestamp
          && transmission.answer == report.observations[report.observations.length / 2]
      ) {
        return (true, round_);
      }
    }

    return (false, 0);
  }

  /// @notice sync data with the primary rounds, return the freshest valid round id.
  /// @return roundId synced round id with the primary feed.
  function _getSyncPrimaryRound() internal view returns (uint32 roundId) {
    // Get the latest round id and the max iterations.
    uint32 latestAggregatorRoundId = s_hotVars.latestAggregatorRoundId;

    // Decreasing loop from the latest primary round id.
    for (uint32 round_ = latestAggregatorRoundId; round_ > 0; --round_) {
      // In case the loop reached the maxIterations, break it.
      if (latestAggregatorRoundId - round_ == i_maxSyncIterations) {
        break;
      }

      // Check if this round does not accomplish the cutoff time condition.
      if (s_transmissions[round_].recordedTimestamp + s_cutoffTime < block.timestamp) {
        return round_;
      }
    }

    // If the loop couldn't find a match, return the latest secondary round id.
    return s_hotVars.latestSecondaryRoundId;
  }

  /// @notice aggregator round in which the latest report was conceded depending on the caller.
  /// @return roundId the latest valid round id.
  function _getLatestRound() internal view returns (uint32) {
    // Get the latest round ids.
    uint32 latestAggregatorRoundId = s_hotVars.latestAggregatorRoundId;
    uint32 latestSecondaryRoundId = s_hotVars.latestSecondaryRoundId;
    bool isLatestSecondary = s_hotVars.isLatestSecondary;

    // Check if the message sender is the secondary proxy.
    if (msg.sender == i_secondaryProxy) {
      // In case the latest secondary round does not accomplish the cutoff time condition,
      // get the round id syncing with the primary rounds.
      if (s_transmissions[latestSecondaryRoundId].recordedTimestamp + s_cutoffTime < block.timestamp) {
        return _getSyncPrimaryRound();
      }

      // In case the latest secondary round accomplish the cutoff time condition, return it.
      return latestSecondaryRoundId;
    }
    // In case the report was sent by the secondary proxy.
    if (latestAggregatorRoundId == latestSecondaryRoundId) {
      // In case the transmission was sent in this same block only by the secondary proxy, return the previous round id.
      if (isLatestSecondary && s_transmissions[latestAggregatorRoundId].recordedTimestamp == block.timestamp) {
        return latestAggregatorRoundId - 1;
      }
    }

    return latestAggregatorRoundId;
  }

  // ================================================================
  // │                        Transmission                          │
  // ================================================================

  /// @notice indicates that a new report was transmitted.
  /// @param aggregatorRoundId the round to which this report was assigned.
  /// @param answer median of the observations attached to this report.
  /// @param transmitter address from which the report was transmitted.
  /// @param observationsTimestamp when were observations made offchain.
  /// @param observations observations transmitted with this report.
  /// @param observers i-th element is the oracle id of the oracle that made the i-th observation.
  /// @param juelsPerFeeCoin exchange rate between feeCoin (e.g. ETH on Ethereum) and LINK, denominated in juels.
  /// @param configDigest configDigest of transmission.
  /// @param epochAndRound least-significant byte is the OCR protocol round number, the other bytes give the OCR protocol epoch number.
  event NewTransmission(
    uint32 indexed aggregatorRoundId,
    int192 answer,
    address transmitter,
    uint32 observationsTimestamp,
    int192[] observations,
    bytes observers,
    int192 juelsPerFeeCoin,
    bytes32 configDigest,
    uint40 epochAndRound
  );

  error CalldataLengthMismatch();
  error StaleReport();
  error UnauthorizedTransmitter();
  error ConfigDigestMismatch();
  error WrongNumberOfSignatures();
  error SignaturesOutOfRegistration();
  error SignatureError();
  error DuplicateSigner();
  error OnlyCallableByEOA();
  error ReportLengthMismatch();
  error NumObservationsOutOfBounds();
  error TooFewValuesToTrustMedian();
  error MedianIsOutOfMinMaxRange();

  /// @notice the constant-length components of the msg.data sent to transmit.
  // See the "If we wanted to call sam" example on for example reasoning
  // https://solidity.readthedocs.io/en/v0.7.2/abi-spec.html
  uint256 private constant TRANSMIT_MSGDATA_CONSTANT_LENGTH_COMPONENT = 4 // Function selector.
    + 32 * 3 // 3 words containing reportContext.
    + 32 // Word containing start location of abiencoded report value.
    + 32 // Word containing start location of abiencoded rs value.
    + 32 // Word containing start location of abiencoded ss value.
    + 32 // RawVs value.
    + 32 // Word containing length of report.
    + 32 // Word containing length rs.
    + 32 // Word containing length of ss.
    + 0; // Placeholder.

  /// @notice decodes a serialized report into a Report struct.
  /// @param rawReport serialized report in raw format.
  /// @return report the decoded report in Report struct format.
  function _decodeReport(
    bytes memory rawReport
  ) internal pure returns (Report memory) {
    (uint32 observationsTimestamp, bytes32 rawObservers, int192[] memory observations, int192 juelsPerFeeCoin) =
      abi.decode(rawReport, (uint32, bytes32, int192[], int192));

    _requireExpectedReportLength(rawReport, observations);

    uint256 numObservations = observations.length;
    bytes memory observers = abi.encodePacked(rawObservers);

    assembly {
      // We truncate observers from length 32 to the number of observations.
      mstore(observers, numObservations)
    }

    return Report({
      observationsTimestamp: observationsTimestamp,
      observers: observers,
      observations: observations,
      juelsPerFeeCoin: juelsPerFeeCoin
    });
  }

  /// @notice make sure the calldata length matches the inputs. Otherwise, the
  /// transmitter could append an arbitrarily long (up to gas-block limit)
  /// string of 0 bytes, which we would reimburse at a rate of 16 gas/byte, but
  /// which would only cost the transmitter 4 gas/byte.
  /// @param reportLength the length of the serialized report.
  /// @param rsLength the length of the rs signatures.
  /// @param ssLength the length of the ss signatures.
  function _requireExpectedMsgDataLength(uint256 reportLength, uint256 rsLength, uint256 ssLength) private pure {
    // Calldata will never be big enough to make this overflow.
    uint256 expected = TRANSMIT_MSGDATA_CONSTANT_LENGTH_COMPONENT + reportLength // One byte per entry in report.
      + rsLength * 32 // 32 bytes per entry in rs.
      + ssLength * 32 // 32 bytes per entry in ss.
      + 0; // Placeholder.
    if (msg.data.length != expected) {
      revert CalldataLengthMismatch();
    }
  }

  /// @inheritdoc OCR2Abstract
  function transmit(
    // reportContext consists of:
    // reportContext[0]: ConfigDigest.
    // reportContext[1]: 27 byte padding, 4-byte epoch and 1-byte round.
    // reportContext[2]: ExtraHash.
    bytes32[3] calldata reportContext,
    bytes calldata report,
    // ECDSA signatures.
    bytes32[] calldata rs,
    bytes32[] calldata ss,
    bytes32 rawVs
  ) external override {
    // Call the internal transmit function without the isSecondary flag.
    _transmit(reportContext, report, rs, ss, rawVs, false);
  }

  /// @notice secondary proxy transmit entrypoint, call the internal transmit function with the isSecondary flag.
  /// @param reportContext serialized report context containing configDigest, epoch, round and extraHash.
  /// @param report serialized report, which the signatures are signing.
  /// @param rs i-th element is the R components of the i-th signature on report. Must have at most maxNumOracles entries.
  /// @param ss i-th element is the S components of the i-th signature on report. Must have at most maxNumOracles entries.
  /// @param rawVs i-th element is the the V component of the i-th signature.
  function transmitSecondary(
    // reportContext consists of:
    // reportContext[0]: ConfigDigest.
    // reportContext[1]: 27 byte padding, 4-byte epoch and 1-byte round.
    // reportContext[2]: ExtraHash.
    bytes32[3] calldata reportContext,
    bytes calldata report,
    // ECDSA signatures.
    bytes32[] calldata rs,
    bytes32[] calldata ss,
    bytes32 rawVs
  ) external {
    _transmit(reportContext, report, rs, ss, rawVs, true);
  }

  /// @notice internal transmit function, is called to post a new report to the contract.
  /// @param reportContext serialized report context containing configDigest, epoch, round and extraHash.
  /// @param report serialized report, which the signatures are signing.
  /// @param rs i-th element is the R components of the i-th signature on report. Must have at most maxNumOracles entries.
  /// @param ss i-th element is the S components of the i-th signature on report. Must have at most maxNumOracles entries.
  /// @param rawVs i-th element is the the V component of the i-th signature.
  /// @param isSecondary whether the transmission was sent by the secondary proxy or not.
  function _transmit(
    // reportContext consists of:
    // reportContext[0]: ConfigDigest.
    // reportContext[1]: 27 byte padding, 4-byte epoch and 1-byte round.
    // reportContext[2]: ExtraHash.
    bytes32[3] calldata reportContext,
    bytes calldata report,
    // ECDSA signatures.
    bytes32[] calldata rs,
    bytes32[] calldata ss,
    bytes32 rawVs,
    bool isSecondary
  ) internal {
    // NOTE: If the arguments to this function are changed, _requireExpectedMsgDataLength and/or
    // TRANSMIT_MSGDATA_CONSTANT_LENGTH_COMPONENT need to be changed accordingly.

    uint256 initialGas = gasleft(); // This line must come first.

    // Validate the report data.
    _validateReport(reportContext, report.length, rs.length, ss.length);

    Report memory report_ = _decodeReport(report); // Decode the report.
    HotVars memory hotVars = s_hotVars; // Load hotVars into memory.

    if (isSecondary) {
      (bool exist, uint32 roundId) = _doesReportExist(report_);
      // In case the report exists, copy the round id and pay the transmitter.
      if (exist) {
        // In case the round has already been processed by the secondary feed.
        if (hotVars.latestSecondaryRoundId >= roundId) {
          revert StaleReport();
        }

        s_hotVars.latestSecondaryRoundId = roundId;
        emit SecondaryRoundIdUpdated(roundId);

        _payTransmitter(hotVars, report_.juelsPerFeeCoin, uint32(initialGas));
        return;
      }
    }

    // Report epoch and round.
    uint40 epochAndRound = uint40(uint256(reportContext[1]));

    // Only skip the report transmission in case the epochAndRound is equal to the latestEpochAndRound
    // and the latest sender was the secondary feed.
    if (epochAndRound != hotVars.latestEpochAndRound || !hotVars.isLatestSecondary) {
      // In case the epochAndRound is lower or equal than the latestEpochAndRound, it's a stale report
      // because it's older or has already been transmitted.
      if (epochAndRound <= hotVars.latestEpochAndRound) {
        revert StaleReport();
      }

      // Verify signatures attached to report.
      _verifySignatures(reportContext, report, rs, ss, rawVs);

      _report(hotVars, reportContext[0], epochAndRound, report_, isSecondary);
    } else {
      // If the report is the same and the latest sender was the secondary feed,
      // we're effectively unlocking the primary feed with this
      emit PrimaryFeedUnlocked(s_hotVars.latestAggregatorRoundId);
    }

    // Store if the latest report was secondary or not.
    s_hotVars.isLatestSecondary = isSecondary;
    _payTransmitter(hotVars, report_.juelsPerFeeCoin, uint32(initialGas));
  }

  /// @notice helper function to validate the report data.
  /// @param reportContext serialized report context containing configDigest, epoch, round and extraHash.
  /// @param reportLength the length of the serialized report.
  /// @param rsLength the length of the rs signatures.
  /// @param ssLength the length of the ss signatures.
  function _validateReport(
    bytes32[3] calldata reportContext,
    uint256 reportLength,
    uint256 rsLength,
    uint256 ssLength
  ) internal view {
    if (!s_transmitters[msg.sender].active) {
      revert UnauthorizedTransmitter();
    }

    if (s_latestConfigDigest != reportContext[0]) {
      revert ConfigDigestMismatch();
    }

    _requireExpectedMsgDataLength(reportLength, rsLength, ssLength);

    if (rsLength != s_hotVars.f + 1) {
      revert WrongNumberOfSignatures();
    }

    if (rsLength != ssLength) {
      revert SignaturesOutOfRegistration();
    }
  }

  /// @notice helper function to verify the report signatures.
  /// @param reportContext serialized report context containing configDigest, epoch, round and extraHash.
  /// @param report serialized report, which the signatures are signing.
  /// @param rs i-th element is the R components of the i-th signature on report. Must have at most maxNumOracles entries.
  /// @param ss i-th element is the S components of the i-th signature on report. Must have at most maxNumOracles entries.
  /// @param rawVs i-th element is the the V component of the i-th signature.
  function _verifySignatures(
    bytes32[3] calldata reportContext,
    bytes calldata report,
    bytes32[] calldata rs,
    bytes32[] calldata ss,
    bytes32 rawVs
  ) internal view {
    bytes32 h = keccak256(abi.encode(keccak256(report), reportContext));

    // i-th byte counts number of sigs made by i-th signer.
    uint256 signedCount = 0;

    Signer memory signer;
    for (uint256 i = 0; i < rs.length; ++i) {
      address signerAddress = ecrecover(h, uint8(rawVs[i]) + 27, rs[i], ss[i]);
      signer = s_signers[signerAddress];
      if (!signer.active) {
        revert SignatureError();
      }
      unchecked {
        signedCount += 1 << (8 * signer.index);
      }
    }

    // The first byte of the mask can be 0, because we only ever have 31 oracles.
    if (signedCount & 0x0001010101010101010101010101010101010101010101010101010101010101 != signedCount) {
      revert DuplicateSigner();
    }
  }

  /// @notice details about the most recent report.
  /// @return configDigest domain separation tag for the latest report.
  /// @return epoch epoch in which the latest report was generated.
  /// @return round OCR round in which the latest report was generated.
  /// @return latestAnswer_ median value from latest report.
  /// @return latestTimestamp_ when the latest report was transmitted.
  function latestTransmissionDetails()
    external
    view
    returns (bytes32 configDigest, uint32 epoch, uint8 round, int192 latestAnswer_, uint64 latestTimestamp_)
  {
    // solhint-disable-next-line avoid-tx-origin
    if (msg.sender != tx.origin) revert OnlyCallableByEOA();
    return (
      s_latestConfigDigest,
      uint32(s_hotVars.latestEpochAndRound >> 8),
      uint8(s_hotVars.latestEpochAndRound),
      s_transmissions[s_hotVars.latestAggregatorRoundId].answer,
      s_transmissions[s_hotVars.latestAggregatorRoundId].recordedTimestamp
    );
  }

  /// @inheritdoc OCR2Abstract
  function latestConfigDigestAndEpoch()
    external
    view
    virtual
    override
    returns (bool scanLogs, bytes32 configDigest, uint32 epoch)
  {
    return (false, s_latestConfigDigest, uint32(s_hotVars.latestEpochAndRound >> 8));
  }

  /// @notice evaluate the serialized report length and compare it with the expected length.
  /// @param report serialized report, which the signatures are signing.
  /// @param observations decoded observations from the report.
  function _requireExpectedReportLength(bytes memory report, int192[] memory observations) private pure {
    uint256 expected = 32 // ObservationsTimestamp.
      + 32 // RawObservers.
      + 32 // Observations offset.
      + 32 // JuelsPerFeeCoin.
      + 32 // Observations length.
      + 32 * observations.length // Observations payload.
      + 0;
    if (report.length != expected) revert ReportLengthMismatch();
  }

  /// @notice report a new transmission and emit the necessary events.
  /// @param hotVars most common fields used in the hot path.
  /// @param configDigest digested configuration.
  /// @param epochAndRound report epoch and round.
  /// @param report decoded report in Report struct format.
  /// @param isSecondary whether the report was sent by the secondary proxy or not.
  function _report(
    HotVars memory hotVars,
    bytes32 configDigest,
    uint40 epochAndRound,
    Report memory report,
    bool isSecondary
  ) internal {
    if (report.observations.length > MAX_NUM_ORACLES) revert NumObservationsOutOfBounds();
    // Offchain logic ensures that a quorum of oracles is operating on a matching set of at least
    // 2f+1 observations. By assumption, up to f of those can be faulty, which includes being
    // malformed. Conversely, more than f observations have to be well-formed and sent on chain.
    if (report.observations.length <= hotVars.f) revert TooFewValuesToTrustMedian();

    hotVars.latestEpochAndRound = epochAndRound;

    // Get median, validate its range, store it in new aggregator round.
    int192 median = report.observations[report.observations.length / 2];
    if (i_minAnswer > median || median > i_maxAnswer) revert MedianIsOutOfMinMaxRange();

    hotVars.latestAggregatorRoundId++;
    s_transmissions[hotVars.latestAggregatorRoundId] = Transmission({
      answer: median,
      observationsTimestamp: report.observationsTimestamp,
      recordedTimestamp: uint32(block.timestamp)
    });

    // In case the sender is the secondary proxy, update the latest secondary round id.
    if (isSecondary) {
      hotVars.latestSecondaryRoundId = hotVars.latestAggregatorRoundId;
      emit SecondaryRoundIdUpdated(hotVars.latestSecondaryRoundId);
    }

    // Persist updates to hotVars.
    s_hotVars = hotVars;

    emit NewTransmission(
      hotVars.latestAggregatorRoundId,
      median,
      msg.sender,
      report.observationsTimestamp,
      report.observations,
      report.observers,
      report.juelsPerFeeCoin,
      configDigest,
      epochAndRound
    );
    // Emit these for backwards compatibility with offchain consumers
    // that only support legacy events.
    emit NewRound(
      hotVars.latestAggregatorRoundId,
      address(0x0), // Use zero address since we don't have anybody "starting" the round here.
      report.observationsTimestamp
    );
    emit AnswerUpdated(median, hotVars.latestAggregatorRoundId, block.timestamp);

    _validateAnswer(hotVars.latestAggregatorRoundId, median);
  }

  // ================================================================
  // │                   v2 AggregatorInterface                     │
  // ================================================================

  /// @notice median from the most recent report.
  /// @return answer the latest answer.
  function latestAnswer() public view virtual override returns (int256) {
    return s_transmissions[_getLatestRound()].answer;
  }

  /// @notice timestamp of block in which last report was transmitted.
  /// @return recordedTimestamp the latest recorded timestamp.
  function latestTimestamp() public view virtual override returns (uint256) {
    return s_transmissions[_getLatestRound()].recordedTimestamp;
  }

  /// @notice Aggregator round (NOT OCR round) in which last report was transmitted.
  /// @return roundId the latest round id.
  function latestRound() public view virtual override returns (uint256) {
    return _getLatestRound();
  }

  /// @notice median of report from given aggregator round (NOT OCR round).
  /// @param roundId the aggregator round of the target report.
  /// @return answer the answer of the round id.
  function getAnswer(
    uint256 roundId
  ) public view virtual override returns (int256) {
    if (roundId > _getLatestRound()) return 0;
    return s_transmissions[uint32(roundId)].answer;
  }

  /// @notice timestamp of block in which report from given aggregator round was transmitted.
  /// @param roundId aggregator round (NOT OCR round) of target report.
  /// @return recordedTimestamp the recorded timestamp of the round id.
  function getTimestamp(
    uint256 roundId
  ) public view virtual override returns (uint256) {
    if (roundId > _getLatestRound()) return 0;
    return s_transmissions[uint32(roundId)].recordedTimestamp;
  }

  // ================================================================
  // │                   v3 AggregatorInterface                     │
  // ================================================================

  error RoundNotFound();

  /// @notice amount of decimals.
  uint8 private immutable i_decimals;

  /// @notice aggregator contract version.
  uint256 internal constant VERSION = 6;

  /// @notice human readable description.
  string internal s_description;

  /// @notice get the amount of decimals.
  /// @return i_decimals amount of decimals.
  function decimals() public view virtual override returns (uint8) {
    return i_decimals;
  }

  /// @notice get the contract version.
  /// @return VERSION the contract version.
  function version() public view virtual override returns (uint256) {
    return VERSION;
  }

  /// @notice human-readable description of observable this contract is reporting on.
  /// @return s_description the contract description.
  function description() public view virtual override returns (string memory) {
    return s_description;
  }

  /// @notice details for the given aggregator round.
  /// @param roundId target aggregator round, must fit in uint32.
  /// @return roundId_ roundId.
  /// @return answer median of report from given roundId.
  /// @return startedAt timestamp of when observations were made offchain.
  /// @return updatedAt timestamp of block in which report from given roundId was transmitted.
  /// @return answeredInRound roundId.
  function getRoundData(
    uint80 roundId
  )
    public
    view
    virtual
    override
    returns (uint80 roundId_, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound)
  {
    if (roundId > _getLatestRound()) return (0, 0, 0, 0, 0);
    Transmission memory transmission = s_transmissions[uint32(roundId)];

    return (roundId, transmission.answer, transmission.observationsTimestamp, transmission.recordedTimestamp, roundId);
  }

  /// @notice aggregator details for the most recently transmitted report.
  /// @return roundId aggregator round of latest report (NOT OCR round).
  /// @return answer median of latest report.
  /// @return startedAt timestamp of when observations were made offchain.
  /// @return updatedAt timestamp of block containing latest report.
  /// @return answeredInRound aggregator round of latest report.
  function latestRoundData()
    public
    view
    virtual
    override
    returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound)
  {
    uint80 latestRoundId = _getLatestRound();
    Transmission memory transmission = s_transmissions[uint32(latestRoundId)];

    return (
      latestRoundId,
      transmission.answer,
      transmission.observationsTimestamp,
      transmission.recordedTimestamp,
      latestRoundId
    );
  }

  // ================================================================
  // │                  Configurable LINK Token                     │
  // ================================================================

  /// @notice emitted when the LINK token contract is set.
  /// @param oldLinkToken the address of the old LINK token contract.
  /// @param newLinkToken the address of the new LINK token contract.
  event LinkTokenSet(LinkTokenInterface indexed oldLinkToken, LinkTokenInterface indexed newLinkToken);

  error TransferRemainingFundsFailed();

  /// @notice we assume that the token contract is correct. This contract is not written
  /// to handle misbehaving ERC20 tokens!
  LinkTokenInterface internal s_linkToken;

  /// @notice sets the LINK token contract used for paying oracles.
  /// @dev this function will return early (without an error) without changing any state
  /// if linkToken equals getLinkToken().
  /// @dev this will trigger a payout so that a malicious owner cannot take from oracles
  /// what is already owed to them.
  /// @dev we assume that the token contract is correct. This contract is not written
  /// to handle misbehaving ERC20 tokens!
  /// @param linkToken the address of the LINK token contract.
  /// @param recipient remaining funds from the previous token contract are transferred
  /// here.
  function setLinkToken(LinkTokenInterface linkToken, address recipient) external onlyOwner {
    LinkTokenInterface oldLinkToken = s_linkToken;
    if (linkToken == oldLinkToken) {
      // No change, nothing to be done.
      return;
    }
    // Call balanceOf as a sanity check on whether we're talking to a token
    // contract.
    linkToken.balanceOf(address(this));
    // We break CEI here, but that's okay because we're dealing with a correct
    // token contract (by assumption).
    _payOracles();
    uint256 remainingBalance = oldLinkToken.balanceOf(address(this));
    if (!oldLinkToken.transfer(recipient, remainingBalance)) revert TransferRemainingFundsFailed();
    // solhint-disable-next-line reentrancy
    s_linkToken = linkToken;
    emit LinkTokenSet(oldLinkToken, linkToken);
  }

  /// @notice gets the LINK token contract used for paying oracles.
  /// @return linkToken the address of the LINK token contract.
  function getLinkToken() external view returns (LinkTokenInterface linkToken) {
    return s_linkToken;
  }

  // ================================================================
  // │             BillingAccessController Management               │
  // ================================================================

  /// @notice emitted when a new access-control contract is set.
  /// @param old the address prior to the current setting.
  /// @param current the address of the new access-control contract.
  event BillingAccessControllerSet(AccessControllerInterface old, AccessControllerInterface current);

  /// @notice controls who can change billing parameters. A billingAdmin is not able to
  /// affect any OCR protocol settings and therefore cannot tamper with the
  /// liveness or integrity of a data feed. However, a billingAdmin can set
  /// faulty billing parameters causing oracles to be underpaid, or causing them
  /// to be paid so much that further calls to setConfig, setBilling,
  /// setLinkToken will always fail due to the contract being underfunded.
  AccessControllerInterface internal s_billingAccessController;

  /// @notice internal function to set a new billingAccessController.
  /// @param billingAccessController new billingAccessController contract address.
  function _setBillingAccessController(
    AccessControllerInterface billingAccessController
  ) internal {
    AccessControllerInterface oldController = s_billingAccessController;
    if (billingAccessController != oldController) {
      s_billingAccessController = billingAccessController;
      emit BillingAccessControllerSet(oldController, billingAccessController);
    }
  }

  /// @notice sets billingAccessController.
  /// @param _billingAccessController new...

// [truncated — 71245 bytes total]
AccessControllerInterface.sol 7 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

// solhint-disable-next-line interface-starts-with-i
interface AccessControllerInterface {
  function hasAccess(address user, bytes calldata data) external view returns (bool);
}
AggregatorV2V3Interface.sol 8 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {AggregatorInterface} from "./AggregatorInterface.sol";
import {AggregatorV3Interface} from "./AggregatorV3Interface.sol";

// solhint-disable-next-line interface-starts-with-i
interface AggregatorV2V3Interface is AggregatorInterface, AggregatorV3Interface {}
AggregatorValidatorInterface.sol 12 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

// solhint-disable-next-line interface-starts-with-i
interface AggregatorValidatorInterface {
  function validate(
    uint256 previousRoundId,
    int256 previousAnswer,
    uint256 currentRoundId,
    int256 currentAnswer
  ) external returns (bool);
}
LinkTokenInterface.sol 29 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

// solhint-disable-next-line interface-starts-with-i
interface LinkTokenInterface {
  function allowance(address owner, address spender) external view returns (uint256 remaining);

  function approve(address spender, uint256 value) external returns (bool success);

  function balanceOf(address owner) external view returns (uint256 balance);

  function decimals() external view returns (uint8 decimalPlaces);

  function decreaseApproval(address spender, uint256 addedValue) external returns (bool success);

  function increaseApproval(address spender, uint256 subtractedValue) external;

  function name() external view returns (string memory tokenName);

  function symbol() external view returns (string memory tokenSymbol);

  function totalSupply() external view returns (uint256 totalTokensIssued);

  function transfer(address to, uint256 value) external returns (bool success);

  function transferAndCall(address to, uint256 value, bytes calldata data) external returns (bool success);

  function transferFrom(address from, address to, uint256 value) external returns (bool success);
}
SimpleReadAccessController.sol 22 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {SimpleWriteAccessController} from "./SimpleWriteAccessController.sol";

/// @title SimpleReadAccessController
/// @notice Gives access to:
/// - any externally owned account (note that off-chain actors can always read
/// any contract storage regardless of on-chain access control measures, so this
/// does not weaken the access control while improving usability)
/// - accounts explicitly added to an access list
/// @dev SimpleReadAccessController is not suitable for access controlling writes
/// since it grants any externally owned account access! See
/// SimpleWriteAccessController for that.
contract SimpleReadAccessController is SimpleWriteAccessController {
  /// @notice Returns the access of an address
  /// @param _user The address to query
  function hasAccess(address _user, bytes memory _calldata) public view virtual override returns (bool) {
    // solhint-disable-next-line avoid-tx-origin
    return super.hasAccess(_user, _calldata) || _user == tx.origin;
  }
}
CallWithExactGas.sol 162 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/// @notice This library contains various callWithExactGas functions. All of them are
/// safe from gas bomb attacks.
/// @dev There is code duplication in this library. This is done to not leave the assembly
/// the blocks.
library CallWithExactGas {
  error NoContract();
  error NoGasForCallExactCheck();
  error NotEnoughGasForCall();

  bytes4 internal constant NO_CONTRACT_SIG = 0x0c3b563c;
  bytes4 internal constant NO_GAS_FOR_CALL_EXACT_CHECK_SIG = 0xafa32a2c;
  bytes4 internal constant NOT_ENOUGH_GAS_FOR_CALL_SIG = 0x37c3be29;

  /// @notice calls target address with exactly gasAmount gas and payload as calldata.
  /// Accounts for gasForCallExactCheck gas that will be used by this function. Will revert
  /// if the target is not a contact. Will revert when there is not enough gas to call the
  /// target with gasAmount gas.
  /// @dev Ignores the return data, which makes it immune to gas bomb attacks.
  /// @return success whether the call succeeded
  function _callWithExactGas(
    bytes memory payload,
    address target,
    uint256 gasLimit,
    uint16 gasForCallExactCheck
  ) internal returns (bool success) {
    assembly {
      // solidity calls check that a contract actually exists at the destination, so we do the same
      // Note we do this check prior to measuring gas so gasForCallExactCheck (our "cushion")
      // doesn't need to account for it.
      if iszero(extcodesize(target)) {
        mstore(0x0, NO_CONTRACT_SIG)
        revert(0x0, 0x4)
      }

      let g := gas()
      // Compute g -= gasForCallExactCheck and check for underflow
      // The gas actually passed to the callee is _min(gasAmount, 63//64*gas available).
      // We want to ensure that we revert if gasAmount >  63//64*gas available
      // as we do not want to provide them with less, however that check itself costs
      // gas. gasForCallExactCheck ensures we have at least enough gas to be able
      // to revert if gasAmount >  63//64*gas available.
      if lt(g, gasForCallExactCheck) {
        mstore(0x0, NO_GAS_FOR_CALL_EXACT_CHECK_SIG)
        revert(0x0, 0x4)
      }
      g := sub(g, gasForCallExactCheck)
      // if g - g//64 <= gasAmount, revert. We subtract g//64 because of EIP-150
      if iszero(gt(sub(g, div(g, 64)), gasLimit)) {
        mstore(0x0, NOT_ENOUGH_GAS_FOR_CALL_SIG)
        revert(0x0, 0x4)
      }

      // call and return whether we succeeded. ignore return data
      // call(gas,addr,value,argsOffset,argsLength,retOffset,retLength)
      success := call(gasLimit, target, 0, add(payload, 0x20), mload(payload), 0x0, 0x0)
    }
    return success;
  }

  /// @notice calls target address with exactly gasAmount gas and payload as calldata.
  /// Account for gasForCallExactCheck gas that will be used by this function. Will revert
  /// if the target is not a contact. Will revert when there is not enough gas to call the
  /// target with gasAmount gas.
  /// @dev Caps the return data length, which makes it immune to gas bomb attacks.
  /// @dev Return data cap logic borrowed from
  /// https://github.com/nomad-xyz/ExcessivelySafeCall/blob/main/src/ExcessivelySafeCall.sol.
  /// @return success whether the call succeeded
  /// @return retData the return data from the call, capped at maxReturnBytes bytes
  /// @return gasUsed the gas used by the external call. Does not include the overhead of this function.
  function _callWithExactGasSafeReturnData(
    bytes memory payload,
    address target,
    uint256 gasLimit,
    uint16 gasForCallExactCheck,
    uint16 maxReturnBytes
  ) internal returns (bool success, bytes memory retData, uint256 gasUsed) {
    // allocate retData memory ahead of time
    retData = new bytes(maxReturnBytes);

    assembly {
      // solidity calls check that a contract actually exists at the destination, so we do the same
      // Note we do this check prior to measuring gas so gasForCallExactCheck (our "cushion")
      // doesn't need to account for it.
      if iszero(extcodesize(target)) {
        mstore(0x0, NO_CONTRACT_SIG)
        revert(0x0, 0x4)
      }

      let g := gas()
      // Compute g -= gasForCallExactCheck and check for underflow
      // The gas actually passed to the callee is _min(gasAmount, 63//64*gas available).
      // We want to ensure that we revert if gasAmount >  63//64*gas available
      // as we do not want to provide them with less, however that check itself costs
      // gas. gasForCallExactCheck ensures we have at least enough gas to be able
      // to revert if gasAmount >  63//64*gas available.
      if lt(g, gasForCallExactCheck) {
        mstore(0x0, NO_GAS_FOR_CALL_EXACT_CHECK_SIG)
        revert(0x0, 0x4)
      }
      g := sub(g, gasForCallExactCheck)
      // if g - g//64 <= gasAmount, revert. We subtract g//64 because of EIP-150
      if iszero(gt(sub(g, div(g, 64)), gasLimit)) {
        mstore(0x0, NOT_ENOUGH_GAS_FOR_CALL_SIG)
        revert(0x0, 0x4)
      }

      // We save the gas before the call so we can calculate how much gas the call used
      let gasBeforeCall := gas()
      // call and return whether we succeeded. ignore return data
      // call(gas,addr,value,argsOffset,argsLength,retOffset,retLength)
      success := call(gasLimit, target, 0, add(payload, 0x20), mload(payload), 0x0, 0x0)
      gasUsed := sub(gasBeforeCall, gas())

      // limit our copy to maxReturnBytes bytes
      let toCopy := returndatasize()
      if gt(toCopy, maxReturnBytes) {
        toCopy := maxReturnBytes
      }
      // Store the length of the copied bytes
      mstore(retData, toCopy)
      // copy the bytes from retData[0:_toCopy]
      returndatacopy(add(retData, 0x20), 0x0, toCopy)
    }
    return (success, retData, gasUsed);
  }

  /// @notice Calls target address with exactly gasAmount gas and payload as calldata
  /// or reverts if at least gasLimit gas is not available.
  /// @dev Does not check if target is a contract. If it is not a contract, the low-level
  /// call will still be made and it will succeed.
  /// @dev Ignores the return data, which makes it immune to gas bomb attacks.
  /// @return success whether the call succeeded
  /// @return sufficientGas Whether there was enough gas to make the call
  function _callWithExactGasEvenIfTargetIsNoContract(
    bytes memory payload,
    address target,
    uint256 gasLimit,
    uint16 gasForCallExactCheck
  ) internal returns (bool success, bool sufficientGas) {
    assembly {
      let g := gas()
      // Compute g -= CALL_WITH_EXACT_GAS_CUSHION and check for underflow. We
      // need the cushion since the logic following the above call to gas also
      // costs gas which we cannot account for exactly. So cushion is a
      // conservative upper bound for the cost of this logic.
      if iszero(lt(g, gasForCallExactCheck)) {
        g := sub(g, gasForCallExactCheck)
        // If g - g//64 <= gasAmount, we don't have enough gas. We subtract g//64 because of EIP-150.
        if gt(sub(g, div(g, 64)), gasLimit) {
          // Call and ignore success/return data. Note that we did not check
          // whether a contract actually exists at the target address.
          success := call(gasLimit, target, 0, add(payload, 0x20), mload(payload), 0x0, 0x0)
          sufficientGas := true
        }
      }
    }
    return (success, sufficientGas);
  }
}
OCR2Abstract.sol 122 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {ITypeAndVersion} from "../interfaces/ITypeAndVersion.sol";

abstract contract OCR2Abstract is ITypeAndVersion {
  uint256 internal constant MAX_NUM_ORACLES = 31;
  uint256 private constant PREFIX_MASK = type(uint256).max << (256 - 16); // 0xFFFF00..00
  uint256 private constant PREFIX = 0x0001 << (256 - 16); // 0x000100..00

  /// @notice triggers a new run of the offchain reporting protocol
  /// @param previousConfigBlockNumber block in which the previous config was set, to simplify historic analysis
  /// @param configDigest configDigest of this configuration
  /// @param configCount ordinal number of this config setting among all config settings over the life of this contract
  /// @param signers ith element is address ith oracle uses to sign a report
  /// @param transmitters ith element is address ith oracle uses to transmit a report via the transmit method
  /// @param f maximum number of faulty/dishonest oracles the protocol can tolerate while still working correctly
  /// @param onchainConfig serialized configuration used by the contract (and possibly oracles)
  /// @param offchainConfigVersion version of the serialization format used for "offchainConfig" parameter
  /// @param offchainConfig serialized configuration used by the oracles exclusively and only passed through the contract
  event ConfigSet(
    uint32 previousConfigBlockNumber,
    bytes32 configDigest,
    uint64 configCount,
    address[] signers,
    address[] transmitters,
    uint8 f,
    bytes onchainConfig,
    uint64 offchainConfigVersion,
    bytes offchainConfig
  );

  /// @notice sets offchain reporting protocol configuration incl. participating oracles
  /// @param signers addresses with which oracles sign the reports
  /// @param transmitters addresses oracles use to transmit the reports
  /// @param f number of faulty oracles the system can tolerate
  /// @param onchainConfig serialized configuration used by the contract (and possibly oracles)
  /// @param offchainConfigVersion version number for offchainEncoding schema
  /// @param offchainConfig serialized configuration used by the oracles exclusively and only passed through the contract
  function setConfig(
    address[] memory signers,
    address[] memory transmitters,
    uint8 f,
    bytes memory onchainConfig,
    uint64 offchainConfigVersion,
    bytes memory offchainConfig
  ) external virtual;

  /// @notice information about current offchain reporting protocol configuration
  /// @return configCount ordinal number of current config, out of all configs applied to this contract so far
  /// @return blockNumber block at which this config was set
  /// @return configDigest domain-separation tag for current config (see _configDigestFromConfigData)
  function latestConfigDetails()
    external
    view
    virtual
    returns (uint32 configCount, uint32 blockNumber, bytes32 configDigest);

  function _configDigestFromConfigData(
    uint256 chainId,
    address contractAddress,
    uint64 configCount,
    address[] memory signers,
    address[] memory transmitters,
    uint8 f,
    bytes memory onchainConfig,
    uint64 offchainConfigVersion,
    bytes memory offchainConfig
  ) internal pure returns (bytes32) {
    uint256 h = uint256(
      keccak256(
        abi.encode(
          chainId,
          contractAddress,
          configCount,
          signers,
          transmitters,
          f,
          onchainConfig,
          offchainConfigVersion,
          offchainConfig
        )
      )
    );
    return bytes32((PREFIX & PREFIX_MASK) | (h & ~PREFIX_MASK));
  }

  /// @notice optionally emitted to indicate the latest configDigest and epoch for
  /// which a report was successfully transmitted. Alternatively, the contract may
  /// use latestConfigDigestAndEpoch with scanLogs set to false.
  event Transmitted(bytes32 configDigest, uint32 epoch);

  /// @notice optionally returns the latest configDigest and epoch for which a
  /// report was successfully transmitted. Alternatively, the contract may return
  /// scanLogs set to true and use Transmitted events to provide this information
  /// to offchain watchers.
  /// @return scanLogs indicates whether to rely on the configDigest and epoch
  /// returned or whether to scan logs for the Transmitted event instead.
  /// @return configDigest
  /// @return epoch
  function latestConfigDigestAndEpoch()
    external
    view
    virtual
    returns (bool scanLogs, bytes32 configDigest, uint32 epoch);

  /// @notice transmit is called to post a new report to the contract
  /// @param reportContext [0]: ConfigDigest, [1]: 27 byte padding, 4-byte epoch and 1-byte round, [2]: ExtraHash
  /// @param report serialized report, which the signatures are signing.
  /// @param rs ith element is the R components of the ith signature on report. Must have at most MAX_NUM_ORACLES entries
  /// @param ss ith element is the S components of the ith signature on report. Must have at most MAX_NUM_ORACLES entries
  /// @param rawVs ith element is the the V component of the ith signature
  function transmit(
    // NOTE: If these parameters are changed, expectedMsgDataLength and/or
    // TRANSMIT_MSGDATA_CONSTANT_LENGTH_COMPONENT need to be changed accordingly
    bytes32[3] calldata reportContext,
    bytes calldata report,
    bytes32[] calldata rs,
    bytes32[] calldata ss,
    bytes32 rawVs // signatures
  ) external virtual;
}
AggregatorInterface.sol 19 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

// solhint-disable-next-line interface-starts-with-i
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 20 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

// solhint-disable-next-line interface-starts-with-i
interface AggregatorV3Interface {
  function decimals() external view returns (uint8);

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

  function version() external view returns (uint256);

  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);
}
SimpleWriteAccessController.sol 73 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {ConfirmedOwner} from "./ConfirmedOwner.sol";
import {AccessControllerInterface} from "../interfaces/AccessControllerInterface.sol";

/// @title SimpleWriteAccessController
/// @notice Gives access to accounts explicitly added to an access list by the controller's owner.
/// @dev does not make any special permissions for externally, see  SimpleReadAccessController for that.
contract SimpleWriteAccessController is AccessControllerInterface, ConfirmedOwner {
  bool public checkEnabled;
  mapping(address => bool) internal s_accessList;

  event AddedAccess(address user);
  event RemovedAccess(address user);
  event CheckAccessEnabled();
  event CheckAccessDisabled();

  constructor() ConfirmedOwner(msg.sender) {
    checkEnabled = true;
  }

  /// @notice Returns the access of an address
  /// @param _user The address to query
  function hasAccess(address _user, bytes memory) public view virtual override returns (bool) {
    return s_accessList[_user] || !checkEnabled;
  }

  /// @notice Adds an address to the access list
  /// @param _user The address to add
  function addAccess(address _user) external onlyOwner {
    if (!s_accessList[_user]) {
      s_accessList[_user] = true;

      emit AddedAccess(_user);
    }
  }

  /// @notice Removes an address from the access list
  /// @param _user The address to remove
  function removeAccess(address _user) external onlyOwner {
    if (s_accessList[_user]) {
      s_accessList[_user] = false;

      emit RemovedAccess(_user);
    }
  }

  /// @notice makes the access check enforced
  function enableAccessCheck() external onlyOwner {
    if (!checkEnabled) {
      checkEnabled = true;

      emit CheckAccessEnabled();
    }
  }

  /// @notice makes the access check unenforced
  function disableAccessCheck() external onlyOwner {
    if (checkEnabled) {
      checkEnabled = false;

      emit CheckAccessDisabled();
    }
  }

  /// @dev reverts if the caller does not have access
  modifier checkAccess() {
    // solhint-disable-next-line gas-custom-errors
    require(hasAccess(msg.sender, msg.data), "No access");
    _;
  }
}
ITypeAndVersion.sol 6 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface ITypeAndVersion {
  function typeAndVersion() external pure returns (string memory);
}
ConfirmedOwner.sol 10 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {ConfirmedOwnerWithProposal} from "./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)) {}
}
ConfirmedOwnerWithProposal.sol 68 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {IOwnable} from "../interfaces/IOwnable.sol";

/// @title The ConfirmedOwner contract
/// @notice A contract with helpers for basic contract ownership.
contract ConfirmedOwnerWithProposal is IOwnable {
  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 newOwner, address pendingOwner) {
    // solhint-disable-next-line gas-custom-errors
    require(newOwner != address(0), "Cannot set owner to zero");

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

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

  /// @notice Allows an ownership transfer to be completed by the recipient.
  function acceptOwnership() external override {
    // solhint-disable-next-line gas-custom-errors
    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 {
    // solhint-disable-next-line gas-custom-errors
    require(to != msg.sender, "Cannot transfer to self");

    s_pendingOwner = to;

    emit OwnershipTransferRequested(s_owner, to);
  }

  /// @notice validate access
  function _validateOwnership() internal view {
    // solhint-disable-next-line gas-custom-errors
    require(msg.sender == s_owner, "Only callable by owner");
  }

  /// @notice Reverts if called by anyone other than the contract owner.
  modifier onlyOwner() {
    _validateOwnership();
    _;
  }
}
IOwnable.sol 10 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

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

  function transferOwnership(address recipient) external;

  function acceptOwnership() external;
}
DualAggregatorHelper.sol 71 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;

import {AccessControllerInterface} from "@chainlink/contracts/src/v0.8/shared/interfaces/AccessControllerInterface.sol";
import {LinkTokenInterface} from "@chainlink/contracts/src/v0.8/shared/interfaces/LinkTokenInterface.sol";

import {DualAggregator} from "src/DualAggregator.sol";

contract DualAggregatorHelper is DualAggregator {
  constructor(
    LinkTokenInterface link,
    int192 minAnswer_,
    int192 maxAnswer_,
    AccessControllerInterface billingAccessController,
    AccessControllerInterface requesterAccessController,
    uint8 decimals_,
    string memory description_,
    address secondaryProxy_,
    uint32 cutoffTime_,
    uint32 maxSyncIterations_
  )
    DualAggregator(
      link,
      minAnswer_,
      maxAnswer_,
      billingAccessController,
      requesterAccessController,
      decimals_,
      description_,
      secondaryProxy_,
      cutoffTime_,
      maxSyncIterations_
    )
  {}

  function getSyncPrimaryRound() public view returns (uint32 roundId) {
    return _getSyncPrimaryRound();
  }

  function configDigestFromConfigData(
    uint256 chainId,
    address contractAddress,
    uint64 configCount,
    address[] memory signers,
    address[] memory transmitters,
    uint8 f,
    bytes memory onchainConfig,
    uint64 offchainConfigVersion,
    bytes memory offchainConfig
  ) public pure returns (bytes32) {
    return _configDigestFromConfigData(
      chainId,
      contractAddress,
      configCount,
      signers,
      transmitters,
      f,
      onchainConfig,
      offchainConfigVersion,
      offchainConfig
    );
  }

  function totalLinkDue() public view returns (uint256 linkDue) {
    return _totalLinkDue();
  }

  function getHotVars() public view returns (HotVars memory) {
    return s_hotVars;
  }
}
SimpleReadAccessController.sol 22 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {SimpleWriteAccessController} from "./SimpleWriteAccessController.sol";

/// @title SimpleReadAccessController
/// @notice Gives access to:
/// - any externally owned account (note that off-chain actors can always read
/// any contract storage regardless of on-chain access control measures, so this
/// does not weaken the access control while improving usability)
/// - accounts explicitly added to an access list
/// @dev SimpleReadAccessController is not suitable for access controlling writes
/// since it grants any externally owned account access! See
/// SimpleWriteAccessController for that.
contract SimpleReadAccessController is SimpleWriteAccessController {
  /// @notice Returns the access of an address
  /// @param _user The address to query
  function hasAccess(address _user, bytes memory _calldata) public view virtual override returns (bool) {
    // solhint-disable-next-line avoid-tx-origin
    return super.hasAccess(_user, _calldata) || _user == tx.origin;
  }
}
CallWithExactGas.sol 162 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/// @notice This library contains various callWithExactGas functions. All of them are
/// safe from gas bomb attacks.
/// @dev There is code duplication in this library. This is done to not leave the assembly
/// the blocks.
library CallWithExactGas {
  error NoContract();
  error NoGasForCallExactCheck();
  error NotEnoughGasForCall();

  bytes4 internal constant NO_CONTRACT_SIG = 0x0c3b563c;
  bytes4 internal constant NO_GAS_FOR_CALL_EXACT_CHECK_SIG = 0xafa32a2c;
  bytes4 internal constant NOT_ENOUGH_GAS_FOR_CALL_SIG = 0x37c3be29;

  /// @notice calls target address with exactly gasAmount gas and payload as calldata.
  /// Accounts for gasForCallExactCheck gas that will be used by this function. Will revert
  /// if the target is not a contact. Will revert when there is not enough gas to call the
  /// target with gasAmount gas.
  /// @dev Ignores the return data, which makes it immune to gas bomb attacks.
  /// @return success whether the call succeeded
  function _callWithExactGas(
    bytes memory payload,
    address target,
    uint256 gasLimit,
    uint16 gasForCallExactCheck
  ) internal returns (bool success) {
    assembly {
      // solidity calls check that a contract actually exists at the destination, so we do the same
      // Note we do this check prior to measuring gas so gasForCallExactCheck (our "cushion")
      // doesn't need to account for it.
      if iszero(extcodesize(target)) {
        mstore(0x0, NO_CONTRACT_SIG)
        revert(0x0, 0x4)
      }

      let g := gas()
      // Compute g -= gasForCallExactCheck and check for underflow
      // The gas actually passed to the callee is _min(gasAmount, 63//64*gas available).
      // We want to ensure that we revert if gasAmount >  63//64*gas available
      // as we do not want to provide them with less, however that check itself costs
      // gas. gasForCallExactCheck ensures we have at least enough gas to be able
      // to revert if gasAmount >  63//64*gas available.
      if lt(g, gasForCallExactCheck) {
        mstore(0x0, NO_GAS_FOR_CALL_EXACT_CHECK_SIG)
        revert(0x0, 0x4)
      }
      g := sub(g, gasForCallExactCheck)
      // if g - g//64 <= gasAmount, revert. We subtract g//64 because of EIP-150
      if iszero(gt(sub(g, div(g, 64)), gasLimit)) {
        mstore(0x0, NOT_ENOUGH_GAS_FOR_CALL_SIG)
        revert(0x0, 0x4)
      }

      // call and return whether we succeeded. ignore return data
      // call(gas,addr,value,argsOffset,argsLength,retOffset,retLength)
      success := call(gasLimit, target, 0, add(payload, 0x20), mload(payload), 0x0, 0x0)
    }
    return success;
  }

  /// @notice calls target address with exactly gasAmount gas and payload as calldata.
  /// Account for gasForCallExactCheck gas that will be used by this function. Will revert
  /// if the target is not a contact. Will revert when there is not enough gas to call the
  /// target with gasAmount gas.
  /// @dev Caps the return data length, which makes it immune to gas bomb attacks.
  /// @dev Return data cap logic borrowed from
  /// https://github.com/nomad-xyz/ExcessivelySafeCall/blob/main/src/ExcessivelySafeCall.sol.
  /// @return success whether the call succeeded
  /// @return retData the return data from the call, capped at maxReturnBytes bytes
  /// @return gasUsed the gas used by the external call. Does not include the overhead of this function.
  function _callWithExactGasSafeReturnData(
    bytes memory payload,
    address target,
    uint256 gasLimit,
    uint16 gasForCallExactCheck,
    uint16 maxReturnBytes
  ) internal returns (bool success, bytes memory retData, uint256 gasUsed) {
    // allocate retData memory ahead of time
    retData = new bytes(maxReturnBytes);

    assembly {
      // solidity calls check that a contract actually exists at the destination, so we do the same
      // Note we do this check prior to measuring gas so gasForCallExactCheck (our "cushion")
      // doesn't need to account for it.
      if iszero(extcodesize(target)) {
        mstore(0x0, NO_CONTRACT_SIG)
        revert(0x0, 0x4)
      }

      let g := gas()
      // Compute g -= gasForCallExactCheck and check for underflow
      // The gas actually passed to the callee is _min(gasAmount, 63//64*gas available).
      // We want to ensure that we revert if gasAmount >  63//64*gas available
      // as we do not want to provide them with less, however that check itself costs
      // gas. gasForCallExactCheck ensures we have at least enough gas to be able
      // to revert if gasAmount >  63//64*gas available.
      if lt(g, gasForCallExactCheck) {
        mstore(0x0, NO_GAS_FOR_CALL_EXACT_CHECK_SIG)
        revert(0x0, 0x4)
      }
      g := sub(g, gasForCallExactCheck)
      // if g - g//64 <= gasAmount, revert. We subtract g//64 because of EIP-150
      if iszero(gt(sub(g, div(g, 64)), gasLimit)) {
        mstore(0x0, NOT_ENOUGH_GAS_FOR_CALL_SIG)
        revert(0x0, 0x4)
      }

      // We save the gas before the call so we can calculate how much gas the call used
      let gasBeforeCall := gas()
      // call and return whether we succeeded. ignore return data
      // call(gas,addr,value,argsOffset,argsLength,retOffset,retLength)
      success := call(gasLimit, target, 0, add(payload, 0x20), mload(payload), 0x0, 0x0)
      gasUsed := sub(gasBeforeCall, gas())

      // limit our copy to maxReturnBytes bytes
      let toCopy := returndatasize()
      if gt(toCopy, maxReturnBytes) {
        toCopy := maxReturnBytes
      }
      // Store the length of the copied bytes
      mstore(retData, toCopy)
      // copy the bytes from retData[0:_toCopy]
      returndatacopy(add(retData, 0x20), 0x0, toCopy)
    }
    return (success, retData, gasUsed);
  }

  /// @notice Calls target address with exactly gasAmount gas and payload as calldata
  /// or reverts if at least gasLimit gas is not available.
  /// @dev Does not check if target is a contract. If it is not a contract, the low-level
  /// call will still be made and it will succeed.
  /// @dev Ignores the return data, which makes it immune to gas bomb attacks.
  /// @return success whether the call succeeded
  /// @return sufficientGas Whether there was enough gas to make the call
  function _callWithExactGasEvenIfTargetIsNoContract(
    bytes memory payload,
    address target,
    uint256 gasLimit,
    uint16 gasForCallExactCheck
  ) internal returns (bool success, bool sufficientGas) {
    assembly {
      let g := gas()
      // Compute g -= CALL_WITH_EXACT_GAS_CUSHION and check for underflow. We
      // need the cushion since the logic following the above call to gas also
      // costs gas which we cannot account for exactly. So cushion is a
      // conservative upper bound for the cost of this logic.
      if iszero(lt(g, gasForCallExactCheck)) {
        g := sub(g, gasForCallExactCheck)
        // If g - g//64 <= gasAmount, we don't have enough gas. We subtract g//64 because of EIP-150.
        if gt(sub(g, div(g, 64)), gasLimit) {
          // Call and ignore success/return data. Note that we did not check
          // whether a contract actually exists at the target address.
          success := call(gasLimit, target, 0, add(payload, 0x20), mload(payload), 0x0, 0x0)
          sufficientGas := true
        }
      }
    }
    return (success, sufficientGas);
  }
}
AccessControllerInterface.sol 7 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

// solhint-disable-next-line interface-starts-with-i
interface AccessControllerInterface {
  function hasAccess(address user, bytes calldata data) external view returns (bool);
}
AggregatorV2V3Interface.sol 8 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {AggregatorInterface} from "./AggregatorInterface.sol";
import {AggregatorV3Interface} from "./AggregatorV3Interface.sol";

// solhint-disable-next-line interface-starts-with-i
interface AggregatorV2V3Interface is AggregatorInterface, AggregatorV3Interface {}
AggregatorValidatorInterface.sol 12 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

// solhint-disable-next-line interface-starts-with-i
interface AggregatorValidatorInterface {
  function validate(
    uint256 previousRoundId,
    int256 previousAnswer,
    uint256 currentRoundId,
    int256 currentAnswer
  ) external returns (bool);
}
LinkTokenInterface.sol 29 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

// solhint-disable-next-line interface-starts-with-i
interface LinkTokenInterface {
  function allowance(address owner, address spender) external view returns (uint256 remaining);

  function approve(address spender, uint256 value) external returns (bool success);

  function balanceOf(address owner) external view returns (uint256 balance);

  function decimals() external view returns (uint8 decimalPlaces);

  function decreaseApproval(address spender, uint256 addedValue) external returns (bool success);

  function increaseApproval(address spender, uint256 subtractedValue) external;

  function name() external view returns (string memory tokenName);

  function symbol() external view returns (string memory tokenSymbol);

  function totalSupply() external view returns (uint256 totalTokensIssued);

  function transfer(address to, uint256 value) external returns (bool success);

  function transferAndCall(address to, uint256 value, bytes calldata data) external returns (bool success);

  function transferFrom(address from, address to, uint256 value) external returns (bool success);
}
OCR2Abstract.sol 122 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {ITypeAndVersion} from "../interfaces/ITypeAndVersion.sol";

abstract contract OCR2Abstract is ITypeAndVersion {
  uint256 internal constant MAX_NUM_ORACLES = 31;
  uint256 private constant PREFIX_MASK = type(uint256).max << (256 - 16); // 0xFFFF00..00
  uint256 private constant PREFIX = 0x0001 << (256 - 16); // 0x000100..00

  /// @notice triggers a new run of the offchain reporting protocol
  /// @param previousConfigBlockNumber block in which the previous config was set, to simplify historic analysis
  /// @param configDigest configDigest of this configuration
  /// @param configCount ordinal number of this config setting among all config settings over the life of this contract
  /// @param signers ith element is address ith oracle uses to sign a report
  /// @param transmitters ith element is address ith oracle uses to transmit a report via the transmit method
  /// @param f maximum number of faulty/dishonest oracles the protocol can tolerate while still working correctly
  /// @param onchainConfig serialized configuration used by the contract (and possibly oracles)
  /// @param offchainConfigVersion version of the serialization format used for "offchainConfig" parameter
  /// @param offchainConfig serialized configuration used by the oracles exclusively and only passed through the contract
  event ConfigSet(
    uint32 previousConfigBlockNumber,
    bytes32 configDigest,
    uint64 configCount,
    address[] signers,
    address[] transmitters,
    uint8 f,
    bytes onchainConfig,
    uint64 offchainConfigVersion,
    bytes offchainConfig
  );

  /// @notice sets offchain reporting protocol configuration incl. participating oracles
  /// @param signers addresses with which oracles sign the reports
  /// @param transmitters addresses oracles use to transmit the reports
  /// @param f number of faulty oracles the system can tolerate
  /// @param onchainConfig serialized configuration used by the contract (and possibly oracles)
  /// @param offchainConfigVersion version number for offchainEncoding schema
  /// @param offchainConfig serialized configuration used by the oracles exclusively and only passed through the contract
  function setConfig(
    address[] memory signers,
    address[] memory transmitters,
    uint8 f,
    bytes memory onchainConfig,
    uint64 offchainConfigVersion,
    bytes memory offchainConfig
  ) external virtual;

  /// @notice information about current offchain reporting protocol configuration
  /// @return configCount ordinal number of current config, out of all configs applied to this contract so far
  /// @return blockNumber block at which this config was set
  /// @return configDigest domain-separation tag for current config (see _configDigestFromConfigData)
  function latestConfigDetails()
    external
    view
    virtual
    returns (uint32 configCount, uint32 blockNumber, bytes32 configDigest);

  function _configDigestFromConfigData(
    uint256 chainId,
    address contractAddress,
    uint64 configCount,
    address[] memory signers,
    address[] memory transmitters,
    uint8 f,
    bytes memory onchainConfig,
    uint64 offchainConfigVersion,
    bytes memory offchainConfig
  ) internal pure returns (bytes32) {
    uint256 h = uint256(
      keccak256(
        abi.encode(
          chainId,
          contractAddress,
          configCount,
          signers,
          transmitters,
          f,
          onchainConfig,
          offchainConfigVersion,
          offchainConfig
        )
      )
    );
    return bytes32((PREFIX & PREFIX_MASK) | (h & ~PREFIX_MASK));
  }

  /// @notice optionally emitted to indicate the latest configDigest and epoch for
  /// which a report was successfully transmitted. Alternatively, the contract may
  /// use latestConfigDigestAndEpoch with scanLogs set to false.
  event Transmitted(bytes32 configDigest, uint32 epoch);

  /// @notice optionally returns the latest configDigest and epoch for which a
  /// report was successfully transmitted. Alternatively, the contract may return
  /// scanLogs set to true and use Transmitted events to provide this information
  /// to offchain watchers.
  /// @return scanLogs indicates whether to rely on the configDigest and epoch
  /// returned or whether to scan logs for the Transmitted event instead.
  /// @return configDigest
  /// @return epoch
  function latestConfigDigestAndEpoch()
    external
    view
    virtual
    returns (bool scanLogs, bytes32 configDigest, uint32 epoch);

  /// @notice transmit is called to post a new report to the contract
  /// @param reportContext [0]: ConfigDigest, [1]: 27 byte padding, 4-byte epoch and 1-byte round, [2]: ExtraHash
  /// @param report serialized report, which the signatures are signing.
  /// @param rs ith element is the R components of the ith signature on report. Must have at most MAX_NUM_ORACLES entries
  /// @param ss ith element is the S components of the ith signature on report. Must have at most MAX_NUM_ORACLES entries
  /// @param rawVs ith element is the the V component of the ith signature
  function transmit(
    // NOTE: If these parameters are changed, expectedMsgDataLength and/or
    // TRANSMIT_MSGDATA_CONSTANT_LENGTH_COMPONENT need to be changed accordingly
    bytes32[3] calldata reportContext,
    bytes calldata report,
    bytes32[] calldata rs,
    bytes32[] calldata ss,
    bytes32 rawVs // signatures
  ) external virtual;
}

Read Contract

checkEnabled 0xdc7f0124 → bool
decimals 0x313ce567 → uint8
description 0x7284e416 → string
getAnswer 0xb5ab58dc → int256
getBilling 0x29937268 → uint32, uint32, uint32, uint32, uint24
getBillingAccessController 0xc4c92b37 → address
getLinkToken 0xe76d5168 → address
getRequesterAccessController 0xdaffc4b5 → address
getRoundData 0x9a6fc8f5 → uint80, int256, uint256, uint256, uint80
getTimestamp 0xb633620c → uint256
getTransmitters 0x666cab8d → address[]
getValidatorConfig 0x9bd2c0b1 → address, uint32
hasAccess 0x6b14daf8 → bool
latestAnswer 0x50d25bcd → int256
latestConfigDetails 0x81ff7048 → uint32, uint32, bytes32
latestConfigDigestAndEpoch 0xafcb95d7 → bool, bytes32, uint32
latestRound 0x668a0f02 → uint256
latestRoundData 0xfeaf968c → uint80, int256, uint256, uint256, uint80
latestTimestamp 0x8205bf6a → uint256
latestTransmissionDetails 0xe5fe4577 → bytes32, uint32, uint8, int192, uint64
linkAvailableForPayment 0xd09dc339 → int256
maxAnswer 0x70da2f67 → int256
minAnswer 0x22adbc78 → int256
oracleObservationCount 0xe4902f82 → uint32
owedPayment 0x0eafb25b → uint256
owner 0x8da5cb5b → address
typeAndVersion 0x181f5a77 → string
version 0x54fd4d50 → uint256

Write Contract 21 functions

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

acceptOwnership 0x79ba5097
No parameters
acceptPayeeship 0xb121e147
address transmitter
addAccess 0xa118f249
address _user
disableAccessCheck 0x0a756983
No parameters
enableAccessCheck 0x8038e4a1
No parameters
removeAccess 0x8823da6c
address _user
requestNewRound 0x98e5b12a
No parameters
returns: uint80
setBilling 0x643dc105
uint32 maximumGasPriceGwei
uint32 reasonableGasPriceGwei
uint32 observationPaymentGjuels
uint32 transmissionPaymentGjuels
uint24 accountingGas
setBillingAccessController 0xfbffd2c1
address _billingAccessController
setConfig 0xe3d0e712
address[] signers
address[] transmitters
uint8 f
bytes onchainConfig
uint64 offchainConfigVersion
bytes offchainConfig
setCutoffTime 0xb17f2a6b
uint32 _cutoffTime
setLinkToken 0x4fb17470
address linkToken
address recipient
setPayees 0x9c849b30
address[] transmitters
address[] payees
setRequesterAccessController 0x9e3ceeab
address requesterAccessController
setValidatorConfig 0xeb457163
address newValidator
uint32 newGasLimit
transferOwnership 0xf2fde38b
address to
transferPayeeship 0xeb5dcd6c
address transmitter
address proposed
transmit 0xb1dc65a4
bytes32[3] reportContext
bytes report
bytes32[] rs
bytes32[] ss
bytes32 rawVs
transmitSecondary 0xba0cb29e
bytes32[3] reportContext
bytes report
bytes32[] rs
bytes32[] ss
bytes32 rawVs
withdrawFunds 0xc1075329
address recipient
uint256 amount
withdrawPayment 0x8ac28d5a
address transmitter

Recent Transactions

No transactions found for this address