Address Contract Partially Verified
Address
0x65Dcc24F8ff9e51F10DCc7Ed1e4e2A61e6E14bd6
Balance
0 ETH
Nonce
1
Code Size
24554 bytes
Creator
0xc25A6B0C...296F at tx 0x1f4bfa34...aa95cc
Indexed Transactions
0
Contract Bytecode
24554 bytes
0x608060405234801561001057600080fd5b50600436106102e95760003560e01c80637341c10c11610191578063b734c0f4116100e3578063e72f6e3011610097578063ea320e0b11610071578063ea320e0b146106dd578063ec2454e5146106f0578063f2fde38b1461071057600080fd5b8063e72f6e30146106a4578063e82622aa146106b7578063e82ad7d4146106ca57600080fd5b8063c3f909d4116100c8578063c3f909d414610669578063cc77470a1461067e578063d7ae1d301461069157600080fd5b8063b734c0f41461064b578063badc3eb61461065357600080fd5b80639f87fad711610145578063a4c0ed361161011f578063a4c0ed361461061d578063a9c9a91814610630578063aab396bd1461064357600080fd5b80639f87fad7146105e2578063a21a23e4146105f5578063a47c7696146105fd57600080fd5b8063823597401161017657806382359740146105a45780638456cb59146105b75780638da5cb5b146105bf57600080fd5b80637341c10c1461058957806379ba50971461059c57600080fd5b806341db4ca31161024a5780635ed6dfba116101fe57806366419970116101d857806366419970146104e1578063674603d0146105085780636a2215de1461055157600080fd5b80635ed6dfba146104a85780636162a323146104bb57806366316d8d146104ce57600080fd5b80634b8832d31161022f5780634b8832d31461045057806355fedefa146104635780635c975abb1461049157600080fd5b806341db4ca31461041c578063461d27621461043d57600080fd5b80631ded3b36116102a1578063330605291161028657806333060529146103e05780633e871e4d146104015780633f4ba83a1461041457600080fd5b80631ded3b361461039f5780632a905ccc146103b257600080fd5b806310fc49c1116102d257806310fc49c11461032357806312b5834914610336578063181f5a771461035657600080fd5b806302bcc5b6146102ee5780630c5d49cb14610303575b600080fd5b6103016102fc366004614ba6565b610723565b005b61030b608481565b60405161ffff90911681526020015b60405180910390f35b610301610331366004614be7565b610783565b6000546040516bffffffffffffffffffffffff909116815260200161031a565b6103926040518060400160405280601781526020017f46756e6374696f6e7320526f757465722076312e302e3000000000000000000081525081565b60405161031a9190614c8e565b6103016103ad366004614ca1565b61087f565b600a5462010000900468ffffffffffffffffff1660405168ffffffffffffffffff909116815260200161031a565b6103f36103ee366004614f8c565b6108b1565b60405161031a929190615074565b61030161040f366004615135565b610c7c565b610301610e91565b61042f61042a366004615249565b610ea3565b60405190815260200161031a565b61042f61044b366004615249565b610f03565b61030161045e3660046152cd565b610f0f565b61042f610471366004614ba6565b67ffffffffffffffff166000908152600360208190526040909120015490565b60065460ff165b604051901515815260200161031a565b6103016104b63660046152fb565b61105d565b6103016104c93660046153bd565b611216565b6103016104dc3660046152fb565b611396565b60025467ffffffffffffffff165b60405167ffffffffffffffff909116815260200161031a565b61051b610516366004615490565b61147f565b6040805182511515815260208084015167ffffffffffffffff90811691830191909152928201519092169082015260600161031a565b61056461055f3660046154be565b61150f565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200161031a565b6103016105973660046152cd565b6115ce565b610301611781565b6103016105b2366004614ba6565b6118a8565b6103016119ef565b600654610100900473ffffffffffffffffffffffffffffffffffffffff16610564565b6103016105f03660046152cd565b6119ff565b6104ef611daa565b61061061060b366004614ba6565b611f37565b60405161031a91906155a7565b61030161062b3660046155ba565b61206c565b61056461063e3660046154be565b6122b8565b60095461042f565b610301612317565b61065b612463565b60405161031a929190615616565b610671612533565b60405161031a919061566d565b6104ef61068c366004615749565b61269a565b61030161069f3660046152cd565b61291a565b6103016106b2366004615749565b61297f565b6103016106c5366004615766565b612af8565b6104986106d8366004614ba6565b612db7565b6103016106eb3660046154be565b612f06565b6107036106fe3660046157dc565b612f13565b60405161031a91906157fa565b61030161071e366004615749565b6131a8565b61072b6131b9565b610734816131c1565b67ffffffffffffffff81166000908152600360205260408120546107809183916c01000000000000000000000000900473ffffffffffffffffffffffffffffffffffffffff1690613237565b50565b67ffffffffffffffff8216600090815260036020819052604082200154600b54911a9081106107e8576040517f45c108ce00000000000000000000000000000000000000000000000000000000815260ff821660048201526024015b60405180910390fd5b6000600a6001018260ff16815481106108035761080361587a565b90600052602060002090600891828204019190066004029054906101000a900463ffffffff1690508063ffffffff168363ffffffff161115610879576040517f1d70f87a00000000000000000000000000000000000000000000000000000000815263ffffffff821660048201526024016107df565b50505050565b6108876131b9565b610890826131c1565b67ffffffffffffffff90911660009081526003602081905260409091200155565b6000806108bc613689565b826020015173ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610925576040517f8bec23e700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b82516000908152600560205260409020548061098a5783516020850151604051600295507f1a90e9a50793db2e394cf581e7c522e10c358a81e70acf6b5a0edd620c08dee19161097891899088906158a9565b60405180910390a25060009050610c71565b808460405160200161099c91906158db565b60405160208183030381529060405280519060200120146109f45783516020850151604051600695507f1a90e9a50793db2e394cf581e7c522e10c358a81e70acf6b5a0edd620c08dee19161097891899088906158a9565b8361012001518460a0015163ffffffff16610a0f9190615a37565b64ffffffffff165a1015610a5a5783516020850151604051600495507f1a90e9a50793db2e394cf581e7c522e10c358a81e70acf6b5a0edd620c08dee19161097891899088906158a9565b506000610a708460a0015163ffffffff16613691565b610a7a9088615a55565b9050600081878660c0015168ffffffffffffffffff16610a9a9190615a7d565b610aa49190615a7d565b9050610ab38560800151611f37565b600001516bffffffffffffffffffffffff16816bffffffffffffffffffffffff161115610b2b5784516020860151604051600596507f1a90e9a50793db2e394cf581e7c522e10c358a81e70acf6b5a0edd620c08dee191610b17918a9089906158a9565b60405180910390a25060009150610c719050565b84604001516bffffffffffffffffffffffff16816bffffffffffffffffffffffff161115610b905784516020860151604051600396507f1a90e9a50793db2e394cf581e7c522e10c358a81e70acf6b5a0edd620c08dee191610b17918a9089906158a9565b505082516000908152600560205260408120819055835160a08501516060860151610bc092918c918c9190613733565b8051909150610bd0576001610bd3565b60005b92506000610c0d8560800151866040015187606001518860c0015168ffffffffffffffffff168c610c078860200151613691565b8d6138f1565b9050846080015167ffffffffffffffff1685600001517f64778f26c70b60a8d7e29e2451b3844302d959448401c0535b768ed88c6b505e836020015189888f8f8960400151604051610c6496959493929190615aa2565b60405180910390a3519150505b965096945050505050565b610c84613c17565b8151815181141580610c965750600881115b15610ccd576040517fee03280800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b81811015610e47576000848281518110610cec57610cec61587a565b602002602001015190506000848381518110610d0a57610d0a61587a565b60200260200101519050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161480610d75575060008281526008602052604090205473ffffffffffffffffffffffffffffffffffffffff8281169116145b15610dac576040517fee03280800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600082815260086020526040908190205490517f8b052f0f4bf82fede7daffea71592b29d5ef86af1f3c7daaa0345dbb2f52f48191610e2c91859173ffffffffffffffffffffffffffffffffffffffff1690859092835273ffffffffffffffffffffffffffffffffffffffff918216602084015216604082015260600190565b60405180910390a1505080610e4090615b25565b9050610cd0565b506040805180820190915283815260208082018490528451600d91610e709183918801906149e6565b506020828101518051610e899260018501920190614a2d565b505050505050565b610e99613c17565b610ea1613c9d565b565b600080610eaf8361150f565b9050610ef783828a8a8a8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152508c92508b9150613d1a9050565b98975050505050505050565b600080610eaf836122b8565b610f17613689565b610f20826140ef565b610f286141b5565b73ffffffffffffffffffffffffffffffffffffffff81161580610f8f575067ffffffffffffffff821660009081526003602052604090206001015473ffffffffffffffffffffffffffffffffffffffff8281166c0100000000000000000000000090920416145b15610fc6576040517f8129bbcd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff821660008181526003602090815260409182902060010180546bffffffffffffffffffffffff166c0100000000000000000000000073ffffffffffffffffffffffffffffffffffffffff8716908102919091179091558251338152918201527f69436ea6df009049404f564eff6622cd00522b0bd6a89efd9e52a355c4a879be910160405180910390a25050565b6110656131b9565b806bffffffffffffffffffffffff1660000361109b5750306000908152600160205260409020546bffffffffffffffffffffffff165b306000908152600160205260409020546bffffffffffffffffffffffff908116908216811015611107576040517f6b0fe56f0000000000000000000000000000000000000000000000000000000081526bffffffffffffffffffffffff821660048201526024016107df565b30600090815260016020526040812080548492906111349084906bffffffffffffffffffffffff16615b5d565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff160217905550816000808282829054906101000a90046bffffffffffffffffffffffff1661118a9190615b5d565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff16021790555061121183836bffffffffffffffffffffffff167f000000000000000000000000514910771af9ca656af840dff83e8264ecf986ca73ffffffffffffffffffffffffffffffffffffffff166142bf9092919063ffffffff16565b505050565b61121e613c17565b8051600a80546020808501516040860151606087015161ffff9081166f01000000000000000000000000000000027fffffffffffffffffffffffffffffff0000ffffffffffffffffffffffffffffff60e09390931c6b01000000000000000000000002929092167fffffffffffffffffffffffffffffff000000000000ffffffffffffffffffffff68ffffffffffffffffff90941662010000027fffffffffffffffffffffffffffffffffffffffffff0000000000000000000000909616919097161793909317169390931717815560808301518051849361130592600b92910190614aa7565b5060a08201516002909101805460c09093015168ffffffffffffffffff1662010000027fffffffffffffffffffffffffffffffffffffffffff000000000000000000000090931661ffff909216919091179190911790556040517ea5832bf95f66c7814294cc4db681f20ee79608bfb8912a5321d66cfed5e9859061138b90839061566d565b60405180910390a150565b61139e613689565b806bffffffffffffffffffffffff166000036113e6576040517f8129bbcd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b336000908152600160205260409020546bffffffffffffffffffffffff908116908216811015611452576040517f6b0fe56f0000000000000000000000000000000000000000000000000000000081526bffffffffffffffffffffffff821660048201526024016107df565b33600090815260016020526040812080548492906111349084906bffffffffffffffffffffffff16615b5d565b60408051606080820183526000808352602080840182905292840181905273ffffffffffffffffffffffffffffffffffffffff861681526004835283812067ffffffffffffffff868116835290845290849020845192830185525460ff81161515835261010081048216938301939093526901000000000000000000909204909116918101919091525b92915050565b6000805b600d5460ff8216101561159857600d805460ff83169081106115375761153761587a565b9060005260206000200154830361158857600e805460ff831690811061155f5761155f61587a565b60009182526020909120015473ffffffffffffffffffffffffffffffffffffffff169392505050565b61159181615b82565b9050611513565b506040517f80833e33000000000000000000000000000000000000000000000000000000008152600481018390526024016107df565b6115d6613689565b6115df826140ef565b6115e76141b5565b60006115f6600a5461ffff1690565b67ffffffffffffffff841660009081526003602052604090206002015490915061ffff821611611658576040517fb72bc70300000000000000000000000000000000000000000000000000000000815261ffff821660048201526024016107df565b73ffffffffffffffffffffffffffffffffffffffff8216600090815260046020908152604080832067ffffffffffffffff8716845290915290205460ff16156116a057505050565b73ffffffffffffffffffffffffffffffffffffffff8216600081815260046020908152604080832067ffffffffffffffff881680855290835281842080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001908117909155600384528285206002018054918201815585529383902090930180547fffffffffffffffffffffffff000000000000000000000000000000000000000016851790555192835290917f43dc749a04ac8fb825cbd514f7c0e13f13bc6f2ee66043b76629d51776cff8e091015b60405180910390a2505050565b60075473ffffffffffffffffffffffffffffffffffffffff163314611802576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e65720000000000000000000060448201526064016107df565b600680547fffffffffffffffffffffff0000000000000000000000000000000000000000ff81166101003381810292909217909355600780547fffffffffffffffffffffffff00000000000000000000000000000000000000001690556040519290910473ffffffffffffffffffffffffffffffffffffffff169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a350565b6118b0613689565b6118b86141b5565b67ffffffffffffffff81166000908152600360205260409020805460019091015473ffffffffffffffffffffffffffffffffffffffff6c010000000000000000000000009283900481169290910416338114611958576040517f4e1d9f1800000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff821660048201526024016107df565b67ffffffffffffffff831660008181526003602090815260409182902080546c01000000000000000000000000339081026bffffffffffffffffffffffff928316178355600190920180549091169055825173ffffffffffffffffffffffffffffffffffffffff87168152918201527f6f1dc65165ffffedfd8e507b4a0f1fcfdada045ed11f6c26ba27cedfe87802f09101611774565b6119f7613c17565b610ea161434c565b611a07613689565b611a10826140ef565b611a186141b5565b73ffffffffffffffffffffffffffffffffffffffff8116600090815260046020908152604080832067ffffffffffffffff8087168552908352928190208151606081018352905460ff8116151582526101008104851693820193909352690100000000000000000090920490921691810191909152611a9782846143a7565b806040015167ffffffffffffffff16816020015167ffffffffffffffff1614611aec576040517f06eb10c800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff8316600090815260036020908152604080832060020180548251818502810185019093528083529192909190830182828015611b6757602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff168152600190910190602001808311611b3c575b5050505050905060005b8151811015611d0f578373ffffffffffffffffffffffffffffffffffffffff16828281518110611ba357611ba361587a565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1603611cff578160018351611bd59190615ba1565b81518110611be557611be561587a565b6020026020010151600360008767ffffffffffffffff1667ffffffffffffffff1681526020019081526020016000206002018281548110611c2857611c2861587a565b600091825260208083209190910180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff949094169390931790925567ffffffffffffffff87168152600390915260409020600201805480611ca257611ca2615bb4565b60008281526020902081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90810180547fffffffffffffffffffffffff0000000000000000000000000000000000000000169055019055611d0f565b611d0881615b25565b9050611b71565b5073ffffffffffffffffffffffffffffffffffffffff8316600081815260046020908152604080832067ffffffffffffffff89168085529083529281902080547fffffffffffffffffffffffffffffff00000000000000000000000000000000001690555192835290917f182bff9831466789164ca77075fffd84916d35a8180ba73c27e45634549b445b910160405180910390a250505050565b6000611db4613689565b611dbc6141b5565b60028054600090611dd69067ffffffffffffffff16615be3565b825467ffffffffffffffff8083166101009490940a93840293021916919091179091556040805160c0810182526000808252336020830152918101829052606081018290529192506080820190604051908082528060200260200182016040528015611e4c578160200160208202803683370190505b5081526000602091820181905267ffffffffffffffff841681526003825260409081902083518484015173ffffffffffffffffffffffffffffffffffffffff9081166c010000000000000000000000009081026bffffffffffffffffffffffff9384161784559386015160608701519091169093029216919091176001820155608083015180519192611ee792600285019290910190614a2d565b5060a0919091015160039091015560405133815267ffffffffffffffff8216907f464722b4166576d3dcbba877b999bc35cf911f4eaf434b7eba68fa113951d0bf9060200160405180910390a290565b6040805160c0810182526000808252602082018190529181018290526060808201839052608082015260a0810191909152611f71826131c1565b67ffffffffffffffff8216600090815260036020908152604091829020825160c08101845281546bffffffffffffffffffffffff808216835273ffffffffffffffffffffffffffffffffffffffff6c0100000000000000000000000092839004811684870152600185015491821684880152919004166060820152600282018054855181860281018601909652808652919492936080860193929083018282801561205257602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff168152600190910190602001808311612027575b505050505081526020016003820154815250509050919050565b612074613689565b3373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000514910771af9ca656af840dff83e8264ecf986ca16146120e3576040517f44b0e3c300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6020811461211d576040517f8129bbcd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600061212b82840184614ba6565b67ffffffffffffffff81166000908152600360205260409020549091506c01000000000000000000000000900473ffffffffffffffffffffffffffffffffffffffff166121a4576040517f1f6a65b600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff8116600090815260036020526040812080546bffffffffffffffffffffffff16918691906121db8385615a7d565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff160217905550846000808282829054906101000a90046bffffffffffffffffffffffff166122319190615a7d565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff1602179055508167ffffffffffffffff167fd39ec07f4e209f627a4c427971473820dc129761ba28de8906bd56f57101d4f88287846122989190615c0a565b6040805192835260208301919091520160405180910390a2505050505050565b60008181526008602052604081205473ffffffffffffffffffffffffffffffffffffffff1680611509576040517f80833e33000000000000000000000000000000000000000000000000000000008152600481018490526024016107df565b61231f613c17565b60005b600d54811015612442576000600d60000182815481106123445761234461587a565b906000526020600020015490506000600d60010183815481106123695761236961587a565b6000918252602080832091909101548483526008825260409283902054835186815273ffffffffffffffffffffffffffffffffffffffff91821693810193909352169181018290529091507ff8a6175bca1ba37d682089187edc5e20a859989727f10ca6bd9a5bc0de8caf949060600160405180910390a160009182526008602052604090912080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff90921691909117905561243b81615b25565b9050612322565b50600d60006124518282614b51565b61245f600183016000614b51565b5050565b606080600d600001600d600101818054806020026020016040519081016040528092919081815260200182805480156124bb57602002820191906000526020600020905b8154815260200190600101908083116124a7575b505050505091508080548060200260200160405190810160405280929190818152602001828054801561252457602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff1681526001909101906020018083116124f9575b50505050509050915091509091565b6040805160e0810182526000808252602082018190529181018290526060808201839052608082015260a0810182905260c08101919091526040805160e08082018352600a805461ffff808216855262010000820468ffffffffffffffffff166020808701919091526b010000000000000000000000830490941b7fffffffff0000000000000000000000000000000000000000000000000000000016858701526f01000000000000000000000000000000909104166060840152600b805485518185028101850190965280865293949193608086019383018282801561266557602002820191906000526020600020906000905b82829054906101000a900463ffffffff1663ffffffff16815260200190600401906020826003010492830192600103820291508084116126285790505b50505091835250506002919091015461ffff8116602083015262010000900468ffffffffffffffffff16604090910152919050565b60006126a4613689565b6126ac6141b5565b600280546000906126c69067ffffffffffffffff16615be3565b825467ffffffffffffffff8083166101009490940a93840293021916919091179091556040805160c081018252600080825233602083015291810182905260608101829052919250608082019060405190808252806020026020018201604052801561273c578160200160208202803683370190505b5081526000602091820181905267ffffffffffffffff841681526003825260409081902083518484015173ffffffffffffffffffffffffffffffffffffffff9081166c010000000000000000000000009081026bffffffffffffffffffffffff93841617845593860151606087015190911690930292169190911760018201556080830151805191926127d792600285019290910190614a2d565b5060a0919091015160039182015567ffffffffffffffff82166000818152602092835260408082206002018054600180820183559184528584200180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff891690811790915583526004855281832084845285529181902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169092179091555133815290917f464722b4166576d3dcbba877b999bc35cf911f4eaf434b7eba68fa113951d0bf910160405180910390a260405173ffffffffffffffffffffffffffffffffffffffff8316815267ffffffffffffffff8216907f43dc749a04ac8fb825cbd514f7c0e13f13bc6f2ee66043b76629d51776cff8e09060200160405180910390a2919050565b612922613689565b61292b826140ef565b6129336141b5565b61293c82612db7565b15612973576040517f06eb10c800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61245f82826001613237565b6129876131b9565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000907f000000000000000000000000514910771af9ca656af840dff83e8264ecf986ca73ffffffffffffffffffffffffffffffffffffffff16906370a0823190602401602060405180830381865afa158015612a14573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a389190615c1d565b6000549091506bffffffffffffffffffffffff1681811015611211576000612a608284615ba1565b9050612aa373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000514910771af9ca656af840dff83e8264ecf986ca1685836142bf565b6040805173ffffffffffffffffffffffffffffffffffffffff86168152602081018390527f59bfc682b673f8cbf945f1e454df9334834abf7dfe7f92237ca29ecb9b436600910160405180910390a150505050565b612b00613689565b60005b81811015611211576000838383818110612b1f57612b1f61587a565b90506101600201803603810190612b369190615c36565b80516080820151600082815260056020908152604091829020549151949550929391929091612b67918691016158db565b6040516020818303038152906040528051906020012014612bb4576040517f8129bbcd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b82610140015163ffffffff16421015612bf9576040517fa2376fe800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60208301516040517f85b214cf0000000000000000000000000000000000000000000000000000000081526004810184905273ffffffffffffffffffffffffffffffffffffffff909116906385b214cf90602401600060405180830381600087803b158015612c6757600080fd5b505af1158015612c7b573d6000803e3d6000fd5b50505060408085015167ffffffffffffffff84166000908152600360205291822060010180549193509190612cbf9084906bffffffffffffffffffffffff16615b5d565b82546bffffffffffffffffffffffff9182166101009390930a928302919092021990911617905550606083015173ffffffffffffffffffffffffffffffffffffffff16600090815260046020908152604080832067ffffffffffffffff808616855292529091208054600192600991612d479185916901000000000000000000900416615c53565b825467ffffffffffffffff9182166101009390930a9283029190920219909116179055506000828152600560205260408082208290555183917ff1ca1e9147be737b04a2b018a79405f687a97de8dd8a2559bbe62357343af41491a250505080612db090615b25565b9050612b03565b67ffffffffffffffff8116600090815260036020908152604080832060020180548251818502810185019093528083528493830182828015612e2f57602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff168152600190910190602001808311612e04575b5050505050905060005b8151811015612efc57600060046000848481518110612e5a57612e5a61587a565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff168252818101929092526040908101600090812067ffffffffffffffff808a168352908452908290208251606081018452905460ff8116151582526101008104831694820185905269010000000000000000009004909116918101829052925014612eeb57506001949350505050565b50612ef581615b25565b9050612e39565b5060009392505050565b612f0e613c17565b600955565b60608167ffffffffffffffff168367ffffffffffffffff161180612f46575060025467ffffffffffffffff908116908316115b80612f5b575060025467ffffffffffffffff16155b15612f92576040517f8129bbcd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612f9c8383615c74565b612fa7906001615c53565b67ffffffffffffffff1667ffffffffffffffff811115612fc957612fc9614ccd565b60405190808252806020026020018201604052801561304657816020015b6040805160c081018252600080825260208083018290529282018190526060808301829052608083015260a082015282527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff909201910181612fe75790505b50905060005b6130568484615c74565b67ffffffffffffffff1681116131a1576003600061307e8367ffffffffffffffff8816615c0a565b67ffffffffffffffff1681526020808201929092526040908101600020815160c08101835281546bffffffffffffffffffffffff808216835273ffffffffffffffffffffffffffffffffffffffff6c010000000000000000000000009283900481168488015260018501549182168487015291900416606082015260028201805484518187028101870190955280855291949293608086019390929083018282801561316057602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff168152600190910190602001808311613135575b505050505081526020016003820154815250508282815181106131855761318561587a565b60200260200101819052508061319a90615b25565b905061304c565b5092915050565b6131b0613c17565b6107808161441b565b610ea1613c17565b67ffffffffffffffff81166000908152600360205260409020546c01000000000000000000000000900473ffffffffffffffffffffffffffffffffffffffff16610780576040517f1f6a65b600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff83166000908152600360209081526040808320815160c08101835281546bffffffffffffffffffffffff808216835273ffffffffffffffffffffffffffffffffffffffff6c010000000000000000000000009283900481168488015260018501549182168487015291900416606082015260028201805484518187028101870190955280855291949293608086019390929083018282801561331857602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff1681526001909101906020018083116132ed575b50505091835250506003919091015460209091015280519091506000805b83608001515181101561342e5760008460800151828151811061335b5761335b61587a565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff8116600090815260048352604080822067ffffffffffffffff808e16845294529020549092506133bb9169010000000000000000009091041684615c53565b73ffffffffffffffffffffffffffffffffffffffff909116600090815260046020908152604080832067ffffffffffffffff8c168452909152902080547fffffffffffffffffffffffffffffff0000000000000000000000000000000000169055915061342781615b25565b9050613336565b5067ffffffffffffffff8616600090815260036020526040812081815560018101829055906134606002830182614b51565b50600060039190910155600c5461ffff81169062010000900468ffffffffffffffffff1685801561349e57508161ffff168367ffffffffffffffff16105b1561355a576000846bffffffffffffffffffffffff168268ffffffffffffffffff16116134d6578168ffffffffffffffffff166134d8565b845b90506bffffffffffffffffffffffff81161561355857306000908152600160205260408120805483929061351b9084906bffffffffffffffffffffffff16615a7d565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff16021790555080856135559190615b5d565b94505b505b6bffffffffffffffffffffffff841615613617576000805485919081906135909084906bffffffffffffffffffffffff16615b5d565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff16021790555061361787856bffffffffffffffffffffffff167f000000000000000000000000514910771af9ca656af840dff83e8264ecf986ca73ffffffffffffffffffffffffffffffffffffffff166142bf9092919063ffffffff16565b6040805173ffffffffffffffffffffffffffffffffffffffff891681526bffffffffffffffffffffffff8616602082015267ffffffffffffffff8a16917fe8ed5b475a5b5987aa9165e8731bb78043f39eee32ec5a1169a89e27fcd49815910160405180910390a25050505050505050565b610ea1614517565b60006bffffffffffffffffffffffff82111561372f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203960448201527f362062697473000000000000000000000000000000000000000000000000000060648201526084016107df565b5090565b60408051606080820183526000808352602083015291810191909152813b1580156137865750506040805160608101825260008082526020808301829052835191825281018352918101919091526138e8565b600a546040516000916b010000000000000000000000900460e01b906137b4908a908a908a90602401615c95565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152918152602080830180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009590951694909417909352600a548151608480825260c0820190935292945061ffff6f01000000000000000000000000000000909104169260009283928392820181803683370190505090505a8481101561388257600080fd5b8490036040810481038a1061389657600080fd5b505a60008087516020890160008d8ff193505a900391503d60848111156138bb575060845b808252806000602084013e5060408051606081018252931515845260208401929092529082015293505050505b95945050505050565b604080518082019091526000808252602082015260006139118486615a55565b90506000816139208886615a7d565b61392a9190615a7d565b67ffffffffffffffff8b166000908152600360205260409020549091506bffffffffffffffffffffffff80831691161080613991575067ffffffffffffffff8a166000908152600360205260409020600101546bffffffffffffffffffffffff808b169116105b156139f45767ffffffffffffffff8a16600090815260036020526040908190205490517f6b0fe56f0000000000000000000000000000000000000000000000000000000081526bffffffffffffffffffffffff90911660048201526024016107df565b67ffffffffffffffff8a1660009081526003602052604081208054839290613a2b9084906bffffffffffffffffffffffff16615b5d565b82546101009290920a6bffffffffffffffffffffffff81810219909316918316021790915567ffffffffffffffff8c16600090815260036020526040812060010180548d94509092613a7f91859116615b5d565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff1602179055508184613ab99190615a7d565b3360009081526001602052604081208054909190613ae69084906bffffffffffffffffffffffff16615a7d565b82546101009290920a6bffffffffffffffffffffffff81810219909316918316021790915530600090815260016020526040812080548b94509092613b2d91859116615a7d565b82546bffffffffffffffffffffffff9182166101009390930a92830291909202199091161790555073ffffffffffffffffffffffffffffffffffffffff8816600090815260046020908152604080832067ffffffffffffffff808f16855292529091208054600192600991613bb19185916901000000000000000000900416615c53565b92506101000a81548167ffffffffffffffff021916908367ffffffffffffffff1602179055506040518060400160405280836bffffffffffffffffffffffff168152602001826bffffffffffffffffffffffff1681525092505050979650505050505050565b600654610100900473ffffffffffffffffffffffffffffffffffffffff163314610ea1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e65720000000000000000000060448201526064016107df565b613ca5614584565b600680547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200160405180910390a1565b6000613d24613689565b613d2d856131c1565b613d3733866143a7565b613d418583610783565b8351600003613d7b576040517ec1cfc000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000613d8686611f37565b90506000613d94338861147f565b600a54604080516101608101825289815267ffffffffffffffff8b1660009081526003602081815293822001549495506201000090930468ffffffffffffffffff169373ffffffffffffffffffffffffffffffffffffffff8d169263a631571e929190820190815233602082015260408881015189519190920191613e1891615b5d565b6bffffffffffffffffffffffff1681526020018568ffffffffffffffffff1681526020018c67ffffffffffffffff168152602001866020015167ffffffffffffffff1681526020018963ffffffff1681526020018a61ffff168152602001866040015167ffffffffffffffff168152602001876020015173ffffffffffffffffffffffffffffffffffffffff168152506040518263ffffffff1660e01b8152600401613ec49190615cc0565b610160604051808303816000875af1158015613ee4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613f089190615e25565b805160009081526005602052604090205490915015613f595780516040517f304f32e800000000000000000000000000000000000000000000000000000000815260048101919091526024016107df565b604051806101600160405280826000015181526020018b73ffffffffffffffffffffffffffffffffffffffff16815260200182604001516bffffffffffffffffffffffff1681526020013373ffffffffffffffffffffffffffffffffffffffff1681526020018a67ffffffffffffffff1681526020018763ffffffff1681526020018368ffffffffffffffffff1681526020018260e0015168ffffffffffffffffff16815260200182610100015164ffffffffff16815260200182610120015164ffffffffff16815260200182610140015163ffffffff1681525060405160200161404491906158db565b60405160208183030381529060405280519060200120600560008360000151815260200190815260200160002081905550614084338a83604001516145f0565b8867ffffffffffffffff168b82600001517ff67aec45c9a7ede407974a3e0c3a743dffeab99ee3f2d4c9a8144c2ebf2c7ec9876020015133328e8e8e8a604001516040516140d89796959493929190615ef8565b60405180910390a4519a9950505050505050505050565b67ffffffffffffffff81166000908152600360205260409020546c01000000000000000000000000900473ffffffffffffffffffffffffffffffffffffffff1680614166576040517f1f6a65b600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3373ffffffffffffffffffffffffffffffffffffffff82161461245f576040517f5a68151d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60095460009081526008602052604090205473ffffffffffffffffffffffffffffffffffffffff16806141e55750565b604080516000815260208101918290527f6b14daf80000000000000000000000000000000000000000000000000000000090915273ffffffffffffffffffffffffffffffffffffffff821690636b14daf89061424690339060248101615f70565b602060405180830381865afa158015614263573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906142879190615f9f565b610780576040517f229062630000000000000000000000000000000000000000000000000000000081523360048201526024016107df565b6040805173ffffffffffffffffffffffffffffffffffffffff8416602482015260448082018490528251808303909101815260649091019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fa9059cbb000000000000000000000000000000000000000000000000000000001790526112119084906146cb565b614354614517565b600680547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a258613cf03390565b73ffffffffffffffffffffffffffffffffffffffff8216600090815260046020908152604080832067ffffffffffffffff8516845290915290205460ff1661245f576040517f71e8313700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3373ffffffffffffffffffffffffffffffffffffffff82160361449a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c6600000000000000000060448201526064016107df565b600780547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff838116918217909255600654604051919261010090910416907fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae127890600090a350565b60065460ff1615610ea1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f5061757361626c653a207061757365640000000000000000000000000000000060448201526064016107df565b60065460ff16610ea1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f5061757361626c653a206e6f742070617573656400000000000000000000000060448201526064016107df565b67ffffffffffffffff82166000908152600360205260408120600101805483929061462a9084906bffffffffffffffffffffffff16615a7d565b82546bffffffffffffffffffffffff91821661010093840a908102920219161790915573ffffffffffffffffffffffffffffffffffffffff8516600090815260046020908152604080832067ffffffffffffffff80891685529252909120805460019450909284926146a0928492900416615c53565b92506101000a81548167ffffffffffffffff021916908367ffffffffffffffff160217905550505050565b600061472d826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65648152508573ffffffffffffffffffffffffffffffffffffffff166147d79092919063ffffffff16565b805190915015611211578080602001905181019061474b9190615f9f565b611211576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f7420737563636565640000000000000000000000000000000000000000000060648201526084016107df565b60606147e684846000856147ee565b949350505050565b606082471015614880576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f60448201527f722063616c6c000000000000000000000000000000000000000000000000000060648201526084016107df565b6000808673ffffffffffffffffffffffffffffffffffffffff1685876040516148a99190615fc1565b60006040518083038185875af1925050503d80600081146148e6576040519150601f19603f3d011682016040523d82523d6000602084013e6148eb565b606091505b50915091506148fc87838387614907565b979650505050505050565b6060831561499d5782516000036149965773ffffffffffffffffffffffffffffffffffffffff85163b614996576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016107df565b50816147e6565b6147e683838151156149b25781518083602001fd5b806040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016107df9190614c8e565b828054828255906000526020600020908101928215614a21579160200282015b82811115614a21578251825591602001919060010190614a06565b5061372f929150614b6b565b828054828255906000526020600020908101928215614a21579160200282015b82811115614a2157825182547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff909116178255602090920191600190910190614a4d565b82805482825590600052602060002090600701600890048101928215614a215791602002820160005b83821115614b1457835183826101000a81548163ffffffff021916908363ffffffff1602179055509260200192600401602081600301049283019260010302614ad0565b8015614b445782816101000a81549063ffffffff0219169055600401602081600301049283019260010302614b14565b505061372f929150614b6b565b508054600082559060005260206000209081019061078091905b5b8082111561372f5760008155600101614b6c565b67ffffffffffffffff8116811461078057600080fd5b8035614ba181614b80565b919050565b600060208284031215614bb857600080fd5b8135614bc381614b80565b9392505050565b63ffffffff8116811461078057600080fd5b8035614ba181614bca565b60008060408385031215614bfa57600080fd5b8235614c0581614b80565b91506020830135614c1581614bca565b809150509250929050565b60005b83811015614c3b578181015183820152602001614c23565b50506000910152565b60008151808452614c5c816020860160208601614c20565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b602081526000614bc36020830184614c44565b60008060408385031215614cb457600080fd5b8235614cbf81614b80565b946020939093013593505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051610160810167ffffffffffffffff81118282101715614d2057614d20614ccd565b60405290565b60405160e0810167ffffffffffffffff81118282101715614d2057614d20614ccd565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715614d9057614d90614ccd565b604052919050565b600082601f830112614da957600080fd5b813567ffffffffffffffff811115614dc357614dc3614ccd565b614df460207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601614d49565b818152846020838601011115614e0957600080fd5b816020850160208301376000918101602001919091529392505050565b6bffffffffffffffffffffffff8116811461078057600080fd5b8035614ba181614e26565b73ffffffffffffffffffffffffffffffffffffffff8116811461078057600080fd5b8035614ba181614e4b565b68ffffffffffffffffff8116811461078057600080fd5b8035614ba181614e78565b64ffffffffff8116811461078057600080fd5b8035614ba181614e9a565b60006101608284031215614ecb57600080fd5b614ed3614cfc565b905081358152614ee560208301614e6d565b6020820152614ef660408301614e40565b6040820152614f0760608301614e6d565b6060820152614f1860808301614b96565b6080820152614f2960a08301614bdc565b60a0820152614f3a60c08301614e8f565b60c0820152614f4b60e08301614e8f565b60e0820152610100614f5e818401614ead565b90820152610120614f70838201614ead565b90820152610140614f82838201614bdc565b9082015292915050565b6000806000806000806102008789031215614fa657600080fd5b863567ffffffffffffffff80821115614fbe57600080fd5b614fca8a838b01614d98565b97506020890135915080821115614fe057600080fd5b50614fed89828a01614d98565b9550506040870135614ffe81614e26565b9350606087013561500e81614e26565b9250608087013561501e81614e4b565b915061502d8860a08901614eb8565b90509295509295509295565b60078110615070577f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b9052565b604081016150828285615039565b6bffffffffffffffffffffffff831660208301529392505050565b600067ffffffffffffffff8211156150b7576150b7614ccd565b5060051b60200190565b600082601f8301126150d257600080fd5b813560206150e76150e28361509d565b614d49565b82815260059290921b8401810191818101908684111561510657600080fd5b8286015b8481101561512a57803561511d81614e4b565b835291830191830161510a565b509695505050505050565b6000806040838503121561514857600080fd5b823567ffffffffffffffff8082111561516057600080fd5b818501915085601f83011261517457600080fd5b813560206151846150e28361509d565b82815260059290921b840181019181810190898411156151a357600080fd5b948201945b838610156151c1578535825294820194908201906151a8565b965050860135925050808211156151d757600080fd5b506151e4858286016150c1565b9150509250929050565b60008083601f84011261520057600080fd5b50813567ffffffffffffffff81111561521857600080fd5b60208301915083602082850101111561523057600080fd5b9250929050565b803561ffff81168114614ba157600080fd5b60008060008060008060a0878903121561526257600080fd5b863561526d81614b80565b9550602087013567ffffffffffffffff81111561528957600080fd5b61529589828a016151ee565b90965094506152a8905060408801615237565b925060608701356152b881614bca565b80925050608087013590509295509295509295565b600080604083850312156152e057600080fd5b82356152eb81614b80565b91506020830135614c1581614e4b565b6000806040838503121561530e57600080fd5b823561531981614e4b565b91506020830135614c1581614e26565b80357fffffffff0000000000000000000000000000000000000000000000000000000081168114614ba157600080fd5b600082601f83011261536a57600080fd5b8135602061537a6150e28361509d565b82815260059290921b8401810191818101908684111561539957600080fd5b8286015b8481101561512a5780356153b081614bca565b835291830191830161539d565b6000602082840312156153cf57600080fd5b813567ffffffffffffffff808211156153e757600080fd5b9083019060e082860312156153fb57600080fd5b615403614d26565b61540c83615237565b815261541a60208401614e8f565b602082015261542b60408401615329565b604082015261543c60608401615237565b606082015260808301358281111561545357600080fd5b61545f87828601615359565b60808301525061547160a08401615237565b60a082015261548260c08401614e8f565b60c082015295945050505050565b600080604083850312156154a357600080fd5b82356154ae81614e4b565b91506020830135614c1581614b80565b6000602082840312156154d057600080fd5b5035919050565b600081518084526020808501945080840160005b8381101561551d57815173ffffffffffffffffffffffffffffffffffffffff16875295820195908201906001016154eb565b509495945050505050565b60006bffffffffffffffffffffffff808351168452602083015173ffffffffffffffffffffffffffffffffffffffff8082166020870152826040860151166040870152806060860151166060870152505050608082015160c0608085015261559360c08501826154d7565b60a093840151949093019390935250919050565b602081526000614bc36020830184615528565b600080600080606085870312156155d057600080fd5b84356155db81614e4b565b935060208501359250604085013567ffffffffffffffff8111156155fe57600080fd5b61560a878288016151ee565b95989497509550505050565b604080825283519082018190526000906020906060840190828701845b8281101561564f57815184529284019290840190600101615633565b5050508381038285015261566381866154d7565b9695505050505050565b60006020808352610100830161ffff808651168386015268ffffffffffffffffff838701511660408601527fffffffff00000000000000000000000000000000000000000000000000000000604087015116606086015280606087015116608086015250608085015160e060a0860152818151808452610120870191508483019350600092505b8083101561571a57835163ffffffff1682529284019260019290920191908401906156f4565b5060a087015161ffff811660c0880152935060c087015168ffffffffffffffffff811660e08801529350615663565b60006020828403121561575b57600080fd5b8135614bc381614e4b565b6000806020838503121561577957600080fd5b823567ffffffffffffffff8082111561579157600080fd5b818501915085601f8301126157a557600080fd5b8135818111156157b457600080fd5b866020610160830285010111156157ca57600080fd5b60209290920196919550909350505050565b600080604083850312156157ef57600080fd5b82356154ae81614b80565b6000602080830181845280855180835260408601915060408160051b870101925083870160005b8281101561586d577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc088860301845261585b858351615528565b94509285019290850190600101615821565b5092979650505050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b73ffffffffffffffffffffffffffffffffffffffff848116825283166020820152606081016147e66040830184615039565b8151815260208083015161016083019161590c9084018273ffffffffffffffffffffffffffffffffffffffff169052565b50604083015161592c60408401826bffffffffffffffffffffffff169052565b506060830151615954606084018273ffffffffffffffffffffffffffffffffffffffff169052565b506080830151615970608084018267ffffffffffffffff169052565b5060a083015161598860a084018263ffffffff169052565b5060c08301516159a560c084018268ffffffffffffffffff169052565b5060e08301516159c260e084018268ffffffffffffffffff169052565b506101008381015164ffffffffff81168483015250506101208381015164ffffffffff81168483015250506101408381015163ffffffff8116848301525b505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b64ffffffffff8181168382160190808211156131a1576131a1615a08565b6bffffffffffffffffffffffff818116838216028082169190828114615a0057615a00615a08565b6bffffffffffffffffffffffff8181168382160190808211156131a1576131a1615a08565b6bffffffffffffffffffffffff8716815273ffffffffffffffffffffffffffffffffffffffff86166020820152615adc6040820186615039565b60c060608201526000615af260c0830186614c44565b8281036080840152615b048186614c44565b905082810360a0840152615b188185614c44565b9998505050505050505050565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203615b5657615b56615a08565b5060010190565b6bffffffffffffffffffffffff8281168282160390808211156131a1576131a1615a08565b600060ff821660ff8103615b9857615b98615a08565b60010192915050565b8181038181111561150957611509615a08565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b600067ffffffffffffffff808316818103615c0057615c00615a08565b6001019392505050565b8082018082111561150957611509615a08565b600060208284031215615c2f57600080fd5b5051919050565b60006101608284031215615c4957600080fd5b614bc38383614eb8565b67ffffffffffffffff8181168382160190808211156131a1576131a1615a08565b67ffffffffffffffff8281168282160390808211156131a1576131a1615a08565b838152606060208201526000615cae6060830185614c44565b82810360408401526156638185614c44565b6020815260008251610160806020850152615cdf610180850183614c44565b9150602085015160408501526040850151615d12606086018273ffffffffffffffffffffffffffffffffffffffff169052565b5060608501516bffffffffffffffffffffffff8116608086015250608085015168ffffffffffffffffff811660a08601525060a085015167ffffffffffffffff811660c08601525060c085015167ffffffffffffffff811660e08601525060e0850151610100615d898187018363ffffffff169052565b8601519050610120615da08682018361ffff169052565b8601519050610140615dbd8682018367ffffffffffffffff169052565b9095015173ffffffffffffffffffffffffffffffffffffffff1693019290925250919050565b8051614ba181614e4b565b8051614ba181614e26565b8051614ba181614b80565b8051614ba181614bca565b8051614ba181614e78565b8051614ba181614e9a565b60006101608284031215615e3857600080fd5b615e40614cfc565b82518152615e5060208401615de3565b6020820152615e6160408401615dee565b6040820152615e7260608401615de3565b6060820152615e8360808401615df9565b6080820152615e9460a08401615e04565b60a0820152615ea560c08401615e0f565b60c0820152615eb660e08401615e0f565b60e0820152610100615ec9818501615e1a565b90820152610120615edb848201615e1a565b90820152610140615eed848201615e04565b908201529392505050565b600073ffffffffffffffffffffffffffffffffffffffff808a168352808916602084015280881660408401525060e06060830152615f3960e0830187614c44565b61ffff9590951660808301525063ffffffff9290921660a08301526bffffffffffffffffffffffff1660c090910152949350505050565b73ffffffffffffffffffffffffffffffffffffffff831681526040602082015260006147e66040830184614c44565b600060208284031215615fb157600080fd5b81518015158114614bc357600080fd5b60008251615fd3818460208701614c20565b919091019291505056fea164736f6c6343000813000a
Verified Source Code Partial Match
Compiler: v0.8.19+commit.7dd6d404
EVM: paris
Optimization: Yes (1000000 runs)
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;
}
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)) {}
}
IERC677Receiver.sol 6 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.6;
interface IERC677Receiver {
function onTokenTransfer(address sender, uint256 amount, bytes calldata data) external;
}
ITypeAndVersion.sol 6 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface ITypeAndVersion {
function typeAndVersion() external pure returns (string memory);
}
FunctionsRouter.sol 587 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import {ITypeAndVersion} from "../../../shared/interfaces/ITypeAndVersion.sol";
import {IFunctionsRouter} from "./interfaces/IFunctionsRouter.sol";
import {IFunctionsCoordinator} from "./interfaces/IFunctionsCoordinator.sol";
import {IAccessController} from "../../../shared/interfaces/IAccessController.sol";
import {FunctionsSubscriptions} from "./FunctionsSubscriptions.sol";
import {FunctionsResponse} from "./libraries/FunctionsResponse.sol";
import {ConfirmedOwner} from "../../../shared/access/ConfirmedOwner.sol";
import {SafeCast} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/utils/math/SafeCast.sol";
import {Pausable} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/security/Pausable.sol";
contract FunctionsRouter is IFunctionsRouter, FunctionsSubscriptions, Pausable, ITypeAndVersion, ConfirmedOwner {
using FunctionsResponse for FunctionsResponse.RequestMeta;
using FunctionsResponse for FunctionsResponse.Commitment;
using FunctionsResponse for FunctionsResponse.FulfillResult;
// solhint-disable-next-line chainlink-solidity/all-caps-constant-storage-variables
string public constant override typeAndVersion = "Functions Router v1.0.0";
// We limit return data to a selector plus 4 words. This is to avoid
// malicious contracts from returning large amounts of data and causing
// repeated out-of-gas scenarios.
uint16 public constant MAX_CALLBACK_RETURN_BYTES = 4 + 4 * 32;
uint8 private constant MAX_CALLBACK_GAS_LIMIT_FLAGS_INDEX = 0;
event RequestStart(
bytes32 indexed requestId,
bytes32 indexed donId,
uint64 indexed subscriptionId,
address subscriptionOwner,
address requestingContract,
address requestInitiator,
bytes data,
uint16 dataVersion,
uint32 callbackGasLimit,
uint96 estimatedTotalCostJuels
);
event RequestProcessed(
bytes32 indexed requestId,
uint64 indexed subscriptionId,
uint96 totalCostJuels,
address transmitter,
FunctionsResponse.FulfillResult resultCode,
bytes response,
bytes err,
bytes callbackReturnData
);
event RequestNotProcessed(
bytes32 indexed requestId,
address coordinator,
address transmitter,
FunctionsResponse.FulfillResult resultCode
);
error EmptyRequestData();
error OnlyCallableFromCoordinator();
error SenderMustAcceptTermsOfService(address sender);
error InvalidGasFlagValue(uint8 value);
error GasLimitTooBig(uint32 limit);
error DuplicateRequestId(bytes32 requestId);
struct CallbackResult {
bool success; // ══════╸ Whether the callback succeeded or not
uint256 gasUsed; // ═══╸ The amount of gas consumed during the callback
bytes returnData; // ══╸ The return of the callback function
}
// ================================================================
// | Route state |
// ================================================================
mapping(bytes32 id => address routableContract) private s_route;
error RouteNotFound(bytes32 id);
// Identifier for the route to the Terms of Service Allow List
bytes32 private s_allowListId;
// ================================================================
// | Configuration state |
// ================================================================
struct Config {
uint16 maxConsumersPerSubscription; // ═════════╗ Maximum number of consumers which can be added to a single subscription. This bound ensures we are able to loop over all subscription consumers as needed, without exceeding gas limits. Should a user require more consumers, they can use multiple subscriptions.
uint72 adminFee; // ║ Flat fee (in Juels of LINK) that will be paid to the Router owner for operation of the network
bytes4 handleOracleFulfillmentSelector; // ║ The function selector that is used when calling back to the Client contract
uint16 gasForCallExactCheck; // ════════════════╝ Used during calling back to the client. Ensures we have at least enough gas to be able to revert if gasAmount > 63//64*gas available.
uint32[] maxCallbackGasLimits; // ══════════════╸ List of max callback gas limits used by flag with GAS_FLAG_INDEX
uint16 subscriptionDepositMinimumRequests; //═══╗ Amount of requests that must be completed before the full subscription balance will be released when closing a subscription account.
uint72 subscriptionDepositJuels; // ════════════╝ Amount of subscription funds that are held as a deposit until Config.subscriptionDepositMinimumRequests are made using the subscription.
}
Config private s_config;
event ConfigUpdated(Config);
// ================================================================
// | Proposal state |
// ================================================================
uint8 private constant MAX_PROPOSAL_SET_LENGTH = 8;
struct ContractProposalSet {
bytes32[] ids; // ══╸ The IDs that key into the routes that will be modified if the update is applied
address[] to; // ═══╸ The address of the contracts that the route will point to if the updated is applied
}
ContractProposalSet private s_proposedContractSet;
event ContractProposed(
bytes32 proposedContractSetId,
address proposedContractSetFromAddress,
address proposedContractSetToAddress
);
event ContractUpdated(bytes32 id, address from, address to);
error InvalidProposal();
error IdentifierIsReserved(bytes32 id);
// ================================================================
// | Initialization |
// ================================================================
constructor(
address linkToken,
Config memory config
) FunctionsSubscriptions(linkToken) ConfirmedOwner(msg.sender) Pausable() {
// Set the intial configuration
updateConfig(config);
}
// ================================================================
// | Configuration |
// ================================================================
/// @notice The identifier of the route to retrieve the address of the access control contract
// The access control contract controls which accounts can manage subscriptions
/// @return id - bytes32 id that can be passed to the "getContractById" of the Router
function getConfig() external view returns (Config memory) {
return s_config;
}
/// @notice The router configuration
function updateConfig(Config memory config) public onlyOwner {
s_config = config;
emit ConfigUpdated(config);
}
/// @inheritdoc IFunctionsRouter
function isValidCallbackGasLimit(uint64 subscriptionId, uint32 callbackGasLimit) public view {
uint8 callbackGasLimitsIndexSelector = uint8(getFlags(subscriptionId)[MAX_CALLBACK_GAS_LIMIT_FLAGS_INDEX]);
if (callbackGasLimitsIndexSelector >= s_config.maxCallbackGasLimits.length) {
revert InvalidGasFlagValue(callbackGasLimitsIndexSelector);
}
uint32 maxCallbackGasLimit = s_config.maxCallbackGasLimits[callbackGasLimitsIndexSelector];
if (callbackGasLimit > maxCallbackGasLimit) {
revert GasLimitTooBig(maxCallbackGasLimit);
}
}
/// @inheritdoc IFunctionsRouter
function getAdminFee() external view override returns (uint72) {
return s_config.adminFee;
}
/// @inheritdoc IFunctionsRouter
function getAllowListId() external view override returns (bytes32) {
return s_allowListId;
}
/// @inheritdoc IFunctionsRouter
function setAllowListId(bytes32 allowListId) external override onlyOwner {
s_allowListId = allowListId;
}
/// @dev Used within FunctionsSubscriptions.sol
function _getMaxConsumers() internal view override returns (uint16) {
return s_config.maxConsumersPerSubscription;
}
/// @dev Used within FunctionsSubscriptions.sol
function _getSubscriptionDepositDetails() internal view override returns (uint16, uint72) {
return (s_config.subscriptionDepositMinimumRequests, s_config.subscriptionDepositJuels);
}
// ================================================================
// | Requests |
// ================================================================
/// @inheritdoc IFunctionsRouter
function sendRequest(
uint64 subscriptionId,
bytes calldata data,
uint16 dataVersion,
uint32 callbackGasLimit,
bytes32 donId
) external override returns (bytes32) {
IFunctionsCoordinator coordinator = IFunctionsCoordinator(getContractById(donId));
return _sendRequest(donId, coordinator, subscriptionId, data, dataVersion, callbackGasLimit);
}
/// @inheritdoc IFunctionsRouter
function sendRequestToProposed(
uint64 subscriptionId,
bytes calldata data,
uint16 dataVersion,
uint32 callbackGasLimit,
bytes32 donId
) external override returns (bytes32) {
IFunctionsCoordinator coordinator = IFunctionsCoordinator(getProposedContractById(donId));
return _sendRequest(donId, coordinator, subscriptionId, data, dataVersion, callbackGasLimit);
}
function _sendRequest(
bytes32 donId,
IFunctionsCoordinator coordinator,
uint64 subscriptionId,
bytes memory data,
uint16 dataVersion,
uint32 callbackGasLimit
) private returns (bytes32) {
_whenNotPaused();
_isExistingSubscription(subscriptionId);
_isAllowedConsumer(msg.sender, subscriptionId);
isValidCallbackGasLimit(subscriptionId, callbackGasLimit);
if (data.length == 0) {
revert EmptyRequestData();
}
Subscription memory subscription = getSubscription(subscriptionId);
Consumer memory consumer = getConsumer(msg.sender, subscriptionId);
uint72 adminFee = s_config.adminFee;
// Forward request to DON
FunctionsResponse.Commitment memory commitment = coordinator.startRequest(
FunctionsResponse.RequestMeta({
requestingContract: msg.sender,
data: data,
subscriptionId: subscriptionId,
dataVersion: dataVersion,
flags: getFlags(subscriptionId),
callbackGasLimit: callbackGasLimit,
adminFee: adminFee,
initiatedRequests: consumer.initiatedRequests,
completedRequests: consumer.completedRequests,
availableBalance: subscription.balance - subscription.blockedBalance,
subscriptionOwner: subscription.owner
})
);
// Do not allow setting a comittment for a requestId that already exists
if (s_requestCommitments[commitment.requestId] != bytes32(0)) {
revert DuplicateRequestId(commitment.requestId);
}
// Store a commitment about the request
s_requestCommitments[commitment.requestId] = keccak256(
abi.encode(
FunctionsResponse.Commitment({
adminFee: adminFee,
coordinator: address(coordinator),
client: msg.sender,
subscriptionId: subscriptionId,
callbackGasLimit: callbackGasLimit,
estimatedTotalCostJuels: commitment.estimatedTotalCostJuels,
timeoutTimestamp: commitment.timeoutTimestamp,
requestId: commitment.requestId,
donFee: commitment.donFee,
gasOverheadBeforeCallback: commitment.gasOverheadBeforeCallback,
gasOverheadAfterCallback: commitment.gasOverheadAfterCallback
})
)
);
_markRequestInFlight(msg.sender, subscriptionId, commitment.estimatedTotalCostJuels);
emit RequestStart({
requestId: commitment.requestId,
donId: donId,
subscriptionId: subscriptionId,
subscriptionOwner: subscription.owner,
requestingContract: msg.sender,
// solhint-disable-next-line avoid-tx-origin
requestInitiator: tx.origin,
data: data,
dataVersion: dataVersion,
callbackGasLimit: callbackGasLimit,
estimatedTotalCostJuels: commitment.estimatedTotalCostJuels
});
return commitment.requestId;
}
// ================================================================
// | Responses |
// ================================================================
/// @inheritdoc IFunctionsRouter
function fulfill(
bytes memory response,
bytes memory err,
uint96 juelsPerGas,
uint96 costWithoutCallback,
address transmitter,
FunctionsResponse.Commitment memory commitment
) external override returns (FunctionsResponse.FulfillResult resultCode, uint96) {
_whenNotPaused();
if (msg.sender != commitment.coordinator) {
revert OnlyCallableFromCoordinator();
}
{
bytes32 commitmentHash = s_requestCommitments[commitment.requestId];
if (commitmentHash == bytes32(0)) {
resultCode = FunctionsResponse.FulfillResult.INVALID_REQUEST_ID;
emit RequestNotProcessed(commitment.requestId, commitment.coordinator, transmitter, resultCode);
return (resultCode, 0);
}
if (keccak256(abi.encode(commitment)) != commitmentHash) {
resultCode = FunctionsResponse.FulfillResult.INVALID_COMMITMENT;
emit RequestNotProcessed(commitment.requestId, commitment.coordinator, transmitter, resultCode);
return (resultCode, 0);
}
// Check that the transmitter has supplied enough gas for the callback to succeed
if (gasleft() < commitment.callbackGasLimit + commitment.gasOverheadAfterCallback) {
resultCode = FunctionsResponse.FulfillResult.INSUFFICIENT_GAS_PROVIDED;
emit RequestNotProcessed(commitment.requestId, commitment.coordinator, transmitter, resultCode);
return (resultCode, 0);
}
}
{
uint96 callbackCost = juelsPerGas * SafeCast.toUint96(commitment.callbackGasLimit);
uint96 totalCostJuels = commitment.adminFee + costWithoutCallback + callbackCost;
// Check that the subscription can still afford to fulfill the request
if (totalCostJuels > getSubscription(commitment.subscriptionId).balance) {
resultCode = FunctionsResponse.FulfillResult.SUBSCRIPTION_BALANCE_INVARIANT_VIOLATION;
emit RequestNotProcessed(commitment.requestId, commitment.coordinator, transmitter, resultCode);
return (resultCode, 0);
}
// Check that the cost has not exceeded the quoted cost
if (totalCostJuels > commitment.estimatedTotalCostJuels) {
resultCode = FunctionsResponse.FulfillResult.COST_EXCEEDS_COMMITMENT;
emit RequestNotProcessed(commitment.requestId, commitment.coordinator, transmitter, resultCode);
return (resultCode, 0);
}
}
delete s_requestCommitments[commitment.requestId];
CallbackResult memory result = _callback(
commitment.requestId,
response,
err,
commitment.callbackGasLimit,
commitment.client
);
resultCode = result.success
? FunctionsResponse.FulfillResult.FULFILLED
: FunctionsResponse.FulfillResult.USER_CALLBACK_ERROR;
Receipt memory receipt = _pay(
commitment.subscriptionId,
commitment.estimatedTotalCostJuels,
commitment.client,
commitment.adminFee,
juelsPerGas,
SafeCast.toUint96(result.gasUsed),
costWithoutCallback
);
emit RequestProcessed({
requestId: commitment.requestId,
subscriptionId: commitment.subscriptionId,
totalCostJuels: receipt.totalCostJuels,
transmitter: transmitter,
resultCode: resultCode,
response: response,
err: err,
callbackReturnData: result.returnData
});
return (resultCode, receipt.callbackGasCostJuels);
}
function _callback(
bytes32 requestId,
bytes memory response,
bytes memory err,
uint32 callbackGasLimit,
address client
) private returns (CallbackResult memory) {
bool destinationNoLongerExists;
assembly {
// solidity calls check that a contract actually exists at the destination, so we do the same
destinationNoLongerExists := iszero(extcodesize(client))
}
if (destinationNoLongerExists) {
// Return without attempting callback
// The subscription will still be charged to reimburse transmitter's gas overhead
return CallbackResult({success: false, gasUsed: 0, returnData: new bytes(0)});
}
bytes memory encodedCallback = abi.encodeWithSelector(
s_config.handleOracleFulfillmentSelector,
requestId,
response,
err
);
uint16 gasForCallExactCheck = s_config.gasForCallExactCheck;
// Call with explicitly the amount of callback gas requested
// Important to not let them exhaust the gas budget and avoid payment.
// NOTE: that callWithExactGas will revert if we do not have sufficient gas
// to give the callee their requested amount.
bool success;
uint256 gasUsed;
// allocate return data memory ahead of time
bytes memory returnData = new bytes(MAX_CALLBACK_RETURN_BYTES);
assembly {
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) {
revert(0, 0)
}
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)), callbackGasLimit)) {
revert(0, 0)
}
// call and report whether we succeeded
// call(gas,addr,value,argsOffset,argsLength,retOffset,retLength)
let gasBeforeCall := gas()
success := call(callbackGasLimit, client, 0, add(encodedCallback, 0x20), mload(encodedCallback), 0, 0)
gasUsed := sub(gasBeforeCall, gas())
// limit our copy to MAX_CALLBACK_RETURN_BYTES bytes
let toCopy := returndatasize()
if gt(toCopy, MAX_CALLBACK_RETURN_BYTES) {
toCopy := MAX_CALLBACK_RETURN_BYTES
}
// Store the length of the copied bytes
mstore(returnData, toCopy)
// copy the bytes from returnData[0:_toCopy]
returndatacopy(add(returnData, 0x20), 0, toCopy)
}
return CallbackResult({success: success, gasUsed: gasUsed, returnData: returnData});
}
// ================================================================
// | Route methods |
// ================================================================
/// @inheritdoc IFunctionsRouter
function getContractById(bytes32 id) public view override returns (address) {
address currentImplementation = s_route[id];
if (currentImplementation == address(0)) {
revert RouteNotFound(id);
}
return currentImplementation;
}
/// @inheritdoc IFunctionsRouter
function getProposedContractById(bytes32 id) public view override returns (address) {
// Iterations will not exceed MAX_PROPOSAL_SET_LENGTH
for (uint8 i = 0; i < s_proposedContractSet.ids.length; ++i) {
if (id == s_proposedContractSet.ids[i]) {
return s_proposedContractSet.to[i];
}
}
revert RouteNotFound(id);
}
// ================================================================
// | Contract Proposal methods |
// ================================================================
/// @inheritdoc IFunctionsRouter
function getProposedContractSet() external view override returns (bytes32[] memory, address[] memory) {
return (s_proposedContractSet.ids, s_proposedContractSet.to);
}
/// @inheritdoc IFunctionsRouter
function proposeContractsUpdate(
bytes32[] memory proposedContractSetIds,
address[] memory proposedContractSetAddresses
) external override onlyOwner {
// IDs and addresses arrays must be of equal length and must not exceed the max proposal length
uint256 idsArrayLength = proposedContractSetIds.length;
if (idsArrayLength != proposedContractSetAddresses.length || idsArrayLength > MAX_PROPOSAL_SET_LENGTH) {
revert InvalidProposal();
}
// NOTE: iterations of this loop will not exceed MAX_PROPOSAL_SET_LENGTH
for (uint256 i = 0; i < idsArrayLength; ++i) {
bytes32 id = proposedContractSetIds[i];
address proposedContract = proposedContractSetAddresses[i];
if (
proposedContract == address(0) || // The Proposed address must be a valid address
s_route[id] == proposedContract // The Proposed address must point to a different address than what is currently set
) {
revert InvalidProposal();
}
emit ContractProposed({
proposedContractSetId: id,
proposedContractSetFromAddress: s_route[id],
proposedContractSetToAddress: proposedContract
});
}
s_proposedContractSet = ContractProposalSet({ids: proposedContractSetIds, to: proposedContractSetAddresses});
}
/// @inheritdoc IFunctionsRouter
function updateContracts() external override onlyOwner {
// Iterations will not exceed MAX_PROPOSAL_SET_LENGTH
for (uint256 i = 0; i < s_proposedContractSet.ids.length; ++i) {
bytes32 id = s_proposedContractSet.ids[i];
address to = s_proposedContractSet.to[i];
emit ContractUpdated({id: id, from: s_route[id], to: to});
s_route[id] = to;
}
delete s_proposedContractSet;
}
// ================================================================
// | Modifiers |
// ================================================================
// Favoring internal functions over actual modifiers to reduce contract size
/// @dev Used within FunctionsSubscriptions.sol
function _whenNotPaused() internal view override {
_requireNotPaused();
}
/// @dev Used within FunctionsSubscriptions.sol
function _onlyRouterOwner() internal view override {
_validateOwnership();
}
/// @dev Used within FunctionsSubscriptions.sol
function _onlySenderThatAcceptedToS() internal view override {
address currentImplementation = s_route[s_allowListId];
if (currentImplementation == address(0)) {
// If not set, ignore this check, allow all access
return;
}
if (!IAccessController(currentImplementation).hasAccess(msg.sender, new bytes(0))) {
revert SenderMustAcceptTermsOfService(msg.sender);
}
}
/// @inheritdoc IFunctionsRouter
function pause() external override onlyOwner {
_pause();
}
/// @inheritdoc IFunctionsRouter
function unpause() external override onlyOwner {
_unpause();
}
}
IAccessController.sol 6 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IAccessController {
function hasAccess(address user, bytes calldata data) external view returns (bool);
}
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 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 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 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 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();
_;
}
}
FunctionsSubscriptions.sol 552 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import {IFunctionsSubscriptions} from "./interfaces/IFunctionsSubscriptions.sol";
import {IERC677Receiver} from "../../../shared/interfaces/IERC677Receiver.sol";
import {IFunctionsBilling} from "./interfaces/IFunctionsBilling.sol";
import {FunctionsResponse} from "./libraries/FunctionsResponse.sol";
import {IERC20} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/utils/SafeERC20.sol";
/// @title Functions Subscriptions contract
/// @notice Contract that coordinates payment from users to the nodes of the Decentralized Oracle Network (DON).
abstract contract FunctionsSubscriptions is IFunctionsSubscriptions, IERC677Receiver {
using SafeERC20 for IERC20;
using FunctionsResponse for FunctionsResponse.Commitment;
// ================================================================
// | Balance state |
// ================================================================
// link token address
IERC20 internal immutable i_linkToken;
// s_totalLinkBalance tracks the total LINK sent to/from
// this contract through onTokenTransfer, cancelSubscription and oracleWithdraw.
// A discrepancy with this contract's LINK balance indicates that someone
// sent tokens using transfer and so we may need to use recoverFunds.
uint96 private s_totalLinkBalance;
/// @dev NOP balances are held as a single amount. The breakdown is held by the Coordinator.
mapping(address coordinator => uint96 balanceJuelsLink) private s_withdrawableTokens;
// ================================================================
// | Subscription state |
// ================================================================
// Keep a count of the number of subscriptions so that its possible to
// loop through all the current subscriptions via .getSubscription().
uint64 private s_currentSubscriptionId;
mapping(uint64 subscriptionId => Subscription) private s_subscriptions;
// Maintains the list of keys in s_consumers.
// We do this for 2 reasons:
// 1. To be able to clean up all keys from s_consumers when canceling a subscription.
// 2. To be able to return the list of all consumers in getSubscription.
// Note that we need the s_consumers map to be able to directly check if a
// consumer is valid without reading all the consumers from storage.
mapping(address consumer => mapping(uint64 subscriptionId => Consumer)) private s_consumers;
event SubscriptionCreated(uint64 indexed subscriptionId, address owner);
event SubscriptionFunded(uint64 indexed subscriptionId, uint256 oldBalance, uint256 newBalance);
event SubscriptionConsumerAdded(uint64 indexed subscriptionId, address consumer);
event SubscriptionConsumerRemoved(uint64 indexed subscriptionId, address consumer);
event SubscriptionCanceled(uint64 indexed subscriptionId, address fundsRecipient, uint256 fundsAmount);
event SubscriptionOwnerTransferRequested(uint64 indexed subscriptionId, address from, address to);
event SubscriptionOwnerTransferred(uint64 indexed subscriptionId, address from, address to);
error TooManyConsumers(uint16 maximumConsumers);
error InsufficientBalance(uint96 currentBalanceJuels);
error InvalidConsumer();
error CannotRemoveWithPendingRequests();
error InvalidSubscription();
error OnlyCallableFromLink();
error InvalidCalldata();
error MustBeSubscriptionOwner();
error TimeoutNotExceeded();
error MustBeProposedOwner(address proposedOwner);
event FundsRecovered(address to, uint256 amount);
// ================================================================
// | Request state |
// ================================================================
mapping(bytes32 requestId => bytes32 commitmentHash) internal s_requestCommitments;
struct Receipt {
uint96 callbackGasCostJuels;
uint96 totalCostJuels;
}
event RequestTimedOut(bytes32 indexed requestId);
// ================================================================
// | Initialization |
// ================================================================
constructor(address link) {
i_linkToken = IERC20(link);
}
// ================================================================
// | Request/Response |
// ================================================================
/// @notice Sets a request as in-flight
/// @dev Only callable within the Router
function _markRequestInFlight(address client, uint64 subscriptionId, uint96 estimatedTotalCostJuels) internal {
// Earmark subscription funds
s_subscriptions[subscriptionId].blockedBalance += estimatedTotalCostJuels;
// Increment sent requests
s_consumers[client][subscriptionId].initiatedRequests += 1;
}
/// @notice Moves funds from one subscription account to another.
/// @dev Only callable by the Coordinator contract that is saved in the request commitment
function _pay(
uint64 subscriptionId,
uint96 estimatedTotalCostJuels,
address client,
uint96 adminFee,
uint96 juelsPerGas,
uint96 gasUsed,
uint96 costWithoutCallbackJuels
) internal returns (Receipt memory) {
uint96 callbackGasCostJuels = juelsPerGas * gasUsed;
uint96 totalCostJuels = costWithoutCallbackJuels + adminFee + callbackGasCostJuels;
if (
s_subscriptions[subscriptionId].balance < totalCostJuels ||
s_subscriptions[subscriptionId].blockedBalance < estimatedTotalCostJuels
) {
revert InsufficientBalance(s_subscriptions[subscriptionId].balance);
}
// Charge the subscription
s_subscriptions[subscriptionId].balance -= totalCostJuels;
// Unblock earmarked funds
s_subscriptions[subscriptionId].blockedBalance -= estimatedTotalCostJuels;
// Pay the DON's fees and gas reimbursement
s_withdrawableTokens[msg.sender] += costWithoutCallbackJuels + callbackGasCostJuels;
// Pay out the administration fee
s_withdrawableTokens[address(this)] += adminFee;
// Increment finished requests
s_consumers[client][subscriptionId].completedRequests += 1;
return Receipt({callbackGasCostJuels: callbackGasCostJuels, totalCostJuels: totalCostJuels});
}
// ================================================================
// | Owner methods |
// ================================================================
/// @inheritdoc IFunctionsSubscriptions
function ownerCancelSubscription(uint64 subscriptionId) external override {
_onlyRouterOwner();
_isExistingSubscription(subscriptionId);
_cancelSubscriptionHelper(subscriptionId, s_subscriptions[subscriptionId].owner, false);
}
/// @inheritdoc IFunctionsSubscriptions
function recoverFunds(address to) external override {
_onlyRouterOwner();
uint256 externalBalance = i_linkToken.balanceOf(address(this));
uint256 internalBalance = uint256(s_totalLinkBalance);
if (internalBalance < externalBalance) {
uint256 amount = externalBalance - internalBalance;
i_linkToken.safeTransfer(to, amount);
emit FundsRecovered(to, amount);
}
// If the balances are equal, nothing to be done.
}
// ================================================================
// | Fund withdrawal |
// ================================================================
/// @inheritdoc IFunctionsSubscriptions
function oracleWithdraw(address recipient, uint96 amount) external override {
_whenNotPaused();
if (amount == 0) {
revert InvalidCalldata();
}
uint96 currentBalance = s_withdrawableTokens[msg.sender];
if (currentBalance < amount) {
revert InsufficientBalance(currentBalance);
}
s_withdrawableTokens[msg.sender] -= amount;
s_totalLinkBalance -= amount;
i_linkToken.safeTransfer(recipient, amount);
}
/// @notice Owner withdraw LINK earned through admin fees
/// @notice If amount is 0 the full balance will be withdrawn
/// @param recipient where to send the funds
/// @param amount amount to withdraw
function ownerWithdraw(address recipient, uint96 amount) external {
_onlyRouterOwner();
if (amount == 0) {
amount = s_withdrawableTokens[address(this)];
}
uint96 currentBalance = s_withdrawableTokens[address(this)];
if (currentBalance < amount) {
revert InsufficientBalance(currentBalance);
}
s_withdrawableTokens[address(this)] -= amount;
s_totalLinkBalance -= amount;
i_linkToken.safeTransfer(recipient, amount);
}
// ================================================================
// | TransferAndCall Deposit helper |
// ================================================================
// This function is to be invoked when using LINK.transferAndCall
/// @dev Note to fund the subscription, use transferAndCall. For example
/// @dev LINKTOKEN.transferAndCall(
/// @dev address(ROUTER),
/// @dev amount,
/// @dev abi.encode(subscriptionId));
function onTokenTransfer(address /* sender */, uint256 amount, bytes calldata data) external override {
_whenNotPaused();
if (msg.sender != address(i_linkToken)) {
revert OnlyCallableFromLink();
}
if (data.length != 32) {
revert InvalidCalldata();
}
uint64 subscriptionId = abi.decode(data, (uint64));
if (s_subscriptions[subscriptionId].owner == address(0)) {
revert InvalidSubscription();
}
// We do not check that the msg.sender is the subscription owner,
// anyone can fund a subscription.
uint256 oldBalance = s_subscriptions[subscriptionId].balance;
s_subscriptions[subscriptionId].balance += uint96(amount);
s_totalLinkBalance += uint96(amount);
emit SubscriptionFunded(subscriptionId, oldBalance, oldBalance + amount);
}
// ================================================================
// | Subscription management |
// ================================================================
/// @inheritdoc IFunctionsSubscriptions
function getTotalBalance() external view override returns (uint96) {
return s_totalLinkBalance;
}
/// @inheritdoc IFunctionsSubscriptions
function getSubscriptionCount() external view override returns (uint64) {
return s_currentSubscriptionId;
}
/// @inheritdoc IFunctionsSubscriptions
function getSubscription(uint64 subscriptionId) public view override returns (Subscription memory) {
_isExistingSubscription(subscriptionId);
return s_subscriptions[subscriptionId];
}
/// @inheritdoc IFunctionsSubscriptions
function getSubscriptionsInRange(
uint64 subscriptionIdStart,
uint64 subscriptionIdEnd
) external view override returns (Subscription[] memory subscriptions) {
if (
subscriptionIdStart > subscriptionIdEnd ||
subscriptionIdEnd > s_currentSubscriptionId ||
s_currentSubscriptionId == 0
) {
revert InvalidCalldata();
}
subscriptions = new Subscription[]((subscriptionIdEnd - subscriptionIdStart) + 1);
for (uint256 i = 0; i <= subscriptionIdEnd - subscriptionIdStart; ++i) {
subscriptions[i] = s_subscriptions[uint64(subscriptionIdStart + i)];
}
return subscriptions;
}
/// @inheritdoc IFunctionsSubscriptions
function getConsumer(address client, uint64 subscriptionId) public view override returns (Consumer memory) {
return s_consumers[client][subscriptionId];
}
/// @dev Used within this file & FunctionsRouter.sol
function _isExistingSubscription(uint64 subscriptionId) internal view {
if (s_subscriptions[subscriptionId].owner == address(0)) {
revert InvalidSubscription();
}
}
/// @dev Used within FunctionsRouter.sol
function _isAllowedConsumer(address client, uint64 subscriptionId) internal view {
if (!s_consumers[client][subscriptionId].allowed) {
revert InvalidConsumer();
}
}
/// @inheritdoc IFunctionsSubscriptions
function createSubscription() external override returns (uint64 subscriptionId) {
_whenNotPaused();
_onlySenderThatAcceptedToS();
subscriptionId = ++s_currentSubscriptionId;
s_subscriptions[subscriptionId] = Subscription({
balance: 0,
blockedBalance: 0,
owner: msg.sender,
proposedOwner: address(0),
consumers: new address[](0),
flags: bytes32(0)
});
emit SubscriptionCreated(subscriptionId, msg.sender);
return subscriptionId;
}
/// @inheritdoc IFunctionsSubscriptions
function createSubscriptionWithConsumer(address consumer) external override returns (uint64 subscriptionId) {
_whenNotPaused();
_onlySenderThatAcceptedToS();
subscriptionId = ++s_currentSubscriptionId;
s_subscriptions[subscriptionId] = Subscription({
balance: 0,
blockedBalance: 0,
owner: msg.sender,
proposedOwner: address(0),
consumers: new address[](0),
flags: bytes32(0)
});
s_subscriptions[subscriptionId].consumers.push(consumer);
s_consumers[consumer][subscriptionId].allowed = true;
emit SubscriptionCreated(subscriptionId, msg.sender);
emit SubscriptionConsumerAdded(subscriptionId, consumer);
return subscriptionId;
}
/// @inheritdoc IFunctionsSubscriptions
function proposeSubscriptionOwnerTransfer(uint64 subscriptionId, address newOwner) external override {
_whenNotPaused();
_onlySubscriptionOwner(subscriptionId);
_onlySenderThatAcceptedToS();
if (newOwner == address(0) || s_subscriptions[subscriptionId].proposedOwner == newOwner) {
revert InvalidCalldata();
}
s_subscriptions[subscriptionId].proposedOwner = newOwner;
emit SubscriptionOwnerTransferRequested(subscriptionId, msg.sender, newOwner);
}
/// @inheritdoc IFunctionsSubscriptions
function acceptSubscriptionOwnerTransfer(uint64 subscriptionId) external override {
_whenNotPaused();
_onlySenderThatAcceptedToS();
address previousOwner = s_subscriptions[subscriptionId].owner;
address proposedOwner = s_subscriptions[subscriptionId].proposedOwner;
if (proposedOwner != msg.sender) {
revert MustBeProposedOwner(proposedOwner);
}
s_subscriptions[subscriptionId].owner = msg.sender;
s_subscriptions[subscriptionId].proposedOwner = address(0);
emit SubscriptionOwnerTransferred(subscriptionId, previousOwner, msg.sender);
}
/// @inheritdoc IFunctionsSubscriptions
function removeConsumer(uint64 subscriptionId, address consumer) external override {
_whenNotPaused();
_onlySubscriptionOwner(subscriptionId);
_onlySenderThatAcceptedToS();
Consumer memory consumerData = s_consumers[consumer][subscriptionId];
_isAllowedConsumer(consumer, subscriptionId);
if (consumerData.initiatedRequests != consumerData.completedRequests) {
revert CannotRemoveWithPendingRequests();
}
// Note bounded by config.maxConsumers
address[] memory consumers = s_subscriptions[subscriptionId].consumers;
for (uint256 i = 0; i < consumers.length; ++i) {
if (consumers[i] == consumer) {
// Storage write to preserve last element
s_subscriptions[subscriptionId].consumers[i] = consumers[consumers.length - 1];
// Storage remove last element
s_subscriptions[subscriptionId].consumers.pop();
break;
}
}
delete s_consumers[consumer][subscriptionId];
emit SubscriptionConsumerRemoved(subscriptionId, consumer);
}
/// @dev Overriden in FunctionsRouter.sol
function _getMaxConsumers() internal view virtual returns (uint16);
/// @inheritdoc IFunctionsSubscriptions
function addConsumer(uint64 subscriptionId, address consumer) external override {
_whenNotPaused();
_onlySubscriptionOwner(subscriptionId);
_onlySenderThatAcceptedToS();
// Already maxed, cannot add any more consumers.
uint16 maximumConsumers = _getMaxConsumers();
if (s_subscriptions[subscriptionId].consumers.length >= maximumConsumers) {
revert TooManyConsumers(maximumConsumers);
}
if (s_consumers[consumer][subscriptionId].allowed) {
// Idempotence - do nothing if already added.
// Ensures uniqueness in s_subscriptions[subscriptionId].consumers.
return;
}
s_consumers[consumer][subscriptionId].allowed = true;
s_subscriptions[subscriptionId].consumers.push(consumer);
emit SubscriptionConsumerAdded(subscriptionId, consumer);
}
/// @dev Overriden in FunctionsRouter.sol
function _getSubscriptionDepositDetails() internal virtual returns (uint16, uint72);
function _cancelSubscriptionHelper(uint64 subscriptionId, address toAddress, bool checkDepositRefundability) private {
Subscription memory subscription = s_subscriptions[subscriptionId];
uint96 balance = subscription.balance;
uint64 completedRequests = 0;
// NOTE: loop iterations are bounded by config.maxConsumers
// If no consumers, does nothing.
for (uint256 i = 0; i < subscription.consumers.length; ++i) {
address consumer = subscription.consumers[i];
completedRequests += s_consumers[consumer][subscriptionId].completedRequests;
delete s_consumers[consumer][subscriptionId];
}
delete s_subscriptions[subscriptionId];
(uint16 subscriptionDepositMinimumRequests, uint72 subscriptionDepositJuels) = _getSubscriptionDepositDetails();
// If subscription has not made enough requests, deposit will be forfeited
if (checkDepositRefundability && completedRequests < subscriptionDepositMinimumRequests) {
uint96 deposit = subscriptionDepositJuels > balance ? balance : subscriptionDepositJuels;
if (deposit > 0) {
s_withdrawableTokens[address(this)] += deposit;
balance -= deposit;
}
}
if (balance > 0) {
s_totalLinkBalance -= balance;
i_linkToken.safeTransfer(toAddress, uint256(balance));
}
emit SubscriptionCanceled(subscriptionId, toAddress, balance);
}
/// @inheritdoc IFunctionsSubscriptions
function cancelSubscription(uint64 subscriptionId, address to) external override {
_whenNotPaused();
_onlySubscriptionOwner(subscriptionId);
_onlySenderThatAcceptedToS();
if (pendingRequestExists(subscriptionId)) {
revert CannotRemoveWithPendingRequests();
}
_cancelSubscriptionHelper(subscriptionId, to, true);
}
/// @inheritdoc IFunctionsSubscriptions
function pendingRequestExists(uint64 subscriptionId) public view override returns (bool) {
address[] memory consumers = s_subscriptions[subscriptionId].consumers;
// NOTE: loop iterations are bounded by config.maxConsumers
for (uint256 i = 0; i < consumers.length; ++i) {
Consumer memory consumer = s_consumers[consumers[i]][subscriptionId];
if (consumer.initiatedRequests != consumer.completedRequests) {
return true;
}
}
return false;
}
/// @inheritdoc IFunctionsSubscriptions
function setFlags(uint64 subscriptionId, bytes32 flags) external override {
_onlyRouterOwner();
_isExistingSubscription(subscriptionId);
s_subscriptions[subscriptionId].flags = flags;
}
/// @inheritdoc IFunctionsSubscriptions
function getFlags(uint64 subscriptionId) public view returns (bytes32) {
return s_subscriptions[subscriptionId].flags;
}
// ================================================================
// | Request Timeout |
// ================================================================
/// @inheritdoc IFunctionsSubscriptions
function timeoutRequests(FunctionsResponse.Commitment[] calldata requestsToTimeoutByCommitment) external override {
_whenNotPaused();
for (uint256 i = 0; i < requestsToTimeoutByCommitment.length; ++i) {
FunctionsResponse.Commitment memory request = requestsToTimeoutByCommitment[i];
bytes32 requestId = request.requestId;
uint64 subscriptionId = request.subscriptionId;
// Check that request ID is valid
if (keccak256(abi.encode(request)) != s_requestCommitments[requestId]) {
revert InvalidCalldata();
}
// Check that request has exceeded allowed request time
if (block.timestamp < request.timeoutTimestamp) {
revert TimeoutNotExceeded();
}
// Notify the Coordinator that the request should no longer be fulfilled
IFunctionsBilling(request.coordinator).deleteCommitment(requestId);
// Release the subscription's balance that had been earmarked for the request
s_subscriptions[subscriptionId].blockedBalance -= request.estimatedTotalCostJuels;
s_consumers[request.client][subscriptionId].completedRequests += 1;
// Delete commitment within Router state
delete s_requestCommitments[requestId];
emit RequestTimedOut(requestId);
}
}
// ================================================================
// | Modifiers |
// ================================================================
function _onlySubscriptionOwner(uint64 subscriptionId) internal view {
address owner = s_subscriptions[subscriptionId].owner;
if (owner == address(0)) {
revert InvalidSubscription();
}
if (msg.sender != owner) {
revert MustBeSubscriptionOwner();
}
}
/// @dev Overriden in FunctionsRouter.sol
function _onlySenderThatAcceptedToS() internal virtual;
/// @dev Overriden in FunctionsRouter.sol
function _onlyRouterOwner() internal virtual;
/// @dev Overriden in FunctionsRouter.sol
function _whenNotPaused() internal virtual;
}
IFunctionsRouter.sol 109 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import {FunctionsResponse} from "../libraries/FunctionsResponse.sol";
/// @title Chainlink Functions Router interface.
interface IFunctionsRouter {
/// @notice The identifier of the route to retrieve the address of the access control contract
/// The access control contract controls which accounts can manage subscriptions
/// @return id - bytes32 id that can be passed to the "getContractById" of the Router
function getAllowListId() external view returns (bytes32);
/// @notice Set the identifier of the route to retrieve the address of the access control contract
/// The access control contract controls which accounts can manage subscriptions
function setAllowListId(bytes32 allowListId) external;
/// @notice Get the flat fee (in Juels of LINK) that will be paid to the Router owner for operation of the network
/// @return adminFee
function getAdminFee() external view returns (uint72 adminFee);
/// @notice Sends a request using the provided subscriptionId
/// @param subscriptionId - A unique subscription ID allocated by billing system,
/// a client can make requests from different contracts referencing the same subscription
/// @param data - CBOR encoded Chainlink Functions request data, use FunctionsClient API to encode a request
/// @param dataVersion - Gas limit for the fulfillment callback
/// @param callbackGasLimit - Gas limit for the fulfillment callback
/// @param donId - An identifier used to determine which route to send the request along
/// @return requestId - A unique request identifier
function sendRequest(
uint64 subscriptionId,
bytes calldata data,
uint16 dataVersion,
uint32 callbackGasLimit,
bytes32 donId
) external returns (bytes32);
/// @notice Sends a request to the proposed contracts
/// @param subscriptionId - A unique subscription ID allocated by billing system,
/// a client can make requests from different contracts referencing the same subscription
/// @param data - CBOR encoded Chainlink Functions request data, use FunctionsClient API to encode a request
/// @param dataVersion - Gas limit for the fulfillment callback
/// @param callbackGasLimit - Gas limit for the fulfillment callback
/// @param donId - An identifier used to determine which route to send the request along
/// @return requestId - A unique request identifier
function sendRequestToProposed(
uint64 subscriptionId,
bytes calldata data,
uint16 dataVersion,
uint32 callbackGasLimit,
bytes32 donId
) external returns (bytes32);
/// @notice Fulfill the request by:
/// - calling back the data that the Oracle returned to the client contract
/// - pay the DON for processing the request
/// @dev Only callable by the Coordinator contract that is saved in the commitment
/// @param response response data from DON consensus
/// @param err error from DON consensus
/// @param juelsPerGas - current rate of juels/gas
/// @param costWithoutFulfillment - The cost of processing the request (in Juels of LINK ), without fulfillment
/// @param transmitter - The Node that transmitted the OCR report
/// @param commitment - The parameters of the request that must be held consistent between request and response time
/// @return fulfillResult -
/// @return callbackGasCostJuels -
function fulfill(
bytes memory response,
bytes memory err,
uint96 juelsPerGas,
uint96 costWithoutFulfillment,
address transmitter,
FunctionsResponse.Commitment memory commitment
) external returns (FunctionsResponse.FulfillResult, uint96);
/// @notice Validate requested gas limit is below the subscription max.
/// @param subscriptionId subscription ID
/// @param callbackGasLimit desired callback gas limit
function isValidCallbackGasLimit(uint64 subscriptionId, uint32 callbackGasLimit) external view;
/// @notice Get the current contract given an ID
/// @param id A bytes32 identifier for the route
/// @return contract The current contract address
function getContractById(bytes32 id) external view returns (address);
/// @notice Get the proposed next contract given an ID
/// @param id A bytes32 identifier for the route
/// @return contract The current or proposed contract address
function getProposedContractById(bytes32 id) external view returns (address);
/// @notice Return the latest proprosal set
/// @return ids The identifiers of the contracts to update
/// @return to The addresses of the contracts that will be updated to
function getProposedContractSet() external view returns (bytes32[] memory, address[] memory);
/// @notice Proposes one or more updates to the contract routes
/// @dev Only callable by owner
function proposeContractsUpdate(bytes32[] memory proposalSetIds, address[] memory proposalSetAddresses) external;
/// @notice Updates the current contract routes to the proposed contracts
/// @dev Only callable by owner
function updateContracts() external;
/// @dev Puts the system into an emergency stopped state.
/// @dev Only callable by owner
function pause() external;
/// @dev Takes the system out of an emergency stopped state.
/// @dev Only callable by owner
function unpause() external;
}
FunctionsResponse.sol 44 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
/// @title Library of types that are used for fulfillment of a Functions request
library FunctionsResponse {
// Used to send request information from the Router to the Coordinator
struct RequestMeta {
bytes data; // ══════════════════╸ CBOR encoded Chainlink Functions request data, use FunctionsRequest library to encode a request
bytes32 flags; // ═══════════════╸ Per-subscription flags
address requestingContract; // ══╗ The client contract that is sending the request
uint96 availableBalance; // ═════╝ Common LINK balance of the subscription that is controlled by the Router to be used for all consumer requests.
uint72 adminFee; // ═════════════╗ Flat fee (in Juels of LINK) that will be paid to the Router Owner for operation of the network
uint64 subscriptionId; // ║ Identifier of the billing subscription that will be charged for the request
uint64 initiatedRequests; // ║ The number of requests that have been started
uint32 callbackGasLimit; // ║ The amount of gas that the callback to the consuming contract will be given
uint16 dataVersion; // ══════════╝ The version of the structure of the CBOR encoded request data
uint64 completedRequests; // ════╗ The number of requests that have successfully completed or timed out
address subscriptionOwner; // ═══╝ The owner of the billing subscription
}
enum FulfillResult {
FULFILLED, // 0
USER_CALLBACK_ERROR, // 1
INVALID_REQUEST_ID, // 2
COST_EXCEEDS_COMMITMENT, // 3
INSUFFICIENT_GAS_PROVIDED, // 4
SUBSCRIPTION_BALANCE_INVARIANT_VIOLATION, // 5
INVALID_COMMITMENT // 6
}
struct Commitment {
bytes32 requestId; // ═════════════════╸ A unique identifier for a Chainlink Functions request
address coordinator; // ═══════════════╗ The Coordinator contract that manages the DON that is servicing a request
uint96 estimatedTotalCostJuels; // ════╝ The maximum cost in Juels (1e18) of LINK that will be charged to fulfill a request
address client; // ════════════════════╗ The client contract that sent the request
uint64 subscriptionId; // ║ Identifier of the billing subscription that will be charged for the request
uint32 callbackGasLimit; // ═══════════╝ The amount of gas that the callback to the consuming contract will be given
uint72 adminFee; // ═══════════════════╗ Flat fee (in Juels of LINK) that will be paid to the Router Owner for operation of the network
uint72 donFee; // ║ Fee (in Juels of LINK) that will be split between Node Operators for servicing a request
uint40 gasOverheadBeforeCallback; // ║ Represents the average gas execution cost before the fulfillment callback.
uint40 gasOverheadAfterCallback; // ║ Represents the average gas execution cost after the fulfillment callback.
uint32 timeoutTimestamp; // ═══════════╝ The timestamp at which a request will be eligible to be timed out
}
}
IFunctionsBilling.sol 44 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
/// @title Chainlink Functions DON billing interface.
interface IFunctionsBilling {
/// @notice Return the current conversion from WEI of ETH to LINK from the configured Chainlink data feed
/// @return weiPerUnitLink - The amount of WEI in one LINK
function getWeiPerUnitLink() external view returns (uint256);
/// @notice Determine the fee that will be split between Node Operators for servicing a request
/// @param requestCBOR - CBOR encoded Chainlink Functions request data, use FunctionsRequest library to encode a request
/// @return fee - Cost in Juels (1e18) of LINK
function getDONFee(bytes memory requestCBOR) external view returns (uint72);
/// @notice Determine the fee that will be paid to the Router owner for operating the network
/// @return fee - Cost in Juels (1e18) of LINK
function getAdminFee() external view returns (uint72);
/// @notice Estimate the total cost that will be charged to a subscription to make a request: transmitter gas re-reimbursement, plus DON fee, plus Registry fee
/// @param - subscriptionId An identifier of the billing account
/// @param - data Encoded Chainlink Functions request data, use FunctionsClient API to encode a request
/// @param - callbackGasLimit Gas limit for the fulfillment callback
/// @param - gasPriceWei The blockchain's gas price to estimate with
/// @return - billedCost Cost in Juels (1e18) of LINK
function estimateCost(
uint64 subscriptionId,
bytes calldata data,
uint32 callbackGasLimit,
uint256 gasPriceWei
) external view returns (uint96);
/// @notice Remove a request commitment that the Router has determined to be stale
/// @param requestId - The request ID to remove
function deleteCommitment(bytes32 requestId) external;
/// @notice Oracle withdraw LINK earned through fulfilling requests
/// @notice If amount is 0 the full balance will be withdrawn
/// @param recipient where to send the funds
/// @param amount amount to withdraw
function oracleWithdraw(address recipient, uint96 amount) external;
/// @notice Withdraw all LINK earned by Oracles through fulfilling requests
function oracleWithdrawAll() external;
}
IFunctionsCoordinator.sol 37 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import {FunctionsResponse} from "../libraries/FunctionsResponse.sol";
/// @title Chainlink Functions DON Coordinator interface.
interface IFunctionsCoordinator {
/// @notice Returns the DON's threshold encryption public key used to encrypt secrets
/// @dev All nodes on the DON have separate key shares of the threshold decryption key
/// and nodes must participate in a threshold decryption OCR round to decrypt secrets
/// @return thresholdPublicKey the DON's threshold encryption public key
function getThresholdPublicKey() external view returns (bytes memory);
/// @notice Sets the DON's threshold encryption public key used to encrypt secrets
/// @dev Used to rotate the key
/// @param thresholdPublicKey The new public key
function setThresholdPublicKey(bytes calldata thresholdPublicKey) external;
/// @notice Returns the DON's secp256k1 public key that is used to encrypt secrets
/// @dev All nodes on the DON have the corresponding private key
/// needed to decrypt the secrets encrypted with the public key
/// @return publicKey the DON's public key
function getDONPublicKey() external view returns (bytes memory);
/// @notice Sets DON's secp256k1 public key used to encrypt secrets
/// @dev Used to rotate the key
/// @param donPublicKey The new public key
function setDONPublicKey(bytes calldata donPublicKey) external;
/// @notice Receives a request to be emitted to the DON for processing
/// @param request The request metadata
/// @dev see the struct for field descriptions
/// @return commitment - The parameters of the request that must be held consistent at response time
function startRequest(
FunctionsResponse.RequestMeta calldata request
) external returns (FunctionsResponse.Commitment memory commitment);
}
IFunctionsSubscriptions.sol 140 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import {FunctionsResponse} from "../libraries/FunctionsResponse.sol";
/// @title Chainlink Functions Subscription interface.
interface IFunctionsSubscriptions {
struct Subscription {
uint96 balance; // ═════════╗ Common LINK balance that is controlled by the Router to be used for all consumer requests.
address owner; // ══════════╝ The owner can fund/withdraw/cancel the subscription.
uint96 blockedBalance; // ══╗ LINK balance that is reserved to pay for pending consumer requests.
address proposedOwner; // ══╝ For safely transferring sub ownership.
address[] consumers; // ════╸ Client contracts that can use the subscription
bytes32 flags; // ══════════╸ Per-subscription flags
}
struct Consumer {
bool allowed; // ══════════════╗ Owner can fund/withdraw/cancel the sub.
uint64 initiatedRequests; // ║ The number of requests that have been started
uint64 completedRequests; // ══╝ The number of requests that have successfully completed or timed out
}
/// @notice Get details about a subscription.
/// @param subscriptionId - the ID of the subscription
/// @return subscription - see IFunctionsSubscriptions.Subscription for more information on the structure
function getSubscription(uint64 subscriptionId) external view returns (Subscription memory);
/// @notice Retrieve details about multiple subscriptions using an inclusive range
/// @param subscriptionIdStart - the ID of the subscription to start the range at
/// @param subscriptionIdEnd - the ID of the subscription to end the range at
/// @return subscriptions - see IFunctionsSubscriptions.Subscription for more information on the structure
function getSubscriptionsInRange(
uint64 subscriptionIdStart,
uint64 subscriptionIdEnd
) external view returns (Subscription[] memory);
/// @notice Get details about a consumer of a subscription.
/// @param client - the consumer contract address
/// @param subscriptionId - the ID of the subscription
/// @return consumer - see IFunctionsSubscriptions.Consumer for more information on the structure
function getConsumer(address client, uint64 subscriptionId) external view returns (Consumer memory);
/// @notice Get details about the total amount of LINK within the system
/// @return totalBalance - total Juels of LINK held by the contract
function getTotalBalance() external view returns (uint96);
/// @notice Get details about the total number of subscription accounts
/// @return count - total number of subscriptions in the system
function getSubscriptionCount() external view returns (uint64);
/// @notice Time out all expired requests: unlocks funds and removes the ability for the request to be fulfilled
/// @param requestsToTimeoutByCommitment - A list of request commitments to time out
/// @dev The commitment can be found on the "OracleRequest" event created when sending the request.
function timeoutRequests(FunctionsResponse.Commitment[] calldata requestsToTimeoutByCommitment) external;
/// @notice Oracle withdraw LINK earned through fulfilling requests
/// @notice If amount is 0 the full balance will be withdrawn
/// @notice Both signing and transmitting wallets will have a balance to withdraw
/// @param recipient where to send the funds
/// @param amount amount to withdraw
function oracleWithdraw(address recipient, uint96 amount) external;
/// @notice Owner cancel subscription, sends remaining link directly to the subscription owner.
/// @dev Only callable by the Router Owner
/// @param subscriptionId subscription id
/// @dev notably can be called even if there are pending requests, outstanding ones may fail onchain
function ownerCancelSubscription(uint64 subscriptionId) external;
/// @notice Recover link sent with transfer instead of transferAndCall.
/// @dev Only callable by the Router Owner
/// @param to address to send link to
function recoverFunds(address to) external;
/// @notice Create a new subscription.
/// @return subscriptionId - A unique subscription id.
/// @dev You can manage the consumer set dynamically with addConsumer/removeConsumer.
/// @dev Note to fund the subscription, use transferAndCall. For example
/// @dev LINKTOKEN.transferAndCall(
/// @dev address(ROUTER),
/// @dev amount,
/// @dev abi.encode(subscriptionId));
function createSubscription() external returns (uint64);
/// @notice Create a new subscription and add a consumer.
/// @return subscriptionId - A unique subscription id.
/// @dev You can manage the consumer set dynamically with addConsumer/removeConsumer.
/// @dev Note to fund the subscription, use transferAndCall. For example
/// @dev LINKTOKEN.transferAndCall(
/// @dev address(ROUTER),
/// @dev amount,
/// @dev abi.encode(subscriptionId));
function createSubscriptionWithConsumer(address consumer) external returns (uint64 subscriptionId);
/// @notice Propose a new owner for a subscription.
/// @dev Only callable by the Subscription's owner
/// @param subscriptionId - ID of the subscription
/// @param newOwner - proposed new owner of the subscription
function proposeSubscriptionOwnerTransfer(uint64 subscriptionId, address newOwner) external;
/// @notice Accept an ownership transfer.
/// @param subscriptionId - ID of the subscription
/// @dev will revert if original owner of subscriptionId has not requested that msg.sender become the new owner.
function acceptSubscriptionOwnerTransfer(uint64 subscriptionId) external;
/// @notice Remove a consumer from a Chainlink Functions subscription.
/// @dev Only callable by the Subscription's owner
/// @param subscriptionId - ID of the subscription
/// @param consumer - Consumer to remove from the subscription
function removeConsumer(uint64 subscriptionId, address consumer) external;
/// @notice Add a consumer to a Chainlink Functions subscription.
/// @dev Only callable by the Subscription's owner
/// @param subscriptionId - ID of the subscription
/// @param consumer - New consumer which can use the subscription
function addConsumer(uint64 subscriptionId, address consumer) external;
/// @notice Cancel a subscription
/// @dev Only callable by the Subscription's owner
/// @param subscriptionId - ID of the subscription
/// @param to - Where to send the remaining LINK to
function cancelSubscription(uint64 subscriptionId, address to) external;
/// @notice Check to see if there exists a request commitment for all consumers for a given sub.
/// @param subscriptionId - ID of the subscription
/// @return true if there exists at least one unfulfilled request for the subscription, false otherwise.
/// @dev Looping is bounded to MAX_CONSUMERS*(number of DONs).
/// @dev Used to disable subscription canceling while outstanding request are present.
function pendingRequestExists(uint64 subscriptionId) external view returns (bool);
/// @notice Set subscription specific flags for a subscription.
/// Each byte of the flag is used to represent a resource tier that the subscription can utilize.
/// @param subscriptionId - ID of the subscription
/// @param flags - desired flag values
function setFlags(uint64 subscriptionId, bytes32 flags) external;
/// @notice Get flags for a given subscription.
/// @param subscriptionId - ID of the subscription
/// @return flags - current flag values
function getFlags(uint64 subscriptionId) external view returns (bytes32);
}
Address.sol 236 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol)
pragma solidity ^0.8.1;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
* ====
*
* [IMPORTANT]
* ====
* You shouldn't rely on `isContract` to protect against flash loan attacks!
*
* Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
* like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
* constructor.
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize/address.code.length, which returns 0
// for contracts in construction, since the code is only stored at the end
// of the constructor execution.
return account.code.length > 0;
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason, it is bubbled up by this
* function (like regular Solidity function calls).
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*
* _Available since v3.1._
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
* the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
*
* _Available since v4.8._
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata,
string memory errorMessage
) internal view returns (bytes memory) {
if (success) {
if (returndata.length == 0) {
// only check isContract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
require(isContract(target), "Address: call to non-contract");
}
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
/**
* @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason or using the provided one.
*
* _Available since v4.3._
*/
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function _revert(bytes memory returndata, string memory errorMessage) private pure {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
Context.sol 24 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
pragma solidity ^0.8.0;
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
}
Pausable.sol 105 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol)
pragma solidity ^0.8.0;
import "../utils/Context.sol";
/**
* @dev Contract module which allows children to implement an emergency stop
* mechanism that can be triggered by an authorized account.
*
* This module is used through inheritance. It will make available the
* modifiers `whenNotPaused` and `whenPaused`, which can be applied to
* the functions of your contract. Note that they will not be pausable by
* simply including this module, only once the modifiers are put in place.
*/
abstract contract Pausable is Context {
/**
* @dev Emitted when the pause is triggered by `account`.
*/
event Paused(address account);
/**
* @dev Emitted when the pause is lifted by `account`.
*/
event Unpaused(address account);
bool private _paused;
/**
* @dev Initializes the contract in unpaused state.
*/
constructor() {
_paused = false;
}
/**
* @dev Modifier to make a function callable only when the contract is not paused.
*
* Requirements:
*
* - The contract must not be paused.
*/
modifier whenNotPaused() {
_requireNotPaused();
_;
}
/**
* @dev Modifier to make a function callable only when the contract is paused.
*
* Requirements:
*
* - The contract must be paused.
*/
modifier whenPaused() {
_requirePaused();
_;
}
/**
* @dev Returns true if the contract is paused, and false otherwise.
*/
function paused() public view virtual returns (bool) {
return _paused;
}
/**
* @dev Throws if the contract is paused.
*/
function _requireNotPaused() internal view virtual {
require(!paused(), "Pausable: paused");
}
/**
* @dev Throws if the contract is not paused.
*/
function _requirePaused() internal view virtual {
require(paused(), "Pausable: not paused");
}
/**
* @dev Triggers stopped state.
*
* Requirements:
*
* - The contract must not be paused.
*/
function _pause() internal virtual whenNotPaused {
_paused = true;
emit Paused(_msgSender());
}
/**
* @dev Returns to normal state.
*
* Requirements:
*
* - The contract must be paused.
*/
function _unpause() internal virtual whenPaused {
_paused = false;
emit Unpaused(_msgSender());
}
}
IERC20.sol 78 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `from` to `to` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 amount) external returns (bool);
}
SafeCast.sol 1136 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SafeCast.sol)
// This file was procedurally generated from scripts/generate/templates/SafeCast.js.
pragma solidity ^0.8.0;
/**
* @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow
* checks.
*
* Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
* easily result in undesired exploitation or bugs, since developers usually
* assume that overflows raise errors. `SafeCast` restores this intuition by
* reverting the transaction when such an operation overflows.
*
* Using this library instead of the unchecked operations eliminates an entire
* class of bugs, so it's recommended to use it always.
*
* Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing
* all math on `uint256` and `int256` and then downcasting.
*/
library SafeCast {
/**
* @dev Returns the downcasted uint248 from uint256, reverting on
* overflow (when the input is greater than largest uint248).
*
* Counterpart to Solidity's `uint248` operator.
*
* Requirements:
*
* - input must fit into 248 bits
*
* _Available since v4.7._
*/
function toUint248(uint256 value) internal pure returns (uint248) {
require(value <= type(uint248).max, "SafeCast: value doesn't fit in 248 bits");
return uint248(value);
}
/**
* @dev Returns the downcasted uint240 from uint256, reverting on
* overflow (when the input is greater than largest uint240).
*
* Counterpart to Solidity's `uint240` operator.
*
* Requirements:
*
* - input must fit into 240 bits
*
* _Available since v4.7._
*/
function toUint240(uint256 value) internal pure returns (uint240) {
require(value <= type(uint240).max, "SafeCast: value doesn't fit in 240 bits");
return uint240(value);
}
/**
* @dev Returns the downcasted uint232 from uint256, reverting on
* overflow (when the input is greater than largest uint232).
*
* Counterpart to Solidity's `uint232` operator.
*
* Requirements:
*
* - input must fit into 232 bits
*
* _Available since v4.7._
*/
function toUint232(uint256 value) internal pure returns (uint232) {
require(value <= type(uint232).max, "SafeCast: value doesn't fit in 232 bits");
return uint232(value);
}
/**
* @dev Returns the downcasted uint224 from uint256, reverting on
* overflow (when the input is greater than largest uint224).
*
* Counterpart to Solidity's `uint224` operator.
*
* Requirements:
*
* - input must fit into 224 bits
*
* _Available since v4.2._
*/
function toUint224(uint256 value) internal pure returns (uint224) {
require(value <= type(uint224).max, "SafeCast: value doesn't fit in 224 bits");
return uint224(value);
}
/**
* @dev Returns the downcasted uint216 from uint256, reverting on
* overflow (when the input is greater than largest uint216).
*
* Counterpart to Solidity's `uint216` operator.
*
* Requirements:
*
* - input must fit into 216 bits
*
* _Available since v4.7._
*/
function toUint216(uint256 value) internal pure returns (uint216) {
require(value <= type(uint216).max, "SafeCast: value doesn't fit in 216 bits");
return uint216(value);
}
/**
* @dev Returns the downcasted uint208 from uint256, reverting on
* overflow (when the input is greater than largest uint208).
*
* Counterpart to Solidity's `uint208` operator.
*
* Requirements:
*
* - input must fit into 208 bits
*
* _Available since v4.7._
*/
function toUint208(uint256 value) internal pure returns (uint208) {
require(value <= type(uint208).max, "SafeCast: value doesn't fit in 208 bits");
return uint208(value);
}
/**
* @dev Returns the downcasted uint200 from uint256, reverting on
* overflow (when the input is greater than largest uint200).
*
* Counterpart to Solidity's `uint200` operator.
*
* Requirements:
*
* - input must fit into 200 bits
*
* _Available since v4.7._
*/
function toUint200(uint256 value) internal pure returns (uint200) {
require(value <= type(uint200).max, "SafeCast: value doesn't fit in 200 bits");
return uint200(value);
}
/**
* @dev Returns the downcasted uint192 from uint256, reverting on
* overflow (when the input is greater than largest uint192).
*
* Counterpart to Solidity's `uint192` operator.
*
* Requirements:
*
* - input must fit into 192 bits
*
* _Available since v4.7._
*/
function toUint192(uint256 value) internal pure returns (uint192) {
require(value <= type(uint192).max, "SafeCast: value doesn't fit in 192 bits");
return uint192(value);
}
/**
* @dev Returns the downcasted uint184 from uint256, reverting on
* overflow (when the input is greater than largest uint184).
*
* Counterpart to Solidity's `uint184` operator.
*
* Requirements:
*
* - input must fit into 184 bits
*
* _Available since v4.7._
*/
function toUint184(uint256 value) internal pure returns (uint184) {
require(value <= type(uint184).max, "SafeCast: value doesn't fit in 184 bits");
return uint184(value);
}
/**
* @dev Returns the downcasted uint176 from uint256, reverting on
* overflow (when the input is greater than largest uint176).
*
* Counterpart to Solidity's `uint176` operator.
*
* Requirements:
*
* - input must fit into 176 bits
*
* _Available since v4.7._
*/
function toUint176(uint256 value) internal pure returns (uint176) {
require(value <= type(uint176).max, "SafeCast: value doesn't fit in 176 bits");
return uint176(value);
}
/**
* @dev Returns the downcasted uint168 from uint256, reverting on
* overflow (when the input is greater than largest uint168).
*
* Counterpart to Solidity's `uint168` operator.
*
* Requirements:
*
* - input must fit into 168 bits
*
* _Available since v4.7._
*/
function toUint168(uint256 value) internal pure returns (uint168) {
require(value <= type(uint168).max, "SafeCast: value doesn't fit in 168 bits");
return uint168(value);
}
/**
* @dev Returns the downcasted uint160 from uint256, reverting on
* overflow (when the input is greater than largest uint160).
*
* Counterpart to Solidity's `uint160` operator.
*
* Requirements:
*
* - input must fit into 160 bits
*
* _Available since v4.7._
*/
function toUint160(uint256 value) internal pure returns (uint160) {
require(value <= type(uint160).max, "SafeCast: value doesn't fit in 160 bits");
return uint160(value);
}
/**
* @dev Returns the downcasted uint152 from uint256, reverting on
* overflow (when the input is greater than largest uint152).
*
* Counterpart to Solidity's `uint152` operator.
*
* Requirements:
*
* - input must fit into 152 bits
*
* _Available since v4.7._
*/
function toUint152(uint256 value) internal pure returns (uint152) {
require(value <= type(uint152).max, "SafeCast: value doesn't fit in 152 bits");
return uint152(value);
}
/**
* @dev Returns the downcasted uint144 from uint256, reverting on
* overflow (when the input is greater than largest uint144).
*
* Counterpart to Solidity's `uint144` operator.
*
* Requirements:
*
* - input must fit into 144 bits
*
* _Available since v4.7._
*/
function toUint144(uint256 value) internal pure returns (uint144) {
require(value <= type(uint144).max, "SafeCast: value doesn't fit in 144 bits");
return uint144(value);
}
/**
* @dev Returns the downcasted uint136 from uint256, reverting on
* overflow (when the input is greater than largest uint136).
*
* Counterpart to Solidity's `uint136` operator.
*
* Requirements:
*
* - input must fit into 136 bits
*
* _Available since v4.7._
*/
function toUint136(uint256 value) internal pure returns (uint136) {
require(value <= type(uint136).max, "SafeCast: value doesn't fit in 136 bits");
return uint136(value);
}
/**
* @dev Returns the downcasted uint128 from uint256, reverting on
* overflow (when the input is greater than largest uint128).
*
* Counterpart to Solidity's `uint128` operator.
*
* Requirements:
*
* - input must fit into 128 bits
*
* _Available since v2.5._
*/
function toUint128(uint256 value) internal pure returns (uint128) {
require(value <= type(uint128).max, "SafeCast: value doesn't fit in 128 bits");
return uint128(value);
}
/**
* @dev Returns the downcasted uint120 from uint256, reverting on
* overflow (when the input is greater than largest uint120).
*
* Counterpart to Solidity's `uint120` operator.
*
* Requirements:
*
* - input must fit into 120 bits
*
* _Available since v4.7._
*/
function toUint120(uint256 value) internal pure returns (uint120) {
require(value <= type(uint120).max, "SafeCast: value doesn't fit in 120 bits");
return uint120(value);
}
/**
* @dev Returns the downcasted uint112 from uint256, reverting on
* overflow (when the input is greater than largest uint112).
*
* Counterpart to Solidity's `uint112` operator.
*
* Requirements:
*
* - input must fit into 112 bits
*
* _Available since v4.7._
*/
function toUint112(uint256 value) internal pure returns (uint112) {
require(value <= type(uint112).max, "SafeCast: value doesn't fit in 112 bits");
return uint112(value);
}
/**
* @dev Returns the downcasted uint104 from uint256, reverting on
* overflow (when the input is greater than largest uint104).
*
* Counterpart to Solidity's `uint104` operator.
*
* Requirements:
*
* - input must fit into 104 bits
*
* _Available since v4.7._
*/
function toUint104(uint256 value) internal pure returns (uint104) {
require(value <= type(uint104).max, "SafeCast: value doesn't fit in 104 bits");
return uint104(value);
}
/**
* @dev Returns the downcasted uint96 from uint256, reverting on
* overflow (when the input is greater than largest uint96).
*
* Counterpart to Solidity's `uint96` operator.
*
* Requirements:
*
* - input must fit into 96 bits
*
* _Available since v4.2._
*/
function toUint96(uint256 value) internal pure returns (uint96) {
require(value <= type(uint96).max, "SafeCast: value doesn't fit in 96 bits");
return uint96(value);
}
/**
* @dev Returns the downcasted uint88 from uint256, reverting on
* overflow (when the input is greater than largest uint88).
*
* Counterpart to Solidity's `uint88` operator.
*
* Requirements:
*
* - input must fit into 88 bits
*
* _Available since v4.7._
*/
function toUint88(uint256 value) internal pure returns (uint88) {
require(value <= type(uint88).max, "SafeCast: value doesn't fit in 88 bits");
return uint88(value);
}
/**
* @dev Returns the downcasted uint80 from uint256, reverting on
* overflow (when the input is greater than largest uint80).
*
* Counterpart to Solidity's `uint80` operator.
*
* Requirements:
*
* - input must fit into 80 bits
*
* _Available since v4.7._
*/
function toUint80(uint256 value) internal pure returns (uint80) {
require(value <= type(uint80).max, "SafeCast: value doesn't fit in 80 bits");
return uint80(value);
}
/**
* @dev Returns the downcasted uint72 from uint256, reverting on
* overflow (when the input is greater than largest uint72).
*
* Counterpart to Solidity's `uint72` operator.
*
* Requirements:
*
* - input must fit into 72 bits
*
* _Available since v4.7._
*/
function toUint72(uint256 value) internal pure returns (uint72) {
require(value <= type(uint72).max, "SafeCast: value doesn't fit in 72 bits");
return uint72(value);
}
/**
* @dev Returns the downcasted uint64 from uint256, reverting on
* overflow (when the input is greater than largest uint64).
*
* Counterpart to Solidity's `uint64` operator.
*
* Requirements:
*
* - input must fit into 64 bits
*
* _Available since v2.5._
*/
function toUint64(uint256 value) internal pure returns (uint64) {
require(value <= type(uint64).max, "SafeCast: value doesn't fit in 64 bits");
return uint64(value);
}
/**
* @dev Returns the downcasted uint56 from uint256, reverting on
* overflow (when the input is greater than largest uint56).
*
* Counterpart to Solidity's `uint56` operator.
*
* Requirements:
*
* - input must fit into 56 bits
*
* _Available since v4.7._
*/
function toUint56(uint256 value) internal pure returns (uint56) {
require(value <= type(uint56).max, "SafeCast: value doesn't fit in 56 bits");
return uint56(value);
}
/**
* @dev Returns the downcasted uint48 from uint256, reverting on
* overflow (when the input is greater than largest uint48).
*
* Counterpart to Solidity's `uint48` operator.
*
* Requirements:
*
* - input must fit into 48 bits
*
* _Available since v4.7._
*/
function toUint48(uint256 value) internal pure returns (uint48) {
require(value <= type(uint48).max, "SafeCast: value doesn't fit in 48 bits");
return uint48(value);
}
/**
* @dev Returns the downcasted uint40 from uint256, reverting on
* overflow (when the input is greater than largest uint40).
*
* Counterpart to Solidity's `uint40` operator.
*
* Requirements:
*
* - input must fit into 40 bits
*
* _Available since v4.7._
*/
function toUint40(uint256 value) internal pure returns (uint40) {
require(value <= type(uint40).max, "SafeCast: value doesn't fit in 40 bits");
return uint40(value);
}
/**
* @dev Returns the downcasted uint32 from uint256, reverting on
* overflow (when the input is greater than largest uint32).
*
* Counterpart to Solidity's `uint32` operator.
*
* Requirements:
*
* - input must fit into 32 bits
*
* _Available since v2.5._
*/
function toUint32(uint256 value) internal pure returns (uint32) {
require(value <= type(uint32).max, "SafeCast: value doesn't fit in 32 bits");
return uint32(value);
}
/**
* @dev Returns the downcasted uint24 from uint256, reverting on
* overflow (when the input is greater than largest uint24).
*
* Counterpart to Solidity's `uint24` operator.
*
* Requirements:
*
* - input must fit into 24 bits
*
* _Available since v4.7._
*/
function toUint24(uint256 value) internal pure returns (uint24) {
require(value <= type(uint24).max, "SafeCast: value doesn't fit in 24 bits");
return uint24(value);
}
/**
* @dev Returns the downcasted uint16 from uint256, reverting on
* overflow (when the input is greater than largest uint16).
*
* Counterpart to Solidity's `uint16` operator.
*
* Requirements:
*
* - input must fit into 16 bits
*
* _Available since v2.5._
*/
function toUint16(uint256 value) internal pure returns (uint16) {
require(value <= type(uint16).max, "SafeCast: value doesn't fit in 16 bits");
return uint16(value);
}
/**
* @dev Returns the downcasted uint8 from uint256, reverting on
* overflow (when the input is greater than largest uint8).
*
* Counterpart to Solidity's `uint8` operator.
*
* Requirements:
*
* - input must fit into 8 bits
*
* _Available since v2.5._
*/
function toUint8(uint256 value) internal pure returns (uint8) {
require(value <= type(uint8).max, "SafeCast: value doesn't fit in 8 bits");
return uint8(value);
}
/**
* @dev Converts a signed int256 into an unsigned uint256.
*
* Requirements:
*
* - input must be greater than or equal to 0.
*
* _Available since v3.0._
*/
function toUint256(int256 value) internal pure returns (uint256) {
require(value >= 0, "SafeCast: value must be positive");
return uint256(value);
}
/**
* @dev Returns the downcasted int248 from int256, reverting on
* overflow (when the input is less than smallest int248 or
* greater than largest int248).
*
* Counterpart to Solidity's `int248` operator.
*
* Requirements:
*
* - input must fit into 248 bits
*
* _Available since v4.7._
*/
function toInt248(int256 value) internal pure returns (int248 downcasted) {
downcasted = int248(value);
require(downcasted == value, "SafeCast: value doesn't fit in 248 bits");
}
/**
* @dev Returns the downcasted int240 from int256, reverting on
* overflow (when the input is less than smallest int240 or
* greater than largest int240).
*
* Counterpart to Solidity's `int240` operator.
*
* Requirements:
*
* - input must fit into 240 bits
*
* _Available since v4.7._
*/
function toInt240(int256 value) internal pure returns (int240 downcasted) {
downcasted = int240(value);
require(downcasted == value, "SafeCast: value doesn't fit in 240 bits");
}
/**
* @dev Returns the downcasted int232 from int256, reverting on
* overflow (when the input is less than smallest int232 or
* greater than largest int232).
*
* Counterpart to Solidity's `int232` operator.
*
* Requirements:
*
* - input must fit into 232 bits
*
* _Available since v4.7._
*/
function toInt232(int256 value) internal pure returns (int232 downcasted) {
downcasted = int232(value);
require(downcasted == value, "SafeCast: value doesn't fit in 232 bits");
}
/**
* @dev Returns the downcasted int224 from int256, reverting on
* overflow (when the input is less than smallest int224 or
* greater than largest int224).
*
* Counterpart to Solidity's `int224` operator.
*
* Requirements:
*
* - input must fit into 224 bits
*
* _Available since v4.7._
*/
function toInt224(int256 value) internal pure returns (int224 downcasted) {
downcasted = int224(value);
require(downcasted == value, "SafeCast: value doesn't fit in 224 bits");
}
/**
* @dev Returns the downcasted int216 from int256, reverting on
* overflow (when the input is less than smallest int216 or
* greater than largest int216).
*
* Counterpart to Solidity's `int216` operator.
*
* Requirements:
*
* - input must fit into 216 bits
*
* _Available since v4.7._
*/
function toInt216(int256 value) internal pure returns (int216 downcasted) {
downcasted = int216(value);
require(downcasted == value, "SafeCast: value doesn't fit in 216 bits");
}
/**
* @dev Returns the downcasted int208 from int256, reverting on
* overflow (when the input is less than smallest int208 or
* greater than largest int208).
*
* Counterpart to Solidity's `int208` operator.
*
* Requirements:
*
* - input must fit into 208 bits
*
* _Available since v4.7._
*/
function toInt208(int256 value) internal pure returns (int208 downcasted) {
downcasted = int208(value);
require(downcasted == value, "SafeCast: value doesn't fit in 208 bits");
}
/**
* @dev Returns the downcasted int200 from int256, reverting on
* overflow (when the input is less than smallest int200 or
* greater than largest int200).
*
* Counterpart to Solidity's `int200` operator.
*
* Requirements:
*
* - input must fit into 200 bits
*
* _Available since v4.7._
*/
function toInt200(int256 value) internal pure returns (int200 downcasted) {
downcasted = int200(value);
require(downcasted == value, "SafeCast: value doesn't fit in 200 bits");
}
/**
* @dev Returns the downcasted int192 from int256, reverting on
* overflow (when the input is less than smallest int192 or
* greater than largest int192).
*
* Counterpart to Solidity's `int192` operator.
*
* Requirements:
*
* - input must fit into 192 bits
*
* _Available since v4.7._
*/
function toInt192(int256 value) internal pure returns (int192 downcasted) {
downcasted = int192(value);
require(downcasted == value, "SafeCast: value doesn't fit in 192 bits");
}
/**
* @dev Returns the downcasted int184 from int256, reverting on
* overflow (when the input is less than smallest int184 or
* greater than largest int184).
*
* Counterpart to Solidity's `int184` operator.
*
* Requirements:
*
* - input must fit into 184 bits
*
* _Available since v4.7._
*/
function toInt184(int256 value) internal pure returns (int184 downcasted) {
downcasted = int184(value);
require(downcasted == value, "SafeCast: value doesn't fit in 184 bits");
}
/**
* @dev Returns the downcasted int176 from int256, reverting on
* overflow (when the input is less than smallest int176 or
* greater than largest int176).
*
* Counterpart to Solidity's `int176` operator.
*
* Requirements:
*
* - input must fit into 176 bits
*
* _Available since v4.7._
*/
function toInt176(int256 value) internal pure returns (int176 downcasted) {
downcasted = int176(value);
require(downcasted == value, "SafeCast: value doesn't fit in 176 bits");
}
/**
* @dev Returns the downcasted int168 from int256, reverting on
* overflow (when the input is less than smallest int168 or
* greater than largest int168).
*
* Counterpart to Solidity's `int168` operator.
*
* Requirements:
*
* - input must fit into 168 bits
*
* _Available since v4.7._
*/
function toInt168(int256 value) internal pure returns (int168 downcasted) {
downcasted = int168(value);
require(downcasted == value, "SafeCast: value doesn't fit in 168 bits");
}
/**
* @dev Returns the downcasted int160 from int256, reverting on
* overflow (when the input is less than smallest int160 or
* greater than largest int160).
*
* Counterpart to Solidity's `int160` operator.
*
* Requirements:
*
* - input must fit into 160 bits
*
* _Available since v4.7._
*/
function toInt160(int256 value) internal pure returns (int160 downcasted) {
downcasted = int160(value);
require(downcasted == value, "SafeCast: value doesn't fit in 160 bits");
}
/**
* @dev Returns the downcasted int152 from int256, reverting on
* overflow (when the input is less than smallest int152 or
* greater than largest int152).
*
* Counterpart to Solidity's `int152` operator.
*
* Requirements:
*
* - input must fit into 152 bits
*
* _Available since v4.7._
*/
function toInt152(int256 value) internal pure returns (int152 downcasted) {
downcasted = int152(value);
require(downcasted == value, "SafeCast: value doesn't fit in 152 bits");
}
/**
* @dev Returns the downcasted int144 from int256, reverting on
* overflow (when the input is less than smallest int144 or
* greater than largest int144).
*
* Counterpart to Solidity's `int144` operator.
*
* Requirements:
*
* - input must fit into 144 bits
*
* _Available since v4.7._
*/
function toInt144(int256 value) internal pure returns (int144 downcasted) {
downcasted = int144(value);
require(downcasted == value, "SafeCast: value doesn't fit in 144 bits");
}
/**
* @dev Returns the downcasted int136 from int256, reverting on
* overflow (when the input is less than smallest int136 or
* greater than largest int136).
*
* Counterpart to Solidity's `int136` operator.
*
* Requirements:
*
* - input must fit into 136 bits
*
* _Available since v4.7._
*/
function toInt136(int256 value) internal pure returns (int136 downcasted) {
downcasted = int136(value);
require(downcasted == value, "SafeCast: value doesn't fit in 136 bits");
}
/**
* @dev Returns the downcasted int128 from int256, reverting on
* overflow (when the input is less than smallest int128 or
* greater than largest int128).
*
* Counterpart to Solidity's `int128` operator.
*
* Requirements:
*
* - input must fit into 128 bits
*
* _Available since v3.1._
*/
function toInt128(int256 value) internal pure returns (int128 downcasted) {
downcasted = int128(value);
require(downcasted == value, "SafeCast: value doesn't fit in 128 bits");
}
/**
* @dev Returns the downcasted int120 from int256, reverting on
* overflow (when the input is less than smallest int120 or
* greater than largest int120).
*
* Counterpart to Solidity's `int120` operator.
*
* Requirements:
*
* - input must fit into 120 bits
*
* _Available since v4.7._
*/
function toInt120(int256 value) internal pure returns (int120 downcasted) {
downcasted = int120(value);
require(downcasted == value, "SafeCast: value doesn't fit in 120 bits");
}
/**
* @dev Returns the downcasted int112 from int256, reverting on
* overflow (when the input is less than smallest int112 or
* greater than largest int112).
*
* Counterpart to Solidity's `int112` operator.
*
* Requirements:
*
* - input must fit into 112 bits
*
* _Available since v4.7._
*/
function toInt112(int256 value) internal pure returns (int112 downcasted) {
downcasted = int112(value);
require(downcasted == value, "SafeCast: value doesn't fit in 112 bits");
}
/**
* @dev Returns the downcasted int104 from int256, reverting on
* overflow (when the input is less than smallest int104 or
* greater than largest int104).
*
* Counterpart to Solidity's `int104` operator.
*
* Requirements:
*
* - input must fit into 104 bits
*
* _Available since v4.7._
*/
function toInt104(int256 value) internal pure returns (int104 downcasted) {
downcasted = int104(value);
require(downcasted == value, "SafeCast: value doesn't fit in 104 bits");
}
/**
* @dev Returns the downcasted int96 from int256, reverting on
* overflow (when the input is less than smallest int96 or
* greater than largest int96).
*
* Counterpart to Solidity's `int96` operator.
*
* Requirements:
*
* - input must fit into 96 bits
*
* _Available since v4.7._
*/
function toInt96(int256 value) internal pure returns (int96 downcasted) {
downcasted = int96(value);
require(downcasted == value, "SafeCast: value doesn't fit in 96 bits");
}
/**
* @dev Returns the downcasted int88 from int256, reverting on
* overflow (when the input is less than smallest int88 or
* greater than largest int88).
*
* Counterpart to Solidity's `int88` operator.
*
* Requirements:
*
* - input must fit into 88 bits
*
* _Available since v4.7._
*/
function toInt88(int256 value) internal pure returns (int88 downcasted) {
downcasted = int88(value);
require(downcasted == value, "SafeCast: value doesn't fit in 88 bits");
}
/**
* @dev Returns the downcasted int80 from int256, reverting on
* overflow (when the input is less than smallest int80 or
* greater than largest int80).
*
* Counterpart to Solidity's `int80` operator.
*
* Requirements:
*
* - input must fit into 80 bits
*
* _Available since v4.7._
*/
function toInt80(int256 value) internal pure returns (int80 downcasted) {
downcasted = int80(value);
require(downcasted == value, "SafeCast: value doesn't fit in 80 bits");
}
/**
* @dev Returns the downcasted int72 from int256, reverting on
* overflow (when the input is less than smallest int72 or
* greater than largest int72).
*
* Counterpart to Solidity's `int72` operator.
*
* Requirements:
*
* - input must fit into 72 bits
*
* _Available since v4.7._
*/
function toInt72(int256 value) internal pure returns (int72 downcasted) {
downcasted = int72(value);
require(downcasted == value, "SafeCast: value doesn't fit in 72 bits");
}
/**
* @dev Returns the downcasted int64 from int256, reverting on
* overflow (when the input is less than smallest int64 or
* greater than largest int64).
*
* Counterpart to Solidity's `int64` operator.
*
* Requirements:
*
* - input must fit into 64 bits
*
* _Available since v3.1._
*/
function toInt64(int256 value) internal pure returns (int64 downcasted) {
downcasted = int64(value);
require(downcasted == value, "SafeCast: value doesn't fit in 64 bits");
}
/**
* @dev Returns the downcasted int56 from int256, reverting on
* overflow (when the input is less than smallest int56 or
* greater than largest int56).
*
* Counterpart to Solidity's `int56` operator.
*
* Requirements:
*
* - input must fit into 56 bits
*
* _Available since v4.7._
*/
function toInt56(int256 value) internal pure returns (int56 downcasted) {
downcasted = int56(value);
require(downcasted == value, "SafeCast: value doesn't fit in 56 bits");
}
/**
* @dev Returns the downcasted int48 from int256, reverting on
* overflow (when the input is less than smallest int48 or
* greater than largest int48).
*
* Counterpart to Solidity's `int48` operator.
*
* Requirements:
*
* - input must fit into 48 bits
*
* _Available since v4.7._
*/
function toInt48(int256 value) internal pure returns (int48 downcasted) {
downcasted = int48(value);
require(downcasted == value, "SafeCast: value doesn't fit in 48 bits");
}
/**
* @dev Returns the downcasted int40 from int256, reverting on
* overflow (when the input is less than smallest int40 or
* greater than largest int40).
*
* Counterpart to Solidity's `int40` operator.
*
* Requirements:
*
* - input must fit into 40 bits
*
* _Available since v4.7._
*/
function toInt40(int256 value) internal pure returns (int40 downcasted) {
downcasted = int40(value);
require(downcasted == value, "SafeCast: value doesn't fit in 40 bits");
}
/**
* @dev Returns the downcasted int32 from int256, reverting on
* overflow (when the input is less than smallest int32 or
* greater than largest int32).
*
* Counterpart to Solidity's `int32` operator.
*
* Requirements:
*
* - input must fit into 32 bits
*
* _Available since v3.1._
*/
function toInt32(int256 value) internal pure returns (int32 downcasted) {
downcasted = int32(value);
require(downcasted == value, "SafeCast: value doesn't fit in 32 bits");
}
/**
* @dev Returns the downcasted int24 from int256, reverting on
* overflow (when the input is less than smallest int24 or
* greater than largest int24).
*
* Counterpart to Solidity's `int24` operator.
*
* Requirements:
*
* - input must fit into 24 bits
*
* _Available since v4.7._
*/
function toInt24(int256 value) internal pure returns (int24 downcasted) {
downcasted = int24(value);
require(downcasted == value, "SafeCast: value doesn't fit in 24 bits");
}
/**
* @dev Returns the downcasted int16 from int256, reverting on
* overflow (when the input is less than smallest int16 or
* greater than largest int16).
*
* Counterpart to Solidity's `int16` operator.
*
* Requirements:
*
* - input must fit into 16 bits
*
* _Available since v3.1._
*/
function toInt16(int256 value) internal pure returns (int16 downcasted) {
downcasted = int16(value);
require(downcasted == value, "SafeCast: value doesn't fit in 16 bits");
}
/**
* @dev Returns the downcasted int8 from int256, reverting on
* overflow (when the input is less than smallest int8 or
* greater than largest int8).
*
* Counterpart to Solidity's `int8` operator.
*
* Requirements:
*
* - input must fit into 8 bits
*
* _Available since v3.1._
*/
function toInt8(int256 value) internal pure returns (int8 downcasted) {
downcasted = int8(value);
require(downcasted == value, "SafeCast: value doesn't fit in 8 bits");
}
/**
* @dev Converts an unsigned uint256 into a signed int256.
*
* Requirements:
*
* - input must be less than or equal to maxInt256.
*
* _Available since v3.0._
*/
function toInt256(uint256 value) internal pure returns (int256) {
// Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive
require(value <= uint256(type(int256).max), "SafeCast: value doesn't fit in an int256");
return int256(value);
}
}
SafeERC20.sol 95 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.0;
import "../IERC20.sol";
import "../extensions/draft-IERC20Permit.sol";
import "../../../utils/Address.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
using Address for address;
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
}
/**
* @dev Deprecated. This function has issues similar to the ones found in
* {IERC20-approve}, and its usage is discouraged.
*
* Whenever possible, use {safeIncreaseAllowance} and
* {safeDecreaseAllowance} instead.
*/
function safeApprove(IERC20 token, address spender, uint256 value) internal {
// safeApprove should only be called when setting an initial allowance,
// or when resetting it to zero. To increase and decrease it, use
// 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
require(
(value == 0) || (token.allowance(address(this), spender) == 0),
"SafeERC20: approve from non-zero to non-zero allowance"
);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
}
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 newAllowance = token.allowance(address(this), spender) + value;
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
unchecked {
uint256 oldAllowance = token.allowance(address(this), spender);
require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
uint256 newAllowance = oldAllowance - value;
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
}
function safePermit(
IERC20Permit token,
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) internal {
uint256 nonceBefore = token.nonces(owner);
token.permit(owner, spender, value, deadline, v, r, s);
uint256 nonceAfter = token.nonces(owner);
require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
// the target address contains contract code and also asserts for success in the low-level call.
bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
if (returndata.length > 0) {
// Return data is optional
require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
}
}
draft-IERC20Permit.sol 60 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-IERC20Permit.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
* https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
*
* Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
* presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
* need to send a transaction, and thus is not required to hold Ether at all.
*/
interface IERC20Permit {
/**
* @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
* given ``owner``'s signed approval.
*
* IMPORTANT: The same issues {IERC20-approve} has related to transaction
* ordering also apply here.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `deadline` must be a timestamp in the future.
* - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
* over the EIP712-formatted function arguments.
* - the signature must use ``owner``'s current nonce (see {nonces}).
*
* For more information on the signature format, see the
* https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
* section].
*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
/**
* @dev Returns the current nonce for `owner`. This value must be
* included whenever a signature is generated for {permit}.
*
* Every successful call to {permit} increases ``owner``'s nonce by one. This
* prevents a signature from being used multiple times.
*/
function nonces(address owner) external view returns (uint256);
/**
* @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
*/
// solhint-disable-next-line func-name-mixedcase
function DOMAIN_SEPARATOR() external view returns (bytes32);
}
IFunctionsBilling.sol 44 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
/// @title Chainlink Functions DON billing interface.
interface IFunctionsBilling {
/// @notice Return the current conversion from WEI of ETH to LINK from the configured Chainlink data feed
/// @return weiPerUnitLink - The amount of WEI in one LINK
function getWeiPerUnitLink() external view returns (uint256);
/// @notice Determine the fee that will be split between Node Operators for servicing a request
/// @param requestCBOR - CBOR encoded Chainlink Functions request data, use FunctionsRequest library to encode a request
/// @return fee - Cost in Juels (1e18) of LINK
function getDONFee(bytes memory requestCBOR) external view returns (uint72);
/// @notice Determine the fee that will be paid to the Router owner for operating the network
/// @return fee - Cost in Juels (1e18) of LINK
function getAdminFee() external view returns (uint72);
/// @notice Estimate the total cost that will be charged to a subscription to make a request: transmitter gas re-reimbursement, plus DON fee, plus Registry fee
/// @param - subscriptionId An identifier of the billing account
/// @param - data Encoded Chainlink Functions request data, use FunctionsClient API to encode a request
/// @param - callbackGasLimit Gas limit for the fulfillment callback
/// @param - gasPriceWei The blockchain's gas price to estimate with
/// @return - billedCost Cost in Juels (1e18) of LINK
function estimateCost(
uint64 subscriptionId,
bytes calldata data,
uint32 callbackGasLimit,
uint256 gasPriceWei
) external view returns (uint96);
/// @notice Remove a request commitment that the Router has determined to be stale
/// @param requestId - The request ID to remove
function deleteCommitment(bytes32 requestId) external;
/// @notice Oracle withdraw LINK earned through fulfilling requests
/// @notice If amount is 0 the full balance will be withdrawn
/// @param recipient where to send the funds
/// @param amount amount to withdraw
function oracleWithdraw(address recipient, uint96 amount) external;
/// @notice Withdraw all LINK earned by Oracles through fulfilling requests
function oracleWithdrawAll() external;
}
IFunctionsCoordinator.sol 37 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import {FunctionsResponse} from "../libraries/FunctionsResponse.sol";
/// @title Chainlink Functions DON Coordinator interface.
interface IFunctionsCoordinator {
/// @notice Returns the DON's threshold encryption public key used to encrypt secrets
/// @dev All nodes on the DON have separate key shares of the threshold decryption key
/// and nodes must participate in a threshold decryption OCR round to decrypt secrets
/// @return thresholdPublicKey the DON's threshold encryption public key
function getThresholdPublicKey() external view returns (bytes memory);
/// @notice Sets the DON's threshold encryption public key used to encrypt secrets
/// @dev Used to rotate the key
/// @param thresholdPublicKey The new public key
function setThresholdPublicKey(bytes calldata thresholdPublicKey) external;
/// @notice Returns the DON's secp256k1 public key that is used to encrypt secrets
/// @dev All nodes on the DON have the corresponding private key
/// needed to decrypt the secrets encrypted with the public key
/// @return publicKey the DON's public key
function getDONPublicKey() external view returns (bytes memory);
/// @notice Sets DON's secp256k1 public key used to encrypt secrets
/// @dev Used to rotate the key
/// @param donPublicKey The new public key
function setDONPublicKey(bytes calldata donPublicKey) external;
/// @notice Receives a request to be emitted to the DON for processing
/// @param request The request metadata
/// @dev see the struct for field descriptions
/// @return commitment - The parameters of the request that must be held consistent at response time
function startRequest(
FunctionsResponse.RequestMeta calldata request
) external returns (FunctionsResponse.Commitment memory commitment);
}
IFunctionsRouter.sol 109 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import {FunctionsResponse} from "../libraries/FunctionsResponse.sol";
/// @title Chainlink Functions Router interface.
interface IFunctionsRouter {
/// @notice The identifier of the route to retrieve the address of the access control contract
/// The access control contract controls which accounts can manage subscriptions
/// @return id - bytes32 id that can be passed to the "getContractById" of the Router
function getAllowListId() external view returns (bytes32);
/// @notice Set the identifier of the route to retrieve the address of the access control contract
/// The access control contract controls which accounts can manage subscriptions
function setAllowListId(bytes32 allowListId) external;
/// @notice Get the flat fee (in Juels of LINK) that will be paid to the Router owner for operation of the network
/// @return adminFee
function getAdminFee() external view returns (uint72 adminFee);
/// @notice Sends a request using the provided subscriptionId
/// @param subscriptionId - A unique subscription ID allocated by billing system,
/// a client can make requests from different contracts referencing the same subscription
/// @param data - CBOR encoded Chainlink Functions request data, use FunctionsClient API to encode a request
/// @param dataVersion - Gas limit for the fulfillment callback
/// @param callbackGasLimit - Gas limit for the fulfillment callback
/// @param donId - An identifier used to determine which route to send the request along
/// @return requestId - A unique request identifier
function sendRequest(
uint64 subscriptionId,
bytes calldata data,
uint16 dataVersion,
uint32 callbackGasLimit,
bytes32 donId
) external returns (bytes32);
/// @notice Sends a request to the proposed contracts
/// @param subscriptionId - A unique subscription ID allocated by billing system,
/// a client can make requests from different contracts referencing the same subscription
/// @param data - CBOR encoded Chainlink Functions request data, use FunctionsClient API to encode a request
/// @param dataVersion - Gas limit for the fulfillment callback
/// @param callbackGasLimit - Gas limit for the fulfillment callback
/// @param donId - An identifier used to determine which route to send the request along
/// @return requestId - A unique request identifier
function sendRequestToProposed(
uint64 subscriptionId,
bytes calldata data,
uint16 dataVersion,
uint32 callbackGasLimit,
bytes32 donId
) external returns (bytes32);
/// @notice Fulfill the request by:
/// - calling back the data that the Oracle returned to the client contract
/// - pay the DON for processing the request
/// @dev Only callable by the Coordinator contract that is saved in the commitment
/// @param response response data from DON consensus
/// @param err error from DON consensus
/// @param juelsPerGas - current rate of juels/gas
/// @param costWithoutFulfillment - The cost of processing the request (in Juels of LINK ), without fulfillment
/// @param transmitter - The Node that transmitted the OCR report
/// @param commitment - The parameters of the request that must be held consistent between request and response time
/// @return fulfillResult -
/// @return callbackGasCostJuels -
function fulfill(
bytes memory response,
bytes memory err,
uint96 juelsPerGas,
uint96 costWithoutFulfillment,
address transmitter,
FunctionsResponse.Commitment memory commitment
) external returns (FunctionsResponse.FulfillResult, uint96);
/// @notice Validate requested gas limit is below the subscription max.
/// @param subscriptionId subscription ID
/// @param callbackGasLimit desired callback gas limit
function isValidCallbackGasLimit(uint64 subscriptionId, uint32 callbackGasLimit) external view;
/// @notice Get the current contract given an ID
/// @param id A bytes32 identifier for the route
/// @return contract The current contract address
function getContractById(bytes32 id) external view returns (address);
/// @notice Get the proposed next contract given an ID
/// @param id A bytes32 identifier for the route
/// @return contract The current or proposed contract address
function getProposedContractById(bytes32 id) external view returns (address);
/// @notice Return the latest proprosal set
/// @return ids The identifiers of the contracts to update
/// @return to The addresses of the contracts that will be updated to
function getProposedContractSet() external view returns (bytes32[] memory, address[] memory);
/// @notice Proposes one or more updates to the contract routes
/// @dev Only callable by owner
function proposeContractsUpdate(bytes32[] memory proposalSetIds, address[] memory proposalSetAddresses) external;
/// @notice Updates the current contract routes to the proposed contracts
/// @dev Only callable by owner
function updateContracts() external;
/// @dev Puts the system into an emergency stopped state.
/// @dev Only callable by owner
function pause() external;
/// @dev Takes the system out of an emergency stopped state.
/// @dev Only callable by owner
function unpause() external;
}
IFunctionsSubscriptions.sol 140 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import {FunctionsResponse} from "../libraries/FunctionsResponse.sol";
/// @title Chainlink Functions Subscription interface.
interface IFunctionsSubscriptions {
struct Subscription {
uint96 balance; // ═════════╗ Common LINK balance that is controlled by the Router to be used for all consumer requests.
address owner; // ══════════╝ The owner can fund/withdraw/cancel the subscription.
uint96 blockedBalance; // ══╗ LINK balance that is reserved to pay for pending consumer requests.
address proposedOwner; // ══╝ For safely transferring sub ownership.
address[] consumers; // ════╸ Client contracts that can use the subscription
bytes32 flags; // ══════════╸ Per-subscription flags
}
struct Consumer {
bool allowed; // ══════════════╗ Owner can fund/withdraw/cancel the sub.
uint64 initiatedRequests; // ║ The number of requests that have been started
uint64 completedRequests; // ══╝ The number of requests that have successfully completed or timed out
}
/// @notice Get details about a subscription.
/// @param subscriptionId - the ID of the subscription
/// @return subscription - see IFunctionsSubscriptions.Subscription for more information on the structure
function getSubscription(uint64 subscriptionId) external view returns (Subscription memory);
/// @notice Retrieve details about multiple subscriptions using an inclusive range
/// @param subscriptionIdStart - the ID of the subscription to start the range at
/// @param subscriptionIdEnd - the ID of the subscription to end the range at
/// @return subscriptions - see IFunctionsSubscriptions.Subscription for more information on the structure
function getSubscriptionsInRange(
uint64 subscriptionIdStart,
uint64 subscriptionIdEnd
) external view returns (Subscription[] memory);
/// @notice Get details about a consumer of a subscription.
/// @param client - the consumer contract address
/// @param subscriptionId - the ID of the subscription
/// @return consumer - see IFunctionsSubscriptions.Consumer for more information on the structure
function getConsumer(address client, uint64 subscriptionId) external view returns (Consumer memory);
/// @notice Get details about the total amount of LINK within the system
/// @return totalBalance - total Juels of LINK held by the contract
function getTotalBalance() external view returns (uint96);
/// @notice Get details about the total number of subscription accounts
/// @return count - total number of subscriptions in the system
function getSubscriptionCount() external view returns (uint64);
/// @notice Time out all expired requests: unlocks funds and removes the ability for the request to be fulfilled
/// @param requestsToTimeoutByCommitment - A list of request commitments to time out
/// @dev The commitment can be found on the "OracleRequest" event created when sending the request.
function timeoutRequests(FunctionsResponse.Commitment[] calldata requestsToTimeoutByCommitment) external;
/// @notice Oracle withdraw LINK earned through fulfilling requests
/// @notice If amount is 0 the full balance will be withdrawn
/// @notice Both signing and transmitting wallets will have a balance to withdraw
/// @param recipient where to send the funds
/// @param amount amount to withdraw
function oracleWithdraw(address recipient, uint96 amount) external;
/// @notice Owner cancel subscription, sends remaining link directly to the subscription owner.
/// @dev Only callable by the Router Owner
/// @param subscriptionId subscription id
/// @dev notably can be called even if there are pending requests, outstanding ones may fail onchain
function ownerCancelSubscription(uint64 subscriptionId) external;
/// @notice Recover link sent with transfer instead of transferAndCall.
/// @dev Only callable by the Router Owner
/// @param to address to send link to
function recoverFunds(address to) external;
/// @notice Create a new subscription.
/// @return subscriptionId - A unique subscription id.
/// @dev You can manage the consumer set dynamically with addConsumer/removeConsumer.
/// @dev Note to fund the subscription, use transferAndCall. For example
/// @dev LINKTOKEN.transferAndCall(
/// @dev address(ROUTER),
/// @dev amount,
/// @dev abi.encode(subscriptionId));
function createSubscription() external returns (uint64);
/// @notice Create a new subscription and add a consumer.
/// @return subscriptionId - A unique subscription id.
/// @dev You can manage the consumer set dynamically with addConsumer/removeConsumer.
/// @dev Note to fund the subscription, use transferAndCall. For example
/// @dev LINKTOKEN.transferAndCall(
/// @dev address(ROUTER),
/// @dev amount,
/// @dev abi.encode(subscriptionId));
function createSubscriptionWithConsumer(address consumer) external returns (uint64 subscriptionId);
/// @notice Propose a new owner for a subscription.
/// @dev Only callable by the Subscription's owner
/// @param subscriptionId - ID of the subscription
/// @param newOwner - proposed new owner of the subscription
function proposeSubscriptionOwnerTransfer(uint64 subscriptionId, address newOwner) external;
/// @notice Accept an ownership transfer.
/// @param subscriptionId - ID of the subscription
/// @dev will revert if original owner of subscriptionId has not requested that msg.sender become the new owner.
function acceptSubscriptionOwnerTransfer(uint64 subscriptionId) external;
/// @notice Remove a consumer from a Chainlink Functions subscription.
/// @dev Only callable by the Subscription's owner
/// @param subscriptionId - ID of the subscription
/// @param consumer - Consumer to remove from the subscription
function removeConsumer(uint64 subscriptionId, address consumer) external;
/// @notice Add a consumer to a Chainlink Functions subscription.
/// @dev Only callable by the Subscription's owner
/// @param subscriptionId - ID of the subscription
/// @param consumer - New consumer which can use the subscription
function addConsumer(uint64 subscriptionId, address consumer) external;
/// @notice Cancel a subscription
/// @dev Only callable by the Subscription's owner
/// @param subscriptionId - ID of the subscription
/// @param to - Where to send the remaining LINK to
function cancelSubscription(uint64 subscriptionId, address to) external;
/// @notice Check to see if there exists a request commitment for all consumers for a given sub.
/// @param subscriptionId - ID of the subscription
/// @return true if there exists at least one unfulfilled request for the subscription, false otherwise.
/// @dev Looping is bounded to MAX_CONSUMERS*(number of DONs).
/// @dev Used to disable subscription canceling while outstanding request are present.
function pendingRequestExists(uint64 subscriptionId) external view returns (bool);
/// @notice Set subscription specific flags for a subscription.
/// Each byte of the flag is used to represent a resource tier that the subscription can utilize.
/// @param subscriptionId - ID of the subscription
/// @param flags - desired flag values
function setFlags(uint64 subscriptionId, bytes32 flags) external;
/// @notice Get flags for a given subscription.
/// @param subscriptionId - ID of the subscription
/// @return flags - current flag values
function getFlags(uint64 subscriptionId) external view returns (bytes32);
}
Pausable.sol 105 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol)
pragma solidity ^0.8.0;
import "../utils/Context.sol";
/**
* @dev Contract module which allows children to implement an emergency stop
* mechanism that can be triggered by an authorized account.
*
* This module is used through inheritance. It will make available the
* modifiers `whenNotPaused` and `whenPaused`, which can be applied to
* the functions of your contract. Note that they will not be pausable by
* simply including this module, only once the modifiers are put in place.
*/
abstract contract Pausable is Context {
/**
* @dev Emitted when the pause is triggered by `account`.
*/
event Paused(address account);
/**
* @dev Emitted when the pause is lifted by `account`.
*/
event Unpaused(address account);
bool private _paused;
/**
* @dev Initializes the contract in unpaused state.
*/
constructor() {
_paused = false;
}
/**
* @dev Modifier to make a function callable only when the contract is not paused.
*
* Requirements:
*
* - The contract must not be paused.
*/
modifier whenNotPaused() {
_requireNotPaused();
_;
}
/**
* @dev Modifier to make a function callable only when the contract is paused.
*
* Requirements:
*
* - The contract must be paused.
*/
modifier whenPaused() {
_requirePaused();
_;
}
/**
* @dev Returns true if the contract is paused, and false otherwise.
*/
function paused() public view virtual returns (bool) {
return _paused;
}
/**
* @dev Throws if the contract is paused.
*/
function _requireNotPaused() internal view virtual {
require(!paused(), "Pausable: paused");
}
/**
* @dev Throws if the contract is not paused.
*/
function _requirePaused() internal view virtual {
require(paused(), "Pausable: not paused");
}
/**
* @dev Triggers stopped state.
*
* Requirements:
*
* - The contract must not be paused.
*/
function _pause() internal virtual whenNotPaused {
_paused = true;
emit Paused(_msgSender());
}
/**
* @dev Returns to normal state.
*
* Requirements:
*
* - The contract must be paused.
*/
function _unpause() internal virtual whenPaused {
_paused = false;
emit Unpaused(_msgSender());
}
}
IERC20.sol 78 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `from` to `to` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 amount) external returns (bool);
}
draft-IERC20Permit.sol 60 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-IERC20Permit.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
* https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
*
* Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
* presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
* need to send a transaction, and thus is not required to hold Ether at all.
*/
interface IERC20Permit {
/**
* @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
* given ``owner``'s signed approval.
*
* IMPORTANT: The same issues {IERC20-approve} has related to transaction
* ordering also apply here.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `deadline` must be a timestamp in the future.
* - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
* over the EIP712-formatted function arguments.
* - the signature must use ``owner``'s current nonce (see {nonces}).
*
* For more information on the signature format, see the
* https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
* section].
*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
/**
* @dev Returns the current nonce for `owner`. This value must be
* included whenever a signature is generated for {permit}.
*
* Every successful call to {permit} increases ``owner``'s nonce by one. This
* prevents a signature from being used multiple times.
*/
function nonces(address owner) external view returns (uint256);
/**
* @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
*/
// solhint-disable-next-line func-name-mixedcase
function DOMAIN_SEPARATOR() external view returns (bytes32);
}
SafeERC20.sol 95 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.0;
import "../IERC20.sol";
import "../extensions/draft-IERC20Permit.sol";
import "../../../utils/Address.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
using Address for address;
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
}
/**
* @dev Deprecated. This function has issues similar to the ones found in
* {IERC20-approve}, and its usage is discouraged.
*
* Whenever possible, use {safeIncreaseAllowance} and
* {safeDecreaseAllowance} instead.
*/
function safeApprove(IERC20 token, address spender, uint256 value) internal {
// safeApprove should only be called when setting an initial allowance,
// or when resetting it to zero. To increase and decrease it, use
// 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
require(
(value == 0) || (token.allowance(address(this), spender) == 0),
"SafeERC20: approve from non-zero to non-zero allowance"
);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
}
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 newAllowance = token.allowance(address(this), spender) + value;
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
unchecked {
uint256 oldAllowance = token.allowance(address(this), spender);
require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
uint256 newAllowance = oldAllowance - value;
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
}
function safePermit(
IERC20Permit token,
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) internal {
uint256 nonceBefore = token.nonces(owner);
token.permit(owner, spender, value, deadline, v, r, s);
uint256 nonceAfter = token.nonces(owner);
require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
// the target address contains contract code and also asserts for success in the low-level call.
bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
if (returndata.length > 0) {
// Return data is optional
require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
}
}
Address.sol 236 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol)
pragma solidity ^0.8.1;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
* ====
*
* [IMPORTANT]
* ====
* You shouldn't rely on `isContract` to protect against flash loan attacks!
*
* Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
* like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
* constructor.
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize/address.code.length, which returns 0
// for contracts in construction, since the code is only stored at the end
// of the constructor execution.
return account.code.length > 0;
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason, it is bubbled up by this
* function (like regular Solidity function calls).
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*
* _Available since v3.1._
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
* the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
*
* _Available since v4.8._
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata,
string memory errorMessage
) internal view returns (bytes memory) {
if (success) {
if (returndata.length == 0) {
// only check isContract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
require(isContract(target), "Address: call to non-contract");
}
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
/**
* @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason or using the provided one.
*
* _Available since v4.3._
*/
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function _revert(bytes memory returndata, string memory errorMessage) private pure {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
Context.sol 24 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
pragma solidity ^0.8.0;
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
}
SafeCast.sol 1136 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SafeCast.sol)
// This file was procedurally generated from scripts/generate/templates/SafeCast.js.
pragma solidity ^0.8.0;
/**
* @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow
* checks.
*
* Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
* easily result in undesired exploitation or bugs, since developers usually
* assume that overflows raise errors. `SafeCast` restores this intuition by
* reverting the transaction when such an operation overflows.
*
* Using this library instead of the unchecked operations eliminates an entire
* class of bugs, so it's recommended to use it always.
*
* Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing
* all math on `uint256` and `int256` and then downcasting.
*/
library SafeCast {
/**
* @dev Returns the downcasted uint248 from uint256, reverting on
* overflow (when the input is greater than largest uint248).
*
* Counterpart to Solidity's `uint248` operator.
*
* Requirements:
*
* - input must fit into 248 bits
*
* _Available since v4.7._
*/
function toUint248(uint256 value) internal pure returns (uint248) {
require(value <= type(uint248).max, "SafeCast: value doesn't fit in 248 bits");
return uint248(value);
}
/**
* @dev Returns the downcasted uint240 from uint256, reverting on
* overflow (when the input is greater than largest uint240).
*
* Counterpart to Solidity's `uint240` operator.
*
* Requirements:
*
* - input must fit into 240 bits
*
* _Available since v4.7._
*/
function toUint240(uint256 value) internal pure returns (uint240) {
require(value <= type(uint240).max, "SafeCast: value doesn't fit in 240 bits");
return uint240(value);
}
/**
* @dev Returns the downcasted uint232 from uint256, reverting on
* overflow (when the input is greater than largest uint232).
*
* Counterpart to Solidity's `uint232` operator.
*
* Requirements:
*
* - input must fit into 232 bits
*
* _Available since v4.7._
*/
function toUint232(uint256 value) internal pure returns (uint232) {
require(value <= type(uint232).max, "SafeCast: value doesn't fit in 232 bits");
return uint232(value);
}
/**
* @dev Returns the downcasted uint224 from uint256, reverting on
* overflow (when the input is greater than largest uint224).
*
* Counterpart to Solidity's `uint224` operator.
*
* Requirements:
*
* - input must fit into 224 bits
*
* _Available since v4.2._
*/
function toUint224(uint256 value) internal pure returns (uint224) {
require(value <= type(uint224).max, "SafeCast: value doesn't fit in 224 bits");
return uint224(value);
}
/**
* @dev Returns the downcasted uint216 from uint256, reverting on
* overflow (when the input is greater than largest uint216).
*
* Counterpart to Solidity's `uint216` operator.
*
* Requirements:
*
* - input must fit into 216 bits
*
* _Available since v4.7._
*/
function toUint216(uint256 value) internal pure returns (uint216) {
require(value <= type(uint216).max, "SafeCast: value doesn't fit in 216 bits");
return uint216(value);
}
/**
* @dev Returns the downcasted uint208 from uint256, reverting on
* overflow (when the input is greater than largest uint208).
*
* Counterpart to Solidity's `uint208` operator.
*
* Requirements:
*
* - input must fit into 208 bits
*
* _Available since v4.7._
*/
function toUint208(uint256 value) internal pure returns (uint208) {
require(value <= type(uint208).max, "SafeCast: value doesn't fit in 208 bits");
return uint208(value);
}
/**
* @dev Returns the downcasted uint200 from uint256, reverting on
* overflow (when the input is greater than largest uint200).
*
* Counterpart to Solidity's `uint200` operator.
*
* Requirements:
*
* - input must fit into 200 bits
*
* _Available since v4.7._
*/
function toUint200(uint256 value) internal pure returns (uint200) {
require(value <= type(uint200).max, "SafeCast: value doesn't fit in 200 bits");
return uint200(value);
}
/**
* @dev Returns the downcasted uint192 from uint256, reverting on
* overflow (when the input is greater than largest uint192).
*
* Counterpart to Solidity's `uint192` operator.
*
* Requirements:
*
* - input must fit into 192 bits
*
* _Available since v4.7._
*/
function toUint192(uint256 value) internal pure returns (uint192) {
require(value <= type(uint192).max, "SafeCast: value doesn't fit in 192 bits");
return uint192(value);
}
/**
* @dev Returns the downcasted uint184 from uint256, reverting on
* overflow (when the input is greater than largest uint184).
*
* Counterpart to Solidity's `uint184` operator.
*
* Requirements:
*
* - input must fit into 184 bits
*
* _Available since v4.7._
*/
function toUint184(uint256 value) internal pure returns (uint184) {
require(value <= type(uint184).max, "SafeCast: value doesn't fit in 184 bits");
return uint184(value);
}
/**
* @dev Returns the downcasted uint176 from uint256, reverting on
* overflow (when the input is greater than largest uint176).
*
* Counterpart to Solidity's `uint176` operator.
*
* Requirements:
*
* - input must fit into 176 bits
*
* _Available since v4.7._
*/
function toUint176(uint256 value) internal pure returns (uint176) {
require(value <= type(uint176).max, "SafeCast: value doesn't fit in 176 bits");
return uint176(value);
}
/**
* @dev Returns the downcasted uint168 from uint256, reverting on
* overflow (when the input is greater than largest uint168).
*
* Counterpart to Solidity's `uint168` operator.
*
* Requirements:
*
* - input must fit into 168 bits
*
* _Available since v4.7._
*/
function toUint168(uint256 value) internal pure returns (uint168) {
require(value <= type(uint168).max, "SafeCast: value doesn't fit in 168 bits");
return uint168(value);
}
/**
* @dev Returns the downcasted uint160 from uint256, reverting on
* overflow (when the input is greater than largest uint160).
*
* Counterpart to Solidity's `uint160` operator.
*
* Requirements:
*
* - input must fit into 160 bits
*
* _Available since v4.7._
*/
function toUint160(uint256 value) internal pure returns (uint160) {
require(value <= type(uint160).max, "SafeCast: value doesn't fit in 160 bits");
return uint160(value);
}
/**
* @dev Returns the downcasted uint152 from uint256, reverting on
* overflow (when the input is greater than largest uint152).
*
* Counterpart to Solidity's `uint152` operator.
*
* Requirements:
*
* - input must fit into 152 bits
*
* _Available since v4.7._
*/
function toUint152(uint256 value) internal pure returns (uint152) {
require(value <= type(uint152).max, "SafeCast: value doesn't fit in 152 bits");
return uint152(value);
}
/**
* @dev Returns the downcasted uint144 from uint256, reverting on
* overflow (when the input is greater than largest uint144).
*
* Counterpart to Solidity's `uint144` operator.
*
* Requirements:
*
* - input must fit into 144 bits
*
* _Available since v4.7._
*/
function toUint144(uint256 value) internal pure returns (uint144) {
require(value <= type(uint144).max, "SafeCast: value doesn't fit in 144 bits");
return uint144(value);
}
/**
* @dev Returns the downcasted uint136 from uint256, reverting on
* overflow (when the input is greater than largest uint136).
*
* Counterpart to Solidity's `uint136` operator.
*
* Requirements:
*
* - input must fit into 136 bits
*
* _Available since v4.7._
*/
function toUint136(uint256 value) internal pure returns (uint136) {
require(value <= type(uint136).max, "SafeCast: value doesn't fit in 136 bits");
return uint136(value);
}
/**
* @dev Returns the downcasted uint128 from uint256, reverting on
* overflow (when the input is greater than largest uint128).
*
* Counterpart to Solidity's `uint128` operator.
*
* Requirements:
*
* - input must fit into 128 bits
*
* _Available since v2.5._
*/
function toUint128(uint256 value) internal pure returns (uint128) {
require(value <= type(uint128).max, "SafeCast: value doesn't fit in 128 bits");
return uint128(value);
}
/**
* @dev Returns the downcasted uint120 from uint256, reverting on
* overflow (when the input is greater than largest uint120).
*
* Counterpart to Solidity's `uint120` operator.
*
* Requirements:
*
* - input must fit into 120 bits
*
* _Available since v4.7._
*/
function toUint120(uint256 value) internal pure returns (uint120) {
require(value <= type(uint120).max, "SafeCast: value doesn't fit in 120 bits");
return uint120(value);
}
/**
* @dev Returns the downcasted uint112 from uint256, reverting on
* overflow (when the input is greater than largest uint112).
*
* Counterpart to Solidity's `uint112` operator.
*
* Requirements:
*
* - input must fit into 112 bits
*
* _Available since v4.7._
*/
function toUint112(uint256 value) internal pure returns (uint112) {
require(value <= type(uint112).max, "SafeCast: value doesn't fit in 112 bits");
return uint112(value);
}
/**
* @dev Returns the downcasted uint104 from uint256, reverting on
* overflow (when the input is greater than largest uint104).
*
* Counterpart to Solidity's `uint104` operator.
*
* Requirements:
*
* - input must fit into 104 bits
*
* _Available since v4.7._
*/
function toUint104(uint256 value) internal pure returns (uint104) {
require(value <= type(uint104).max, "SafeCast: value doesn't fit in 104 bits");
return uint104(value);
}
/**
* @dev Returns the downcasted uint96 from uint256, reverting on
* overflow (when the input is greater than largest uint96).
*
* Counterpart to Solidity's `uint96` operator.
*
* Requirements:
*
* - input must fit into 96 bits
*
* _Available since v4.2._
*/
function toUint96(uint256 value) internal pure returns (uint96) {
require(value <= type(uint96).max, "SafeCast: value doesn't fit in 96 bits");
return uint96(value);
}
/**
* @dev Returns the downcasted uint88 from uint256, reverting on
* overflow (when the input is greater than largest uint88).
*
* Counterpart to Solidity's `uint88` operator.
*
* Requirements:
*
* - input must fit into 88 bits
*
* _Available since v4.7._
*/
function toUint88(uint256 value) internal pure returns (uint88) {
require(value <= type(uint88).max, "SafeCast: value doesn't fit in 88 bits");
return uint88(value);
}
/**
* @dev Returns the downcasted uint80 from uint256, reverting on
* overflow (when the input is greater than largest uint80).
*
* Counterpart to Solidity's `uint80` operator.
*
* Requirements:
*
* - input must fit into 80 bits
*
* _Available since v4.7._
*/
function toUint80(uint256 value) internal pure returns (uint80) {
require(value <= type(uint80).max, "SafeCast: value doesn't fit in 80 bits");
return uint80(value);
}
/**
* @dev Returns the downcasted uint72 from uint256, reverting on
* overflow (when the input is greater than largest uint72).
*
* Counterpart to Solidity's `uint72` operator.
*
* Requirements:
*
* - input must fit into 72 bits
*
* _Available since v4.7._
*/
function toUint72(uint256 value) internal pure returns (uint72) {
require(value <= type(uint72).max, "SafeCast: value doesn't fit in 72 bits");
return uint72(value);
}
/**
* @dev Returns the downcasted uint64 from uint256, reverting on
* overflow (when the input is greater than largest uint64).
*
* Counterpart to Solidity's `uint64` operator.
*
* Requirements:
*
* - input must fit into 64 bits
*
* _Available since v2.5._
*/
function toUint64(uint256 value) internal pure returns (uint64) {
require(value <= type(uint64).max, "SafeCast: value doesn't fit in 64 bits");
return uint64(value);
}
/**
* @dev Returns the downcasted uint56 from uint256, reverting on
* overflow (when the input is greater than largest uint56).
*
* Counterpart to Solidity's `uint56` operator.
*
* Requirements:
*
* - input must fit into 56 bits
*
* _Available since v4.7._
*/
function toUint56(uint256 value) internal pure returns (uint56) {
require(value <= type(uint56).max, "SafeCast: value doesn't fit in 56 bits");
return uint56(value);
}
/**
* @dev Returns the downcasted uint48 from uint256, reverting on
* overflow (when the input is greater than largest uint48).
*
* Counterpart to Solidity's `uint48` operator.
*
* Requirements:
*
* - input must fit into 48 bits
*
* _Available since v4.7._
*/
function toUint48(uint256 value) internal pure returns (uint48) {
require(value <= type(uint48).max, "SafeCast: value doesn't fit in 48 bits");
return uint48(value);
}
/**
* @dev Returns the downcasted uint40 from uint256, reverting on
* overflow (when the input is greater than largest uint40).
*
* Counterpart to Solidity's `uint40` operator.
*
* Requirements:
*
* - input must fit into 40 bits
*
* _Available since v4.7._
*/
function toUint40(uint256 value) internal pure returns (uint40) {
require(value <= type(uint40).max, "SafeCast: value doesn't fit in 40 bits");
return uint40(value);
}
/**
* @dev Returns the downcasted uint32 from uint256, reverting on
* overflow (when the input is greater than largest uint32).
*
* Counterpart to Solidity's `uint32` operator.
*
* Requirements:
*
* - input must fit into 32 bits
*
* _Available since v2.5._
*/
function toUint32(uint256 value) internal pure returns (uint32) {
require(value <= type(uint32).max, "SafeCast: value doesn't fit in 32 bits");
return uint32(value);
}
/**
* @dev Returns the downcasted uint24 from uint256, reverting on
* overflow (when the input is greater than largest uint24).
*
* Counterpart to Solidity's `uint24` operator.
*
* Requirements:
*
* - input must fit into 24 bits
*
* _Available since v4.7._
*/
function toUint24(uint256 value) internal pure returns (uint24) {
require(value <= type(uint24).max, "SafeCast: value doesn't fit in 24 bits");
return uint24(value);
}
/**
* @dev Returns the downcasted uint16 from uint256, reverting on
* overflow (when the input is greater than largest uint16).
*
* Counterpart to Solidity's `uint16` operator.
*
* Requirements:
*
* - input must fit into 16 bits
*
* _Available since v2.5._
*/
function toUint16(uint256 value) internal pure returns (uint16) {
require(value <= type(uint16).max, "SafeCast: value doesn't fit in 16 bits");
return uint16(value);
}
/**
* @dev Returns the downcasted uint8 from uint256, reverting on
* overflow (when the input is greater than largest uint8).
*
* Counterpart to Solidity's `uint8` operator.
*
* Requirements:
*
* - input must fit into 8 bits
*
* _Available since v2.5._
*/
function toUint8(uint256 value) internal pure returns (uint8) {
require(value <= type(uint8).max, "SafeCast: value doesn't fit in 8 bits");
return uint8(value);
}
/**
* @dev Converts a signed int256 into an unsigned uint256.
*
* Requirements:
*
* - input must be greater than or equal to 0.
*
* _Available since v3.0._
*/
function toUint256(int256 value) internal pure returns (uint256) {
require(value >= 0, "SafeCast: value must be positive");
return uint256(value);
}
/**
* @dev Returns the downcasted int248 from int256, reverting on
* overflow (when the input is less than smallest int248 or
* greater than largest int248).
*
* Counterpart to Solidity's `int248` operator.
*
* Requirements:
*
* - input must fit into 248 bits
*
* _Available since v4.7._
*/
function toInt248(int256 value) internal pure returns (int248 downcasted) {
downcasted = int248(value);
require(downcasted == value, "SafeCast: value doesn't fit in 248 bits");
}
/**
* @dev Returns the downcasted int240 from int256, reverting on
* overflow (when the input is less than smallest int240 or
* greater than largest int240).
*
* Counterpart to Solidity's `int240` operator.
*
* Requirements:
*
* - input must fit into 240 bits
*
* _Available since v4.7._
*/
function toInt240(int256 value) internal pure returns (int240 downcasted) {
downcasted = int240(value);
require(downcasted == value, "SafeCast: value doesn't fit in 240 bits");
}
/**
* @dev Returns the downcasted int232 from int256, reverting on
* overflow (when the input is less than smallest int232 or
* greater than largest int232).
*
* Counterpart to Solidity's `int232` operator.
*
* Requirements:
*
* - input must fit into 232 bits
*
* _Available since v4.7._
*/
function toInt232(int256 value) internal pure returns (int232 downcasted) {
downcasted = int232(value);
require(downcasted == value, "SafeCast: value doesn't fit in 232 bits");
}
/**
* @dev Returns the downcasted int224 from int256, reverting on
* overflow (when the input is less than smallest int224 or
* greater than largest int224).
*
* Counterpart to Solidity's `int224` operator.
*
* Requirements:
*
* - input must fit into 224 bits
*
* _Available since v4.7._
*/
function toInt224(int256 value) internal pure returns (int224 downcasted) {
downcasted = int224(value);
require(downcasted == value, "SafeCast: value doesn't fit in 224 bits");
}
/**
* @dev Returns the downcasted int216 from int256, reverting on
* overflow (when the input is less than smallest int216 or
* greater than largest int216).
*
* Counterpart to Solidity's `int216` operator.
*
* Requirements:
*
* - input must fit into 216 bits
*
* _Available since v4.7._
*/
function toInt216(int256 value) internal pure returns (int216 downcasted) {
downcasted = int216(value);
require(downcasted == value, "SafeCast: value doesn't fit in 216 bits");
}
/**
* @dev Returns the downcasted int208 from int256, reverting on
* overflow (when the input is less than smallest int208 or
* greater than largest int208).
*
* Counterpart to Solidity's `int208` operator.
*
* Requirements:
*
* - input must fit into 208 bits
*
* _Available since v4.7._
*/
function toInt208(int256 value) internal pure returns (int208 downcasted) {
downcasted = int208(value);
require(downcasted == value, "SafeCast: value doesn't fit in 208 bits");
}
/**
* @dev Returns the downcasted int200 from int256, reverting on
* overflow (when the input is less than smallest int200 or
* greater than largest int200).
*
* Counterpart to Solidity's `int200` operator.
*
* Requirements:
*
* - input must fit into 200 bits
*
* _Available since v4.7._
*/
function toInt200(int256 value) internal pure returns (int200 downcasted) {
downcasted = int200(value);
require(downcasted == value, "SafeCast: value doesn't fit in 200 bits");
}
/**
* @dev Returns the downcasted int192 from int256, reverting on
* overflow (when the input is less than smallest int192 or
* greater than largest int192).
*
* Counterpart to Solidity's `int192` operator.
*
* Requirements:
*
* - input must fit into 192 bits
*
* _Available since v4.7._
*/
function toInt192(int256 value) internal pure returns (int192 downcasted) {
downcasted = int192(value);
require(downcasted == value, "SafeCast: value doesn't fit in 192 bits");
}
/**
* @dev Returns the downcasted int184 from int256, reverting on
* overflow (when the input is less than smallest int184 or
* greater than largest int184).
*
* Counterpart to Solidity's `int184` operator.
*
* Requirements:
*
* - input must fit into 184 bits
*
* _Available since v4.7._
*/
function toInt184(int256 value) internal pure returns (int184 downcasted) {
downcasted = int184(value);
require(downcasted == value, "SafeCast: value doesn't fit in 184 bits");
}
/**
* @dev Returns the downcasted int176 from int256, reverting on
* overflow (when the input is less than smallest int176 or
* greater than largest int176).
*
* Counterpart to Solidity's `int176` operator.
*
* Requirements:
*
* - input must fit into 176 bits
*
* _Available since v4.7._
*/
function toInt176(int256 value) internal pure returns (int176 downcasted) {
downcasted = int176(value);
require(downcasted == value, "SafeCast: value doesn't fit in 176 bits");
}
/**
* @dev Returns the downcasted int168 from int256, reverting on
* overflow (when the input is less than smallest int168 or
* greater than largest int168).
*
* Counterpart to Solidity's `int168` operator.
*
* Requirements:
*
* - input must fit into 168 bits
*
* _Available since v4.7._
*/
function toInt168(int256 value) internal pure returns (int168 downcasted) {
downcasted = int168(value);
require(downcasted == value, "SafeCast: value doesn't fit in 168 bits");
}
/**
* @dev Returns the downcasted int160 from int256, reverting on
* overflow (when the input is less than smallest int160 or
* greater than largest int160).
*
* Counterpart to Solidity's `int160` operator.
*
* Requirements:
*
* - input must fit into 160 bits
*
* _Available since v4.7._
*/
function toInt160(int256 value) internal pure returns (int160 downcasted) {
downcasted = int160(value);
require(downcasted == value, "SafeCast: value doesn't fit in 160 bits");
}
/**
* @dev Returns the downcasted int152 from int256, reverting on
* overflow (when the input is less than smallest int152 or
* greater than largest int152).
*
* Counterpart to Solidity's `int152` operator.
*
* Requirements:
*
* - input must fit into 152 bits
*
* _Available since v4.7._
*/
function toInt152(int256 value) internal pure returns (int152 downcasted) {
downcasted = int152(value);
require(downcasted == value, "SafeCast: value doesn't fit in 152 bits");
}
/**
* @dev Returns the downcasted int144 from int256, reverting on
* overflow (when the input is less than smallest int144 or
* greater than largest int144).
*
* Counterpart to Solidity's `int144` operator.
*
* Requirements:
*
* - input must fit into 144 bits
*
* _Available since v4.7._
*/
function toInt144(int256 value) internal pure returns (int144 downcasted) {
downcasted = int144(value);
require(downcasted == value, "SafeCast: value doesn't fit in 144 bits");
}
/**
* @dev Returns the downcasted int136 from int256, reverting on
* overflow (when the input is less than smallest int136 or
* greater than largest int136).
*
* Counterpart to Solidity's `int136` operator.
*
* Requirements:
*
* - input must fit into 136 bits
*
* _Available since v4.7._
*/
function toInt136(int256 value) internal pure returns (int136 downcasted) {
downcasted = int136(value);
require(downcasted == value, "SafeCast: value doesn't fit in 136 bits");
}
/**
* @dev Returns the downcasted int128 from int256, reverting on
* overflow (when the input is less than smallest int128 or
* greater than largest int128).
*
* Counterpart to Solidity's `int128` operator.
*
* Requirements:
*
* - input must fit into 128 bits
*
* _Available since v3.1._
*/
function toInt128(int256 value) internal pure returns (int128 downcasted) {
downcasted = int128(value);
require(downcasted == value, "SafeCast: value doesn't fit in 128 bits");
}
/**
* @dev Returns the downcasted int120 from int256, reverting on
* overflow (when the input is less than smallest int120 or
* greater than largest int120).
*
* Counterpart to Solidity's `int120` operator.
*
* Requirements:
*
* - input must fit into 120 bits
*
* _Available since v4.7._
*/
function toInt120(int256 value) internal pure returns (int120 downcasted) {
downcasted = int120(value);
require(downcasted == value, "SafeCast: value doesn't fit in 120 bits");
}
/**
* @dev Returns the downcasted int112 from int256, reverting on
* overflow (when the input is less than smallest int112 or
* greater than largest int112).
*
* Counterpart to Solidity's `int112` operator.
*
* Requirements:
*
* - input must fit into 112 bits
*
* _Available since v4.7._
*/
function toInt112(int256 value) internal pure returns (int112 downcasted) {
downcasted = int112(value);
require(downcasted == value, "SafeCast: value doesn't fit in 112 bits");
}
/**
* @dev Returns the downcasted int104 from int256, reverting on
* overflow (when the input is less than smallest int104 or
* greater than largest int104).
*
* Counterpart to Solidity's `int104` operator.
*
* Requirements:
*
* - input must fit into 104 bits
*
* _Available since v4.7._
*/
function toInt104(int256 value) internal pure returns (int104 downcasted) {
downcasted = int104(value);
require(downcasted == value, "SafeCast: value doesn't fit in 104 bits");
}
/**
* @dev Returns the downcasted int96 from int256, reverting on
* overflow (when the input is less than smallest int96 or
* greater than largest int96).
*
* Counterpart to Solidity's `int96` operator.
*
* Requirements:
*
* - input must fit into 96 bits
*
* _Available since v4.7._
*/
function toInt96(int256 value) internal pure returns (int96 downcasted) {
downcasted = int96(value);
require(downcasted == value, "SafeCast: value doesn't fit in 96 bits");
}
/**
* @dev Returns the downcasted int88 from int256, reverting on
* overflow (when the input is less than smallest int88 or
* greater than largest int88).
*
* Counterpart to Solidity's `int88` operator.
*
* Requirements:
*
* - input must fit into 88 bits
*
* _Available since v4.7._
*/
function toInt88(int256 value) internal pure returns (int88 downcasted) {
downcasted = int88(value);
require(downcasted == value, "SafeCast: value doesn't fit in 88 bits");
}
/**
* @dev Returns the downcasted int80 from int256, reverting on
* overflow (when the input is less than smallest int80 or
* greater than largest int80).
*
* Counterpart to Solidity's `int80` operator.
*
* Requirements:
*
* - input must fit into 80 bits
*
* _Available since v4.7._
*/
function toInt80(int256 value) internal pure returns (int80 downcasted) {
downcasted = int80(value);
require(downcasted == value, "SafeCast: value doesn't fit in 80 bits");
}
/**
* @dev Returns the downcasted int72 from int256, reverting on
* overflow (when the input is less than smallest int72 or
* greater than largest int72).
*
* Counterpart to Solidity's `int72` operator.
*
* Requirements:
*
* - input must fit into 72 bits
*
* _Available since v4.7._
*/
function toInt72(int256 value) internal pure returns (int72 downcasted) {
downcasted = int72(value);
require(downcasted == value, "SafeCast: value doesn't fit in 72 bits");
}
/**
* @dev Returns the downcasted int64 from int256, reverting on
* overflow (when the input is less than smallest int64 or
* greater than largest int64).
*
* Counterpart to Solidity's `int64` operator.
*
* Requirements:
*
* - input must fit into 64 bits
*
* _Available since v3.1._
*/
function toInt64(int256 value) internal pure returns (int64 downcasted) {
downcasted = int64(value);
require(downcasted == value, "SafeCast: value doesn't fit in 64 bits");
}
/**
* @dev Returns the downcasted int56 from int256, reverting on
* overflow (when the input is less than smallest int56 or
* greater than largest int56).
*
* Counterpart to Solidity's `int56` operator.
*
* Requirements:
*
* - input must fit into 56 bits
*
* _Available since v4.7._
*/
function toInt56(int256 value) internal pure returns (int56 downcasted) {
downcasted = int56(value);
require(downcasted == value, "SafeCast: value doesn't fit in 56 bits");
}
/**
* @dev Returns the downcasted int48 from int256, reverting on
* overflow (when the input is less than smallest int48 or
* greater than largest int48).
*
* Counterpart to Solidity's `int48` operator.
*
* Requirements:
*
* - input must fit into 48 bits
*
* _Available since v4.7._
*/
function toInt48(int256 value) internal pure returns (int48 downcasted) {
downcasted = int48(value);
require(downcasted == value, "SafeCast: value doesn't fit in 48 bits");
}
/**
* @dev Returns the downcasted int40 from int256, reverting on
* overflow (when the input is less than smallest int40 or
* greater than largest int40).
*
* Counterpart to Solidity's `int40` operator.
*
* Requirements:
*
* - input must fit into 40 bits
*
* _Available since v4.7._
*/
function toInt40(int256 value) internal pure returns (int40 downcasted) {
downcasted = int40(value);
require(downcasted == value, "SafeCast: value doesn't fit in 40 bits");
}
/**
* @dev Returns the downcasted int32 from int256, reverting on
* overflow (when the input is less than smallest int32 or
* greater than largest int32).
*
* Counterpart to Solidity's `int32` operator.
*
* Requirements:
*
* - input must fit into 32 bits
*
* _Available since v3.1._
*/
function toInt32(int256 value) internal pure returns (int32 downcasted) {
downcasted = int32(value);
require(downcasted == value, "SafeCast: value doesn't fit in 32 bits");
}
/**
* @dev Returns the downcasted int24 from int256, reverting on
* overflow (when the input is less than smallest int24 or
* greater than largest int24).
*
* Counterpart to Solidity's `int24` operator.
*
* Requirements:
*
* - input must fit into 24 bits
*
* _Available since v4.7._
*/
function toInt24(int256 value) internal pure returns (int24 downcasted) {
downcasted = int24(value);
require(downcasted == value, "SafeCast: value doesn't fit in 24 bits");
}
/**
* @dev Returns the downcasted int16 from int256, reverting on
* overflow (when the input is less than smallest int16 or
* greater than largest int16).
*
* Counterpart to Solidity's `int16` operator.
*
* Requirements:
*
* - input must fit into 16 bits
*
* _Available since v3.1._
*/
function toInt16(int256 value) internal pure returns (int16 downcasted) {
downcasted = int16(value);
require(downcasted == value, "SafeCast: value doesn't fit in 16 bits");
}
/**
* @dev Returns the downcasted int8 from int256, reverting on
* overflow (when the input is less than smallest int8 or
* greater than largest int8).
*
* Counterpart to Solidity's `int8` operator.
*
* Requirements:
*
* - input must fit into 8 bits
*
* _Available since v3.1._
*/
function toInt8(int256 value) internal pure returns (int8 downcasted) {
downcasted = int8(value);
require(downcasted == value, "SafeCast: value doesn't fit in 8 bits");
}
/**
* @dev Converts an unsigned uint256 into a signed int256.
*
* Requirements:
*
* - input must be less than or equal to maxInt256.
*
* _Available since v3.0._
*/
function toInt256(uint256 value) internal pure returns (int256) {
// Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive
require(value <= uint256(type(int256).max), "SafeCast: value doesn't fit in an int256");
return int256(value);
}
}
FunctionsRouter.sol 587 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import {ITypeAndVersion} from "../../../shared/interfaces/ITypeAndVersion.sol";
import {IFunctionsRouter} from "./interfaces/IFunctionsRouter.sol";
import {IFunctionsCoordinator} from "./interfaces/IFunctionsCoordinator.sol";
import {IAccessController} from "../../../shared/interfaces/IAccessController.sol";
import {FunctionsSubscriptions} from "./FunctionsSubscriptions.sol";
import {FunctionsResponse} from "./libraries/FunctionsResponse.sol";
import {ConfirmedOwner} from "../../../shared/access/ConfirmedOwner.sol";
import {SafeCast} from "../../../vendor/openzeppelin-solidity/v4.8.0/contracts/utils/math/SafeCast.sol";
import {Pausable} from "../../../vendor/openzeppelin-solidity/v4.8.0/contracts/security/Pausable.sol";
contract FunctionsRouter is IFunctionsRouter, FunctionsSubscriptions, Pausable, ITypeAndVersion, ConfirmedOwner {
using FunctionsResponse for FunctionsResponse.RequestMeta;
using FunctionsResponse for FunctionsResponse.Commitment;
using FunctionsResponse for FunctionsResponse.FulfillResult;
string public constant override typeAndVersion = "Functions Router v1.0.0";
// We limit return data to a selector plus 4 words. This is to avoid
// malicious contracts from returning large amounts of data and causing
// repeated out-of-gas scenarios.
uint16 public constant MAX_CALLBACK_RETURN_BYTES = 4 + 4 * 32;
uint8 private constant MAX_CALLBACK_GAS_LIMIT_FLAGS_INDEX = 0;
event RequestStart(
bytes32 indexed requestId,
bytes32 indexed donId,
uint64 indexed subscriptionId,
address subscriptionOwner,
address requestingContract,
address requestInitiator,
bytes data,
uint16 dataVersion,
uint32 callbackGasLimit,
uint96 estimatedTotalCostJuels
);
event RequestProcessed(
bytes32 indexed requestId,
uint64 indexed subscriptionId,
uint96 totalCostJuels,
address transmitter,
FunctionsResponse.FulfillResult resultCode,
bytes response,
bytes err,
bytes callbackReturnData
);
event RequestNotProcessed(
bytes32 indexed requestId,
address coordinator,
address transmitter,
FunctionsResponse.FulfillResult resultCode
);
error EmptyRequestData();
error OnlyCallableFromCoordinator();
error SenderMustAcceptTermsOfService(address sender);
error InvalidGasFlagValue(uint8 value);
error GasLimitTooBig(uint32 limit);
error DuplicateRequestId(bytes32 requestId);
struct CallbackResult {
bool success; // ══════╸ Whether the callback succeeded or not
uint256 gasUsed; // ═══╸ The amount of gas consumed during the callback
bytes returnData; // ══╸ The return of the callback function
}
// ================================================================
// | Route state |
// ================================================================
mapping(bytes32 id => address routableContract) private s_route;
error RouteNotFound(bytes32 id);
// Identifier for the route to the Terms of Service Allow List
bytes32 private s_allowListId;
// ================================================================
// | Configuration state |
// ================================================================
struct Config {
uint16 maxConsumersPerSubscription; // ═════════╗ Maximum number of consumers which can be added to a single subscription. This bound ensures we are able to loop over all subscription consumers as needed, without exceeding gas limits. Should a user require more consumers, they can use multiple subscriptions.
uint72 adminFee; // ║ Flat fee (in Juels of LINK) that will be paid to the Router owner for operation of the network
bytes4 handleOracleFulfillmentSelector; // ║ The function selector that is used when calling back to the Client contract
uint16 gasForCallExactCheck; // ════════════════╝ Used during calling back to the client. Ensures we have at least enough gas to be able to revert if gasAmount > 63//64*gas available.
uint32[] maxCallbackGasLimits; // ══════════════╸ List of max callback gas limits used by flag with GAS_FLAG_INDEX
uint16 subscriptionDepositMinimumRequests; //═══╗ Amount of requests that must be completed before the full subscription balance will be released when closing a subscription account.
uint72 subscriptionDepositJuels; // ════════════╝ Amount of subscription funds that are held as a deposit until Config.subscriptionDepositMinimumRequests are made using the subscription.
}
Config private s_config;
event ConfigUpdated(Config);
// ================================================================
// | Proposal state |
// ================================================================
uint8 private constant MAX_PROPOSAL_SET_LENGTH = 8;
struct ContractProposalSet {
bytes32[] ids; // ══╸ The IDs that key into the routes that will be modified if the update is applied
address[] to; // ═══╸ The address of the contracts that the route will point to if the updated is applied
}
ContractProposalSet private s_proposedContractSet;
event ContractProposed(
bytes32 proposedContractSetId,
address proposedContractSetFromAddress,
address proposedContractSetToAddress
);
event ContractUpdated(bytes32 id, address from, address to);
error InvalidProposal();
error IdentifierIsReserved(bytes32 id);
// ================================================================
// | Initialization |
// ================================================================
constructor(
address linkToken,
Config memory config
) FunctionsSubscriptions(linkToken) ConfirmedOwner(msg.sender) Pausable() {
// Set the intial configuration
updateConfig(config);
}
// ================================================================
// | Configuration |
// ================================================================
/// @notice The identifier of the route to retrieve the address of the access control contract
// The access control contract controls which accounts can manage subscriptions
/// @return id - bytes32 id that can be passed to the "getContractById" of the Router
function getConfig() external view returns (Config memory) {
return s_config;
}
/// @notice The router configuration
function updateConfig(Config memory config) public onlyOwner {
s_config = config;
emit ConfigUpdated(config);
}
/// @inheritdoc IFunctionsRouter
function isValidCallbackGasLimit(uint64 subscriptionId, uint32 callbackGasLimit) public view {
uint8 callbackGasLimitsIndexSelector = uint8(getFlags(subscriptionId)[MAX_CALLBACK_GAS_LIMIT_FLAGS_INDEX]);
if (callbackGasLimitsIndexSelector >= s_config.maxCallbackGasLimits.length) {
revert InvalidGasFlagValue(callbackGasLimitsIndexSelector);
}
uint32 maxCallbackGasLimit = s_config.maxCallbackGasLimits[callbackGasLimitsIndexSelector];
if (callbackGasLimit > maxCallbackGasLimit) {
revert GasLimitTooBig(maxCallbackGasLimit);
}
}
/// @inheritdoc IFunctionsRouter
function getAdminFee() external view override returns (uint72) {
return s_config.adminFee;
}
/// @inheritdoc IFunctionsRouter
function getAllowListId() external view override returns (bytes32) {
return s_allowListId;
}
/// @inheritdoc IFunctionsRouter
function setAllowListId(bytes32 allowListId) external override onlyOwner {
s_allowListId = allowListId;
}
/// @dev Used within FunctionsSubscriptions.sol
function _getMaxConsumers() internal view override returns (uint16) {
return s_config.maxConsumersPerSubscription;
}
/// @dev Used within FunctionsSubscriptions.sol
function _getSubscriptionDepositDetails() internal view override returns (uint16, uint72) {
return (s_config.subscriptionDepositMinimumRequests, s_config.subscriptionDepositJuels);
}
// ================================================================
// | Requests |
// ================================================================
/// @inheritdoc IFunctionsRouter
function sendRequest(
uint64 subscriptionId,
bytes calldata data,
uint16 dataVersion,
uint32 callbackGasLimit,
bytes32 donId
) external override returns (bytes32) {
IFunctionsCoordinator coordinator = IFunctionsCoordinator(getContractById(donId));
return _sendRequest(donId, coordinator, subscriptionId, data, dataVersion, callbackGasLimit);
}
/// @inheritdoc IFunctionsRouter
function sendRequestToProposed(
uint64 subscriptionId,
bytes calldata data,
uint16 dataVersion,
uint32 callbackGasLimit,
bytes32 donId
) external override returns (bytes32) {
IFunctionsCoordinator coordinator = IFunctionsCoordinator(getProposedContractById(donId));
return _sendRequest(donId, coordinator, subscriptionId, data, dataVersion, callbackGasLimit);
}
function _sendRequest(
bytes32 donId,
IFunctionsCoordinator coordinator,
uint64 subscriptionId,
bytes memory data,
uint16 dataVersion,
uint32 callbackGasLimit
) private returns (bytes32) {
_whenNotPaused();
_isExistingSubscription(subscriptionId);
_isAllowedConsumer(msg.sender, subscriptionId);
isValidCallbackGasLimit(subscriptionId, callbackGasLimit);
if (data.length == 0) {
revert EmptyRequestData();
}
Subscription memory subscription = getSubscription(subscriptionId);
Consumer memory consumer = getConsumer(msg.sender, subscriptionId);
uint72 adminFee = s_config.adminFee;
// Forward request to DON
FunctionsResponse.Commitment memory commitment = coordinator.startRequest(
FunctionsResponse.RequestMeta({
requestingContract: msg.sender,
data: data,
subscriptionId: subscriptionId,
dataVersion: dataVersion,
flags: getFlags(subscriptionId),
callbackGasLimit: callbackGasLimit,
adminFee: adminFee,
initiatedRequests: consumer.initiatedRequests,
completedRequests: consumer.completedRequests,
availableBalance: subscription.balance - subscription.blockedBalance,
subscriptionOwner: subscription.owner
})
);
// Do not allow setting a comittment for a requestId that already exists
if (s_requestCommitments[commitment.requestId] != bytes32(0)) {
revert DuplicateRequestId(commitment.requestId);
}
// Store a commitment about the request
s_requestCommitments[commitment.requestId] = keccak256(
abi.encode(
FunctionsResponse.Commitment({
adminFee: adminFee,
coordinator: address(coordinator),
client: msg.sender,
subscriptionId: subscriptionId,
callbackGasLimit: callbackGasLimit,
estimatedTotalCostJuels: commitment.estimatedTotalCostJuels,
timeoutTimestamp: commitment.timeoutTimestamp,
requestId: commitment.requestId,
donFee: commitment.donFee,
gasOverheadBeforeCallback: commitment.gasOverheadBeforeCallback,
gasOverheadAfterCallback: commitment.gasOverheadAfterCallback
})
)
);
_markRequestInFlight(msg.sender, subscriptionId, commitment.estimatedTotalCostJuels);
emit RequestStart({
requestId: commitment.requestId,
donId: donId,
subscriptionId: subscriptionId,
subscriptionOwner: subscription.owner,
requestingContract: msg.sender,
requestInitiator: tx.origin,
data: data,
dataVersion: dataVersion,
callbackGasLimit: callbackGasLimit,
estimatedTotalCostJuels: commitment.estimatedTotalCostJuels
});
return commitment.requestId;
}
// ================================================================
// | Responses |
// ================================================================
/// @inheritdoc IFunctionsRouter
function fulfill(
bytes memory response,
bytes memory err,
uint96 juelsPerGas,
uint96 costWithoutCallback,
address transmitter,
FunctionsResponse.Commitment memory commitment
) external override returns (FunctionsResponse.FulfillResult resultCode, uint96) {
_whenNotPaused();
if (msg.sender != commitment.coordinator) {
revert OnlyCallableFromCoordinator();
}
{
bytes32 commitmentHash = s_requestCommitments[commitment.requestId];
if (commitmentHash == bytes32(0)) {
resultCode = FunctionsResponse.FulfillResult.INVALID_REQUEST_ID;
emit RequestNotProcessed(commitment.requestId, commitment.coordinator, transmitter, resultCode);
return (resultCode, 0);
}
if (keccak256(abi.encode(commitment)) != commitmentHash) {
resultCode = FunctionsResponse.FulfillResult.INVALID_COMMITMENT;
emit RequestNotProcessed(commitment.requestId, commitment.coordinator, transmitter, resultCode);
return (resultCode, 0);
}
// Check that the transmitter has supplied enough gas for the callback to succeed
if (gasleft() < commitment.callbackGasLimit + commitment.gasOverheadAfterCallback) {
resultCode = FunctionsResponse.FulfillResult.INSUFFICIENT_GAS_PROVIDED;
emit RequestNotProcessed(commitment.requestId, commitment.coordinator, transmitter, resultCode);
return (resultCode, 0);
}
}
{
uint96 callbackCost = juelsPerGas * SafeCast.toUint96(commitment.callbackGasLimit);
uint96 totalCostJuels = commitment.adminFee + costWithoutCallback + callbackCost;
// Check that the subscription can still afford to fulfill the request
if (totalCostJuels > getSubscription(commitment.subscriptionId).balance) {
resultCode = FunctionsResponse.FulfillResult.SUBSCRIPTION_BALANCE_INVARIANT_VIOLATION;
emit RequestNotProcessed(commitment.requestId, commitment.coordinator, transmitter, resultCode);
return (resultCode, 0);
}
// Check that the cost has not exceeded the quoted cost
if (totalCostJuels > commitment.estimatedTotalCostJuels) {
resultCode = FunctionsResponse.FulfillResult.COST_EXCEEDS_COMMITMENT;
emit RequestNotProcessed(commitment.requestId, commitment.coordinator, transmitter, resultCode);
return (resultCode, 0);
}
}
delete s_requestCommitments[commitment.requestId];
CallbackResult memory result = _callback(
commitment.requestId,
response,
err,
commitment.callbackGasLimit,
commitment.client
);
resultCode = result.success
? FunctionsResponse.FulfillResult.FULFILLED
: FunctionsResponse.FulfillResult.USER_CALLBACK_ERROR;
Receipt memory receipt = _pay(
commitment.subscriptionId,
commitment.estimatedTotalCostJuels,
commitment.client,
commitment.adminFee,
juelsPerGas,
SafeCast.toUint96(result.gasUsed),
costWithoutCallback
);
emit RequestProcessed({
requestId: commitment.requestId,
subscriptionId: commitment.subscriptionId,
totalCostJuels: receipt.totalCostJuels,
transmitter: transmitter,
resultCode: resultCode,
response: response,
err: err,
callbackReturnData: result.returnData
});
return (resultCode, receipt.callbackGasCostJuels);
}
function _callback(
bytes32 requestId,
bytes memory response,
bytes memory err,
uint32 callbackGasLimit,
address client
) private returns (CallbackResult memory) {
bool destinationNoLongerExists;
// solhint-disable-next-line no-inline-assembly
assembly {
// solidity calls check that a contract actually exists at the destination, so we do the same
destinationNoLongerExists := iszero(extcodesize(client))
}
if (destinationNoLongerExists) {
// Return without attempting callback
// The subscription will still be charged to reimburse transmitter's gas overhead
return CallbackResult({success: false, gasUsed: 0, returnData: new bytes(0)});
}
bytes memory encodedCallback = abi.encodeWithSelector(
s_config.handleOracleFulfillmentSelector,
requestId,
response,
err
);
uint16 gasForCallExactCheck = s_config.gasForCallExactCheck;
// Call with explicitly the amount of callback gas requested
// Important to not let them exhaust the gas budget and avoid payment.
// NOTE: that callWithExactGas will revert if we do not have sufficient gas
// to give the callee their requested amount.
bool success;
uint256 gasUsed;
// allocate return data memory ahead of time
bytes memory returnData = new bytes(MAX_CALLBACK_RETURN_BYTES);
// solhint-disable-next-line no-inline-assembly
assembly {
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) {
revert(0, 0)
}
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)), callbackGasLimit)) {
revert(0, 0)
}
// call and report whether we succeeded
// call(gas,addr,value,argsOffset,argsLength,retOffset,retLength)
let gasBeforeCall := gas()
success := call(callbackGasLimit, client, 0, add(encodedCallback, 0x20), mload(encodedCallback), 0, 0)
gasUsed := sub(gasBeforeCall, gas())
// limit our copy to MAX_CALLBACK_RETURN_BYTES bytes
let toCopy := returndatasize()
if gt(toCopy, MAX_CALLBACK_RETURN_BYTES) {
toCopy := MAX_CALLBACK_RETURN_BYTES
}
// Store the length of the copied bytes
mstore(returnData, toCopy)
// copy the bytes from returnData[0:_toCopy]
returndatacopy(add(returnData, 0x20), 0, toCopy)
}
return CallbackResult({success: success, gasUsed: gasUsed, returnData: returnData});
}
// ================================================================
// | Route methods |
// ================================================================
/// @inheritdoc IFunctionsRouter
function getContractById(bytes32 id) public view override returns (address) {
address currentImplementation = s_route[id];
if (currentImplementation == address(0)) {
revert RouteNotFound(id);
}
return currentImplementation;
}
/// @inheritdoc IFunctionsRouter
function getProposedContractById(bytes32 id) public view override returns (address) {
// Iterations will not exceed MAX_PROPOSAL_SET_LENGTH
for (uint8 i = 0; i < s_proposedContractSet.ids.length; ++i) {
if (id == s_proposedContractSet.ids[i]) {
return s_proposedContractSet.to[i];
}
}
revert RouteNotFound(id);
}
// ================================================================
// | Contract Proposal methods |
// ================================================================
/// @inheritdoc IFunctionsRouter
function getProposedContractSet() external view override returns (bytes32[] memory, address[] memory) {
return (s_proposedContractSet.ids, s_proposedContractSet.to);
}
/// @inheritdoc IFunctionsRouter
function proposeContractsUpdate(
bytes32[] memory proposedContractSetIds,
address[] memory proposedContractSetAddresses
) external override onlyOwner {
// IDs and addresses arrays must be of equal length and must not exceed the max proposal length
uint256 idsArrayLength = proposedContractSetIds.length;
if (idsArrayLength != proposedContractSetAddresses.length || idsArrayLength > MAX_PROPOSAL_SET_LENGTH) {
revert InvalidProposal();
}
// NOTE: iterations of this loop will not exceed MAX_PROPOSAL_SET_LENGTH
for (uint256 i = 0; i < idsArrayLength; ++i) {
bytes32 id = proposedContractSetIds[i];
address proposedContract = proposedContractSetAddresses[i];
if (
proposedContract == address(0) || // The Proposed address must be a valid address
s_route[id] == proposedContract // The Proposed address must point to a different address than what is currently set
) {
revert InvalidProposal();
}
emit ContractProposed({
proposedContractSetId: id,
proposedContractSetFromAddress: s_route[id],
proposedContractSetToAddress: proposedContract
});
}
s_proposedContractSet = ContractProposalSet({ids: proposedContractSetIds, to: proposedContractSetAddresses});
}
/// @inheritdoc IFunctionsRouter
function updateContracts() external override onlyOwner {
// Iterations will not exceed MAX_PROPOSAL_SET_LENGTH
for (uint256 i = 0; i < s_proposedContractSet.ids.length; ++i) {
bytes32 id = s_proposedContractSet.ids[i];
address to = s_proposedContractSet.to[i];
emit ContractUpdated({id: id, from: s_route[id], to: to});
s_route[id] = to;
}
delete s_proposedContractSet;
}
// ================================================================
// | Modifiers |
// ================================================================
// Favoring internal functions over actual modifiers to reduce contract size
/// @dev Used within FunctionsSubscriptions.sol
function _whenNotPaused() internal view override {
_requireNotPaused();
}
/// @dev Used within FunctionsSubscriptions.sol
function _onlyRouterOwner() internal view override {
_validateOwnership();
}
/// @dev Used within FunctionsSubscriptions.sol
function _onlySenderThatAcceptedToS() internal view override {
address currentImplementation = s_route[s_allowListId];
if (currentImplementation == address(0)) {
// If not set, ignore this check, allow all access
return;
}
if (!IAccessController(currentImplementation).hasAccess(msg.sender, new bytes(0))) {
revert SenderMustAcceptTermsOfService(msg.sender);
}
}
/// @inheritdoc IFunctionsRouter
function pause() external override onlyOwner {
_pause();
}
/// @inheritdoc IFunctionsRouter
function unpause() external override onlyOwner {
_unpause();
}
}
FunctionsSubscriptions.sol 556 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import {IFunctionsSubscriptions} from "./interfaces/IFunctionsSubscriptions.sol";
import {IERC677Receiver} from "../../../shared/interfaces/IERC677Receiver.sol";
import {LinkTokenInterface} from "../../../shared/interfaces/LinkTokenInterface.sol";
import {IFunctionsBilling} from "./interfaces/IFunctionsBilling.sol";
import {IFunctionsRouter} from "./interfaces/IFunctionsRouter.sol";
import {FunctionsResponse} from "./libraries/FunctionsResponse.sol";
import {IERC20} from "../../../vendor/openzeppelin-solidity/v4.8.0/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "../../../vendor/openzeppelin-solidity/v4.8.0/contracts/token/ERC20/utils/SafeERC20.sol";
import {SafeCast} from "../../../vendor/openzeppelin-solidity/v4.8.0/contracts/utils/math/SafeCast.sol";
/// @title Functions Subscriptions contract
/// @notice Contract that coordinates payment from users to the nodes of the Decentralized Oracle Network (DON).
/// @dev THIS CONTRACT HAS NOT GONE THROUGH ANY SECURITY REVIEW. DO NOT USE IN PROD.
abstract contract FunctionsSubscriptions is IFunctionsSubscriptions, IERC677Receiver {
using SafeERC20 for IERC20;
using FunctionsResponse for FunctionsResponse.Commitment;
// ================================================================
// | Balance state |
// ================================================================
// link token address
IERC20 internal immutable i_linkToken;
// s_totalLinkBalance tracks the total LINK sent to/from
// this contract through onTokenTransfer, cancelSubscription and oracleWithdraw.
// A discrepancy with this contract's LINK balance indicates that someone
// sent tokens using transfer and so we may need to use recoverFunds.
uint96 private s_totalLinkBalance;
/// @dev NOP balances are held as a single amount. The breakdown is held by the Coordinator.
mapping(address coordinator => uint96 balanceJuelsLink) private s_withdrawableTokens;
// ================================================================
// | Subscription state |
// ================================================================
// Keep a count of the number of subscriptions so that its possible to
// loop through all the current subscriptions via .getSubscription().
uint64 private s_currentSubscriptionId;
mapping(uint64 subscriptionId => Subscription) private s_subscriptions;
// Maintains the list of keys in s_consumers.
// We do this for 2 reasons:
// 1. To be able to clean up all keys from s_consumers when canceling a subscription.
// 2. To be able to return the list of all consumers in getSubscription.
// Note that we need the s_consumers map to be able to directly check if a
// consumer is valid without reading all the consumers from storage.
mapping(address consumer => mapping(uint64 subscriptionId => Consumer)) private s_consumers;
event SubscriptionCreated(uint64 indexed subscriptionId, address owner);
event SubscriptionFunded(uint64 indexed subscriptionId, uint256 oldBalance, uint256 newBalance);
event SubscriptionConsumerAdded(uint64 indexed subscriptionId, address consumer);
event SubscriptionConsumerRemoved(uint64 indexed subscriptionId, address consumer);
event SubscriptionCanceled(uint64 indexed subscriptionId, address fundsRecipient, uint256 fundsAmount);
event SubscriptionOwnerTransferRequested(uint64 indexed subscriptionId, address from, address to);
event SubscriptionOwnerTransferred(uint64 indexed subscriptionId, address from, address to);
error TooManyConsumers(uint16 maximumConsumers);
error InsufficientBalance(uint96 currentBalanceJuels);
error InvalidConsumer();
error CannotRemoveWithPendingRequests();
error InvalidSubscription();
error OnlyCallableFromLink();
error InvalidCalldata();
error MustBeSubscriptionOwner();
error TimeoutNotExceeded();
error MustBeProposedOwner(address proposedOwner);
event FundsRecovered(address to, uint256 amount);
// ================================================================
// | Request state |
// ================================================================
mapping(bytes32 requestId => bytes32 commitmentHash) internal s_requestCommitments;
struct Receipt {
uint96 callbackGasCostJuels;
uint96 totalCostJuels;
}
event RequestTimedOut(bytes32 indexed requestId);
// ================================================================
// | Initialization |
// ================================================================
constructor(address link) {
i_linkToken = IERC20(link);
}
// ================================================================
// | Request/Response |
// ================================================================
/// @notice Sets a request as in-flight
/// @dev Only callable within the Router
function _markRequestInFlight(address client, uint64 subscriptionId, uint96 estimatedTotalCostJuels) internal {
// Earmark subscription funds
s_subscriptions[subscriptionId].blockedBalance += estimatedTotalCostJuels;
// Increment sent requests
s_consumers[client][subscriptionId].initiatedRequests += 1;
}
/// @notice Moves funds from one subscription account to another.
/// @dev Only callable by the Coordinator contract that is saved in the request commitment
function _pay(
uint64 subscriptionId,
uint96 estimatedTotalCostJuels,
address client,
uint96 adminFee,
uint96 juelsPerGas,
uint96 gasUsed,
uint96 costWithoutCallbackJuels
) internal returns (Receipt memory) {
uint96 callbackGasCostJuels = juelsPerGas * gasUsed;
uint96 totalCostJuels = costWithoutCallbackJuels + adminFee + callbackGasCostJuels;
if (
s_subscriptions[subscriptionId].balance < totalCostJuels ||
s_subscriptions[subscriptionId].blockedBalance < estimatedTotalCostJuels
) {
revert InsufficientBalance(s_subscriptions[subscriptionId].balance);
}
// Charge the subscription
s_subscriptions[subscriptionId].balance -= totalCostJuels;
// Unblock earmarked funds
s_subscriptions[subscriptionId].blockedBalance -= estimatedTotalCostJuels;
// Pay the DON's fees and gas reimbursement
s_withdrawableTokens[msg.sender] += costWithoutCallbackJuels + callbackGasCostJuels;
// Pay out the administration fee
s_withdrawableTokens[address(this)] += adminFee;
// Increment finished requests
s_consumers[client][subscriptionId].completedRequests += 1;
return Receipt({callbackGasCostJuels: callbackGasCostJuels, totalCostJuels: totalCostJuels});
}
// ================================================================
// | Owner methods |
// ================================================================
/// @inheritdoc IFunctionsSubscriptions
function ownerCancelSubscription(uint64 subscriptionId) external override {
_onlyRouterOwner();
_isExistingSubscription(subscriptionId);
_cancelSubscriptionHelper(subscriptionId, s_subscriptions[subscriptionId].owner, false);
}
/// @inheritdoc IFunctionsSubscriptions
function recoverFunds(address to) external override {
_onlyRouterOwner();
uint256 externalBalance = i_linkToken.balanceOf(address(this));
uint256 internalBalance = uint256(s_totalLinkBalance);
if (internalBalance < externalBalance) {
uint256 amount = externalBalance - internalBalance;
i_linkToken.safeTransfer(to, amount);
emit FundsRecovered(to, amount);
}
// If the balances are equal, nothing to be done.
}
// ================================================================
// | Fund withdrawal |
// ================================================================
/// @inheritdoc IFunctionsSubscriptions
function oracleWithdraw(address recipient, uint96 amount) external override {
_whenNotPaused();
if (amount == 0) {
revert InvalidCalldata();
}
uint96 currentBalance = s_withdrawableTokens[msg.sender];
if (currentBalance < amount) {
revert InsufficientBalance(currentBalance);
}
s_withdrawableTokens[msg.sender] -= amount;
s_totalLinkBalance -= amount;
i_linkToken.safeTransfer(recipient, amount);
}
/// @notice Owner withdraw LINK earned through admin fees
/// @notice If amount is 0 the full balance will be withdrawn
/// @param recipient where to send the funds
/// @param amount amount to withdraw
function ownerWithdraw(address recipient, uint96 amount) external {
_onlyRouterOwner();
if (amount == 0) {
amount = s_withdrawableTokens[address(this)];
}
uint96 currentBalance = s_withdrawableTokens[address(this)];
if (currentBalance < amount) {
revert InsufficientBalance(currentBalance);
}
s_withdrawableTokens[address(this)] -= amount;
s_totalLinkBalance -= amount;
i_linkToken.safeTransfer(recipient, amount);
}
// ================================================================
// | TransferAndCall Deposit helper |
// ================================================================
// This function is to be invoked when using LINK.transferAndCall
/// @dev Note to fund the subscription, use transferAndCall. For example
/// @dev LINKTOKEN.transferAndCall(
/// @dev address(ROUTER),
/// @dev amount,
/// @dev abi.encode(subscriptionId));
function onTokenTransfer(address /* sender */, uint256 amount, bytes calldata data) external override {
_whenNotPaused();
if (msg.sender != address(i_linkToken)) {
revert OnlyCallableFromLink();
}
if (data.length != 32) {
revert InvalidCalldata();
}
uint64 subscriptionId = abi.decode(data, (uint64));
if (s_subscriptions[subscriptionId].owner == address(0)) {
revert InvalidSubscription();
}
// We do not check that the msg.sender is the subscription owner,
// anyone can fund a subscription.
uint256 oldBalance = s_subscriptions[subscriptionId].balance;
s_subscriptions[subscriptionId].balance += uint96(amount);
s_totalLinkBalance += uint96(amount);
emit SubscriptionFunded(subscriptionId, oldBalance, oldBalance + amount);
}
// ================================================================
// | Subscription management |
// ================================================================
/// @inheritdoc IFunctionsSubscriptions
function getTotalBalance() external view override returns (uint96) {
return s_totalLinkBalance;
}
/// @inheritdoc IFunctionsSubscriptions
function getSubscriptionCount() external view override returns (uint64) {
return s_currentSubscriptionId;
}
/// @inheritdoc IFunctionsSubscriptions
function getSubscription(uint64 subscriptionId) public view override returns (Subscription memory) {
_isExistingSubscription(subscriptionId);
return s_subscriptions[subscriptionId];
}
/// @inheritdoc IFunctionsSubscriptions
function getSubscriptionsInRange(
uint64 subscriptionIdStart,
uint64 subscriptionIdEnd
) external view override returns (Subscription[] memory subscriptions) {
if (
subscriptionIdStart > subscriptionIdEnd ||
subscriptionIdEnd > s_currentSubscriptionId ||
s_currentSubscriptionId == 0
) {
revert InvalidCalldata();
}
subscriptions = new Subscription[]((subscriptionIdEnd - subscriptionIdStart) + 1);
for (uint256 i = 0; i <= subscriptionIdEnd - subscriptionIdStart; ++i) {
subscriptions[i] = s_subscriptions[uint64(subscriptionIdStart + i)];
}
return subscriptions;
}
/// @inheritdoc IFunctionsSubscriptions
function getConsumer(address client, uint64 subscriptionId) public view override returns (Consumer memory) {
return s_consumers[client][subscriptionId];
}
/// @dev Used within this file & FunctionsRouter.sol
function _isExistingSubscription(uint64 subscriptionId) internal view {
if (s_subscriptions[subscriptionId].owner == address(0)) {
revert InvalidSubscription();
}
}
/// @dev Used within FunctionsRouter.sol
function _isAllowedConsumer(address client, uint64 subscriptionId) internal view {
if (!s_consumers[client][subscriptionId].allowed) {
revert InvalidConsumer();
}
}
/// @inheritdoc IFunctionsSubscriptions
function createSubscription() external override returns (uint64 subscriptionId) {
_whenNotPaused();
_onlySenderThatAcceptedToS();
subscriptionId = ++s_currentSubscriptionId;
s_subscriptions[subscriptionId] = Subscription({
balance: 0,
blockedBalance: 0,
owner: msg.sender,
proposedOwner: address(0),
consumers: new address[](0),
flags: bytes32(0)
});
emit SubscriptionCreated(subscriptionId, msg.sender);
return subscriptionId;
}
/// @inheritdoc IFunctionsSubscriptions
function createSubscriptionWithConsumer(address consumer) external override returns (uint64 subscriptionId) {
_whenNotPaused();
_onlySenderThatAcceptedToS();
subscriptionId = ++s_currentSubscriptionId;
s_subscriptions[subscriptionId] = Subscription({
balance: 0,
blockedBalance: 0,
owner: msg.sender,
proposedOwner: address(0),
consumers: new address[](0),
flags: bytes32(0)
});
s_subscriptions[subscriptionId].consumers.push(consumer);
s_consumers[consumer][subscriptionId].allowed = true;
emit SubscriptionCreated(subscriptionId, msg.sender);
emit SubscriptionConsumerAdded(subscriptionId, consumer);
return subscriptionId;
}
/// @inheritdoc IFunctionsSubscriptions
function proposeSubscriptionOwnerTransfer(uint64 subscriptionId, address newOwner) external override {
_whenNotPaused();
_onlySubscriptionOwner(subscriptionId);
_onlySenderThatAcceptedToS();
if (newOwner == address(0) || s_subscriptions[subscriptionId].proposedOwner == newOwner) {
revert InvalidCalldata();
}
s_subscriptions[subscriptionId].proposedOwner = newOwner;
emit SubscriptionOwnerTransferRequested(subscriptionId, msg.sender, newOwner);
}
/// @inheritdoc IFunctionsSubscriptions
function acceptSubscriptionOwnerTransfer(uint64 subscriptionId) external override {
_whenNotPaused();
_onlySenderThatAcceptedToS();
address previousOwner = s_subscriptions[subscriptionId].owner;
address proposedOwner = s_subscriptions[subscriptionId].proposedOwner;
if (proposedOwner != msg.sender) {
revert MustBeProposedOwner(proposedOwner);
}
s_subscriptions[subscriptionId].owner = msg.sender;
s_subscriptions[subscriptionId].proposedOwner = address(0);
emit SubscriptionOwnerTransferred(subscriptionId, previousOwner, msg.sender);
}
/// @inheritdoc IFunctionsSubscriptions
function removeConsumer(uint64 subscriptionId, address consumer) external override {
_whenNotPaused();
_onlySubscriptionOwner(subscriptionId);
_onlySenderThatAcceptedToS();
Consumer memory consumerData = s_consumers[consumer][subscriptionId];
_isAllowedConsumer(consumer, subscriptionId);
if (consumerData.initiatedRequests != consumerData.completedRequests) {
revert CannotRemoveWithPendingRequests();
}
// Note bounded by config.maxConsumers
address[] memory consumers = s_subscriptions[subscriptionId].consumers;
for (uint256 i = 0; i < consumers.length; ++i) {
if (consumers[i] == consumer) {
// Storage write to preserve last element
s_subscriptions[subscriptionId].consumers[i] = consumers[consumers.length - 1];
// Storage remove last element
s_subscriptions[subscriptionId].consumers.pop();
break;
}
}
delete s_consumers[consumer][subscriptionId];
emit SubscriptionConsumerRemoved(subscriptionId, consumer);
}
/// @dev Overriden in FunctionsRouter.sol
function _getMaxConsumers() internal view virtual returns (uint16);
/// @inheritdoc IFunctionsSubscriptions
function addConsumer(uint64 subscriptionId, address consumer) external override {
_whenNotPaused();
_onlySubscriptionOwner(subscriptionId);
_onlySenderThatAcceptedToS();
// Already maxed, cannot add any more consumers.
uint16 maximumConsumers = _getMaxConsumers();
if (s_subscriptions[subscriptionId].consumers.length >= maximumConsumers) {
revert TooManyConsumers(maximumConsumers);
}
if (s_consumers[consumer][subscriptionId].allowed) {
// Idempotence - do nothing if already added.
// Ensures uniqueness in s_subscriptions[subscriptionId].consumers.
return;
}
s_consumers[consumer][subscriptionId].allowed = true;
s_subscriptions[subscriptionId].consumers.push(consumer);
emit SubscriptionConsumerAdded(subscriptionId, consumer);
}
/// @dev Overriden in FunctionsRouter.sol
function _getSubscriptionDepositDetails() internal virtual returns (uint16, uint72);
function _cancelSubscriptionHelper(uint64 subscriptionId, address toAddress, bool checkDepositRefundability) private {
Subscription memory subscription = s_subscriptions[subscriptionId];
uint96 balance = subscription.balance;
uint64 completedRequests = 0;
// NOTE: loop iterations are bounded by config.maxConsumers
// If no consumers, does nothing.
for (uint256 i = 0; i < subscription.consumers.length; ++i) {
address consumer = subscription.consumers[i];
completedRequests += s_consumers[consumer][subscriptionId].completedRequests;
delete s_consumers[consumer][subscriptionId];
}
delete s_subscriptions[subscriptionId];
(uint16 subscriptionDepositMinimumRequests, uint72 subscriptionDepositJuels) = _getSubscriptionDepositDetails();
// If subscription has not made enough requests, deposit will be forfeited
if (checkDepositRefundability && completedRequests < subscriptionDepositMinimumRequests) {
uint96 deposit = subscriptionDepositJuels > balance ? balance : subscriptionDepositJuels;
if (deposit > 0) {
s_withdrawableTokens[address(this)] += deposit;
balance -= deposit;
}
}
if (balance > 0) {
s_totalLinkBalance -= balance;
i_linkToken.safeTransfer(toAddress, uint256(balance));
}
emit SubscriptionCanceled(subscriptionId, toAddress, balance);
}
/// @inheritdoc IFunctionsSubscriptions
function cancelSubscription(uint64 subscriptionId, address to) external override {
_whenNotPaused();
_onlySubscriptionOwner(subscriptionId);
_onlySenderThatAcceptedToS();
if (pendingRequestExists(subscriptionId)) {
revert CannotRemoveWithPendingRequests();
}
_cancelSubscriptionHelper(subscriptionId, to, true);
}
/// @inheritdoc IFunctionsSubscriptions
function pendingRequestExists(uint64 subscriptionId) public view override returns (bool) {
address[] memory consumers = s_subscriptions[subscriptionId].consumers;
// NOTE: loop iterations are bounded by config.maxConsumers
for (uint256 i = 0; i < consumers.length; ++i) {
Consumer memory consumer = s_consumers[consumers[i]][subscriptionId];
if (consumer.initiatedRequests != consumer.completedRequests) {
return true;
}
}
return false;
}
/// @inheritdoc IFunctionsSubscriptions
function setFlags(uint64 subscriptionId, bytes32 flags) external override {
_onlyRouterOwner();
_isExistingSubscription(subscriptionId);
s_subscriptions[subscriptionId].flags = flags;
}
/// @inheritdoc IFunctionsSubscriptions
function getFlags(uint64 subscriptionId) public view returns (bytes32) {
return s_subscriptions[subscriptionId].flags;
}
// ================================================================
// | Request Timeout |
// ================================================================
/// @inheritdoc IFunctionsSubscriptions
function timeoutRequests(FunctionsResponse.Commitment[] calldata requestsToTimeoutByCommitment) external override {
_whenNotPaused();
for (uint256 i = 0; i < requestsToTimeoutByCommitment.length; ++i) {
FunctionsResponse.Commitment memory request = requestsToTimeoutByCommitment[i];
bytes32 requestId = request.requestId;
uint64 subscriptionId = request.subscriptionId;
// Check that request ID is valid
if (keccak256(abi.encode(request)) != s_requestCommitments[requestId]) {
revert InvalidCalldata();
}
// Check that request has exceeded allowed request time
if (block.timestamp < request.timeoutTimestamp) {
revert TimeoutNotExceeded();
}
// Notify the Coordinator that the request should no longer be fulfilled
IFunctionsBilling(request.coordinator).deleteCommitment(requestId);
// Release the subscription's balance that had been earmarked for the request
s_subscriptions[subscriptionId].blockedBalance -= request.estimatedTotalCostJuels;
s_consumers[request.client][subscriptionId].completedRequests += 1;
// Delete commitment within Router state
delete s_requestCommitments[requestId];
emit RequestTimedOut(requestId);
}
}
// ================================================================
// | Modifiers |
// ================================================================
function _onlySubscriptionOwner(uint64 subscriptionId) internal view {
address owner = s_subscriptions[subscriptionId].owner;
if (owner == address(0)) {
revert InvalidSubscription();
}
if (msg.sender != owner) {
revert MustBeSubscriptionOwner();
}
}
/// @dev Overriden in FunctionsRouter.sol
function _onlySenderThatAcceptedToS() internal virtual;
/// @dev Overriden in FunctionsRouter.sol
function _onlyRouterOwner() internal virtual;
/// @dev Overriden in FunctionsRouter.sol
function _whenNotPaused() internal virtual;
}
FunctionsResponse.sol 46 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import {IFunctionsSubscriptions} from "../interfaces/IFunctionsSubscriptions.sol";
/// @title Library of types that are used for fulfillment of a Functions request
library FunctionsResponse {
// Used to send request information from the Router to the Coordinator
struct RequestMeta {
bytes data; // ══════════════════╸ CBOR encoded Chainlink Functions request data, use FunctionsRequest library to encode a request
bytes32 flags; // ═══════════════╸ Per-subscription flags
address requestingContract; // ══╗ The client contract that is sending the request
uint96 availableBalance; // ═════╝ Common LINK balance of the subscription that is controlled by the Router to be used for all consumer requests.
uint72 adminFee; // ═════════════╗ Flat fee (in Juels of LINK) that will be paid to the Router Owner for operation of the network
uint64 subscriptionId; // ║ Identifier of the billing subscription that will be charged for the request
uint64 initiatedRequests; // ║ The number of requests that have been started
uint32 callbackGasLimit; // ║ The amount of gas that the callback to the consuming contract will be given
uint16 dataVersion; // ══════════╝ The version of the structure of the CBOR encoded request data
uint64 completedRequests; // ════╗ The number of requests that have successfully completed or timed out
address subscriptionOwner; // ═══╝ The owner of the billing subscription
}
enum FulfillResult {
FULFILLED, // 0
USER_CALLBACK_ERROR, // 1
INVALID_REQUEST_ID, // 2
COST_EXCEEDS_COMMITMENT, // 3
INSUFFICIENT_GAS_PROVIDED, // 4
SUBSCRIPTION_BALANCE_INVARIANT_VIOLATION, // 5
INVALID_COMMITMENT // 6
}
struct Commitment {
bytes32 requestId; // ═════════════════╸ A unique identifier for a Chainlink Functions request
address coordinator; // ═══════════════╗ The Coordinator contract that manages the DON that is servicing a request
uint96 estimatedTotalCostJuels; // ════╝ The maximum cost in Juels (1e18) of LINK that will be charged to fulfill a request
address client; // ════════════════════╗ The client contract that sent the request
uint64 subscriptionId; // ║ Identifier of the billing subscription that will be charged for the request
uint32 callbackGasLimit; // ═══════════╝ The amount of gas that the callback to the consuming contract will be given
uint72 adminFee; // ═══════════════════╗ Flat fee (in Juels of LINK) that will be paid to the Router Owner for operation of the network
uint72 donFee; // ║ Fee (in Juels of LINK) that will be split between Node Operators for servicing a request
uint40 gasOverheadBeforeCallback; // ║ Represents the average gas execution cost before the fulfillment callback.
uint40 gasOverheadAfterCallback; // ║ Represents the average gas execution cost after the fulfillment callback.
uint32 timeoutTimestamp; // ═══════════╝ The timestamp at which a request will be eligible to be timed out
}
}
LinkTokenInterface.sol 28 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
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);
}
Read Contract
MAX_CALLBACK_RETURN_BYTES 0x0c5d49cb → uint16
getAdminFee 0x2a905ccc → uint72
getAllowListId 0xaab396bd → bytes32
getConfig 0xc3f909d4 → tuple
getConsumer 0x674603d0 → tuple
getContractById 0xa9c9a918 → address
getFlags 0x55fedefa → bytes32
getProposedContractById 0x6a2215de → address
getProposedContractSet 0xbadc3eb6 → bytes32[], address[]
getSubscription 0xa47c7696 → tuple
getSubscriptionCount 0x66419970 → uint64
getSubscriptionsInRange 0xec2454e5 → tuple[]
getTotalBalance 0x12b58349 → uint96
isValidCallbackGasLimit 0x10fc49c1
owner 0x8da5cb5b → address
paused 0x5c975abb → bool
pendingRequestExists 0xe82ad7d4 → bool
typeAndVersion 0x181f5a77 → string
Write Contract 25 functions
These functions modify contract state and require a wallet transaction to execute.
acceptOwnership 0x79ba5097
No parameters
acceptSubscriptionOwnerTransfer 0x82359740
uint64 subscriptionId
addConsumer 0x7341c10c
uint64 subscriptionId
address consumer
cancelSubscription 0xd7ae1d30
uint64 subscriptionId
address to
createSubscription 0xa21a23e4
No parameters
returns: uint64
createSubscriptionWithConsumer 0xcc77470a
address consumer
returns: uint64
fulfill 0x6ace3f87
bytes response
bytes err
uint96 juelsPerGas
uint96 costWithoutCallback
address transmitter
tuple commitment
returns: uint8, uint96
onTokenTransfer 0xa4c0ed36
address
uint256 amount
bytes data
oracleWithdraw 0x66316d8d
address recipient
uint96 amount
ownerCancelSubscription 0x02bcc5b6
uint64 subscriptionId
ownerWithdraw 0x5ed6dfba
address recipient
uint96 amount
pause 0x8456cb59
No parameters
proposeContractsUpdate 0x3e871e4d
bytes32[] proposedContractSetIds
address[] proposedContractSetAddresses
proposeSubscriptionOwnerTransfer 0x4b8832d3
uint64 subscriptionId
address newOwner
recoverFunds 0xe72f6e30
address to
removeConsumer 0x9f87fad7
uint64 subscriptionId
address consumer
sendRequest 0x461d2762
uint64 subscriptionId
bytes data
uint16 dataVersion
uint32 callbackGasLimit
bytes32 donId
returns: bytes32
sendRequestToProposed 0x41db4ca3
uint64 subscriptionId
bytes data
uint16 dataVersion
uint32 callbackGasLimit
bytes32 donId
returns: bytes32
setAllowListId 0xea320e0b
bytes32 allowListId
setFlags 0x1ded3b36
uint64 subscriptionId
bytes32 flags
timeoutRequests 0x388d4bfd
tuple[] requestsToTimeoutByCommitment
transferOwnership 0xf2fde38b
address to
unpause 0x3f4ba83a
No parameters
updateConfig 0xf3e51e8a
tuple config
updateContracts 0xb734c0f4
No parameters
Token Balances (1)
View Transfers →Recent Transactions
No transactions found for this address