Address: 0x061Db1D68dbAED1aF4f71910892Cb6DAC0CE4295
Balance (XRP): 0 XRP
Bytecode: 0x608060405234801561001057600080fd5b50600436106102065760003560e01c80638a87fe681161011a578063c829b66d116100ad578063e30c39781161007c578063e30c397814610585578063ecd0c0c314610596578063f2fde38b146105a9578063f6ed2017146105bc578063fdff9b4d146105e557600080fd5b8063c829b66d14610503578063ce8b486f14610516578063cec6e51f14610529578063d96d57671461057257600080fd5b8063ab56b4f0116100e9578063ab56b4f01461049c578063ac18de43146104ca578063c0c53b8b146104dd578063c3b6f726146104f057600080fd5b80638a87fe68146104455780638da5cb5b1461045857806391b8105a1461046957806396bb1fef1461048957600080fd5b80633150fa8a1161019d5780635653f6791161016c5780635653f679146103ef5780635dba03281461040f578063715018a61461042257806378c041a61461042a57806379ba50971461043d57600080fd5b80633150fa8a1461036d57806331d7a2621461038057806340768fd3146103a057806351e9d686146103b357600080fd5b80631e903c04116101d95780631e903c04146102bd5780631ed9fb0f146102dd5780632073de5c146102fd5780632d06177a1461035857600080fd5b806301ffc9a71461020b578063032fd1ba1461024457806307b4fc01146102725780630ab2502214610292575b600080fd5b61022f610219366004612006565b6001600160e01b031916631ed895ad60e11b1490565b60405190151581526020015b60405180910390f35b610264610252366004612030565b60d16020526000908152604090205481565b60405190815260200161023b565b61026461028036600461205e565b60d06020526000908152604090205481565b60cb546102a5906001600160a01b031681565b6040516001600160a01b03909116815260200161023b565b6102646102cb366004612030565b600090815260d1602052604090205490565b6102646102eb366004612030565b60d26020526000908152604090205481565b61033561030b366004612030565b60d360205260009081526040902080546001820154600283015460039093015491929091600f0b84565b60408051948552602085019390935291830152600f0b606082015260800161023b565b61036b61036636600461205e565b610605565b005b61026461037b36600461207b565b610650565b61026461038e36600461205e565b60ce6020526000908152604090205481565b61036b6103ae3660046120ab565b61067b565b6103da6103c1366004612030565b60cd602052600090815260409020805460019091015482565b6040805192835260208301919091520161023b565b6102646103fd366004612030565b60cf6020526000908152604090205481565b61036b61041d3660046120d7565b610a05565b61036b610b71565b6102646104383660046120d7565b610b85565b61036b610bd0565b61026461045336600461207b565b610c4f565b6033546001600160a01b03166102a5565b610264610477366004612030565b600090815260d2602052604090205490565b61026461049736600461205e565b610c7f565b6104af6104aa3660046120d7565b610e1c565b6040805182518152602092830151928101929092520161023b565b61036b6104d836600461205e565b610e73565b61036b6104eb366004612105565b610e95565b60ca546102a5906001600160a01b031681565b61036b61051136600461205e565b61107c565b6102646105243660046120d7565b611228565b61053c61053736600461207b565b6112af565b60405161023b9190815181526020808301519082015260408083015190820152606091820151600f0b9181019190915260800190565b61026461058036600461207b565b611335565b6065546001600160a01b03166102a5565b60c9546102a5906001600160a01b031681565b61036b6105b736600461205e565b61135f565b6102646105ca36600461205e565b6001600160a01b0316600090815260ce602052604090205490565b6102646105f336600461205e565b60976020526000908152604090205481565b61060d6113d0565b6001600160a01b038116610634576040516309045a0360e41b815260040160405180910390fd5b6001600160a01b03166000908152609760205260409020439055565b600060d360006106608585610c4f565b81526020019081526020016000206000015490505b92915050565b3360009081526097602052604081205490036106a957604051620ab82160e31b815260040160405180910390fd5b6001600160a01b0382166106d057604051630381bf9b60e01b815260040160405180910390fd5b806000036106f15760405163d11b25af60e01b815260040160405180910390fd5b60008060cb60009054906101000a90046001600160a01b03166001600160a01b031663e34bf0136040518163ffffffff1660e01b815260040161014060405180830381865afa158015610748573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061076c91906121b8565b91509150600060d360006107808588610c4f565b8152602001908152602001600020905080600101546000036107b557604051631656dc4d60e21b815260040160405180910390fd5b6001600160a01b038516600090815260d060205260409020548311156107f1576001600160a01b038516600090815260d0602052604090208390555b60006108096107ff8661142a565b8460600151611497565b90506108158186612281565b60cf6000610823898a610b85565b81526020019081526020016000205461083c9190612294565b60cf600061084a898a610b85565b8152602001908152602001600020819055508060ce6000886001600160a01b03166001600160a01b031681526020019081526020016000205461088d9190612294565b6001600160a01b038716600090815260ce6020526040812091909155825490036108ec576001600160a01b038616600090815260cc60205260409020546003830180546001600160801b0319166001600160801b039092169190911790555b81546108f9908290612294565b82556002820154819061091590677fffffffffffffff906122a7565b101561093457604051636d77051560e11b815260040160405180910390fd5b6001600160a01b038616600090815260cc602052604090205461097790600f0b610972610960846114c7565b61096d86600201546114c7565b61151e565b611576565b6001600160a01b038716600090815260cc6020908152604080832080546001600160801b0319166001600160801b03959095169490941790935586825260d1905220546109c5908690612294565b600085815260d1602090815260408083209390935560d2905220546109eb908290612294565b600094855260d26020526040909420939093555050505050565b336000908152609760205260408120549003610a3357604051620ab82160e31b815260040160405180910390fd5b60cb5460408051631118d57f60e31b815290516000926001600160a01b0316916388c6abf89160048083019260209291908290030181865afa158015610a7d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610aa191906122be565b90506000610ab9610ab3836001612294565b856112af565b604001511115610adc576040516332b9e6d760e21b815260040160405180910390fd5b6000610ae88484610b85565b90506000610af78286866115a9565b6001600160a01b038616600090815260ce6020526040902054909150610b1e908290612281565b6001600160a01b038616600090815260ce602090815260408083209390935584825260cf90522054610b51908290612294565b600083815260cf6020526040902055610b6a85856117e4565b5050505050565b610b796113d0565b610b83600061190d565b565b6040516bffffffffffffffffffffffff19606084811b8216602084015283901b1660348201526000906048015b60405160208183030381529060405280519060200120905092915050565b60655433906001600160a01b03168114610c435760405162461bcd60e51b815260206004820152602960248201527f4f776e61626c6532537465703a2063616c6c6572206973206e6f7420746865206044820152683732bb9037bbb732b960b91b60648201526084015b60405180910390fd5b610c4c8161190d565b50565b60008282604051602001610bb292919091825260601b6bffffffffffffffffffffffff1916602082015260340190565b60006001600160a01b038216610ca857604051630381bf9b60e01b815260040160405180910390fd5b60cb5460408051631118d57f60e31b815290516000926001600160a01b0316916388c6abf89160048083019260209291908290030181865afa158015610cf2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d1691906122be565b90506000610d28610ab3836001612294565b604001511115610d4b576040516332b9e6d760e21b815260040160405180910390fd5b6000610d578433610b85565b90506000610d668286336115a9565b600083815260cf602052604081205491925090610d839083612294565b905080600003610da657604051635aa9184d60e01b815260040160405180910390fd5b600083815260cf602090815260408083208390556001600160a01b038916835260ce909152902054610dd9908390612281565b6001600160a01b038716600090815260ce6020526040902055610dfc86336117e4565b60c954610e13906001600160a01b03163383611926565b95945050505050565b604080518082019091526000808252602082015260cd6000610e3e8585610b85565b815260200190815260200160002060405180604001604052908160008201548152602001600182015481525050905092915050565b610e7b6113d0565b6001600160a01b0316600090815260976020526040812055565b600054610100900460ff1615808015610eb55750600054600160ff909116105b80610ecf5750303b158015610ecf575060005460ff166001145b610f325760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b6064820152608401610c3a565b6000805460ff191660011790558015610f55576000805461ff0019166101001790555b6001600160a01b038416610f7c57604051635f5d339960e01b815260040160405180910390fd5b610fb46040518060400160405280600e81526020016d29ba30b5b4b733a6b0b730b3b2b960911b815250846303e0261b60e41b61197d565b610feb6040518060400160405280600d81526020016c22b837b1b439a6b0b730b3b2b960991b8152508363078a0a4b60e41b61197d565b610ff3611a81565b60c980546001600160a01b038087166001600160a01b03199283161790925560cb805485841690831617905560ca8054928616929091169190911790558015611076576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b50505050565b3360009081526097602052604081205490036110aa57604051620ab82160e31b815260040160405180910390fd5b6001600160a01b0381166110d157604051630381bf9b60e01b815260040160405180910390fd5b60cb546040805163b3e123db60e01b815290516000926001600160a01b03169163b3e123db9160048083019260209291908290030181865afa15801561111b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061113f91906122be565b9050600060d360006111518486610c4f565b81526020019081526020016000209050806001015460001461118657604051637182438960e01b815260040160405180910390fd5b60ca546040516313cdd31b60e01b81526001600160a01b03858116600483015260009216906313cdd31b90602401602060405180830381865afa1580156111d1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111f591906122be565b9050806000036112185760405163353dae3760e11b815260040160405180910390fd5b6002820155436001909101555050565b60006001600160a01b03831661125157604051630381bf9b60e01b815260040160405180910390fd5b6001600160a01b03821661127857604051632a5d705360e11b815260040160405180910390fd5b60006112848484610b85565b905060006112938286866115a9565b600083815260cf6020526040902054909150610e139082612294565b6112dd60405180608001604052806000815260200160008152602001600081526020016000600f0b81525090565b60d360006112eb8585610c4f565b8152602080820192909252604090810160002081516080810183528154815260018201549381019390935260028101549183019190915260030154600f0b60608201529392505050565b600060d360006113458585610c4f565b815260200190815260200160002060020154905092915050565b6113676113d0565b606580546001600160a01b0383166001600160a01b031990911681179091556113986033546001600160a01b031690565b6001600160a01b03167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e2270060405160405180910390a350565b6033546001600160a01b03163314610b835760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610c3a565b60006001600160801b038211156114935760405162461bcd60e51b815260206004820152602760248201527f53616665436173743a2076616c756520646f65736e27742066697420696e20316044820152663238206269747360c81b6064820152608401610c3a565b5090565b6000620186a06114b663ffffffff84166001600160801b0386166122a7565b6114c091906122ed565b9392505050565b6000806114e46114df670de0b6b3a7640000856122ed565b611ab0565b905060006114fd6114df670de0b6b3a764000086612301565b905061151682610972836503782dace9d960521b61151e565b949350505050565b600081600f0b60000361153057600080fd5b600082600f0b604085600f0b901b8161154b5761154b6122d7565b05905060016001607f1b0319811280159061156d575060016001607f1b038113155b6114c057600080fd5b6000600f83810b9083900b0160016001607f1b0319811280159061156d575060016001607f1b038113156114c057600080fd5b6000806115b68585611ace565b905060006115e860405180608001604052806000815260200160008152602001600081526020016000600f0b81525090565b60cb5460408051631118d57f60e31b815290516000926001600160a01b0316916388c6abf89160048083019260209291908290030181865afa158015611632573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061165691906122be565b600089815260cd602052604081205491925090611674906001612294565b90505b818110156116fc5760d3600061168d838b610c4f565b81526020808201929092526040908101600020815160808101835281548152600182015493810184905260028201549281019290925260030154600f0b60608201529350158015906116df5750825115155b156116ec578093506116fc565b6116f581612315565b9050611677565b508260000361171157839450505050506114c0565b60ca5460405163f731cb4960e01b81526001600160a01b0389811660048301528881166024830152600092169063f731cb4990604401606060405180830381865afa158015611764573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611788919061232e565b9050600061179982600001516114c7565b606085015190915060006117ad8b86611bfa565b90506117ca6117c5846117c08486611c86565b611cb9565b611cef565b6117d49089612294565b9c9b505050505050505050505050565b60006117f08383610b85565b600081815260cd6020908152604080832060cb548251631118d57f60e31b8152925195965090946001600160a01b03909116926388c6abf892600480820193918290030181865afa158015611849573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061186d91906122be565b60ca5460405163f731cb4960e01b81526001600160a01b03888116600483015287811660248301529293506000929091169063f731cb4990604401606060405180830381865afa1580156118c5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118e9919061232e565b9050818360000154036118fe57505050505050565b90825551600190910155505050565b606580546001600160a01b0319169055610c4c81611d70565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b179052611978908490611dc2565b505050565b825160000361199f5760405163d1fc12f760e01b815260040160405180910390fd5b6001600160a01b0382166119c6578260405161f56160eb1b8152600401610c3a91906123e8565b6001600160e01b031981166119ee5760405163308c995160e01b815260040160405180910390fd5b6040516301ffc9a760e01b81526001600160e01b0319821660048201526001600160a01b038316906301ffc9a790602401602060405180830381865afa158015611a3c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a6091906123fb565b611978578281604051633f8ccd3b60e21b8152600401610c3a92919061241d565b600054610100900460ff16611aa85760405162461bcd60e51b8152600401610c3a90612448565b610b83611e97565b6000677fffffffffffffff821115611ac757600080fd5b5060401b90565b600082815260cd602090815260408083208151808301835281548082526001909201548185015260cb548351631118d57f60e31b81529351919492936001600160a01b03909116926388c6abf8926004808401938290030181865afa158015611b3b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b5f91906122be565b03611b6e576000915050610675565b600060d36000611b82846000015187610c4f565b815260208082019290925260409081016000908120825160808101845281548152600182015494810194909452600281015492840183905260030154600f0b60608401529192509003611bda57600092505050610675565b604081015160208301518251611bf091906122a7565b610e1391906122ed565b6001600160a01b038216600090815260d060205260408120548190831115611c3e57506001600160a01b038316600090815260cc6020526040902054600f0b6114c0565b6001600160a01b038416600090815260d0602052604081205460d3908290611c669088610c4f565b8152602081019190915260400160002060030154600f0b95945050505050565b6000600f82810b9084900b0360016001607f1b0319811280159061156d575060016001607f1b038113156114c057600080fd5b6000600f83810b9083900b0260401d60016001607f1b0319811280159061156d575060016001607f1b038113156114c057600080fd5b600080611cfb83611ec7565b67ffffffffffffffff1690506000611d1b670de0b6b3a7640000836122a7565b90506000611d3185611d2c85611ab0565b611c86565b90506000611d50611d4b836503782dace9d960521b611cb9565b611ec7565b67ffffffffffffffff169050611d668184612294565b9695505050505050565b603380546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b6000611e17826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316611ee39092919063ffffffff16565b9050805160001480611e38575080806020019051810190611e3891906123fb565b6119785760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610c3a565b600054610100900460ff16611ebe5760405162461bcd60e51b8152600401610c3a90612448565b610b833361190d565b60008082600f0b1215611ed957600080fd5b50600f0b60401d90565b6060611516848460008585600080866001600160a01b03168587604051611f0a9190612493565b60006040518083038185875af1925050503d8060008114611f47576040519150601f19603f3d011682016040523d82523d6000602084013e611f4c565b606091505b5091509150611f5d87838387611f68565b979650505050505050565b60608315611fd7578251600003611fd0576001600160a01b0385163b611fd05760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610c3a565b5081611516565b6115168383815115611fec5781518083602001fd5b8060405162461bcd60e51b8152600401610c3a91906123e8565b60006020828403121561201857600080fd5b81356001600160e01b0319811681146114c057600080fd5b60006020828403121561204257600080fd5b5035919050565b6001600160a01b0381168114610c4c57600080fd5b60006020828403121561207057600080fd5b81356114c081612049565b6000806040838503121561208e57600080fd5b8235915060208301356120a081612049565b809150509250929050565b600080604083850312156120be57600080fd5b82356120c981612049565b946020939093013593505050565b600080604083850312156120ea57600080fd5b82356120f581612049565b915060208301356120a081612049565b60008060006060848603121561211a57600080fd5b833561212581612049565b9250602084013561213581612049565b9150604084013561214581612049565b809150509250925092565b604051610120810167ffffffffffffffff8111828210171561218257634e487b7160e01b600052604160045260246000fd5b60405290565b805163ffffffff8116811461219c57600080fd5b919050565b80516001600160801b038116811461219c57600080fd5b6000808284036101408112156121cd57600080fd5b8351925061012080601f19830112156121e557600080fd5b6121ed612150565b915060208501518252604085015160208301526060850151604083015261221660808601612188565b606083015261222760a08601612188565b608083015260c085015160a083015261224260e086016121a1565b60c08301526101006122558187016121a1565b60e0840152940151938101939093525092909150565b634e487b7160e01b600052601160045260246000fd5b818103818111156106755761067561226b565b808201808211156106755761067561226b565b80820281158282048414176106755761067561226b565b6000602082840312156122d057600080fd5b5051919050565b634e487b7160e01b600052601260045260246000fd5b6000826122fc576122fc6122d7565b500490565b600082612310576123106122d7565b500690565b6000600182016123275761232761226b565b5060010190565b60006060828403121561234057600080fd5b6040516060810181811067ffffffffffffffff8211171561237157634e487b7160e01b600052604160045260246000fd5b80604052508251815260208301516020820152604083015160408201528091505092915050565b60005b838110156123b357818101518382015260200161239b565b50506000910152565b600081518084526123d4816020860160208601612398565b601f01601f19169290920160200192915050565b6020815260006114c060208301846123bc565b60006020828403121561240d57600080fd5b815180151581146114c057600080fd5b60408152600061243060408301856123bc565b905063ffffffff60e01b831660208301529392505050565b6020808252602b908201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960408201526a6e697469616c697a696e6760a81b606082015260800190565b600082516124a5818460208701612398565b919091019291505056fea26469706673582212207f0d9bd939105273cb52d3a29b1ea0508ee04dc318b7c57bf03ed00c35cf4b0364736f6c63430008120033
SyloToken.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.18; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; contract SyloToken is ERC20 { constructor() ERC20("Sylo", "SYLO") { _mint(msg.sender, 10_000_000_000 ether); } }
Registries.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.18; import "@openzeppelin/contracts/utils/Strings.sol"; import "@openzeppelin/contracts/token/ERC721/IERC721.sol"; import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import "@openzeppelin/contracts/utils/introspection/ERC165.sol"; import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol"; import "./libraries/SyloUtils.sol"; import "./interfaces/IRegistries.sol"; /** * @notice This contract manages Registries for Nodes. A Registry is a * set of parameters configured by the Node itself. A Node is required * to have a valid Registry to be able to participate in the network. */ contract Registries is IRegistries, Initializable, Ownable2StepUpgradeable, IERC165 { using ECDSA for bytes32; /** * @notice ERC721 contract for bridged Seekers. Used for verifying ownership * of a seeker. */ IERC721 public _rootSeekers; /** * @notice Tracks each Node's registry. */ mapping(address => IRegistries.Registry) public registries; /** * @notice Tracks the node address that each seeker id is registered with */ mapping(uint256 => address) public seekerRegistration; /** * @notice Tracks the address of every registered node. */ address[] public nodes; /** * @notice Tracks nonces used when registering the seeker account * to prevent signature re-use. */ mapping(bytes32 => address) private signatureNonces; /** * @notice Payout percentage refers to the portion of a tickets reward * that will be allocated to the Node's stakers. This is global, and is * currently set for all Nodes. */ uint32 public defaultPayoutPercentage; event DefaultPayoutPercentageUpdated(uint32 defaultPayoutPercentage); error NonceCannotBeReused(); error EndMustBeGreaterThanStart(); error PercentageCannotExceed100000(); error PublicEndpointCannotBeEmpty(); error SeekerAccountMustOwnSeekerId(); error SeekerAccountMustBeMsgSender(); error ProofNotSignedBySeekerAccount(); error RootSeekersCannotBeZeroAddress(); error SeekerAccountCannotBeZeroAddress(); error EndCannotExceedNumberOfNodes(uint256 nodeLength); function initialize( IERC721 rootSeekers, uint32 _defaultPayoutPercentage ) external initializer { if (address(rootSeekers) == address(0)) { revert RootSeekersCannotBeZeroAddress(); } if (_defaultPayoutPercentage > 100000) { revert PercentageCannotExceed100000(); } Ownable2StepUpgradeable.__Ownable2Step_init(); _rootSeekers = rootSeekers; defaultPayoutPercentage = _defaultPayoutPercentage; } /** * @notice Returns true if the contract implements the interface defined by * `interfaceId` from ERC165. */ function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { return interfaceId == type(IRegistries).interfaceId; } /** * @notice Set the global default payout percentage value. Only callable * by the owner. * @param _defaultPayoutPercentage The payout percentage as a value where the * denominator is 100000. */ function setDefaultPayoutPercentage(uint32 _defaultPayoutPercentage) external onlyOwner { if (_defaultPayoutPercentage > 100000) { revert PercentageCannotExceed100000(); } defaultPayoutPercentage = _defaultPayoutPercentage; emit DefaultPayoutPercentageUpdated(_defaultPayoutPercentage); } /** * @notice Call this as a Node to set or update your Registry entry. * @param publicEndpoint The public endpoint of your Node. Essential for * clients to be able to retrieve additional information, such as * an address to establish a p2p connection. */ function register(string calldata publicEndpoint) external { if (bytes(publicEndpoint).length == 0) { revert PublicEndpointCannotBeEmpty(); } // This is the nodes first registration if (bytes(registries[msg.sender].publicEndpoint).length == 0) { nodes.push(msg.sender); } registries[msg.sender].publicEndpoint = publicEndpoint; } function setSeekerAccount( address seekerAccount, uint256 seekerId, bytes32 nonce, bytes calldata signature ) external { if (seekerAccount == address(0)) { revert SeekerAccountCannotBeZeroAddress(); } if (signatureNonces[nonce] != address(0)) { revert NonceCannotBeReused(); } bytes memory proofMessage = getProofMessage(seekerId, msg.sender, nonce); bytes32 ethProof = ECDSA.toEthSignedMessageHash(proofMessage); if (ECDSA.recover(ethProof, signature) != seekerAccount) { revert ProofNotSignedBySeekerAccount(); } // Now verify the seeker account actually owns the seeker address owner = _rootSeekers.ownerOf(seekerId); if (seekerAccount != owner) { revert SeekerAccountMustOwnSeekerId(); } delete registries[seekerRegistration[seekerId]].seekerId; delete registries[seekerRegistration[seekerId]].seekerAccount; registries[msg.sender].seekerAccount = seekerAccount; registries[msg.sender].seekerId = seekerId; seekerRegistration[seekerId] = msg.sender; signatureNonces[nonce] = seekerAccount; } function revokeSeekerAccount(address node) external { Registry storage registry = registries[node]; if (registry.seekerAccount != msg.sender) { revert SeekerAccountMustBeMsgSender(); } delete registry.seekerAccount; delete seekerRegistration[registry.seekerId]; delete registry.seekerId; } /** * @notice Retrieve the registry associated with a Node. * @param account The address of the Node. * @return The Node's Registry. */ function getRegistry(address account) external view returns (Registry memory) { return registries[account]; } /** * @notice Retrieve all registered nodes. * @return An array of node addresses. */ function getNodes() external view returns (address[] memory) { return nodes; } /** * @notice Retrieves a list of registries. Takes in a * a start and end indices to allow pagination. * @param start The start index which is inclusive. * @param end The end index which is exclusive. * @return An array of Registries. */ function getRegistries( uint256 start, uint256 end ) external view returns (address[] memory, Registry[] memory) { uint256 nodesLength = nodes.length; if (end <= start) { revert EndMustBeGreaterThanStart(); } if (end > nodesLength) { revert EndCannotExceedNumberOfNodes(nodesLength); } address[] memory _nodes = new address[](end - start); Registry[] memory _registries = new Registry[](_nodes.length); for (uint256 i = start; i < end; ++i) { _nodes[i - start] = nodes[i]; _registries[i - start] = registries[nodes[i]]; } return (_nodes, _registries); } /** * @notice Returns the total number of registered nodes. * @return The number of registered nodes. */ function getTotalNodes() external view returns (uint256) { return nodes.length; } /** * @notice Helper function for deriving the proof message used to * validate seeker ownership. * @param seekerId The tokenId of the seeker used for operation. * @param node The address of the node which that will be operated * by the specified seeker. * @param nonce The nonce used for this message. */ function getProofMessage( uint256 seekerId, address node, bytes32 nonce ) public pure returns (bytes memory) { return abi.encodePacked( unicode"🤖 Hi frend! 🤖\n\n📜 Signing this message proves that you're the owner of this Seeker NFT and allows your Seeker to be used to operate your Seeker's Node. It's a simple but important step to ensure smooth operation.\n\nThis request will not trigger a blockchain transaction or cost any gas fees.\n\n🔥 Your node's address: ", Strings.toHexString(uint256(uint160(node)), 20), unicode"\n\n🆔 Your seeker id: ", Strings.toString(seekerId), unicode"\n\n📦 A unique random value which secures this message: ", Strings.toHexString(uint256(nonce), 32) ); } }
SeekerPowerOracle.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.18; import "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import "@openzeppelin/contracts/utils/Strings.sol"; import "@openzeppelin/contracts/utils/introspection/ERC165.sol"; import "./interfaces/ISeekerPowerOracle.sol"; /** * @notice Acts as a source of information for Seeker Powers. Allows setting * a Seeker's power level via a restricted oracle account call. Seeker Power can also * be set by any account if the correct Oracle signature proof is provided. */ contract SeekerPowerOracle is ISeekerPowerOracle, Initializable, Ownable2StepUpgradeable, ERC165 { /** * @notice The oracle account. This contract accepts any attestations of * Seeker power that have been signed by this account. */ address public oracle; /** * @notice Tracks nonce used when register the Seeker power to * prevent signature re-use. */ mapping(bytes32 => address) private proofNonces; /** * @notice Tracks the set of Seeker Power levels. */ mapping(uint256 => uint256) public seekerPowers; event SeekerPowerUpdated(uint256 indexed seekerId, uint256 indexed power); error UnauthorizedRegisterSeekerPowerCall(); error NonceCannotBeReused(); error PowerCannotBeZero(); error OracleCannotBeZeroAddress(); function initialize(address _oracle) external initializer { Ownable2StepUpgradeable.__Ownable2Step_init(); if (_oracle == address(0)) { revert OracleCannotBeZeroAddress(); } oracle = _oracle; } /** * @notice Returns true if the contract implements the interface defined by * `interfaceId` from ERC165. */ function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { return interfaceId == type(ISeekerPowerOracle).interfaceId; } /** * @notice Sets the oracle account. * @param _oracle The oracle account. */ function setOracle(address _oracle) external onlyOwner { oracle = _oracle; } /** * @notice Registers a Seeker's power level. Only callable by the * owner or the oracle account. * @param seekerId The id of the Seeker. * @param power The power level of the Seeker. */ function registerSeekerPowerRestricted(uint256 seekerId, uint256 power) external { if (msg.sender != oracle) { revert UnauthorizedRegisterSeekerPowerCall(); } if (power == 0) { revert PowerCannotBeZero(); } seekerPowers[seekerId] = power; emit SeekerPowerUpdated(seekerId, power); } /** * @notice Registers a Seeker's power level. Callable by any account * but requires a proof signed by the oracle. * @param seekerId The id of the Seeker. * @param power The power level of the Seeker. */ function registerSeekerPower( uint256 seekerId, uint256 power, bytes32 nonce, bytes calldata proof ) external { if (proofNonces[nonce] != address(0)) { revert NonceCannotBeReused(); } if (power == 0) { revert PowerCannotBeZero(); } bytes memory proofMessage = getProofMessage(seekerId, power, nonce); bytes32 ecdsaHash = ECDSA.toEthSignedMessageHash(proofMessage); if (ECDSA.recover(ecdsaHash, proof) != oracle) { revert UnauthorizedRegisterSeekerPowerCall(); } seekerPowers[seekerId] = power; proofNonces[nonce] = oracle; emit SeekerPowerUpdated(seekerId, power); } /** * @notice Retrieves a Seeker's stored power level. * @param seekerId The id of the Seeker. */ function getSeekerPower(uint256 seekerId) external view returns (uint256) { return seekerPowers[seekerId]; } /** * @notice Constructs a proof message for the oracle to sign. * @param seekerId The id of the Seeker. * @param power The power level of the Seeker. */ function getProofMessage( uint256 seekerId, uint256 power, bytes32 nonce ) public pure returns (bytes memory) { return abi.encodePacked( Strings.toString(seekerId), ":", Strings.toString(power), ":", Strings.toHexString(uint256(nonce), 32) ); } }
Directory.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.18; import "@openzeppelin/contracts/utils/math/Math.sol"; import "./StakingManager.sol"; import "../libraries/SyloUtils.sol"; import "../libraries/Manageable.sol"; import "../interfaces/staking/IDirectory.sol"; import "../payments/ticketing/RewardsManager.sol"; /** * @notice The Directory contract constructs and manages a structure holding the current stakes, * which is queried against using the scan function. The scan function allows submitting * random points which will return a staked node's address in proportion to the stake it has. */ contract Directory is IDirectory, Initializable, Manageable, IERC165 { /** Sylo Staking Manager contract */ StakingManager public _stakingManager; /** Sylo Rewards Manager contract */ RewardsManager public _rewardsManager; /** * @notice The epoch ID of the current directory. */ uint256 public currentDirectory; /** * @notice Tracks every directory, which will be indexed by an epoch ID */ mapping(uint256 => EpochDirectory) public directories; event CurrentDirectoryUpdated(uint256 indexed currentDirectory); error NoStakeToJoinEpoch(); error StakeeAlreadyJoinedEpoch(); error StakeeCannotBeZeroAddress(); error NoJoiningStakeToJoinEpoch(); function initialize( StakingManager stakingManager, RewardsManager rewardsManager ) external initializer { SyloUtils.validateContractInterface( "StakingManager", address(stakingManager), type(IStakingManager).interfaceId ); SyloUtils.validateContractInterface( "RewardsManager", address(rewardsManager), type(IRewardsManager).interfaceId ); Ownable2StepUpgradeable.__Ownable2Step_init(); _stakingManager = stakingManager; _rewardsManager = rewardsManager; } /** * @notice Returns true if the contract implements the interface defined by * `interfaceId` from ERC165. */ function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { return interfaceId == type(IDirectory).interfaceId; } /** * @notice This function should be called when a new epoch is initialized. * This will set the current directory to the specified epoch. This is only * callable by the owner of this contract, which should be the EpochsManager * contract. * @dev After deployment, the EpochsManager should immediately be set as * the owner. * @param epochId The ID of the specified epoch. */ function setCurrentDirectory(uint256 epochId) external onlyManager { currentDirectory = epochId; emit CurrentDirectoryUpdated(epochId); } /** * @notice This function is called by the epochs manager as a prerequisite to when the node joins the next epoch. * @param stakee The address of the node. * @param seekerId The seekerId of the Seeker that the node is * registered with when joining the epoch. It is used to determine the nodes * staking capacity based on its seeker power. * * @dev This will construct the directory as nodes join. The directory is constructed * by creating a boundary value which is a sum of the current directory's total stake, and * the current stakee's total stake, and pushing the new boundary into the entries array. * The previous boundary and the current boundary essentially create a range, where if a * random point were to fall within that range, it would belong to the respective stakee. * The boundary value grows in size as each stakee joins, thus the directory array * always remains sorted. This allows us to perform a binary search on the directory. * * Example * * Stakes: [ Alice/20, Bob/10, Carl/40, Dave/25 ] * TotalStake: 95 * * EpochDirectory: * * |-----------|------|----------------|--------| * Alice/20 Bob/30 Carl/70 Dave/95 * * The amount of stake that a node will join a directory with is dependent on its * different capacity values. There are two distinct capacity values, one * calculated from the seeker power, and another from the minimum stake * proportion. The final staking amount will not exceed either capacities, * and in the case that the current total stake exceeds both, then the final * will be the minimum of the two values. */ function joinNextDirectory(address stakee, uint256 seekerId) external onlyManager { if (stakee == address(0)) { revert StakeeCannotBeZeroAddress(); } uint256 nextEpochId = currentDirectory + 1; uint256 totalStake = _stakingManager.getStakeeTotalManagedStake(stakee); if (totalStake == 0) { revert NoStakeToJoinEpoch(); } // staking capacity based on seeker power uint256 seekerStakingCapacity = _stakingManager.calculateCapacityFromSeekerPower(seekerId); // staking capacity based on the min staking proportion constant uint256 minProportionStakingCapacity = _stakingManager.calculateCapacityFromMinStakingProportion(stakee); uint256 joiningStake; if (totalStake > seekerStakingCapacity && totalStake > minProportionStakingCapacity) { joiningStake = Math.min(seekerStakingCapacity, minProportionStakingCapacity); } else if (totalStake > seekerStakingCapacity) { joiningStake = seekerStakingCapacity; } else if (totalStake > minProportionStakingCapacity) { joiningStake = minProportionStakingCapacity; } else { // uncapped joiningStake = totalStake; } if (joiningStake == 0) { revert NoJoiningStakeToJoinEpoch(); } if (directories[nextEpochId].stakes[stakee] > 0) { revert StakeeAlreadyJoinedEpoch(); } uint256 nextBoundary = directories[nextEpochId].totalStake + joiningStake; directories[nextEpochId].entries.push(DirectoryEntry(stakee, nextBoundary)); directories[nextEpochId].stakes[stakee] = joiningStake; directories[nextEpochId].totalStake = nextBoundary; } /** * @notice Call this to perform a stake-weighted scan to find the Node assigned * to the given point of the current directory. * @param point The point, which will usually be a hash of a public key. */ function scan(uint128 point) external view returns (address stakee) { return _scan(point, currentDirectory); } /** * @notice Call this to perform a stake-weighted scan to find the Node assigned * to the given point of the requested directory. * @param point The point, which will usually be a hash of a public key. * @param epochId The epoch id associated with the directory to scan. */ function scanWithEpochId( uint128 point, uint256 epochId ) external view returns (address stakee) { return _scan(point, epochId); } /** * @notice Call this to perform a stake-weighted scan to find the Node assigned * to the given point of the requested directory (internal). * @dev The current implementation will perform a binary search through * the directory. This can allow gas costs to be low if this needs to be * used in a transaction. * @param point The point, which will usually be a hash of a public key. * @param epochId The epoch id associated with the directory to scan. */ function _scan(uint128 point, uint256 epochId) internal view returns (address stakee) { uint256 entryLength = directories[epochId].entries.length; if (entryLength == 0) { return address(0); } // Staking all the Sylo would only be 94 bits, so multiplying this with // a uint128 cannot overflow a uint256. uint256 expectedVal = (directories[epochId].totalStake * uint256(point)) >> 128; uint256 left; uint256 right = entryLength - 1; // perform a binary search through the directory uint256 lower; uint256 upper; uint256 index; while (left <= right) { index = (left + right) >> 1; lower = index == 0 ? 0 : directories[epochId].entries[index - 1].boundary; upper = directories[epochId].entries[index].boundary; if (expectedVal >= lower && expectedVal < upper) { return directories[epochId].entries[index].stakee; } else if (expectedVal < lower) { right = index - 1; } else { // expectedVal >= upper left = index + 1; } } } /** * @notice Retrieve the total stake a Node has for the directory in the * specified epoch. * @param epochId The ID of the epoch. * @param stakee The address of the Node. * @return The amount of stake the Node has for the given directory in SOLO. */ function getTotalStakeForStakee( uint256 epochId, address stakee ) external view returns (uint256) { return directories[epochId].stakes[stakee]; } /** * @notice Retrieve the total stake for a directory in the specified epoch, which * will be the sum of the stakes for all Nodes participating in that epoch. * @param epochId The ID of the epoch. * @return The total amount of stake in SOLO. */ function getTotalStake(uint256 epochId) external view returns (uint256) { return directories[epochId].totalStake; } /** * @notice Retrieve all entries for a directory in a specified epoch. * @return An array of all the directory entries. */ function getEntries( uint256 epochId ) external view returns (address[] memory, uint256[] memory) { uint256 entryLength = directories[epochId].entries.length; address[] memory stakees = new address[](entryLength); uint256[] memory boundaries = new uint256[](entryLength); DirectoryEntry memory entry; DirectoryEntry[] memory entries = directories[epochId].entries; for (uint256 i; i < entryLength; ++i) { entry = entries[i]; stakees[i] = entry.stakee; boundaries[i] = entry.boundary; } return (stakees, boundaries); } }
SyloUtils.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.18; import "@openzeppelin/contracts/utils/math/SafeCast.sol"; import "@openzeppelin/contracts/utils/introspection/ERC165.sol"; error ContractNameCannotBeEmpty(); error InterfaceIdCannotBeZeroBytes(); error TargetContractCannotBeZeroAddress(string name); error TargetNotSupportInterface(string name, bytes4 interfaceId); library SyloUtils { /** * @dev The maximum possible SYLO that exists in the network. */ uint256 public constant MAX_SYLO = 10_000_000_000 ether; /** * @dev Percentages are expressed as a ratio where 100000 is the denominator. * A large denominator allows for more precision, e.g representing 12.5% * can be done as 12500 / 100000 */ uint32 public constant PERCENTAGE_DENOMINATOR = 100000; /** * @dev Multiply a value by a given percentage. Converts the provided * uint128 value to uint256 to avoid any reverts on overflow. * @param value The value to multiply. * @param percentage The percentage, as a ratio of 100000. */ function percOf(uint128 value, uint32 percentage) internal pure returns (uint256) { return (uint256(value) * percentage) / PERCENTAGE_DENOMINATOR; } /** * @dev Return a fraction as a percentage. * @param numerator The numerator limited to a uint128 value to prevent * phantom overflow. * @param denominator The denominator. * @return The percentage, as a ratio of 100000. */ function asPerc(uint128 numerator, uint256 denominator) internal pure returns (uint32) { return SafeCast.toUint32((uint256(numerator) * PERCENTAGE_DENOMINATOR) / denominator); } /** * @dev Validate that a contract implements a given interface. * @param name The name of the contract, used in error messages. * @param target The address of the contract. * @param interfaceId The interface ID to check. */ function validateContractInterface( string memory name, address target, bytes4 interfaceId ) internal view { if (bytes(name).length == 0) { revert ContractNameCannotBeEmpty(); } if (target == address(0)) { revert TargetContractCannotBeZeroAddress(name); } if (interfaceId == bytes4(0)) { revert InterfaceIdCannotBeZeroBytes(); } if (!ERC165(target).supportsInterface(interfaceId)) { revert TargetNotSupportInterface(name, interfaceId); } } }
EpochsManager.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.18; import "@openzeppelin/contracts/token/ERC721/IERC721.sol"; import "@openzeppelin/contracts/utils/introspection/ERC165.sol"; import "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import "../Registries.sol"; import "../staking/Directory.sol"; import "../interfaces/epochs/IEpochsManager.sol"; import "../payments/ticketing/TicketingParameters.sol"; contract EpochsManager is IEpochsManager, Initializable, Ownable2StepUpgradeable, ERC165 { Directory public _directory; Registries public _registries; IERC721 public _rootSeekers; TicketingParameters public _ticketingParameters; /** * @notice Track seekers that have joined for a specific epoch. */ mapping(uint256 => mapping(uint256 => address)) public activeSeekers; // Define all Epoch specific parameters here. // When initializing an epoch, these parameters are read, // along with parameters from the other contracts to create the // new epoch. /** * @notice A mapping of all epochs that have been initialized. */ mapping(uint256 => Epoch) public epochs; /** * @notice The block number since the first epoch can be initialized. */ uint256 public initialEpoch; /** * @notice The duration in blocks an epoch will last for. */ uint256 public epochDuration; /** * @notice The value of the integer used as the current * epoch's identifier. This value is incremented as each epoch * is initialized. */ uint256 public currentIteration; event NewEpoch(uint256 indexed epochId); event EpochJoined(uint256 indexed epochId, address indexed node, uint256 indexed seekerId); event InitialEpochUpdated(uint256 initialEpoch); event EpochDurationUpdated(uint256 epochDuration); error SeekerOwnerMismatch(); error InitialEpochCannotBeZero(); error EpochDurationCannotBeZero(); error DirectoryCannotBeZeroAddress(); error RegistriesCannotBeZeroAddress(); error RootSeekerCannotBeZeroAddress(); error EpochHasNotEnded(uint256 epochId); error SeekerAccountCannotBeZeroAddress(); error TicketingParametersCannotBeZeroAddress(); error SeekerAlreadyJoinedEpoch(uint256 epochId, uint256 seekerId); error InitialEpochNotYetReady(uint256 expectedBlock, uint256 currentBlock); function initialize( IERC721 rootSeekers, Directory directory, Registries registries, TicketingParameters ticketingParameters, uint256 _initialEpoch, uint256 _epochDuration ) external initializer { if (address(rootSeekers) == address(0)) { revert RootSeekerCannotBeZeroAddress(); } SyloUtils.validateContractInterface( "Directory", address(directory), type(IDirectory).interfaceId ); SyloUtils.validateContractInterface( "Registries", address(registries), type(IRegistries).interfaceId ); SyloUtils.validateContractInterface( "TicketingParameters", address(ticketingParameters), type(ITicketingParameters).interfaceId ); Ownable2StepUpgradeable.__Ownable2Step_init(); _rootSeekers = rootSeekers; _directory = directory; _registries = registries; _ticketingParameters = ticketingParameters; initialEpoch = _initialEpoch; epochDuration = _epochDuration; } /** * @notice Returns true if the contract implements the interface defined by * `interfaceId` from ERC165. */ function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { return interfaceId == type(IEpochsManager).interfaceId; } /** * @notice Call this to initialize the next epoch. On success, a `NewEpoch` event * will be emitted. * @dev The function will read the current set of network parameters, and store * the parameters in a new Epoch struct. The end block of the current epoch * will also be set to a non-zero value. */ function initializeEpoch() external returns (uint256) { if (currentIteration == 0 && initialEpoch > block.number) { revert InitialEpochNotYetReady(initialEpoch, block.number); } Epoch storage current = epochs[currentIteration]; uint256 end = current.startBlock + current.duration; if (end > block.number) { revert EpochHasNotEnded(currentIteration); } ( uint256 faceValue, uint128 baseLiveWinProb, uint128 expiredWinProb, uint256 ticketDuration, uint32 decayRate ) = _ticketingParameters.getTicketingParameters(); uint256 nextEpochId = getNextEpochId(); epochs[nextEpochId] = Epoch( block.number, epochDuration, 0, _registries.defaultPayoutPercentage(), decayRate, faceValue, baseLiveWinProb, expiredWinProb, ticketDuration ); current.endBlock = block.number; currentIteration = nextEpochId; _directory.setCurrentDirectory(nextEpochId); emit NewEpoch(nextEpochId); return nextEpochId; } /** * @notice Set the first epoch starting block. Only callable by the owner. * @param _initialEpoch The block number when the first epoch can be initialized. */ function setInitialEpoch(uint256 _initialEpoch) external onlyOwner { if (_initialEpoch == 0) { revert InitialEpochCannotBeZero(); } initialEpoch = _initialEpoch; emit InitialEpochUpdated(_initialEpoch); } /** * @notice Set the epoch duration. Will take effect in the next epoch. Only * callable by the owner. * @param _epochDuration The epoch duration in number of blocks. */ function setEpochDuration(uint256 _epochDuration) external onlyOwner { if (_epochDuration == 0) { revert EpochDurationCannotBeZero(); } epochDuration = _epochDuration; emit EpochDurationUpdated(epochDuration); } /** * @notice Retrieve the parameters for the current epoch. * @return The current Epoch parameters. */ function getCurrentActiveEpoch() external view returns (uint256, Epoch memory) { return (currentIteration, epochs[currentIteration]); } /** * @notice Nodes should call this to join the next epoch. It will * initialize the next reward pool and set the stake for the next directory. * @dev This is a proxy function for `initalizeNextRewardPool` and * `joinNextDirectory`. */ function joinNextEpoch() external { Registries.Registry memory registry = _registries.getRegistry(msg.sender); // validate the node's seeker ownership if (registry.seekerAccount == address(0)) { revert SeekerAccountCannotBeZeroAddress(); } uint256 seekerId = registry.seekerId; address owner = _rootSeekers.ownerOf(seekerId); if (registry.seekerAccount != owner) { revert SeekerOwnerMismatch(); } uint256 nextEpoch = getNextEpochId(); if (activeSeekers[nextEpoch][seekerId] != address(0)) { revert SeekerAlreadyJoinedEpoch(nextEpoch, seekerId); } activeSeekers[nextEpoch][seekerId] = msg.sender; _directory._rewardsManager().initializeNextRewardPool(msg.sender); _directory.joinNextDirectory(msg.sender, seekerId); emit EpochJoined(nextEpoch, msg.sender, seekerId); } /** * @notice Retrieve the epoch parameter for the given id. * @param epochId The id of the epoch to retrieve. * @return The epoch parameters associated with the id. */ function getEpoch(uint256 epochId) external view returns (Epoch memory) { return epochs[epochId]; } /** * @notice Retrieve the integer value that will be used for the * next epoch id. * @return The next epoch id identifier. */ function getNextEpochId() public view returns (uint256) { return currentIteration + 1; } }
Manageable.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.18; import "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol"; /** * @dev Contract module which provides a basic access control mechanism, where * there is an list of public managers who may be added or removed. * * This module is used through inheritance. It will make available the modifier * `onlyManager`, which can be applied to your functions to restrict their use to * other contracts which have explicitly been added. */ abstract contract Manageable is Ownable2StepUpgradeable { /** * @dev Tracks the managers added to this contract, where they key is the * address of the managing contract, and the value is the block the manager was added in. * We use this mapping to restrict access to those functions in a similar * fashion to the onlyOwner construct. */ mapping(address => uint256) public managers; error OnlyManagers(); error ManagerCannotBeZeroAddress(); /** * @notice Adds a manager to this contract. Only callable by the owner. * @param manager The address of the manager contract. */ function addManager(address manager) external onlyOwner { if (manager == address(0)) { revert ManagerCannotBeZeroAddress(); } managers[manager] = block.number; } /** * @notice Removes a manager from this contract. Only callable by the owner. * @param manager The address of the manager contract. */ function removeManager(address manager) external onlyOwner { delete managers[manager]; } /** * @dev This modifier allows us to specify that certain contracts have * special privileges to call restricted functions. */ modifier onlyManager() { if (managers[msg.sender] == 0) { revert OnlyManagers(); } _; } // Reserve storage slots for future variables uint256[49] private __gap; }
IRegistries.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.18; interface IRegistries { struct Registry { // Percentage of a tickets value that will be rewarded to // delegated stakers expressed as a fraction of 100000. // This value is currently locked to the default payout percentage // until epochs are implemented. uint32 payoutPercentage; // Public http/s endpoint to retrieve additional metadata // about the node. // The current metadata schema is as follows: // { name: string, multiaddrs: string[] } string publicEndpoint; // The account which owns a seeker that will be used to // operate the Node for this registry. address seekerAccount; // The id of the seeker used to operate the node. The owner // of this id should be the seeker account. uint256 seekerId; } function register(string calldata publicEndpoint) external; function setDefaultPayoutPercentage(uint32 _defaultPayoutPercentage) external; function setSeekerAccount( address seekerAccount, uint256 seekerId, bytes32 nonce, bytes calldata signature ) external; function revokeSeekerAccount(address node) external; function getRegistry(address account) external view returns (Registry memory); function getNodes() external view returns (address[] memory); function getRegistries( uint256 start, uint256 end ) external view returns (address[] memory, Registry[] memory); function getTotalNodes() external view returns (uint256); }
StakingManager.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.18; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol"; import "../SyloToken.sol"; import "../libraries/SyloUtils.sol"; import "../SeekerPowerOracle.sol"; import "../epochs/EpochsManager.sol"; import "../payments/ticketing/RewardsManager.sol"; import "../interfaces/staking/IStakingManager.sol"; /** * @notice Manages stakes and delegated stakes for Nodes. Holding * staked Sylo is necessary for a Node to participate in the * Sylo Network. The stake is used in stake-weighted scan function, * and delegated stakers are rewarded on a pro-rata basis. */ contract StakingManager is IStakingManager, Initializable, Ownable2StepUpgradeable, ERC165 { /** ERC 20 compatible token we are dealing with */ IERC20 public _token; /** * @notice Rewards Manager contract. Any changes to stake will automatically * trigger a claim to any outstanding rewards. */ RewardsManager public _rewardsManager; EpochsManager public _epochsManager; SeekerPowerOracle public _seekerPowerOracle; /** * @notice Tracks the managed stake for every Node. */ mapping(address => Stake) public stakes; /** @notice Tracks overall total stake held by this contract */ uint256 public totalManagedStake; /** * @notice Tracks funds that are in the process of being unlocked. This * is indexed by a key that hashes both the address of the staked Node and * the address of the staker. */ mapping(bytes32 => Unlock) public unlockings; /** * @notice The number of blocks a user must wait after calling "unlock" * before they can withdraw their stake */ uint256 public unlockDuration; /** * @notice Minimum amount of stake that a Node needs to stake * against itself in order to participate in the network. This is * represented as a percentage of the Node's total stake, where * the value is a ratio of 10000. */ uint32 public minimumStakeProportion; /** * @notice The multiplier used in determining a Seeker's staking * capacity based on its power level. */ uint256 public seekerPowerMultiplier; event UnlockDurationUpdated(uint256 unlockDuration); event MinimumStakeProportionUpdated(uint256 minimumStakeProportion); event SeekerPowerMultiplierUpdated(uint256 seekerPowerMultipler); error NoStakeToUnlock(); error StakeNotYetUnlocked(); error CannotStakeZeroAmount(); error CannotUnlockZeroAmount(); error TokenCannotBeZeroAddress(); error StakeeCannotBeZeroAddress(); error UnlockDurationCannotBeZero(); error CannotCancelUnlockZeroAmount(); error CannotUnlockMoreThanStaked(uint256 stakeAmount, uint256 unlockAmount); error StakeCapacityReached(uint256 maxCapacity, uint256 currentCapacity); error SeekerPowerNotRegistered(uint256 seekerId); function initialize( IERC20 token, RewardsManager rewardsManager, EpochsManager epochsManager, SeekerPowerOracle seekerPowerOracle, uint256 _unlockDuration, uint32 _minimumStakeProportion, uint256 _seekerPowerMultiplier ) external initializer { if (address(token) == address(0)) { revert TokenCannotBeZeroAddress(); } SyloUtils.validateContractInterface( "RewardsManager", address(rewardsManager), type(IRewardsManager).interfaceId ); SyloUtils.validateContractInterface( "EpochsManager", address(epochsManager), type(IEpochsManager).interfaceId ); SyloUtils.validateContractInterface( "SeekerPowerOracle", address(seekerPowerOracle), type(ISeekerPowerOracle).interfaceId ); if (_unlockDuration == 0) { revert UnlockDurationCannotBeZero(); } Ownable2StepUpgradeable.__Ownable2Step_init(); _token = token; _rewardsManager = rewardsManager; _epochsManager = epochsManager; _seekerPowerOracle = seekerPowerOracle; unlockDuration = _unlockDuration; minimumStakeProportion = _minimumStakeProportion; seekerPowerMultiplier = _seekerPowerMultiplier; } /** * @notice Returns true if the contract implements the interface defined by * `interfaceId` from ERC165. */ function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { return interfaceId == type(IStakingManager).interfaceId; } /** * @notice Sets the unlock duration for stakes. Only callable by * the owner. * @param _unlockDuration The unlock duration in number of blocks. */ function setUnlockDuration(uint256 _unlockDuration) external onlyOwner { if (_unlockDuration == 0) { revert UnlockDurationCannotBeZero(); } unlockDuration = _unlockDuration; emit UnlockDurationUpdated(_unlockDuration); } function setSeekerPowerMultiplier(uint256 _seekerPowerMultiplier) external onlyOwner { seekerPowerMultiplier = _seekerPowerMultiplier; emit SeekerPowerMultiplierUpdated(seekerPowerMultiplier); } /** * @notice Sets the minimum stake proportion for Nodes. Only callable by * the owner. * @param _minimumStakeProportion The minimum stake proportion in SOLO. */ function setMinimumStakeProportion(uint32 _minimumStakeProportion) external onlyOwner { minimumStakeProportion = _minimumStakeProportion; emit MinimumStakeProportionUpdated(_minimumStakeProportion); } /** * @notice Called by Nodes and delegated stakers to add stake. * This function will fail under the following conditions: * - If the Node address is invalid * - If the specified stake value is zero * - If the additional stake causes the Node to fail to meet the * minimum stake proportion requirement. * @param amount The amount of stake to add in SOLO. * @param stakee The address of the staked Node. */ function addStake(uint256 amount, address stakee) external { if (stakee == address(0)) { revert StakeeCannotBeZeroAddress(); } if (amount == 0) { revert CannotStakeZeroAmount(); } _addStake(amount, stakee); SafeERC20.safeTransferFrom(_token, msg.sender, address(this), amount); } function _addStake(uint256 amount, address stakee) internal { // automatically move any pending rewards generated by their existing stake _rewardsManager.updatePendingRewards(stakee, msg.sender); uint256 currentEpochId = _epochsManager.currentIteration(); Stake storage stake = stakes[stakee]; uint256 currentStake = getCurrentStakerAmount(stakee, msg.sender); stake.stakeEntries[msg.sender] = StakeEntry( currentStake + amount, block.number, currentEpochId ); stake.totalManagedStake = stake.totalManagedStake + amount; totalManagedStake = totalManagedStake + amount; } /** * @notice Call this function to begin the unlocking process. Calling this * will trigger an automatic claim of any outstanding staking rewards. Any * stake that was already in the unlocking phase will have the specified * amount added to it, and its duration refreshed. This function will fail * under the following conditions: * - If no stake exists for the caller * - If the unlock amount is zero * - If the unlock amount is more than what is staked * Note: If calling as a Node, this function will *not* revert if it causes * the Node to fail to meet the minimum stake proportion. However it will still * prevent the Node from participating in the network until the minimum is met * again. * @param amount The amount of stake to unlock in SOLO. * @param stakee The address of the staked Node. */ function unlockStake(uint256 amount, address stakee) external returns (uint256) { if (stakee == address(0)) { revert StakeeCannotBeZeroAddress(); } if (amount == 0) { revert CannotUnlockZeroAmount(); } uint256 currentStake = getCurrentStakerAmount(stakee, msg.sender); if (currentStake == 0) { revert NoStakeToUnlock(); } if (currentStake < amount) { revert CannotUnlockMoreThanStaked(currentStake, amount); } // automatically move any pending rewards generated by their existing stake _rewardsManager.updatePendingRewards(stakee, msg.sender); uint256 currentEpochId = _epochsManager.currentIteration(); Stake storage stake = stakes[stakee]; stake.stakeEntries[msg.sender] = StakeEntry( currentStake - amount, block.number, currentEpochId ); stake.totalManagedStake = stake.totalManagedStake - amount; totalManagedStake = totalManagedStake - amount; bytes32 key = getKey(stakee, msg.sender); // Keep track of when the stake can be withdrawn Unlock storage unlock = unlockings[key]; uint256 unlockAt = block.number + unlockDuration; if (unlock.unlockAt < unlockAt) { unlock.unlockAt = unlockAt; } unlock.amount = unlock.amount + amount; return unlockAt; } /** * @notice Call this function to withdraw stake that has finished unlocking. * This will fail if the stake has not yet unlocked. * @param stakee The address of the staked Node. */ function withdrawStake(address stakee) external { if (stakee == address(0)) { revert StakeeCannotBeZeroAddress(); } bytes32 key = getKey(stakee, msg.sender); Unlock storage unlock = unlockings[key]; if (unlock.unlockAt >= block.number) { revert StakeNotYetUnlocked(); } uint256 amount = unlock.amount; delete unlockings[key]; SafeERC20.safeTransfer(_token, msg.sender, amount); } /** * @notice Call this function to cancel any stake that is in the process * of unlocking. As this essentially adds back stake to the Node, this * will trigger an automatic claim of any outstanding staking rewards. * If the specified amount to cancel is greater than the stake that is * currently being unlocked, it will cancel the maximum stake possible. * @param amount The amount of unlocking stake to cancel in SOLO. * @param stakee The address of the staked Node. */ function cancelUnlocking(uint256 amount, address stakee) external { if (stakee == address(0)) { revert StakeeCannotBeZeroAddress(); } if (amount == 0) { revert CannotCancelUnlockZeroAmount(); } bytes32 key = getKey(stakee, msg.sender); Unlock storage unlock = unlockings[key]; if (amount >= unlock.amount) { amount = unlock.amount; delete unlockings[key]; } else { unlock.amount = unlock.amount - amount; } _addStake(amount, stakee); } /** * @notice This function determines the staking capacity of * a Seeker based on its power level. The method will revert if * the Seeker's power level has not been registered with the oracle. * * Currently the algorithm is as follows: * staking_capacity = seeker_power * seeker_power_multiplier; */ function calculateCapacityFromSeekerPower(uint256 seekerId) external view returns (uint256) { uint256 seekerPower = _seekerPowerOracle.getSeekerPower(seekerId); if (seekerPower == 0) { revert SeekerPowerNotRegistered(seekerId); } // If the Seeker Power is already // at the maximum sylo, then we just return the max sylo value directly. if (seekerPower >= SyloUtils.MAX_SYLO) { return SyloUtils.MAX_SYLO; } uint256 capacity = seekerPower * seekerPowerMultiplier; return capacity > SyloUtils.MAX_SYLO ? SyloUtils.MAX_SYLO : capacity; } /** * @notice This function can be used to a determine a Node's staking capacity, * based on the minimum stake proportion constant. * @param stakee The address of the staked Node. */ function calculateCapacityFromMinStakingProportion(address stakee) public view returns (uint256) { if (stakee == address(0)) { revert StakeeCannotBeZeroAddress(); } Stake storage stake = stakes[stakee]; uint256 currentlyOwnedStake = stake.stakeEntries[stakee].amount; return (currentlyOwnedStake * SyloUtils.PERCENTAGE_DENOMINATOR) / minimumStakeProportion; } /** * @notice This function should be called by clients to determine how much * additional delegated stake can be allocated to a Node via an addStake or * cancelUnlocking call. This is useful to avoid a revert due to * the minimum stake proportion requirement not being met from the additional stake. * @param stakee The address of the staked Node. */ function calculateMaxAdditionalDelegatedStake(address stakee) external view returns (uint256) { uint256 totalMaxStake = calculateCapacityFromMinStakingProportion(stakee); Stake storage stake = stakes[stakee]; if (totalMaxStake < stake.totalManagedStake) { revert StakeCapacityReached(totalMaxStake, stake.totalManagedStake); } return totalMaxStake - stake.totalManagedStake; } /** * @notice Retrieve the key used to index a stake entry. The key is a hash * which takes both address of the Node and the staker as input. * @param stakee The address of the staked Node. * @param staker The address of the staker. * @return A byte-array representing the key. */ function getKey(address stakee, address staker) public pure returns (bytes32) { return keccak256(abi.encodePacked(stakee, staker)); } /** * @notice Retrieve the total stake being managed by this contract. * @return The total amount of managed stake in SOLO. */ function getTotalManagedStake() external view returns (uint256) { return totalManagedStake; } /** * @notice Retrieve a stake entry. * @param stakee The address of the staked Node. * @param staker The address of the staker. * @return The stake entry. */ function getStakeEntry( address stakee, address staker ) external view returns (StakeEntry memory) { return stakes[stakee].stakeEntries[staker]; } /** * @notice Retrieve the total amount of SOLO staked against a Node. * @param stakee The address of the staked Node. * @return The amount of staked SOLO. */ function getStakeeTotalManagedStake(address stakee) external view returns (uint256) { return stakes[stakee].totalManagedStake; } /** * @notice Check if a Node is meeting the minimum stake proportion requirement. * @param stakee The address of the staked Node. * @return True if the Node is meeting minimum stake proportion requirement. */ function checkMinimumStakeProportion(address stakee) public view returns (bool) { if (stakee == address(0)) { revert StakeeCannotBeZeroAddress(); } Stake storage stake = stakes[stakee]; uint256 currentlyOwnedStake = stake.stakeEntries[stakee].amount; uint32 ownedStakeProportion = SyloUtils.asPerc( SafeCast.toUint128(currentlyOwnedStake), stake.totalManagedStake ); return ownedStakeProportion >= minimumStakeProportion; } /** * @notice Retrieve the current amount of SOLO staked against a Node by * a specified staker. * @param stakee The address of the staked Node. * @param staker The address of the staker. * @return The amount of staked SOLO. */ function getCurrentStakerAmount(address stakee, address staker) public view returns (uint256) { return stakes[stakee].stakeEntries[staker].amount; } }
Address.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.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 * * Furthermore, `isContract` will also return true if the target contract within * the same transaction is already scheduled for destruction by `SELFDESTRUCT`, * which only has an effect at the end of a transaction. * ==== * * [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://consensys.net/diligence/blog/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.8.0/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
// 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; } }
Strings.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (utils/Strings.sol) pragma solidity ^0.8.0; import "./math/Math.sol"; import "./math/SignedMath.sol"; /** * @dev String operations. */ library Strings { bytes16 private constant _SYMBOLS = "0123456789abcdef"; uint8 private constant _ADDRESS_LENGTH = 20; /** * @dev Converts a `uint256` to its ASCII `string` decimal representation. */ function toString(uint256 value) internal pure returns (string memory) { unchecked { uint256 length = Math.log10(value) + 1; string memory buffer = new string(length); uint256 ptr; /// @solidity memory-safe-assembly assembly { ptr := add(buffer, add(32, length)) } while (true) { ptr--; /// @solidity memory-safe-assembly assembly { mstore8(ptr, byte(mod(value, 10), _SYMBOLS)) } value /= 10; if (value == 0) break; } return buffer; } } /** * @dev Converts a `int256` to its ASCII `string` decimal representation. */ function toString(int256 value) internal pure returns (string memory) { return string(abi.encodePacked(value < 0 ? "-" : "", toString(SignedMath.abs(value)))); } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation. */ function toHexString(uint256 value) internal pure returns (string memory) { unchecked { return toHexString(value, Math.log256(value) + 1); } } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length. */ function toHexString(uint256 value, uint256 length) internal pure returns (string memory) { bytes memory buffer = new bytes(2 * length + 2); buffer[0] = "0"; buffer[1] = "x"; for (uint256 i = 2 * length + 1; i > 1; --i) { buffer[i] = _SYMBOLS[value & 0xf]; value >>= 4; } require(value == 0, "Strings: hex length insufficient"); return string(buffer); } /** * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation. */ function toHexString(address addr) internal pure returns (string memory) { return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH); } /** * @dev Returns true if the two strings are equal. */ function equal(string memory a, string memory b) internal pure returns (bool) { return keccak256(bytes(a)) == keccak256(bytes(b)); } }
ABDKMath64x64.sol
// SPDX-License-Identifier: BSD-4-Clause /* * ABDK Math 64.64 Smart Contract Library. Copyright © 2019 by ABDK Consulting. * Author: Mikhail Vladimirov <mikhail.vladimirov@gmail.com> */ pragma solidity ^0.8.0; /** * Smart contract library of mathematical functions operating with signed * 64.64-bit fixed point numbers. Signed 64.64-bit fixed point number is * basically a simple fraction whose numerator is signed 128-bit integer and * denominator is 2^64. As long as denominator is always the same, there is no * need to store it, thus in Solidity signed 64.64-bit fixed point numbers are * represented by int128 type holding only the numerator. */ library ABDKMath64x64 { /* * Minimum value signed 64.64-bit fixed point number may have. */ int128 private constant MIN_64x64 = -0x80000000000000000000000000000000; /* * Maximum value signed 64.64-bit fixed point number may have. */ int128 private constant MAX_64x64 = 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; /** * Convert signed 256-bit integer number into signed 64.64-bit fixed point * number. Revert on overflow. * * @param x signed 256-bit integer number * @return signed 64.64-bit fixed point number */ function fromInt (int256 x) internal pure returns (int128) { unchecked { require (x >= -0x8000000000000000 && x <= 0x7FFFFFFFFFFFFFFF); return int128 (x << 64); } } /** * Convert signed 64.64 fixed point number into signed 64-bit integer number * rounding down. * * @param x signed 64.64-bit fixed point number * @return signed 64-bit integer number */ function toInt (int128 x) internal pure returns (int64) { unchecked { return int64 (x >> 64); } } /** * Convert unsigned 256-bit integer number into signed 64.64-bit fixed point * number. Revert on overflow. * * @param x unsigned 256-bit integer number * @return signed 64.64-bit fixed point number */ function fromUInt (uint256 x) internal pure returns (int128) { unchecked { require (x <= 0x7FFFFFFFFFFFFFFF); return int128 (int256 (x << 64)); } } /** * Convert signed 64.64 fixed point number into unsigned 64-bit integer * number rounding down. Revert on underflow. * * @param x signed 64.64-bit fixed point number * @return unsigned 64-bit integer number */ function toUInt (int128 x) internal pure returns (uint64) { unchecked { require (x >= 0); return uint64 (uint128 (x >> 64)); } } /** * Convert signed 128.128 fixed point number into signed 64.64-bit fixed point * number rounding down. Revert on overflow. * * @param x signed 128.128-bin fixed point number * @return signed 64.64-bit fixed point number */ function from128x128 (int256 x) internal pure returns (int128) { unchecked { int256 result = x >> 64; require (result >= MIN_64x64 && result <= MAX_64x64); return int128 (result); } } /** * Convert signed 64.64 fixed point number into signed 128.128 fixed point * number. * * @param x signed 64.64-bit fixed point number * @return signed 128.128 fixed point number */ function to128x128 (int128 x) internal pure returns (int256) { unchecked { return int256 (x) << 64; } } /** * Calculate x + y. Revert on overflow. * * @param x signed 64.64-bit fixed point number * @param y signed 64.64-bit fixed point number * @return signed 64.64-bit fixed point number */ function add (int128 x, int128 y) internal pure returns (int128) { unchecked { int256 result = int256(x) + y; require (result >= MIN_64x64 && result <= MAX_64x64); return int128 (result); } } /** * Calculate x - y. Revert on overflow. * * @param x signed 64.64-bit fixed point number * @param y signed 64.64-bit fixed point number * @return signed 64.64-bit fixed point number */ function sub (int128 x, int128 y) internal pure returns (int128) { unchecked { int256 result = int256(x) - y; require (result >= MIN_64x64 && result <= MAX_64x64); return int128 (result); } } /** * Calculate x * y rounding down. Revert on overflow. * * @param x signed 64.64-bit fixed point number * @param y signed 64.64-bit fixed point number * @return signed 64.64-bit fixed point number */ function mul (int128 x, int128 y) internal pure returns (int128) { unchecked { int256 result = int256(x) * y >> 64; require (result >= MIN_64x64 && result <= MAX_64x64); return int128 (result); } } /** * Calculate x * y rounding towards zero, where x is signed 64.64 fixed point * number and y is signed 256-bit integer number. Revert on overflow. * * @param x signed 64.64 fixed point number * @param y signed 256-bit integer number * @return signed 256-bit integer number */ function muli (int128 x, int256 y) internal pure returns (int256) { unchecked { if (x == MIN_64x64) { require (y >= -0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF && y <= 0x1000000000000000000000000000000000000000000000000); return -y << 63; } else { bool negativeResult = false; if (x < 0) { x = -x; negativeResult = true; } if (y < 0) { y = -y; // We rely on overflow behavior here negativeResult = !negativeResult; } uint256 absoluteResult = mulu (x, uint256 (y)); if (negativeResult) { require (absoluteResult <= 0x8000000000000000000000000000000000000000000000000000000000000000); return -int256 (absoluteResult); // We rely on overflow behavior here } else { require (absoluteResult <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); return int256 (absoluteResult); } } } } /** * Calculate x * y rounding down, where x is signed 64.64 fixed point number * and y is unsigned 256-bit integer number. Revert on overflow. * * @param x signed 64.64 fixed point number * @param y unsigned 256-bit integer number * @return unsigned 256-bit integer number */ function mulu (int128 x, uint256 y) internal pure returns (uint256) { unchecked { if (y == 0) return 0; require (x >= 0); uint256 lo = (uint256 (int256 (x)) * (y & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)) >> 64; uint256 hi = uint256 (int256 (x)) * (y >> 128); require (hi <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); hi <<= 64; require (hi <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - lo); return hi + lo; } } /** * Calculate x / y rounding towards zero. Revert on overflow or when y is * zero. * * @param x signed 64.64-bit fixed point number * @param y signed 64.64-bit fixed point number * @return signed 64.64-bit fixed point number */ function div (int128 x, int128 y) internal pure returns (int128) { unchecked { require (y != 0); int256 result = (int256 (x) << 64) / y; require (result >= MIN_64x64 && result <= MAX_64x64); return int128 (result); } } /** * Calculate x / y rounding towards zero, where x and y are signed 256-bit * integer numbers. Revert on overflow or when y is zero. * * @param x signed 256-bit integer number * @param y signed 256-bit integer number * @return signed 64.64-bit fixed point number */ function divi (int256 x, int256 y) internal pure returns (int128) { unchecked { require (y != 0); bool negativeResult = false; if (x < 0) { x = -x; // We rely on overflow behavior here negativeResult = true; } if (y < 0) { y = -y; // We rely on overflow behavior here negativeResult = !negativeResult; } uint128 absoluteResult = divuu (uint256 (x), uint256 (y)); if (negativeResult) { require (absoluteResult <= 0x80000000000000000000000000000000); return -int128 (absoluteResult); // We rely on overflow behavior here } else { require (absoluteResult <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); return int128 (absoluteResult); // We rely on overflow behavior here } } } /** * Calculate x / y rounding towards zero, where x and y are unsigned 256-bit * integer numbers. Revert on overflow or when y is zero. * * @param x unsigned 256-bit integer number * @param y unsigned 256-bit integer number * @return signed 64.64-bit fixed point number */ function divu (uint256 x, uint256 y) internal pure returns (int128) { unchecked { require (y != 0); uint128 result = divuu (x, y); require (result <= uint128 (MAX_64x64)); return int128 (result); } } /** * Calculate -x. Revert on overflow. * * @param x signed 64.64-bit fixed point number * @return signed 64.64-bit fixed point number */ function neg (int128 x) internal pure returns (int128) { unchecked { require (x != MIN_64x64); return -x; } } /** * Calculate |x|. Revert on overflow. * * @param x signed 64.64-bit fixed point number * @return signed 64.64-bit fixed point number */ function abs (int128 x) internal pure returns (int128) { unchecked { require (x != MIN_64x64); return x < 0 ? -x : x; } } /** * Calculate 1 / x rounding towards zero. Revert on overflow or when x is * zero. * * @param x signed 64.64-bit fixed point number * @return signed 64.64-bit fixed point number */ function inv (int128 x) internal pure returns (int128) { unchecked { require (x != 0); int256 result = int256 (0x100000000000000000000000000000000) / x; require (result >= MIN_64x64 && result <= MAX_64x64); return int128 (result); } } /** * Calculate arithmetics average of x and y, i.e. (x + y) / 2 rounding down. * * @param x signed 64.64-bit fixed point number * @param y signed 64.64-bit fixed point number * @return signed 64.64-bit fixed point number */ function avg (int128 x, int128 y) internal pure returns (int128) { unchecked { return int128 ((int256 (x) + int256 (y)) >> 1); } } /** * Calculate geometric average of x and y, i.e. sqrt (x * y) rounding down. * Revert on overflow or in case x * y is negative. * * @param x signed 64.64-bit fixed point number * @param y signed 64.64-bit fixed point number * @return signed 64.64-bit fixed point number */ function gavg (int128 x, int128 y) internal pure returns (int128) { unchecked { int256 m = int256 (x) * int256 (y); require (m >= 0); require (m < 0x4000000000000000000000000000000000000000000000000000000000000000); return int128 (sqrtu (uint256 (m))); } } /** * Calculate x^y assuming 0^0 is 1, where x is signed 64.64 fixed point number * and y is unsigned 256-bit integer number. Revert on overflow. * * @param x signed 64.64-bit fixed point number * @param y uint256 value * @return signed 64.64-bit fixed point number */ function pow (int128 x, uint256 y) internal pure returns (int128) { unchecked { bool negative = x < 0 && y & 1 == 1; uint256 absX = uint128 (x < 0 ? -x : x); uint256 absResult; absResult = 0x100000000000000000000000000000000; if (absX <= 0x10000000000000000) { absX <<= 63; while (y != 0) { if (y & 0x1 != 0) { absResult = absResult * absX >> 127; } absX = absX * absX >> 127; if (y & 0x2 != 0) { absResult = absResult * absX >> 127; } absX = absX * absX >> 127; if (y & 0x4 != 0) { absResult = absResult * absX >> 127; } absX = absX * absX >> 127; if (y & 0x8 != 0) { absResult = absResult * absX >> 127; } absX = absX * absX >> 127; y >>= 4; } absResult >>= 64; } else { uint256 absXShift = 63; if (absX < 0x1000000000000000000000000) { absX <<= 32; absXShift -= 32; } if (absX < 0x10000000000000000000000000000) { absX <<= 16; absXShift -= 16; } if (absX < 0x1000000000000000000000000000000) { absX <<= 8; absXShift -= 8; } if (absX < 0x10000000000000000000000000000000) { absX <<= 4; absXShift -= 4; } if (absX < 0x40000000000000000000000000000000) { absX <<= 2; absXShift -= 2; } if (absX < 0x80000000000000000000000000000000) { absX <<= 1; absXShift -= 1; } uint256 resultShift = 0; while (y != 0) { require (absXShift < 64); if (y & 0x1 != 0) { absResult = absResult * absX >> 127; resultShift += absXShift; if (absResult > 0x100000000000000000000000000000000) { absResult >>= 1; resultShift += 1; } } absX = absX * absX >> 127; absXShift <<= 1; if (absX >= 0x100000000000000000000000000000000) { absX >>= 1; absXShift += 1; } y >>= 1; } require (resultShift < 64); absResult >>= 64 - resultShift; } int256 result = negative ? -int256 (absResult) : int256 (absResult); require (result >= MIN_64x64 && result <= MAX_64x64); return int128 (result); } } /** * Calculate sqrt (x) rounding down. Revert if x < 0. * * @param x signed 64.64-bit fixed point number * @return signed 64.64-bit fixed point number */ function sqrt (int128 x) internal pure returns (int128) { unchecked { require (x >= 0); return int128 (sqrtu (uint256 (int256 (x)) << 64)); } } /** * Calculate binary logarithm of x. Revert if x <= 0. * * @param x signed 64.64-bit fixed point number * @return signed 64.64-bit fixed point number */ function log_2 (int128 x) internal pure returns (int128) { unchecked { require (x > 0); int256 msb = 0; int256 xc = x; if (xc >= 0x10000000000000000) { xc >>= 64; msb += 64; } if (xc >= 0x100000000) { xc >>= 32; msb += 32; } if (xc >= 0x10000) { xc >>= 16; msb += 16; } if (xc >= 0x100) { xc >>= 8; msb += 8; } if (xc >= 0x10) { xc >>= 4; msb += 4; } if (xc >= 0x4) { xc >>= 2; msb += 2; } if (xc >= 0x2) msb += 1; // No need to shift xc anymore int256 result = msb - 64 << 64; uint256 ux = uint256 (int256 (x)) << uint256 (127 - msb); for (int256 bit = 0x8000000000000000; bit > 0; bit >>= 1) { ux *= ux; uint256 b = ux >> 255; ux >>= 127 + b; result += bit * int256 (b); } return int128 (result); } } /** * Calculate natural logarithm of x. Revert if x <= 0. * * @param x signed 64.64-bit fixed point number * @return signed 64.64-bit fixed point number */ function ln (int128 x) internal pure returns (int128) { unchecked { require (x > 0); return int128 (int256 ( uint256 (int256 (log_2 (x))) * 0xB17217F7D1CF79ABC9E3B39803F2F6AF >> 128)); } } /** * Calculate binary exponent of x. Revert on overflow. * * @param x signed 64.64-bit fixed point number * @return signed 64.64-bit fixed point number */ function exp_2 (int128 x) internal pure returns (int128) { unchecked { require (x < 0x400000000000000000); // Overflow if (x < -0x400000000000000000) return 0; // Underflow uint256 result = 0x80000000000000000000000000000000; if (x & 0x8000000000000000 > 0) result = result * 0x16A09E667F3BCC908B2FB1366EA957D3E >> 128; if (x & 0x4000000000000000 > 0) result = result * 0x1306FE0A31B7152DE8D5A46305C85EDEC >> 128; if (x & 0x2000000000000000 > 0) result = result * 0x1172B83C7D517ADCDF7C8C50EB14A791F >> 128; if (x & 0x1000000000000000 > 0) result = result * 0x10B5586CF9890F6298B92B71842A98363 >> 128; if (x & 0x800000000000000 > 0) result = result * 0x1059B0D31585743AE7C548EB68CA417FD >> 128; if (x & 0x400000000000000 > 0) result = result * 0x102C9A3E778060EE6F7CACA4F7A29BDE8 >> 128; if (x & 0x200000000000000 > 0) result = result * 0x10163DA9FB33356D84A66AE336DCDFA3F >> 128; if (x & 0x100000000000000 > 0) result = result * 0x100B1AFA5ABCBED6129AB13EC11DC9543 >> 128; if (x & 0x80000000000000 > 0) result = result * 0x10058C86DA1C09EA1FF19D294CF2F679B >> 128; if (x & 0x40000000000000 > 0) result = result * 0x1002C605E2E8CEC506D21BFC89A23A00F >> 128; if (x & 0x20000000000000 > 0) result = result * 0x100162F3904051FA128BCA9C55C31E5DF >> 128; if (x & 0x10000000000000 > 0) result = result * 0x1000B175EFFDC76BA38E31671CA939725 >> 128; if (x & 0x8000000000000 > 0) result = result * 0x100058BA01FB9F96D6CACD4B180917C3D >> 128; if (x & 0x4000000000000 > 0) result = result * 0x10002C5CC37DA9491D0985C348C68E7B3 >> 128; if (x & 0x2000000000000 > 0) result = result * 0x1000162E525EE054754457D5995292026 >> 128; if (x & 0x1000000000000 > 0) result = result * 0x10000B17255775C040618BF4A4ADE83FC >> 128; if (x & 0x800000000000 > 0) result = result * 0x1000058B91B5BC9AE2EED81E9B7D4CFAB >> 128; if (x & 0x400000000000 > 0) result = result * 0x100002C5C89D5EC6CA4D7C8ACC017B7C9 >> 128; if (x & 0x200000000000 > 0) result = result * 0x10000162E43F4F831060E02D839A9D16D >> 128; if (x & 0x100000000000 > 0) result = result * 0x100000B1721BCFC99D9F890EA06911763 >> 128; if (x & 0x80000000000 > 0) result = result * 0x10000058B90CF1E6D97F9CA14DBCC1628 >> 128; if (x & 0x40000000000 > 0) result = result * 0x1000002C5C863B73F016468F6BAC5CA2B >> 128; if (x & 0x20000000000 > 0) result = result * 0x100000162E430E5A18F6119E3C02282A5 >> 128; if (x & 0x10000000000 > 0) result = result * 0x1000000B1721835514B86E6D96EFD1BFE >> 128; if (x & 0x8000000000 > 0) result = result * 0x100000058B90C0B48C6BE5DF846C5B2EF >> 128; if (x & 0x4000000000 > 0) result = result * 0x10000002C5C8601CC6B9E94213C72737A >> 128; if (x & 0x2000000000 > 0) result = result * 0x1000000162E42FFF037DF38AA2B219F06 >> 128; if (x & 0x1000000000 > 0) result = result * 0x10000000B17217FBA9C739AA5819F44F9 >> 128; if (x & 0x800000000 > 0) result = result * 0x1000000058B90BFCDEE5ACD3C1CEDC823 >> 128; if (x & 0x400000000 > 0) result = result * 0x100000002C5C85FE31F35A6A30DA1BE50 >> 128; if (x & 0x200000000 > 0) result = result * 0x10000000162E42FF0999CE3541B9FFFCF >> 128; if (x & 0x100000000 > 0) result = result * 0x100000000B17217F80F4EF5AADDA45554 >> 128; if (x & 0x80000000 > 0) result = result * 0x10000000058B90BFBF8479BD5A81B51AD >> 128; if (x & 0x40000000 > 0) result = result * 0x1000000002C5C85FDF84BD62AE30A74CC >> 128; if (x & 0x20000000 > 0) result = result * 0x100000000162E42FEFB2FED257559BDAA >> 128; if (x & 0x10000000 > 0) result = result * 0x1000000000B17217F7D5A7716BBA4A9AE >> 128; if (x & 0x8000000 > 0) result = result * 0x100000000058B90BFBE9DDBAC5E109CCE >> 128; if (x & 0x4000000 > 0) result = result * 0x10000000002C5C85FDF4B15DE6F17EB0D >> 128; if (x & 0x2000000 > 0) result = result * 0x1000000000162E42FEFA494F1478FDE05 >> 128; if (x & 0x1000000 > 0) result = result * 0x10000000000B17217F7D20CF927C8E94C >> 128; if (x & 0x800000 > 0) result = result * 0x1000000000058B90BFBE8F71CB4E4B33D >> 128; if (x & 0x400000 > 0) result = result * 0x100000000002C5C85FDF477B662B26945 >> 128; if (x & 0x200000 > 0) result = result * 0x10000000000162E42FEFA3AE53369388C >> 128; if (x & 0x100000 > 0) result = result * 0x100000000000B17217F7D1D351A389D40 >> 128; if (x & 0x80000 > 0) result = result * 0x10000000000058B90BFBE8E8B2D3D4EDE >> 128; if (x & 0x40000 > 0) result = result * 0x1000000000002C5C85FDF4741BEA6E77E >> 128; if (x & 0x20000 > 0) result = result * 0x100000000000162E42FEFA39FE95583C2 >> 128; if (x & 0x10000 > 0) result = result * 0x1000000000000B17217F7D1CFB72B45E1 >> 128; if (x & 0x8000 > 0) result = result * 0x100000000000058B90BFBE8E7CC35C3F0 >> 128; if (x & 0x4000 > 0) result = result * 0x10000000000002C5C85FDF473E242EA38 >> 128; if (x & 0x2000 > 0) result = result * 0x1000000000000162E42FEFA39F02B772C >> 128; if (x & 0x1000 > 0) result = result * 0x10000000000000B17217F7D1CF7D83C1A >> 128; if (x & 0x800 > 0) result = result * 0x1000000000000058B90BFBE8E7BDCBE2E >> 128; if (x & 0x400 > 0) result = result * 0x100000000000002C5C85FDF473DEA871F >> 128; if (x & 0x200 > 0) result = result * 0x10000000000000162E42FEFA39EF44D91 >> 128; if (x & 0x100 > 0) result = result * 0x100000000000000B17217F7D1CF79E949 >> 128; if (x & 0x80 > 0) result = result * 0x10000000000000058B90BFBE8E7BCE544 >> 128; if (x & 0x40 > 0) result = result * 0x1000000000000002C5C85FDF473DE6ECA >> 128; if (x & 0x20 > 0) result = result * 0x100000000000000162E42FEFA39EF366F >> 128; if (x & 0x10 > 0) result = result * 0x1000000000000000B17217F7D1CF79AFA >> 128; if (x & 0x8 > 0) result = result * 0x100000000000000058B90BFBE8E7BCD6D >> 128; if (x & 0x4 > 0) result = result * 0x10000000000000002C5C85FDF473DE6B2 >> 128; if (x & 0x2 > 0) result = result * 0x1000000000000000162E42FEFA39EF358 >> 128; if (x & 0x1 > 0) result = result * 0x10000000000000000B17217F7D1CF79AB >> 128; result >>= uint256 (int256 (63 - (x >> 64))); require (result <= uint256 (int256 (MAX_64x64))); return int128 (int256 (result)); } } /** * Calculate natural exponent of x. Revert on overflow. * * @param x signed 64.64-bit fixed point number * @return signed 64.64-bit fixed point number */ function exp (int128 x) internal pure returns (int128) { unchecked { require (x < 0x400000000000000000); // Overflow if (x < -0x400000000000000000) return 0; // Underflow return exp_2 ( int128 (int256 (x) * 0x171547652B82FE1777D0FFDA0D23A7D12 >> 128)); } } /** * Calculate x / y rounding towards zero, where x and y are unsigned 256-bit * integer numbers. Revert on overflow or when y is zero. * * @param x unsigned 256-bit integer number * @param y unsigned 256-bit integer number * @return unsigned 64.64-bit fixed point number */ function divuu (uint256 x, uint256 y) private pure returns (uint128) { unchecked { require (y != 0); uint256 result; if (x <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF) result = (x << 64) / y; else { uint256 msb = 192; uint256 xc = x >> 192; if (xc >= 0x100000000) { xc >>= 32; msb += 32; } if (xc >= 0x10000) { xc >>= 16; msb += 16; } if (xc >= 0x100) { xc >>= 8; msb += 8; } if (xc >= 0x10) { xc >>= 4; msb += 4; } if (xc >= 0x4) { xc >>= 2; msb += 2; } if (xc >= 0x2) msb += 1; // No need to shift xc anymore result = (x << 255 - msb) / ((y - 1 >> msb - 191) + 1); require (result <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); uint256 hi = result * (y >> 128); uint256 lo = result * (y & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); uint256 xh = x >> 192; uint256 xl = x << 64; if (xl < lo) xh -= 1; xl -= lo; // We rely on overflow behavior here lo = hi << 128; if (xl < lo) xh -= 1; xl -= lo; // We rely on overflow behavior here result += xh == hi >> 128 ? xl / y : 1; } require (result <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); return uint128 (result); } } /** * Calculate sqrt (x) rounding down, where x is unsigned 256-bit integer * number. * * @param x unsigned 256-bit integer number * @return unsigned 128-bit integer number */ function sqrtu (uint256 x) private pure returns (uint128) { unchecked { if (x == 0) return 0; else { uint256 xx = x; uint256 r = 1; if (xx >= 0x100000000000000000000000000000000) { xx >>= 128; r <<= 64; } if (xx >= 0x10000000000000000) { xx >>= 64; r <<= 32; } if (xx >= 0x100000000) { xx >>= 32; r <<= 16; } if (xx >= 0x10000) { xx >>= 16; r <<= 8; } if (xx >= 0x100) { xx >>= 8; r <<= 4; } if (xx >= 0x10) { xx >>= 4; r <<= 2; } if (xx >= 0x4) { r <<= 1; } r = (r + x / r) >> 1; r = (r + x / r) >> 1; r = (r + x / r) >> 1; r = (r + x / r) >> 1; r = (r + x / r) >> 1; r = (r + x / r) >> 1; r = (r + x / r) >> 1; // Seven iterations should be enough uint256 r1 = x / r; return uint128 (r < r1 ? r : r1); } } } }
Math.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol) pragma solidity ^0.8.0; /** * @dev Standard math utilities missing in the Solidity language. */ library Math { enum Rounding { Down, // Toward negative infinity Up, // Toward infinity Zero // Toward zero } /** * @dev Returns the largest of two numbers. */ function max(uint256 a, uint256 b) internal pure returns (uint256) { return a > b ? a : b; } /** * @dev Returns the smallest of two numbers. */ function min(uint256 a, uint256 b) internal pure returns (uint256) { return a < b ? a : b; } /** * @dev Returns the average of two numbers. The result is rounded towards * zero. */ function average(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b) / 2 can overflow. return (a & b) + (a ^ b) / 2; } /** * @dev Returns the ceiling of the division of two numbers. * * This differs from standard division with `/` in that it rounds up instead * of rounding down. */ function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b - 1) / b can overflow on addition, so we distribute. return a == 0 ? 0 : (a - 1) / b + 1; } /** * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0 * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) * with further edits by Uniswap Labs also under MIT license. */ function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) { unchecked { // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256 // variables such that product = prod1 * 2^256 + prod0. uint256 prod0; // Least significant 256 bits of the product uint256 prod1; // Most significant 256 bits of the product assembly { let mm := mulmod(x, y, not(0)) prod0 := mul(x, y) prod1 := sub(sub(mm, prod0), lt(mm, prod0)) } // Handle non-overflow cases, 256 by 256 division. if (prod1 == 0) { // Solidity will revert if denominator == 0, unlike the div opcode on its own. // The surrounding unchecked block does not change this fact. // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic. return prod0 / denominator; } // Make sure the result is less than 2^256. Also prevents denominator == 0. require(denominator > prod1, "Math: mulDiv overflow"); /////////////////////////////////////////////// // 512 by 256 division. /////////////////////////////////////////////// // Make division exact by subtracting the remainder from [prod1 prod0]. uint256 remainder; assembly { // Compute remainder using mulmod. remainder := mulmod(x, y, denominator) // Subtract 256 bit number from 512 bit number. prod1 := sub(prod1, gt(remainder, prod0)) prod0 := sub(prod0, remainder) } // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1. // See https://cs.stackexchange.com/q/138556/92363. // Does not overflow because the denominator cannot be zero at this stage in the function. uint256 twos = denominator & (~denominator + 1); assembly { // Divide denominator by twos. denominator := div(denominator, twos) // Divide [prod1 prod0] by twos. prod0 := div(prod0, twos) // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one. twos := add(div(sub(0, twos), twos), 1) } // Shift in bits from prod1 into prod0. prod0 |= prod1 * twos; // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for // four bits. That is, denominator * inv = 1 mod 2^4. uint256 inverse = (3 * denominator) ^ 2; // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works // in modular arithmetic, doubling the correct bits in each step. inverse *= 2 - denominator * inverse; // inverse mod 2^8 inverse *= 2 - denominator * inverse; // inverse mod 2^16 inverse *= 2 - denominator * inverse; // inverse mod 2^32 inverse *= 2 - denominator * inverse; // inverse mod 2^64 inverse *= 2 - denominator * inverse; // inverse mod 2^128 inverse *= 2 - denominator * inverse; // inverse mod 2^256 // Because the division is now exact we can divide by multiplying with the modular inverse of denominator. // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1 // is no longer required. result = prod0 * inverse; return result; } } /** * @notice Calculates x * y / denominator with full precision, following the selected rounding direction. */ function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) { uint256 result = mulDiv(x, y, denominator); if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) { result += 1; } return result; } /** * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down. * * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11). */ function sqrt(uint256 a) internal pure returns (uint256) { if (a == 0) { return 0; } // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target. // // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`. // // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)` // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))` // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)` // // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit. uint256 result = 1 << (log2(a) >> 1); // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128, // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision // into the expected uint128 result. unchecked { result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; return min(result, a / result); } } /** * @notice Calculates sqrt(a), following the selected rounding direction. */ function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = sqrt(a); return result + (rounding == Rounding.Up && result * result < a ? 1 : 0); } } /** * @dev Return the log in base 2, rounded down, of a positive value. * Returns 0 if given 0. */ function log2(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >> 128 > 0) { value >>= 128; result += 128; } if (value >> 64 > 0) { value >>= 64; result += 64; } if (value >> 32 > 0) { value >>= 32; result += 32; } if (value >> 16 > 0) { value >>= 16; result += 16; } if (value >> 8 > 0) { value >>= 8; result += 8; } if (value >> 4 > 0) { value >>= 4; result += 4; } if (value >> 2 > 0) { value >>= 2; result += 2; } if (value >> 1 > 0) { result += 1; } } return result; } /** * @dev Return the log in base 2, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log2(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log2(value); return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0); } } /** * @dev Return the log in base 10, rounded down, of a positive value. * Returns 0 if given 0. */ function log10(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >= 10 ** 64) { value /= 10 ** 64; result += 64; } if (value >= 10 ** 32) { value /= 10 ** 32; result += 32; } if (value >= 10 ** 16) { value /= 10 ** 16; result += 16; } if (value >= 10 ** 8) { value /= 10 ** 8; result += 8; } if (value >= 10 ** 4) { value /= 10 ** 4; result += 4; } if (value >= 10 ** 2) { value /= 10 ** 2; result += 2; } if (value >= 10 ** 1) { result += 1; } } return result; } /** * @dev Return the log in base 10, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log10(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log10(value); return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0); } } /** * @dev Return the log in base 256, rounded down, of a positive value. * Returns 0 if given 0. * * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string. */ function log256(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >> 128 > 0) { value >>= 128; result += 16; } if (value >> 64 > 0) { value >>= 64; result += 8; } if (value >> 32 > 0) { value >>= 32; result += 4; } if (value >> 16 > 0) { value >>= 16; result += 2; } if (value >> 8 > 0) { result += 1; } } return result; } /** * @dev Return the log in base 256, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log256(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log256(value); return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0); } } }
ISeekerPowerOracle.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.18; interface ISeekerPowerOracle { function setOracle(address oracle) external; function registerSeekerPowerRestricted(uint256 seekerId, uint256 power) external; function registerSeekerPower( uint256 seekerId, uint256 power, bytes32 nonce, bytes calldata proof ) external; function getSeekerPower(uint256 seekerId) external view returns (uint256); function getProofMessage( uint256 seekerId, uint256 power, bytes32 nonce ) external pure returns (bytes memory); }
IDirectory.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.18; interface IDirectory { /** * @dev A DirectoryEntry will be stored for every node that joins the * network in a specific epoch. The entry will contain the stakee's * address, and a boundary value which is a sum of the current directory's * total stake, and the current stakee's total stake. */ struct DirectoryEntry { address stakee; uint256 boundary; } /** * @dev An EpochDirectory will be stored for every epoch. The * directory will be constructed piece by piece as Nodes join, * each adding their own directory entry based on their current * stake value. */ struct EpochDirectory { DirectoryEntry[] entries; mapping(address => uint256) stakes; uint256 totalStake; } function setCurrentDirectory(uint256 epochId) external; function joinNextDirectory(address stakee, uint256 seekerId) external; function scan(uint128 point) external view returns (address stakee); function scanWithEpochId( uint128 point, uint256 epochId ) external view returns (address stakee); function getTotalStakeForStakee( uint256 epochId, address stakee ) external view returns (uint256); function getTotalStake(uint256 epochId) external view returns (uint256); function getEntries( uint256 epochId ) external view returns (address[] memory, uint256[] memory); }
ERC20.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/ERC20.sol) pragma solidity ^0.8.0; import "./IERC20.sol"; import "./extensions/IERC20Metadata.sol"; import "../../utils/Context.sol"; /** * @dev Implementation of the {IERC20} interface. * * This implementation is agnostic to the way tokens are created. This means * that a supply mechanism has to be added in a derived contract using {_mint}. * For a generic mechanism see {ERC20PresetMinterPauser}. * * TIP: For a detailed writeup see our guide * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How * to implement supply mechanisms]. * * The default value of {decimals} is 18. To change this, you should override * this function so it returns a different value. * * We have followed general OpenZeppelin Contracts guidelines: functions revert * instead returning `false` on failure. This behavior is nonetheless * conventional and does not conflict with the expectations of ERC20 * applications. * * Additionally, an {Approval} event is emitted on calls to {transferFrom}. * This allows applications to reconstruct the allowance for all accounts just * by listening to said events. Other implementations of the EIP may not emit * these events, as it isn't required by the specification. * * Finally, the non-standard {decreaseAllowance} and {increaseAllowance} * functions have been added to mitigate the well-known issues around setting * allowances. See {IERC20-approve}. */ contract ERC20 is Context, IERC20, IERC20Metadata { mapping(address => uint256) private _balances; mapping(address => mapping(address => uint256)) private _allowances; uint256 private _totalSupply; string private _name; string private _symbol; /** * @dev Sets the values for {name} and {symbol}. * * All two of these values are immutable: they can only be set once during * construction. */ constructor(string memory name_, string memory symbol_) { _name = name_; _symbol = symbol_; } /** * @dev Returns the name of the token. */ function name() public view virtual override returns (string memory) { return _name; } /** * @dev Returns the symbol of the token, usually a shorter version of the * name. */ function symbol() public view virtual override returns (string memory) { return _symbol; } /** * @dev Returns the number of decimals used to get its user representation. * For example, if `decimals` equals `2`, a balance of `505` tokens should * be displayed to a user as `5.05` (`505 / 10 ** 2`). * * Tokens usually opt for a value of 18, imitating the relationship between * Ether and Wei. This is the default value returned by this function, unless * it's overridden. * * NOTE: This information is only used for _display_ purposes: it in * no way affects any of the arithmetic of the contract, including * {IERC20-balanceOf} and {IERC20-transfer}. */ function decimals() public view virtual override returns (uint8) { return 18; } /** * @dev See {IERC20-totalSupply}. */ function totalSupply() public view virtual override returns (uint256) { return _totalSupply; } /** * @dev See {IERC20-balanceOf}. */ function balanceOf(address account) public view virtual override returns (uint256) { return _balances[account]; } /** * @dev See {IERC20-transfer}. * * Requirements: * * - `to` cannot be the zero address. * - the caller must have a balance of at least `amount`. */ function transfer(address to, uint256 amount) public virtual override returns (bool) { address owner = _msgSender(); _transfer(owner, to, amount); return true; } /** * @dev See {IERC20-allowance}. */ function allowance(address owner, address spender) public view virtual override returns (uint256) { return _allowances[owner][spender]; } /** * @dev See {IERC20-approve}. * * NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on * `transferFrom`. This is semantically equivalent to an infinite approval. * * Requirements: * * - `spender` cannot be the zero address. */ function approve(address spender, uint256 amount) public virtual override returns (bool) { address owner = _msgSender(); _approve(owner, spender, amount); return true; } /** * @dev See {IERC20-transferFrom}. * * Emits an {Approval} event indicating the updated allowance. This is not * required by the EIP. See the note at the beginning of {ERC20}. * * NOTE: Does not update the allowance if the current allowance * is the maximum `uint256`. * * Requirements: * * - `from` and `to` cannot be the zero address. * - `from` must have a balance of at least `amount`. * - the caller must have allowance for ``from``'s tokens of at least * `amount`. */ function transferFrom(address from, address to, uint256 amount) public virtual override returns (bool) { address spender = _msgSender(); _spendAllowance(from, spender, amount); _transfer(from, to, amount); return true; } /** * @dev Atomically increases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. */ function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { address owner = _msgSender(); _approve(owner, spender, allowance(owner, spender) + addedValue); return true; } /** * @dev Atomically decreases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. * - `spender` must have allowance for the caller of at least * `subtractedValue`. */ function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) { address owner = _msgSender(); uint256 currentAllowance = allowance(owner, spender); require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero"); unchecked { _approve(owner, spender, currentAllowance - subtractedValue); } return true; } /** * @dev Moves `amount` of tokens from `from` to `to`. * * This internal function is equivalent to {transfer}, and can be used to * e.g. implement automatic token fees, slashing mechanisms, etc. * * Emits a {Transfer} event. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `from` must have a balance of at least `amount`. */ function _transfer(address from, address to, uint256 amount) internal virtual { require(from != address(0), "ERC20: transfer from the zero address"); require(to != address(0), "ERC20: transfer to the zero address"); _beforeTokenTransfer(from, to, amount); uint256 fromBalance = _balances[from]; require(fromBalance >= amount, "ERC20: transfer amount exceeds balance"); unchecked { _balances[from] = fromBalance - amount; // Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by // decrementing then incrementing. _balances[to] += amount; } emit Transfer(from, to, amount); _afterTokenTransfer(from, to, amount); } /** @dev Creates `amount` tokens and assigns them to `account`, increasing * the total supply. * * Emits a {Transfer} event with `from` set to the zero address. * * Requirements: * * - `account` cannot be the zero address. */ function _mint(address account, uint256 amount) internal virtual { require(account != address(0), "ERC20: mint to the zero address"); _beforeTokenTransfer(address(0), account, amount); _totalSupply += amount; unchecked { // Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above. _balances[account] += amount; } emit Transfer(address(0), account, amount); _afterTokenTransfer(address(0), account, amount); } /** * @dev Destroys `amount` tokens from `account`, reducing the * total supply. * * Emits a {Transfer} event with `to` set to the zero address. * * Requirements: * * - `account` cannot be the zero address. * - `account` must have at least `amount` tokens. */ function _burn(address account, uint256 amount) internal virtual { require(account != address(0), "ERC20: burn from the zero address"); _beforeTokenTransfer(account, address(0), amount); uint256 accountBalance = _balances[account]; require(accountBalance >= amount, "ERC20: burn amount exceeds balance"); unchecked { _balances[account] = accountBalance - amount; // Overflow not possible: amount <= accountBalance <= totalSupply. _totalSupply -= amount; } emit Transfer(account, address(0), amount); _afterTokenTransfer(account, address(0), amount); } /** * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens. * * This internal function is equivalent to `approve`, and can be used to * e.g. set automatic allowances for certain subsystems, etc. * * Emits an {Approval} event. * * Requirements: * * - `owner` cannot be the zero address. * - `spender` cannot be the zero address. */ function _approve(address owner, address spender, uint256 amount) internal virtual { require(owner != address(0), "ERC20: approve from the zero address"); require(spender != address(0), "ERC20: approve to the zero address"); _allowances[owner][spender] = amount; emit Approval(owner, spender, amount); } /** * @dev Updates `owner` s allowance for `spender` based on spent `amount`. * * Does not update the allowance amount in case of infinite allowance. * Revert if not enough allowance is available. * * Might emit an {Approval} event. */ function _spendAllowance(address owner, address spender, uint256 amount) internal virtual { uint256 currentAllowance = allowance(owner, spender); if (currentAllowance != type(uint256).max) { require(currentAllowance >= amount, "ERC20: insufficient allowance"); unchecked { _approve(owner, spender, currentAllowance - amount); } } } /** * @dev Hook that is called before any transfer of tokens. This includes * minting and burning. * * Calling conditions: * * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens * will be transferred to `to`. * - when `from` is zero, `amount` tokens will be minted for `to`. * - when `to` is zero, `amount` of ``from``'s tokens will be burned. * - `from` and `to` are never both zero. * * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. */ function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual {} /** * @dev Hook that is called after any transfer of tokens. This includes * minting and burning. * * Calling conditions: * * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens * has been transferred to `to`. * - when `from` is zero, `amount` tokens have been minted for `to`. * - when `to` is zero, `amount` of ``from``'s tokens have been burned. * - `from` and `to` are never both zero. * * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. */ function _afterTokenTransfer(address from, address to, uint256 amount) internal virtual {} }
IERC20.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.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); }
IEpochsManager.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.18; interface IEpochsManager { /** * @dev This struct will hold all network parameters that will be static * for the entire epoch. This value will be stored in a mapping, where the * key is the current epoch id. */ struct Epoch { // time related variables uint256 startBlock; // Block the epoch was initialized uint256 duration; // Minimum time epoch will be alive measured in number of blocks uint256 endBlock; // Block the epoch ended (and when the next epoch was initialized) // Zero here represents the epoch has not yet ended. // registry variables uint32 defaultPayoutPercentage; // ticketing variables uint32 decayRate; uint256 faceValue; uint128 baseLiveWinProb; uint128 expiredWinProb; uint256 ticketDuration; } function initializeEpoch() external returns (uint256); function setEpochDuration(uint256 _epochDuration) external; function getCurrentActiveEpoch() external view returns (uint256, Epoch memory); function joinNextEpoch() external; function getEpoch(uint256 epochId) external view returns (Epoch memory); function getNextEpochId() external view returns (uint256); }
SafeCast.sol
// 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); } }
RewardsManager.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.18; import "abdk-libraries-solidity/ABDKMath64x64.sol"; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/utils/math/SafeCast.sol"; import "@openzeppelin/contracts/utils/introspection/ERC165.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import "../../libraries/SyloUtils.sol"; import "../../libraries/Manageable.sol"; import "../../epochs/EpochsManager.sol"; import "../../staking/StakingManager.sol"; import "../../interfaces/epochs/IEpochsManager.sol"; import "../../interfaces/staking/IStakingManager.sol"; import "../../interfaces/payments/ticketing/IRewardsManager.sol"; /** * @notice Handles epoch based reward pools that are incremented from redeeming tickets. * Nodes use this contract to set up their reward pool for the next epoch, * and stakers use this contract to track and claim staking rewards. * @dev After deployment, the SyloTicketing contract should be * set up as a manager to be able to call certain restricted functions. */ contract RewardsManager is IRewardsManager, Initializable, Manageable, ERC165 { uint256 internal constant ONE_SYLO = 1 ether; // 64x64 Fixed point representation of 1 SYLO (10**18 >> 64) int128 internal constant ONE_SYLO_FIXED = 18446744073709551616000000000000000000; uint256 internal constant MAX_INT64 = 9223372036854775807; /** ERC20 Sylo token contract. */ IERC20 public _token; /** Sylo Staking Manager contract. */ StakingManager public _stakingManager; /** Sylo Epochs Manager. */ EpochsManager public _epochsManager; /** * @dev Each node will manage a cumulative reward factor (CRF) that is incremented * whenever a ticket is redeemed. This factor acts as a single value * that can be used to calculate any particular staker's reward share. This * prevents the need to individually track each staker's proportion, and also allows * a claim calculation to be performed without iterating through every epoch. * * The CRF is calculated as CRF = CRF + Reward / TotalStake. */ mapping(address => int128) private cumulativeRewardFactors; /** * @notice Tracks the last epoch a delegated staker made a reward claim in. * The key to this mapping is a hash of the Node's address and the delegated * stakers address. */ mapping(bytes32 => LastClaim) public lastClaims; /** * @notice Tracks each Nodes total pending rewards in SOLOs. This * value is accumulated as Node's redeem tickets. Rewards are pending if the * distribution amongst the stakers has not been accounted for yet. Pending rewards * are transferred to unclaimed rewards once the the staker's share has been * calculated. */ mapping(address => uint256) public pendingRewards; /** * @notice Tracks rewards for stakers after the stakers share has been calculated, * but has not actually been claimed by the staker. * The node fee reward is also added to the node's unclaimedStakingRewards. */ mapping(bytes32 => uint256) public unclaimedStakingRewards; /** * @notice Tracks each Node's most recently active reward pool */ mapping(address => uint256) public latestActiveRewardPools; /** * @notice Tracks total accumulated rewards in each epoch */ mapping(uint256 => uint256) public totalEpochRewards; /** * @notice Tracks total accumulated staking rewards in each epoch */ mapping(uint256 => uint256) public totalEpochStakingRewards; /** * @notice Tracks each reward pool initialized by a Node. The key to this map * is derived from the epochId and the Node's address. */ mapping(bytes32 => RewardPool) public rewardPools; error NoRewardToClaim(); error AmountCannotBeZero(); error RewardPoolNotExist(); error RewardPoolAlreadyExist(); error DoNotAllowZeroAddress(); error TokenCannotBeZeroAddress(); error NoStakeToCreateRewardPool(); error StakeeCannotBeZeroAddress(); error StakerCannotBeZeroAddress(); error StakerKeyCannotBeZeroBytes(); error InvalidFixedPointResult(); error NextRewardPoolAlreadyActive(); function initialize( IERC20 token, StakingManager stakingManager, EpochsManager epochsManager ) external initializer { if (address(token) == address(0)) { revert TokenCannotBeZeroAddress(); } SyloUtils.validateContractInterface( "StakingManager", address(stakingManager), type(IStakingManager).interfaceId ); SyloUtils.validateContractInterface( "EpochsManager", address(epochsManager), type(IEpochsManager).interfaceId ); Ownable2StepUpgradeable.__Ownable2Step_init(); _token = token; _epochsManager = epochsManager; _stakingManager = stakingManager; } /** * @notice Returns true if the contract implements the interface defined by * `interfaceId` from ERC165. */ function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { return interfaceId == type(IRewardsManager).interfaceId; } /** * @notice Returns the key used to index a reward pool. The key is a hash of * the epochId and Node's address. * @param epochId The epoch ID the reward pool was created in. * @param stakee The address of the Node. * @return A byte-array representing the reward pool key. */ function getRewardPoolKey(uint256 epochId, address stakee) public pure returns (bytes32) { return keccak256(abi.encodePacked(epochId, stakee)); } /** * @notice Returns the key used to index staking claims. The key is a hash of * the Node's address and the staker's address. * @param stakee The address of the Node. * @param staker The address of the stake. * @return A byte-array representing the key. */ function getStakerKey(address stakee, address staker) public pure returns (bytes32) { return keccak256(abi.encodePacked(stakee, staker)); } /** * @notice Retrieve the reward pool initialized by the given node, at the specified * epoch. * @param epochId The ID of the epoch the reward pool was initialized in. * @param stakee The address of the Node. * @return The reward pool. */ function getRewardPool( uint256 epochId, address stakee ) public view returns (RewardPool memory) { return rewardPools[getRewardPoolKey(epochId, stakee)]; } /** * @notice Retrieve the total accumulated reward that will be distributed to a Node's * delegated stakers for a given epoch. * @param epochId The ID of the epoch the reward pool was initialized in. * @param stakee The address of the Node. * @return The total accumulated staker reward in SOLO. */ function getRewardPoolStakersTotal( uint256 epochId, address stakee ) external view returns (uint256) { return rewardPools[getRewardPoolKey(epochId, stakee)].stakersRewardTotal; } /** * @notice Retrieve the total active stake that will be used for a Node's reward * pool in a given epoch. * @param epochId The ID of the epoch the reward pool was initialized in. * @param stakee The address of the Node. * @return The total active stake for that reward pool in SOLO. */ function getRewardPoolActiveStake( uint256 epochId, address stakee ) external view returns (uint256) { return rewardPools[getRewardPoolKey(epochId, stakee)].totalActiveStake; } /** * @notice Retrieve the total pending staking reward allocated to a Node's * stakers. * @param stakee The address of the Node. * @return The total pending staking reward in SOLO. */ function getPendingRewards(address stakee) external view returns (uint256) { return pendingRewards[stakee]; } /** * @notice Retrieves the ID of the epoch in which a staker last made their * staking claim. * @param stakee The address of the Node. * @param staker The address of the staker. * @return The ID of the epoch. */ function getLastClaim( address stakee, address staker ) external view returns (LastClaim memory) { return lastClaims[getStakerKey(stakee, staker)]; } /** * @notice Retrieves the total accumulated rewards for a specific epoch. * @param epochId The epoch id. * @return The total reward in that epoch, including staking rewards and fee * reward. */ function getTotalEpochRewards(uint256 epochId) external view returns (uint256) { return totalEpochRewards[epochId]; } /** * @notice Retrieves the total accumulated rewards for stakers in a specific epoch. * @param epochId The epoch id. * @return The total staking reward in that epoch. */ function getTotalEpochStakingRewards(uint256 epochId) external view returns (uint256) { return totalEpochStakingRewards[epochId]; } /** * @notice This is used by Nodes to initialize their reward pool for * the next epoch. This function will revert if the caller has no stake, or * if the reward pool has already been initialized. The total active stake * for the next reward pool is calculated by summing up the total managed * stake held by the RewardsManager contract. */ function initializeNextRewardPool(address stakee) external onlyManager { if (stakee == address(0)) { revert StakeeCannotBeZeroAddress(); } uint256 nextEpochId = _epochsManager.getNextEpochId(); RewardPool storage nextRewardPool = rewardPools[getRewardPoolKey(nextEpochId, stakee)]; if (nextRewardPool.initializedAt != 0) { revert RewardPoolAlreadyExist(); } uint256 totalStake = _stakingManager.getStakeeTotalManagedStake(stakee); if (totalStake == 0) { revert NoStakeToCreateRewardPool(); } nextRewardPool.totalActiveStake = totalStake; nextRewardPool.initializedAt = block.number; } /** * @dev This function should be called by the Ticketing contract when a * ticket is successfully redeemed. The face value of the ticket * should be split between incrementing the node's reward balance, * and the reward balance for the node's delegated stakers. The face value * will be added to the current reward pool's balance. This function will * fail if the Ticketing contract has not been set as a manager. * @param stakee The address of the Node. * @param amount The face value of the ticket in SOLO. */ function incrementRewardPool(address stakee, uint256 amount) external onlyManager { if (stakee == address(0)) { revert StakeeCannotBeZeroAddress(); } if (amount == 0) { revert AmountCannotBeZero(); } (uint256 epochId, IEpochsManager.Epoch memory currentEpoch) = _epochsManager .getCurrentActiveEpoch(); RewardPool storage rewardPool = rewardPools[getRewardPoolKey(epochId, stakee)]; if (rewardPool.initializedAt == 0) { revert RewardPoolNotExist(); } // Update the latest active reward pool for the node to be this pool if (latestActiveRewardPools[stakee] < epochId) { latestActiveRewardPools[stakee] = epochId; } uint256 stakersReward = SyloUtils.percOf( SafeCast.toUint128(amount), currentEpoch.defaultPayoutPercentage ); // transfer the node's fee reward to it's unclaimed reward value unclaimedStakingRewards[getStakerKey(stakee, stakee)] = unclaimedStakingRewards[getStakerKey(stakee, stakee)] + (amount - stakersReward); // update the value of the reward owed to the stakers pendingRewards[stakee] = pendingRewards[stakee] + stakersReward; // if this is the first ticket redeemed for this reward, set the initial // CRF value for this pool if (rewardPool.stakersRewardTotal == 0) { rewardPool.initialCumulativeRewardFactor = cumulativeRewardFactors[stakee]; } rewardPool.stakersRewardTotal = rewardPool.stakersRewardTotal + stakersReward; // We preemptively prevent an overflow revert with the abdk library. // Dividing the stakers reward with the pool's total active // stake may produce a value that is greater than the maximum possible // 64.64 fixed point value. // This error is incredibly niche and unlikely to happen, so we just // revert here as well (with a named error) and remediation involves // just increasing the amount of stake a node has. if (rewardPool.totalActiveStake * MAX_INT64 < stakersReward) { revert InvalidFixedPointResult(); } cumulativeRewardFactors[stakee] = ABDKMath64x64.add( cumulativeRewardFactors[stakee], ABDKMath64x64.div( toFixedPointSYLO(stakersReward), toFixedPointSYLO(rewardPool.totalActiveStake) ) ); totalEpochRewards[epochId] = totalEpochRewards[epochId] + amount; totalEpochStakingRewards[epochId] = totalEpochStakingRewards[epochId] + stakersReward; } /** * @dev This function utilizes the cumulative reward factors, and the staker's * value in stake to calculate the staker's share of the pending reward. */ function calculatePendingClaim( bytes32 stakerKey, address stakee, address staker ) internal view returns (uint256) { uint256 claim = calculateInitialClaim(stakerKey, stakee); // find the first reward pool where their stake was active and had // generated rewards uint256 activeAt; RewardPool memory initialActivePool; uint256 currentEpochId = _epochsManager.currentIteration(); for (uint256 i = lastClaims[stakerKey].claimedAt + 1; i < currentEpochId; ++i) { initialActivePool = rewardPools[getRewardPoolKey(i, stakee)]; // check if node initialized a reward pool for this epoch and // gained rewards if (initialActivePool.initializedAt > 0 && initialActivePool.stakersRewardTotal > 0) { activeAt = i; break; } } if (activeAt == 0) { return claim; } IStakingManager.StakeEntry memory stakeEntry = _stakingManager.getStakeEntry( stakee, staker ); // We convert the staker amount to SYLO as the maximum uint256 value that // can be used for the fixed point representation is 2^64-1. int128 initialStake = toFixedPointSYLO(stakeEntry.amount); int128 initialCumulativeRewardFactor = initialActivePool.initialCumulativeRewardFactor; int128 finalCumulativeRewardFactor = getFinalCumulativeRewardFactor( stakee, currentEpochId ); return claim + fromFixedPointSYLO( ABDKMath64x64.mul( initialStake, ABDKMath64x64.sub(finalCumulativeRewardFactor, initialCumulativeRewardFactor) ) ); } /** * Manually calculates the reward claim for the first epoch the claim is being * made for. This manual calculation is necessary as claims are only made up * to the previous epoch. */ function calculateInitialClaim( bytes32 stakerKey, address stakee ) internal view returns (uint256) { LastClaim memory lastClaim = lastClaims[stakerKey]; // if we have already made a claim up to the previous epoch, then // there is no need to calculate the initial claim if (_epochsManager.currentIteration() == lastClaim.claimedAt) { return 0; } RewardPool memory firstRewardPool = rewardPools[ getRewardPoolKey(lastClaim.claimedAt, stakee) ]; // if there was no reward pool initialized for the first epoch, // then there is no need to calculate the initial claim if (firstRewardPool.totalActiveStake == 0) { return 0; } return (firstRewardPool.stakersRewardTotal * lastClaim.stake) / firstRewardPool.totalActiveStake; } /** * Determines the cumulative reward factor to use for claim calculations. The * CRF will depend on when the Node last initialized a reward pool, and also when * the staker last made their claim. */ function getFinalCumulativeRewardFactor( address stakee, uint256 currentEpochId ) internal view returns (int128) { int128 finalCumulativeRewardFactor; // Get the cumulative reward factor for the Node // for the start of this epoch, since we only perform // calculations up to the end of the previous epoch. if (latestActiveRewardPools[stakee] < currentEpochId) { // If the Node has not been active, then the final // cumulative reward factor will just be the current one. finalCumulativeRewardFactor = cumulativeRewardFactors[stakee]; } else { // We are calculating the claim for an active epoch, the // final cumulative reward factor will be taken from the start of this // epoch (end of previous epoch). RewardPool storage latestRewardPool = rewardPools[ getRewardPoolKey(latestActiveRewardPools[stakee], stakee) ]; finalCumulativeRewardFactor = latestRewardPool.initialCumulativeRewardFactor; } return finalCumulativeRewardFactor; } /** * @notice Call this function to calculate the total reward owed to a staker. * This value will include all epochs since the last claim was made up to * the previous epoch. This will also add any pending rewards to the * final value as well. * @dev This function will utilize the cumulative reward factor to perform the * calculation, keeping the gas cost scaling of this function to a constant value. * @param stakee The address of the Node. * @param staker The address of the staker. * @return The value of the reward owed to the staker in SOLO. */ function calculateStakerClaim(address stakee, address staker) public view returns (uint256) { if (stakee == address(0)) { revert StakeeCannotBeZeroAddress(); } if (staker == address(0)) { revert StakerCannotBeZeroAddress(); } bytes32 stakerKey = getStakerKey(stakee, staker); uint256 pendingClaim = calculatePendingClaim(stakerKey, stakee, staker); return pendingClaim + unclaimedStakingRewards[stakerKey]; } /** * Helper function to convert a uint256 value in SOLOs to a 64.64 fixed point * representation in SYLOs while avoiding any possibility of overflow. * Any remainders from converting SOLO to SYLO is explicitly handled to mitigate * precision loss. The error when using this function is [-1/2^64, 0]. */ function toFixedPointSYLO(uint256 amount) internal pure returns (int128) { int128 fullSylos = ABDKMath64x64.fromUInt(amount / ONE_SYLO); int128 fracSylos = ABDKMath64x64.fromUInt(amount % ONE_SYLO); // remainder return ABDKMath64x64.add(fullSylos, ABDKMath64x64.div(fracSylos, ONE_SYLO_FIXED)); } /** * Helper function to convert a 64.64 fixed point value in SYLOs to a uint256 * representation in SOLOs while avoiding any possibility of overflow. */ function fromFixedPointSYLO(int128 amount) internal pure returns (uint256) { uint256 fullSylos = ABDKMath64x64.toUInt(amount); uint256 fullSolos = fullSylos * ONE_SYLO; // calculate the value lost when converting the fixed point amount to a uint int128 fracSylos = ABDKMath64x64.sub(amount, ABDKMath64x64.fromUInt(fullSylos)); uint256 fracSolos = ABDKMath64x64.toUInt(ABDKMath64x64.mul(fracSylos, ONE_SYLO_FIXED)); return fullSolos + fracSolos; } /** * @notice Call this function to claim rewards as a staker. The * SYLO tokens will be transferred to the caller's account. This function will * fail if there exists no reward to claim. Note: Calling this will remove * the current unclaimed reward from being used as stake in the next round. * @param stakee The address of the Node to claim against. */ function claimStakingRewards(address stakee) external returns (uint256) { if (stakee == address(0)) { revert StakeeCannotBeZeroAddress(); } uint256 currentEpoch = _epochsManager.currentIteration(); if (getRewardPool(currentEpoch + 1, stakee).totalActiveStake > 0) { revert NextRewardPoolAlreadyActive(); } bytes32 stakerKey = getStakerKey(stakee, msg.sender); uint256 pendingReward = calculatePendingClaim(stakerKey, stakee, msg.sender); uint256 totalClaim = pendingReward + unclaimedStakingRewards[stakerKey]; if (totalClaim == 0) { revert NoRewardToClaim(); } delete unclaimedStakingRewards[stakerKey]; pendingRewards[stakee] = pendingRewards[stakee] - pendingReward; updateLastClaim(stakee, msg.sender); SafeERC20.safeTransfer(_token, msg.sender, totalClaim); return totalClaim; } /** * @notice This is called by the staking manager to transfer pending rewards * to unclaimed rewards for a staker. This is required as the last used CRF * needs to be updated whenever stake changes. */ function updatePendingRewards(address stakee, address staker) external onlyManager { uint256 currentEpoch = _epochsManager.currentIteration(); if (getRewardPool(currentEpoch + 1, stakee).totalActiveStake > 0) { revert NextRewardPoolAlreadyActive(); } bytes32 stakerKey = getStakerKey(stakee, staker); uint256 pendingReward = calculatePendingClaim(stakerKey, stakee, staker); pendingRewards[stakee] = pendingRewards[stakee] - pendingReward; unclaimedStakingRewards[stakerKey] = unclaimedStakingRewards[stakerKey] + pendingReward; updateLastClaim(stakee, staker); } function updateLastClaim(address stakee, address staker) internal { bytes32 stakerKey = getStakerKey(stakee, staker); LastClaim storage lastClaim = lastClaims[stakerKey]; uint256 claimAt = _epochsManager.currentIteration(); IStakingManager.StakeEntry memory stakeEntry = _stakingManager.getStakeEntry( stakee, staker ); // If we have already updated the last claim for this epoch, then // we skip updating it again. if (lastClaim.claimedAt == claimAt) { return; } lastClaim.claimedAt = claimAt; lastClaim.stake = stakeEntry.amount; } }
IERC721.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC721/IERC721.sol) pragma solidity ^0.8.0; import "../../utils/introspection/IERC165.sol"; /** * @dev Required interface of an ERC721 compliant contract. */ interface IERC721 is IERC165 { /** * @dev Emitted when `tokenId` token is transferred from `from` to `to`. */ event Transfer(address indexed from, address indexed to, uint256 indexed tokenId); /** * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token. */ event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId); /** * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets. */ event ApprovalForAll(address indexed owner, address indexed operator, bool approved); /** * @dev Returns the number of tokens in ``owner``'s account. */ function balanceOf(address owner) external view returns (uint256 balance); /** * @dev Returns the owner of the `tokenId` token. * * Requirements: * * - `tokenId` must exist. */ function ownerOf(uint256 tokenId) external view returns (address owner); /** * @dev Safely transfers `tokenId` token from `from` to `to`. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must exist and be owned by `from`. * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}. * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. * * Emits a {Transfer} event. */ function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external; /** * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients * are aware of the ERC721 protocol to prevent tokens from being forever locked. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must exist and be owned by `from`. * - If the caller is not `from`, it must have been allowed to move this token by either {approve} or {setApprovalForAll}. * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. * * Emits a {Transfer} event. */ function safeTransferFrom(address from, address to, uint256 tokenId) external; /** * @dev Transfers `tokenId` token from `from` to `to`. * * WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721 * or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must * understand this adds an external call which potentially creates a reentrancy vulnerability. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must be owned by `from`. * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}. * * Emits a {Transfer} event. */ function transferFrom(address from, address to, uint256 tokenId) external; /** * @dev Gives permission to `to` to transfer `tokenId` token to another account. * The approval is cleared when the token is transferred. * * Only a single account can be approved at a time, so approving the zero address clears previous approvals. * * Requirements: * * - The caller must own the token or be an approved operator. * - `tokenId` must exist. * * Emits an {Approval} event. */ function approve(address to, uint256 tokenId) external; /** * @dev Approve or remove `operator` as an operator for the caller. * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller. * * Requirements: * * - The `operator` cannot be the caller. * * Emits an {ApprovalForAll} event. */ function setApprovalForAll(address operator, bool approved) external; /** * @dev Returns the account approved for `tokenId` token. * * Requirements: * * - `tokenId` must exist. */ function getApproved(uint256 tokenId) external view returns (address operator); /** * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`. * * See {setApprovalForAll} */ function isApprovedForAll(address owner, address operator) external view returns (bool); }
IStakingManager.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.18; interface IStakingManager { /** * For every Node, there will be a mapping of the staker to a * StakeEntry. The stake entry tracks the amount of stake in SOLO, * and also when the stake was updated. */ struct StakeEntry { uint256 amount; // Block number this entry was updated at uint256 updatedAt; // Epoch this entry was updated. The stake will become active // in the following epoch uint256 epochId; } /** * Every Node must have stake in order to participate in the Epoch. * Stake can be provided by the Node itself or by other accounts in * the network. */ struct Stake { // Track each stake entry associated to a node mapping(address => StakeEntry) stakeEntries; // The total stake held by this contract for a node, // which will be the sum of all addStake and unlockStake calls uint256 totalManagedStake; } /** * This struct will track stake that is in the process of unlocking. */ struct Unlock { uint256 amount; // Amount of stake unlocking uint256 unlockAt; // Block number the stake becomes withdrawable } function setUnlockDuration(uint256 _unlockDuration) external; function setMinimumStakeProportion(uint32 _minimumStakeProportion) external; function addStake(uint256 amount, address stakee) external; function unlockStake(uint256 amount, address stakee) external returns (uint256); function withdrawStake(address stakee) external; function cancelUnlocking(uint256 amount, address stakee) external; function calculateCapacityFromSeekerPower(uint256 seekerId) external view returns (uint256); function calculateMaxAdditionalDelegatedStake(address stakee) external view returns (uint256); function getTotalManagedStake() external view returns (uint256); function getStakeEntry( address stakee, address staker ) external view returns (StakeEntry memory); function getStakeeTotalManagedStake(address stakee) external view returns (uint256); }
SignedMath.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SignedMath.sol) pragma solidity ^0.8.0; /** * @dev Standard signed math utilities missing in the Solidity language. */ library SignedMath { /** * @dev Returns the largest of two signed numbers. */ function max(int256 a, int256 b) internal pure returns (int256) { return a > b ? a : b; } /** * @dev Returns the smallest of two signed numbers. */ function min(int256 a, int256 b) internal pure returns (int256) { return a < b ? a : b; } /** * @dev Returns the average of two signed numbers without overflow. * The result is rounded towards zero. */ function average(int256 a, int256 b) internal pure returns (int256) { // Formula from the book "Hacker's Delight" int256 x = (a & b) + ((a ^ b) >> 1); return x + (int256(uint256(x) >> 255) & (a ^ b)); } /** * @dev Returns the absolute unsigned value of a signed value. */ function abs(int256 n) internal pure returns (uint256) { unchecked { // must be unchecked in order to support `n = type(int256).min` return uint256(n >= 0 ? n : -n); } } }
ECDSA.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (utils/cryptography/ECDSA.sol) pragma solidity ^0.8.0; import "../Strings.sol"; /** * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations. * * These functions can be used to verify that a message was signed by the holder * of the private keys of a given address. */ library ECDSA { enum RecoverError { NoError, InvalidSignature, InvalidSignatureLength, InvalidSignatureS, InvalidSignatureV // Deprecated in v4.8 } function _throwError(RecoverError error) private pure { if (error == RecoverError.NoError) { return; // no error: do nothing } else if (error == RecoverError.InvalidSignature) { revert("ECDSA: invalid signature"); } else if (error == RecoverError.InvalidSignatureLength) { revert("ECDSA: invalid signature length"); } else if (error == RecoverError.InvalidSignatureS) { revert("ECDSA: invalid signature 's' value"); } } /** * @dev Returns the address that signed a hashed message (`hash`) with * `signature` or error string. This address can then be used for verification purposes. * * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures: * this function rejects them by requiring the `s` value to be in the lower * half order, and the `v` value to be either 27 or 28. * * IMPORTANT: `hash` _must_ be the result of a hash operation for the * verification to be secure: it is possible to craft signatures that * recover to arbitrary addresses for non-hashed data. A safe way to ensure * this is by receiving a hash of the original message (which may otherwise * be too long), and then calling {toEthSignedMessageHash} on it. * * Documentation for signature generation: * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js] * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers] * * _Available since v4.3._ */ function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) { if (signature.length == 65) { bytes32 r; bytes32 s; uint8 v; // ecrecover takes the signature parameters, and the only way to get them // currently is to use assembly. /// @solidity memory-safe-assembly assembly { r := mload(add(signature, 0x20)) s := mload(add(signature, 0x40)) v := byte(0, mload(add(signature, 0x60))) } return tryRecover(hash, v, r, s); } else { return (address(0), RecoverError.InvalidSignatureLength); } } /** * @dev Returns the address that signed a hashed message (`hash`) with * `signature`. This address can then be used for verification purposes. * * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures: * this function rejects them by requiring the `s` value to be in the lower * half order, and the `v` value to be either 27 or 28. * * IMPORTANT: `hash` _must_ be the result of a hash operation for the * verification to be secure: it is possible to craft signatures that * recover to arbitrary addresses for non-hashed data. A safe way to ensure * this is by receiving a hash of the original message (which may otherwise * be too long), and then calling {toEthSignedMessageHash} on it. */ function recover(bytes32 hash, bytes memory signature) internal pure returns (address) { (address recovered, RecoverError error) = tryRecover(hash, signature); _throwError(error); return recovered; } /** * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately. * * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures] * * _Available since v4.3._ */ function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address, RecoverError) { bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff); uint8 v = uint8((uint256(vs) >> 255) + 27); return tryRecover(hash, v, r, s); } /** * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately. * * _Available since v4.2._ */ function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) { (address recovered, RecoverError error) = tryRecover(hash, r, vs); _throwError(error); return recovered; } /** * @dev Overload of {ECDSA-tryRecover} that receives the `v`, * `r` and `s` signature fields separately. * * _Available since v4.3._ */ function tryRecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address, RecoverError) { // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most // signatures from current libraries generate a unique signature with an s-value in the lower half order. // // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept // these malleable signatures as well. if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) { return (address(0), RecoverError.InvalidSignatureS); } // If the signature is valid (and not malleable), return the signer address address signer = ecrecover(hash, v, r, s); if (signer == address(0)) { return (address(0), RecoverError.InvalidSignature); } return (signer, RecoverError.NoError); } /** * @dev Overload of {ECDSA-recover} that receives the `v`, * `r` and `s` signature fields separately. */ function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) { (address recovered, RecoverError error) = tryRecover(hash, v, r, s); _throwError(error); return recovered; } /** * @dev Returns an Ethereum Signed Message, created from a `hash`. This * produces hash corresponding to the one signed with the * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] * JSON-RPC method as part of EIP-191. * * See {recover}. */ function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32 message) { // 32 is the length in bytes of hash, // enforced by the type signature above /// @solidity memory-safe-assembly assembly { mstore(0x00, "\x19Ethereum Signed Message:\n32") mstore(0x1c, hash) message := keccak256(0x00, 0x3c) } } /** * @dev Returns an Ethereum Signed Message, created from `s`. This * produces hash corresponding to the one signed with the * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] * JSON-RPC method as part of EIP-191. * * See {recover}. */ function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) { return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n", Strings.toString(s.length), s)); } /** * @dev Returns an Ethereum Signed Typed Data, created from a * `domainSeparator` and a `structHash`. This produces hash corresponding * to the one signed with the * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`] * JSON-RPC method as part of EIP-712. * * See {recover}. */ function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32 data) { /// @solidity memory-safe-assembly assembly { let ptr := mload(0x40) mstore(ptr, "\x19\x01") mstore(add(ptr, 0x02), domainSeparator) mstore(add(ptr, 0x22), structHash) data := keccak256(ptr, 0x42) } } /** * @dev Returns an Ethereum Signed Data with intended validator, created from a * `validator` and `data` according to the version 0 of EIP-191. * * See {recover}. */ function toDataWithIntendedValidatorHash(address validator, bytes memory data) internal pure returns (bytes32) { return keccak256(abi.encodePacked("\x19\x00", validator, data)); } }
TicketingParameters.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.18; import "@openzeppelin/contracts/utils/introspection/ERC165.sol"; import "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import "../../libraries/SyloUtils.sol"; import "../../interfaces/payments/ticketing/ITicketingParameters.sol"; /** * @dev Persists the parameters for the ticketing mechanism. This contract is * read by the EpochManager. Extracting the parameters into another * contract is necessary to avoid a cyclic dependency between the ticketing * and epoch contracts. */ contract TicketingParameters is ITicketingParameters, Initializable, Ownable2StepUpgradeable, ERC165 { /** @notice The value of a winning ticket in SOLO. */ uint256 public faceValue; /** * @notice The probability of a ticket winning during the start of its lifetime. * This is a uint128 value representing the numerator in the probability * ratio where 2^128 - 1 is the denominator. */ uint128 public baseLiveWinProb; /** * @notice The probability of a ticket winning after it has expired. * This is a uint128 value representing the numerator in the probability * ratio where 2^128 - 1 is the denominator. Note: Redeeming expired * tickets is currently not supported. */ uint128 public expiredWinProb; /** * @notice The length in blocks before a ticket is considered expired. * The default initialization value is 80,000. This equates * to roughly two weeks (15s per block). */ uint256 public ticketDuration; /** * @notice A percentage value representing the proportion of the base win * probability that will be decayed once a ticket has expired. * Example: 80% decayRate indicates that a ticket will decay down to 20% of its * base win probability upon reaching the block before its expiry. * The value is expressed as a fraction of 100000. */ uint32 public decayRate; event FaceValueUpdated(uint256 faceValue); event BaseLiveWinProbUpdated(uint128 baseLiveWinprob); event ExpiredWinProbUpdated(uint128 expiredWinProb); event TicketDurationUpdated(uint256 ticketDuration); event DecayRateUpdated(uint32 decayRate); error FaceValueCannotBeZero(); error TicketDurationCannotBeZero(); function initialize( uint256 _faceValue, uint128 _baseLiveWinProb, uint128 _expiredWinProb, uint32 _decayRate, uint256 _ticketDuration ) external initializer { if (_faceValue == 0) { revert FaceValueCannotBeZero(); } if (_ticketDuration == 0) { revert TicketDurationCannotBeZero(); } Ownable2StepUpgradeable.__Ownable2Step_init(); faceValue = _faceValue; baseLiveWinProb = _baseLiveWinProb; expiredWinProb = _expiredWinProb; decayRate = _decayRate; ticketDuration = _ticketDuration; } /** * @notice Returns true if the contract implements the interface defined by * `interfaceId` from ERC165. */ function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { return interfaceId == type(ITicketingParameters).interfaceId; } /** * @notice Set the face value for tickets in SOLO. Only callable by * the contract owner. * @param _faceValue The face value to set in SOLO. */ function setFaceValue(uint256 _faceValue) external onlyOwner { if (_faceValue == 0) { revert FaceValueCannotBeZero(); } faceValue = _faceValue; emit FaceValueUpdated(_faceValue); } /** * @notice Set the base live win probability of a ticket. Only callable by * the contract owner. * @param _baseLiveWinProb The probability represented as a value * between 0 to 2**128 - 1. */ function setBaseLiveWinProb(uint128 _baseLiveWinProb) external onlyOwner { baseLiveWinProb = _baseLiveWinProb; emit BaseLiveWinProbUpdated(_baseLiveWinProb); } /** * @notice Set the expired win probability of a ticket. Only callable by * the contract owner. * @param _expiredWinProb The probability represented as a value * between 0 to 2**128 - 1. */ function setExpiredWinProb(uint128 _expiredWinProb) external onlyOwner { expiredWinProb = _expiredWinProb; emit ExpiredWinProbUpdated(_expiredWinProb); } /** * @notice Set the decay rate of a ticket. Only callable by the * the contract owner. * @param _decayRate The decay rate as a percentage, where the * denominator is 10000. */ function setDecayRate(uint32 _decayRate) external onlyOwner { decayRate = _decayRate; emit DecayRateUpdated(_decayRate); } /** * @notice Set the ticket duration of a ticket. Only callable by the * contract owner. * @param _ticketDuration The duration of a ticket in number of blocks. */ function setTicketDuration(uint256 _ticketDuration) external onlyOwner { if (_ticketDuration == 0) { revert TicketDurationCannotBeZero(); } ticketDuration = _ticketDuration; emit TicketDurationUpdated(_ticketDuration); } /** * @notice Retrieve the current ticketing parameters. * @return faceValue The face value of a ticket in SOLO. * @return baseLiveWinProb The base live win probability of a ticket. * @return expiredWinProb The expired win probability of a ticket. * @return ticketDuration The duration of a ticket in number of blocks. * @return decayRate The decay rate of a ticket. */ function getTicketingParameters() external view returns (uint256, uint128, uint128, uint256, uint32) { return (faceValue, baseLiveWinProb, expiredWinProb, ticketDuration, decayRate); } }
ERC165.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol) pragma solidity ^0.8.0; import "./IERC165.sol"; /** * @dev Implementation of the {IERC165} interface. * * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check * for the additional interface id that will be supported. For example: * * ```solidity * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { * return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId); * } * ``` * * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation. */ abstract contract ERC165 is IERC165 { /** * @dev See {IERC165-supportsInterface}. */ function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { return interfaceId == type(IERC165).interfaceId; } }
SafeERC20.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/utils/SafeERC20.sol) pragma solidity ^0.8.0; import "../IERC20.sol"; import "../extensions/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; /** * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value, * non-reverting calls are assumed to be successful. */ function safeTransfer(IERC20 token, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); } /** * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful. */ 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)); } /** * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value, * non-reverting calls are assumed to be successful. */ function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal { uint256 oldAllowance = token.allowance(address(this), spender); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value)); } /** * @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value, * non-reverting calls are assumed to be successful. */ 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"); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value)); } } /** * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value, * non-reverting calls are assumed to be successful. Compatible with tokens that require the approval to be set to * 0 before setting it to a non-zero value. */ function forceApprove(IERC20 token, address spender, uint256 value) internal { bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value); if (!_callOptionalReturnBool(token, approvalCall)) { _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0)); _callOptionalReturn(token, approvalCall); } } /** * @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`. * Revert on invalid signature. */ 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"); require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation 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). * * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead. */ function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) { // 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 cannot use {Address-functionCall} here since this should return false // and not revert is the subcall reverts. (bool success, bytes memory returndata) = address(token).call(data); return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && Address.isContract(address(token)); } }
IERC165.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC165 standard, as defined in the * https://eips.ethereum.org/EIPS/eip-165[EIP]. * * Implementers can declare support of contract interfaces, which can then be * queried by others ({ERC165Checker}). * * For an implementation, see {ERC165}. */ interface IERC165 { /** * @dev Returns true if this contract implements the interface defined by * `interfaceId`. See the corresponding * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] * to learn more about how these ids are created. * * This function call must use less than 30 000 gas. */ function supportsInterface(bytes4 interfaceId) external view returns (bool); }
IRewardsManager.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.18; interface IRewardsManager { /** * @dev This type will hold the necessary information for delegated stakers * to make reward claims against their Node. Every Node will initialize * and store a new Reward Pool for each epoch they participate in. */ struct RewardPool { // Tracks the balance of the reward pool owed to the stakers uint256 stakersRewardTotal; // Tracks the block number this reward pool was initialized uint256 initializedAt; // The total active stake for the node for will be the sum of the // stakes owned by its delegators and the node's own stake. uint256 totalActiveStake; // track the cumulative reward factor as of the time the first ticket // for this pool was redeemed int128 initialCumulativeRewardFactor; } struct LastClaim { // The epoch the claim was made. uint256 claimedAt; // The stake at the time the claim was made. This is tracked as // rewards can only be claimed after an epoch has ended, but the // user's stake may have changed by then. This field tracks the // staking value before the change so the reward for that epoch // can be manually calculated. uint256 stake; } function getRewardPool( uint256 epochId, address stakee ) external view returns (RewardPool memory); function getRewardPoolKey(uint256 epochId, address stakee) external pure returns (bytes32); function getRewardPoolStakersTotal( uint256 epochId, address stakee ) external view returns (uint256); function getRewardPoolActiveStake( uint256 epochId, address stakee ) external view returns (uint256); function getPendingRewards(address stakee) external view returns (uint256); function getLastClaim(address stakee, address staker) external view returns (LastClaim memory); function getTotalEpochRewards(uint256 epochId) external view returns (uint256); function getTotalEpochStakingRewards(uint256 epochId) external view returns (uint256); function initializeNextRewardPool(address stakee) external; function incrementRewardPool(address stakee, uint256 amount) external; function claimStakingRewards(address stakee) external returns (uint256); function updatePendingRewards(address stakee, address staker) external; }
IERC20Permit.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/extensions/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); }
AddressUpgradeable.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol) pragma solidity ^0.8.1; /** * @dev Collection of functions related to the address type */ library AddressUpgradeable { /** * @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 * * Furthermore, `isContract` will also return true if the target contract within * the same transaction is already scheduled for destruction by `SELFDESTRUCT`, * which only has an effect at the end of a transaction. * ==== * * [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://consensys.net/diligence/blog/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.8.0/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); } } }
ContextUpgradeable.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/Context.sol) pragma solidity ^0.8.0; import "../proxy/utils/Initializable.sol"; /** * @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 ContextUpgradeable is Initializable { function __Context_init() internal onlyInitializing { } function __Context_init_unchained() internal onlyInitializing { } function _msgSender() internal view virtual returns (address) { return msg.sender; } function _msgData() internal view virtual returns (bytes calldata) { return msg.data; } /** * @dev This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps */ uint256[50] private __gap; }
ITicketingParameters.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.18; interface ITicketingParameters { function setFaceValue(uint256 _faceValue) external; function setBaseLiveWinProb(uint128 _baseLiveWinProb) external; function setExpiredWinProb(uint128 _expiredWinProb) external; function setDecayRate(uint32 _decayRate) external; function setTicketDuration(uint256 _ticketDuration) external; function getTicketingParameters() external view returns (uint256, uint128, uint128, uint256, uint32); }
OwnableUpgradeable.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol) pragma solidity ^0.8.0; import "../utils/ContextUpgradeable.sol"; import "../proxy/utils/Initializable.sol"; /** * @dev Contract module which provides a basic access control mechanism, where * there is an account (an owner) that can be granted exclusive access to * specific functions. * * By default, the owner account will be the one that deploys the contract. This * can later be changed with {transferOwnership}. * * This module is used through inheritance. It will make available the modifier * `onlyOwner`, which can be applied to your functions to restrict their use to * the owner. */ abstract contract OwnableUpgradeable is Initializable, ContextUpgradeable { address private _owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev Initializes the contract setting the deployer as the initial owner. */ function __Ownable_init() internal onlyInitializing { __Ownable_init_unchained(); } function __Ownable_init_unchained() internal onlyInitializing { _transferOwnership(_msgSender()); } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { _checkOwner(); _; } /** * @dev Returns the address of the current owner. */ function owner() public view virtual returns (address) { return _owner; } /** * @dev Throws if the sender is not the owner. */ function _checkOwner() internal view virtual { require(owner() == _msgSender(), "Ownable: caller is not the owner"); } /** * @dev Leaves the contract without owner. It will not be possible to call * `onlyOwner` functions. Can only be called by the current owner. * * NOTE: Renouncing ownership will leave the contract without an owner, * thereby disabling any functionality that is only available to the owner. */ function renounceOwnership() public virtual onlyOwner { _transferOwnership(address(0)); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Can only be called by the current owner. */ function transferOwnership(address newOwner) public virtual onlyOwner { require(newOwner != address(0), "Ownable: new owner is the zero address"); _transferOwnership(newOwner); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Internal function without access restriction. */ function _transferOwnership(address newOwner) internal virtual { address oldOwner = _owner; _owner = newOwner; emit OwnershipTransferred(oldOwner, newOwner); } /** * @dev This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps */ uint256[49] private __gap; }
Initializable.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (proxy/utils/Initializable.sol) pragma solidity ^0.8.2; import "../../utils/AddressUpgradeable.sol"; /** * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect. * * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in * case an upgrade adds a module that needs to be initialized. * * For example: * * [.hljs-theme-light.nopadding] * ```solidity * contract MyToken is ERC20Upgradeable { * function initialize() initializer public { * __ERC20_init("MyToken", "MTK"); * } * } * * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable { * function initializeV2() reinitializer(2) public { * __ERC20Permit_init("MyToken"); * } * } * ``` * * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}. * * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity. * * [CAUTION] * ==== * Avoid leaving a contract uninitialized. * * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed: * * [.hljs-theme-light.nopadding] * ``` * /// @custom:oz-upgrades-unsafe-allow constructor * constructor() { * _disableInitializers(); * } * ``` * ==== */ abstract contract Initializable { /** * @dev Indicates that the contract has been initialized. * @custom:oz-retyped-from bool */ uint8 private _initialized; /** * @dev Indicates that the contract is in the process of being initialized. */ bool private _initializing; /** * @dev Triggered when the contract has been initialized or reinitialized. */ event Initialized(uint8 version); /** * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope, * `onlyInitializing` functions can be used to initialize parent contracts. * * Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a * constructor. * * Emits an {Initialized} event. */ modifier initializer() { bool isTopLevelCall = !_initializing; require( (isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1), "Initializable: contract is already initialized" ); _initialized = 1; if (isTopLevelCall) { _initializing = true; } _; if (isTopLevelCall) { _initializing = false; emit Initialized(1); } } /** * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be * used to initialize parent contracts. * * A reinitializer may be used after the original initialization step. This is essential to configure modules that * are added through upgrades and that require initialization. * * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer` * cannot be nested. If one is invoked in the context of another, execution will revert. * * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in * a contract, executing them in the right order is up to the developer or operator. * * WARNING: setting the version to 255 will prevent any future reinitialization. * * Emits an {Initialized} event. */ modifier reinitializer(uint8 version) { require(!_initializing && _initialized < version, "Initializable: contract is already initialized"); _initialized = version; _initializing = true; _; _initializing = false; emit Initialized(version); } /** * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the * {initializer} and {reinitializer} modifiers, directly or indirectly. */ modifier onlyInitializing() { require(_initializing, "Initializable: contract is not initializing"); _; } /** * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call. * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized * to any version. It is recommended to use this to lock implementation contracts that are designed to be called * through proxies. * * Emits an {Initialized} event the first time it is successfully executed. */ function _disableInitializers() internal virtual { require(!_initializing, "Initializable: contract is initializing"); if (_initialized != type(uint8).max) { _initialized = type(uint8).max; emit Initialized(type(uint8).max); } } /** * @dev Returns the highest version that has been initialized. See {reinitializer}. */ function _getInitializedVersion() internal view returns (uint8) { return _initialized; } /** * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}. */ function _isInitializing() internal view returns (bool) { return _initializing; } }
IERC20Metadata.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol) pragma solidity ^0.8.0; import "../IERC20.sol"; /** * @dev Interface for the optional metadata functions from the ERC20 standard. * * _Available since v4.1._ */ interface IERC20Metadata is IERC20 { /** * @dev Returns the name of the token. */ function name() external view returns (string memory); /** * @dev Returns the symbol of the token. */ function symbol() external view returns (string memory); /** * @dev Returns the decimals places of the token. */ function decimals() external view returns (uint8); }
Ownable2StepUpgradeable.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable2Step.sol) pragma solidity ^0.8.0; import "./OwnableUpgradeable.sol"; import "../proxy/utils/Initializable.sol"; /** * @dev Contract module which provides access control mechanism, where * there is an account (an owner) that can be granted exclusive access to * specific functions. * * By default, the owner account will be the one that deploys the contract. This * can later be changed with {transferOwnership} and {acceptOwnership}. * * This module is used through inheritance. It will make available all functions * from parent (Ownable). */ abstract contract Ownable2StepUpgradeable is Initializable, OwnableUpgradeable { function __Ownable2Step_init() internal onlyInitializing { __Ownable_init_unchained(); } function __Ownable2Step_init_unchained() internal onlyInitializing { } address private _pendingOwner; event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner); /** * @dev Returns the address of the pending owner. */ function pendingOwner() public view virtual returns (address) { return _pendingOwner; } /** * @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one. * Can only be called by the current owner. */ function transferOwnership(address newOwner) public virtual override onlyOwner { _pendingOwner = newOwner; emit OwnershipTransferStarted(owner(), newOwner); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner. * Internal function without access restriction. */ function _transferOwnership(address newOwner) internal virtual override { delete _pendingOwner; super._transferOwnership(newOwner); } /** * @dev The new owner accepts the ownership transfer. */ function acceptOwnership() public virtual { address sender = _msgSender(); require(pendingOwner() == sender, "Ownable2Step: caller is not the new owner"); _transferOwnership(sender); } /** * @dev This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps */ uint256[49] private __gap; }
Gas Token: