Address Contract Verified
Address
0xC1459fcFe23d5db9Ddb04935ab7a426Bd398EAb0
Balance
0 ETH
Nonce
1
Code Size
15772 bytes
Creator
0x6Ac005d9...cE42 at tx 0xfa512c6b...229c0a
Indexed Transactions
0
Contract Bytecode
15772 bytes
0x6080604052600436106102ab575f3560e01c8063986e189011610165578063be7756dd116100c6578063cf6eefb71161007c578063d602b9fd11610062578063d602b9fd14610818578063f0141d841461082c578063f211abcc14610847575f80fd5b8063cf6eefb7146107a1578063d547741f146107f9575f80fd5b8063c9371327116100ac578063c93713271461075b578063cc8463c814610779578063cefc14291461078d575f80fd5b8063be7756dd14610728578063c4fa8bf41461073c575f80fd5b8063ab8b2f341161011b578063af933b5711610101578063af933b57146106b0578063b1529fbf146106cf578063b222ec71146106f6575f80fd5b8063ab8b2f3414610669578063af2d32bb1461067d575f80fd5b80639e87a5cd1161014b5780639e87a5cd146105f1578063a1eda53c14610623578063a217fddf14610656575f80fd5b8063986e1890146105be57806398793ff9146105dd575f80fd5b8063634e93da1161020f57806384ef8ffc116101c557806391d14854116101ab57806391d148541461053657806393f22b9d1461058757806394218074146105a6575f80fd5b806384ef8ffc146104f85780638da5cb5b14610522575f80fd5b806375b238fc116101f557806375b238fc1461045e57806378399b0b14610491578063831bcbf3146104e4575f80fd5b8063634e93da14610420578063649a5ec71461043f575f80fd5b8063248a9ca31161026457806336568abe1161024a57806336568abe146103c35780633f5bd8a9146103e257806363174c7c14610401575f80fd5b8063248a9ca3146103685780632f2ff15d146103a4575f80fd5b80630aa6220b116102945780630aa6220b1461030b5780630b6313a614610321578063191fe1ed14610340575f80fd5b806301ffc9a7146102af578063022d63fb146102e3575b5f80fd5b3480156102ba575f80fd5b506102ce6102c9366004613267565b61087c565b60405190151581526020015b60405180910390f35b3480156102ee575f80fd5b50620697805b60405165ffffffffffff90911681526020016102da565b348015610316575f80fd5b5061031f6108d7565b005b34801561032c575f80fd5b5061031f61033b3660046132dd565b6108ec565b34801561034b575f80fd5b5061035561271081565b60405161ffff90911681526020016102da565b348015610373575f80fd5b50610396610382366004613312565b5f9081526002602052604090206001015490565b6040519081526020016102da565b3480156103af575f80fd5b5061031f6103be366004613329565b610a83565b3480156103ce575f80fd5b5061031f6103dd366004613329565b610ac8565b3480156103ed575f80fd5b506103966103fc36600461339f565b610bd2565b34801561040c575f80fd5b5061031f61041b366004613404565b610bf6565b34801561042b575f80fd5b5061031f61043a36600461341f565b610d31565b34801561044a575f80fd5b5061031f61045936600461343a565b610d44565b348015610469575f80fd5b506103967fa49807205ce4d355092ef5a8a18f56e8913cf4a201fbe287825b095693c2177581565b34801561049c575f80fd5b507f0000000000000000000000004305fb66699c3b2702d4d05cf36551390a4c69c65b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016102da565b3480156104ef575f80fd5b50600554610396565b348015610503575f80fd5b5060045473ffffffffffffffffffffffffffffffffffffffff166104bf565b34801561052d575f80fd5b506104bf610d57565b348015610541575f80fd5b506102ce610550366004613329565b5f91825260026020908152604080842073ffffffffffffffffffffffffffffffffffffffff93909316845291905290205460ff1690565b348015610592575f80fd5b5061031f6105a136600461345f565b610d7c565b3480156105b1575f80fd5b5060065461ffff16610355565b3480156105c9575f80fd5b5061031f6105d8366004613312565b610e55565b3480156105e8575f80fd5b50610355610eb4565b3480156105fc575f80fd5b507f0000000000000000000000005f4ec3df9cbd43714fe2740f5e3616155c5b84196104bf565b34801561062e575f80fd5b50610637610ec4565b6040805165ffffffffffff9384168152929091166020830152016102da565b348015610661575f80fd5b506103965f81565b348015610674575f80fd5b50610396610f3e565b348015610688575f80fd5b506103967f800000000000000000000000000000000000000000000000000000000000000081565b3480156106bb575f80fd5b5061031f6106ca36600461341f565b610fd4565b3480156106da575f80fd5b505f5460405167ffffffffffffffff90911681526020016102da565b348015610701575f80fd5b507fff61491a931112ddf1bd8147cd1b641375f79f5825126d665480874634fd0ace610396565b348015610733575f80fd5b50600154610396565b348015610747575f80fd5b5061031f610756366004613312565b6110fe565b348015610766575f80fd5b5060065462010000900461ffff16610355565b348015610784575f80fd5b506102f461115d565b348015610798575f80fd5b5061031f6111fa565b3480156107ac575f80fd5b506003546040805173ffffffffffffffffffffffffffffffffffffffff831681527401000000000000000000000000000000000000000090920465ffffffffffff166020830152016102da565b348015610804575f80fd5b5061031f610813366004613329565b611256565b348015610823575f80fd5b5061031f611297565b348015610837575f80fd5b50604051601281526020016102da565b61085a610855366004613495565b6112a9565b60408051825181526020808401519082015291810151908201526060016102da565b5f7fffffffff0000000000000000000000000000000000000000000000000000000082167f314987860000000000000000000000000000000000000000000000000000000014806108d157506108d1826113da565b92915050565b5f6108e181611470565b6108e961147a565b50565b7fa49807205ce4d355092ef5a8a18f56e8913cf4a201fbe287825b095693c2177561091681611470565b6115188361ffff161115610956576040517f14a7fd0f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8173ffffffffffffffffffffffffffffffffffffffff16634aee7f9f6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561099f573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906109c39190613501565b6fffffffffffffffffffffffffffffffff168361ffff161015610a12576040517f14a7fd0f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600680547fffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000ffff166201000061ffff8616908102919091179091556040519081527fdd0f8a3132b698ac11a3f9bcd9f8e2799f39bc4f08b761827d9e24e0ace6edd59060200160405180910390a1505050565b81610aba576040517f3fc3c27a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610ac48282611486565b5050565b81158015610af0575060045473ffffffffffffffffffffffffffffffffffffffff8281169116145b15610bc85760035473ffffffffffffffffffffffffffffffffffffffff81169074010000000000000000000000000000000000000000900465ffffffffffff1681151580610b44575065ffffffffffff8116155b80610b5757504265ffffffffffff821610155b15610b9d576040517f19ca5ebb00000000000000000000000000000000000000000000000000000000815265ffffffffffff821660048201526024015b60405180910390fd5b5050600380547fffffffffffff000000000000ffffffffffffffffffffffffffffffffffffffff1690555b610ac482826114b0565b5f610bdd8484611509565b15610bef57610bec8484611528565b90505b9392505050565b7fa49807205ce4d355092ef5a8a18f56e8913cf4a201fbe287825b095693c21775610c2081611470565b600a8267ffffffffffffffff161015610c71576040517fd44ff2b100000000000000000000000000000000000000000000000000000000815267ffffffffffffffff83166004820152602401610b94565b6102588267ffffffffffffffff161115610cc3576040517fd44ff2b100000000000000000000000000000000000000000000000000000000815267ffffffffffffffff83166004820152602401610b94565b5f80547fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000001667ffffffffffffffff84169081179091556040519081527f241c93c63d73e9b5365f977482dd8048b228ee742453ca59b21448b4a6a4b5b8906020015b60405180910390a15050565b5f610d3b81611470565b610ac482611665565b5f610d4e81611470565b610ac4826116e4565b5f610d7760045473ffffffffffffffffffffffffffffffffffffffff1690565b905090565b7fa49807205ce4d355092ef5a8a18f56e8913cf4a201fbe287825b095693c21775610da681611470565b610db36127106002613549565b61ffff168261ffff161115610df4576040517fc18fce8e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600680547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00001661ffff84169081179091556040519081527f444babe03f2cf1ecf2d92f386b4b36c9bc9a3cb1874b3abe1c5d5a55291395a890602001610d25565b7fa49807205ce4d355092ef5a8a18f56e8913cf4a201fbe287825b095693c21775610e7f81611470565b60058290556040518281527f42e08bed13b165e657b77ec405fe78a7cc5893381b77bf557b457f00ee8fa10a90602001610d25565b610ec16127106002613549565b81565b6004545f907a010000000000000000000000000000000000000000000000000000900465ffffffffffff168015158015610f0657504265ffffffffffff821610155b610f11575f80610f36565b60045474010000000000000000000000000000000000000000900465ffffffffffff16815b915091509091565b5f7f0000000000000000000000005f4ec3df9cbd43714fe2740f5e3616155c5b841973ffffffffffffffffffffffffffffffffffffffff1663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015610fa8573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610fcc9190613566565b60ff16905090565b7fa49807205ce4d355092ef5a8a18f56e8913cf4a201fbe287825b095693c21775610ffe81611470565b73ffffffffffffffffffffffffffffffffffffffff821661104b576040517f80bc7b9100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f8273ffffffffffffffffffffffffffffffffffffffff16476040515f6040518083038185875af1925050503d805f81146110a1576040519150601f19603f3d011682016040523d82523d5f602084013e6110a6565b606091505b50509050806110f9576040517fb5ed065300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff84166004820152602401610b94565b505050565b7fa49807205ce4d355092ef5a8a18f56e8913cf4a201fbe287825b095693c2177561112881611470565b60018290556040518281527fb253a4f7edf0a3926be13935f7bfcce96181bceff9941569c8124dc09d0ecc6990602001610d25565b6004545f907a010000000000000000000000000000000000000000000000000000900465ffffffffffff16801515801561119e57504265ffffffffffff8216105b6111d0576003547a010000000000000000000000000000000000000000000000000000900465ffffffffffff166111f4565b60045474010000000000000000000000000000000000000000900465ffffffffffff165b91505090565b60035473ffffffffffffffffffffffffffffffffffffffff1633811461124e576040517fc22c8022000000000000000000000000000000000000000000000000000000008152336004820152602401610b94565b6108e961174c565b8161128d576040517f3fc3c27a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610ac4828261183d565b5f6112a181611470565b6108e9611861565b6112ca60405180606001604052805f81526020015f81526020015f81525090565b5f6112d8878787878761186b565b90505f7f0000000000000000000000007f39c581f595b53c5cb19bd0b3f8da6c935e2ca073ffffffffffffffffffffffffffffffffffffffff1663035faf826040518163ffffffff1660e01b8152600401602060405180830381865afa158015611344573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906113689190613586565b90506040518060600160405280670de0b6b3a764000083855f015161138d919061359d565b61139791906135e1565b8152602001670de0b6b3a76400008385602001516113b5919061359d565b6113bf91906135e1565b81526020018360400151815250925050505b95945050505050565b5f7fffffffff0000000000000000000000000000000000000000000000000000000082167f7965db0b0000000000000000000000000000000000000000000000000000000014806108d157507f01ffc9a7000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008316146108d1565b6108e98133611a4a565b6114845f80611ad1565b565b5f828152600260205260409020600101546114a081611470565b6114aa8383611c2a565b50505050565b73ffffffffffffffffffffffffffffffffffffffff811633146114ff576040517f6697b23200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6110f98282611ce8565b5f6020821161151957505f6108d1565b50503560e01c63504e41551490565b6040805160018082528183019092525f91829190816020015b606081526020019060019003908161154157905050905083838080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201829052508551869450909250151590506115a1576115a1613621565b60209081029190910101526040517fd47eed4500000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000004305fb66699c3b2702d4d05cf36551390a4c69c6169063d47eed459061161e908490600401613704565b602060405180830381865afa158015611639573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061165d9190613586565b949350505050565b5f61166e61115d565b61167742611d49565b6116819190613716565b905061168d8282611d98565b60405165ffffffffffff8216815273ffffffffffffffffffffffffffffffffffffffff8316907f3377dc44241e779dd06afab5b788a35ca5f3b778836e2990bdb26a2a4b2e5ed69060200160405180910390a25050565b5f6116ee82611e33565b6116f742611d49565b6117019190613716565b905061170d8282611ad1565b6040805165ffffffffffff8085168252831660208201527ff1038c18cf84a56e432fdbfaf746924b7ea511dfe03a6506a0ceba4888788d9b9101610d25565b60035473ffffffffffffffffffffffffffffffffffffffff81169074010000000000000000000000000000000000000000900465ffffffffffff1680158061179c57504265ffffffffffff821610155b156117dd576040517f19ca5ebb00000000000000000000000000000000000000000000000000000000815265ffffffffffff82166004820152602401610b94565b6118055f61180060045473ffffffffffffffffffffffffffffffffffffffff1690565b611ce8565b506118105f83611c2a565b5050600380547fffffffffffff000000000000000000000000000000000000000000000000000016905550565b5f8281526002602052604090206001015461185781611470565b6114aa8383611ce8565b6114845f80611d98565b61188c60405180606001604052805f81526020015f81526020015f81525090565b5f84600a81111561189f5761189f613734565b036118d4576006546118cd908490849088906002906118c89062010000900461ffff1683613761565b611e7a565b90506113d1565b600184600a8111156118e8576118e8613734565b036118f9576118cd83836002611ee9565b600384600a81111561190d5761190d613734565b0361191f576118cd83838760016120b6565b600584600a81111561193357611933613734565b03611944576118cd8383875f6120b6565b600784600a81111561195857611958613734565b03611969576118cd8383875f6120b6565b600984600a81111561197d5761197d613734565b0361198f576118cd83838760016120b6565b600a84600a8111156119a3576119a3613734565b036119b6576118cd83835f60025f611e7a565b600284600a8111156119ca576119ca613734565b036119db576118cd83836002611ee9565b600484600a8111156119ef576119ef613734565b03611a00576118cd83836002611ee9565b600684600a811115611a1457611a14613734565b03611a25576118cd83836002611ee9565b600884600a811115611a3957611a39613734565b036113d1576118cd83836002611ee9565b5f82815260026020908152604080832073ffffffffffffffffffffffffffffffffffffffff8516845290915290205460ff16610ac4576040517fe2517d3f00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8216600482015260248101839052604401610b94565b6004547a010000000000000000000000000000000000000000000000000000900465ffffffffffff168015611ba5574265ffffffffffff82161015611b7c576004546003805479ffffffffffffffffffffffffffffffffffffffffffffffffffff167401000000000000000000000000000000000000000090920465ffffffffffff167a01000000000000000000000000000000000000000000000000000002919091179055611ba5565b6040517f2b1fa2edafe6f7b9e97c1a9e0c3660e645beb2dcaa2d45bdbf9beaf5472e1ec5905f90a15b506004805473ffffffffffffffffffffffffffffffffffffffff167401000000000000000000000000000000000000000065ffffffffffff9485160279ffffffffffffffffffffffffffffffffffffffffffffffffffff16177a0100000000000000000000000000000000000000000000000000009290931691909102919091179055565b5f82611cde575f611c5060045473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff1614611c9d576040517f3fc3c27a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600480547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff84161790555b610bef838361219f565b5f82158015611d11575060045473ffffffffffffffffffffffffffffffffffffffff8381169116145b15611d3f57600480547fffffffffffffffffffffffff00000000000000000000000000000000000000001690555b610bef838361229c565b5f65ffffffffffff821115611d94576040517f6dfcc6500000000000000000000000000000000000000000000000000000000081526030600482015260248101839052604401610b94565b5090565b600380547401000000000000000000000000000000000000000065ffffffffffff84811682027fffffffffffff0000000000000000000000000000000000000000000000000000841673ffffffffffffffffffffffffffffffffffffffff8816171790935590041680156110f9576040517f8886ebfc4259abdbc16601dd8fb5678e54878f47b3c34836cfc51154a9605109905f90a1505050565b5f80611e3d61115d565b90508065ffffffffffff168365ffffffffffff1611611e6557611e608382613789565b610bef565b610bef65ffffffffffff841662069780612359565b611e9b60405180606001604052805f81526020015f81526020015f81525090565b6fffffffffffffffffffffffffffffffff841615611ec357600554611ec09085613761565b93505b5f611ed2878787601287612368565b9050611ede81856123ef565b979650505050505050565b611f0a60405180606001604052805f81526020015f81526020015f81525090565b8215611f2457611f1d84845f855f611e7a565b9050610bef565b3415611f5c576040517fd7d2537300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f611f6760126124dc565b90505f611f7460126125ec565b90508060400151826020015111611fea57600154611f9290426137a7565b81604001511015611fd75780604001516040517f18802ca0000000000000000000000000000000000000000000000000000000008152600401610b9491815260200190565b611fe181856123ef565b92505050610bef565b81517f80000000000000000000000000000000000000000000000000000000000000000161204c5781602001516040517f18802ca0000000000000000000000000000000000000000000000000000000008152600401610b9491815260200190565b81515f1261208c5781516040517fd3b9d7b80000000000000000000000000000000000000000000000000000000081526004810191909152602401610b94565b50604080516060810182528251815282516020808301919091529092015190820152949350505050565b6120d760405180606001604052805f81526020015f81526020015f81525090565b6006545f906120f09062010000900461ffff1685613761565b9050806fffffffffffffffffffffffffffffffff164211612120576121188686868685611e7a565b91505061165d565b3415612158576040517fd7d2537300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f612165868801886137d1565b90505f6121728383612782565b60408051606081018252825181528251602080830191909152909201519082015298975050505050505050565b5f82815260026020908152604080832073ffffffffffffffffffffffffffffffffffffffff8516845290915281205460ff16612295575f83815260026020908152604080832073ffffffffffffffffffffffffffffffffffffffff86168452909152902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790556122333390565b73ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16847f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45060016108d1565b505f6108d1565b5f82815260026020908152604080832073ffffffffffffffffffffffffffffffffffffffff8516845290915281205460ff1615612295575f83815260026020908152604080832073ffffffffffffffffffffffffffffffffffffffff8616808552925280832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016905551339286917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45060016108d1565b5f828218828410028218610bef565b61238960405180606001604052805f81526020015f81526020015f81525090565b5f61239687878786612a33565b90505f816040015160030b13156123e55760408082015190517f19c6ad6600000000000000000000000000000000000000000000000000000000815260039190910b6004820152602401610b94565b611ede8185612edb565b61241060405180606001604052805f81526020015f81526020015f81525090565b600182600281111561242457612424613734565b036124715760065460208401515f91612710916124459161ffff169061359d565b61244f91906135e1565b84519091508110612463576001825261246b565b835181900382525b506124c7565b5f82600281111561248457612484613734565b036124c2576006546020840151612710916124a59161ffff9091169061359d565b6124af91906135e1565b83516124bb91906137ec565b81526124c7565b825181525b60408084015190820152915160208301525090565b604080518082019091525f80825260208201525f7f0000000000000000000000005f4ec3df9cbd43714fe2740f5e3616155c5b841973ffffffffffffffffffffffffffffffffffffffff1663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015612559573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061257d9190613566565b9050612587612fa3565b80519092507f8000000000000000000000000000000000000000000000000000000000000000016125b85750919050565b6125c381600a613920565b6125ce84600a61392e565b83516125da9190613939565b6125e49190613984565b825250919050565b61260d60405180606001604052805f81526020015f81526020015f81525090565b604080516080810182525f80825260208201819052818301819052606082015290517f96834ad30000000000000000000000000000000000000000000000000000000081527fff61491a931112ddf1bd8147cd1b641375f79f5825126d665480874634fd0ace600482015273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000004305fb66699c3b2702d4d05cf36551390a4c69c616906396834ad390602401608060405180830381865afa925050508015612710575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016820190925261270d91810190613aed565b60015b156127185790505b5f815f015160070b1361272b5750919050565b5f816040015160030b13156127785760408082015190517f19c6ad6600000000000000000000000000000000000000000000000000000000815260039190910b6004820152602401610b94565b610bef8184612edb565b604080518082019091525f80825260208201526127a060128361309c565b90505f815f0151136127e45780516040517fd3b9d7b80000000000000000000000000000000000000000000000000000000081526004810191909152602401610b94565b5f73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000005f4ec3df9cbd43714fe2740f5e3616155c5b841916639a6fc8f561282c600186613b07565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b16815269ffffffffffffffffffff909116600482015260240160a060405180830381865afa158015612889573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906128ad9190613b29565b509350505050805f036129915761ffff604084901c165f6128db69ffff000000000000000086166001613b7d565b90508469ffffffffffffffffffff168169ffffffffffffffffffff161461292e576040517f649fc61100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60015461294d906fffffffffffffffffffffffffffffffff88166137ec565b8460200151111561298a576040517f649fc61100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50506129dd565b836fffffffffffffffffffffffffffffffff168111156129dd576040517f649fc61100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b836fffffffffffffffffffffffffffffffff16826020015111612a2c576040517f649fc61100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5092915050565b604080516080810182525f80825260208201819052818301819052606082018190528251600180825281850190945291929091908160200160208202803683370190505090507fff61491a931112ddf1bd8147cd1b641375f79f5825126d665480874634fd0ace815f81518110612aac57612aac613621565b60209081029190910101526040805160018082528183019092525f91816020015b6060815260200190600190039081612acd57905050905086868080601f0160208091040260200160405190810160405280939291908181526020018383808284375f920182905250855186945090925015159050612b2d57612b2d613621565b60200260200101819052505f7f0000000000000000000000004305fb66699c3b2702d4d05cf36551390a4c69c673ffffffffffffffffffffffffffffffffffffffff1663d47eed45836040518263ffffffff1660e01b8152600401612b929190613704565b602060405180830381865afa158015612bad573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612bd19190613586565b9050662386f26fc10000811115612c17576040517f2ff94f9b00000000000000000000000000000000000000000000000000000000815260048101829052602401610b94565b803414612c50576040517fd7d2537300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6060866fffffffffffffffffffffffffffffffff165f03612d4d575f5473ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000004305fb66699c3b2702d4d05cf36551390a4c69c61690634716e9c590849086908890612cc59067ffffffffffffffff1642613b9f565b426040518663ffffffff1660e01b8152600401612ce59493929190613bbf565b5f6040518083038185885af1158015612d00573d5f803e3d5ffd5b50505050506040513d5f823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201604052612d469190810190613c3b565b9050612e2b565b6040517faccca7f900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000004305fb66699c3b2702d4d05cf36551390a4c69c6169063accca7f9908490612dc790879089908d908d90600401613bbf565b5f6040518083038185885af1158015612de2573d5f803e3d5ffd5b50505050506040513d5f823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201604052612e289190810190613c3b565b90505b5f815f81518110612e3e57612e3e613621565b6020026020010151602001515f015160070b13612eaf57805f81518110612e6757612e67613621565b6020908102919091018101510151516040517fd3b9d7b800000000000000000000000000000000000000000000000000000000815260079190910b6004820152602401610b94565b805f81518110612ec157612ec1613621565b602002602001015160200151945050505050949350505050565b612efc60405180606001604052805f81526020015f81526020015f81525090565b5f8360400151612f0b90613d2a565b63ffffffff169050604051806060016040528082600a612f2b919061392e565b612f3686600a61392e565b8751612f45919060070b61359d565b612f4f91906135e1565b8152602001612f5f83600a61392e565b612f6a86600a61392e565b876020015167ffffffffffffffff16612f83919061359d565b612f8d91906135e1565b8152602001856060015181525091505092915050565b604080518082019091525f80825260208201525f807f0000000000000000000000005f4ec3df9cbd43714fe2740f5e3616155c5b841973ffffffffffffffffffffffffffffffffffffffff1663feaf968c6040518163ffffffff1660e01b815260040160a060405180830381865afa158015613021573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906130459190613b29565b509350509250506001544261305a91906137a7565b811015613085577f800000000000000000000000000000000000000000000000000000000000000091505b604080518082019091529182526020820152919050565b604080518082019091525f80825260208201525f7f0000000000000000000000005f4ec3df9cbd43714fe2740f5e3616155c5b841973ffffffffffffffffffffffffffffffffffffffff1663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015613119573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061313d9190613566565b90506131488361318f565b91505f825f01511361315a57506108d1565b61316581600a613920565b61317085600a61392e565b835161317c9190613939565b6131869190613984565b82525092915050565b6040805180820182525f808252602082015290517f9a6fc8f500000000000000000000000000000000000000000000000000000000815269ffffffffffffffffffff8316600482015273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000005f4ec3df9cbd43714fe2740f5e3616155c5b84191690639a6fc8f59060240160a060405180830381865afa158015613234573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906132589190613b29565b50602085015250825250919050565b5f60208284031215613277575f80fd5b81357fffffffff0000000000000000000000000000000000000000000000000000000081168114610bef575f80fd5b803561ffff811681146132b7575f80fd5b919050565b73ffffffffffffffffffffffffffffffffffffffff811681146108e9575f80fd5b5f80604083850312156132ee575f80fd5b6132f7836132a6565b91506020830135613307816132bc565b809150509250929050565b5f60208284031215613322575f80fd5b5035919050565b5f806040838503121561333a575f80fd5b823591506020830135613307816132bc565b5f8083601f84011261335c575f80fd5b50813567ffffffffffffffff811115613373575f80fd5b60208301915083602082850101111561338a575f80fd5b9250929050565b8035600b81106132b7575f80fd5b5f805f604084860312156133b1575f80fd5b833567ffffffffffffffff8111156133c7575f80fd5b6133d38682870161334c565b90945092506133e6905060208501613391565b90509250925092565b67ffffffffffffffff811681146108e9575f80fd5b5f60208284031215613414575f80fd5b8135610bef816133ef565b5f6020828403121561342f575f80fd5b8135610bef816132bc565b5f6020828403121561344a575f80fd5b813565ffffffffffff81168114610bef575f80fd5b5f6020828403121561346f575f80fd5b610bef826132a6565b6fffffffffffffffffffffffffffffffff811681146108e9575f80fd5b5f805f805f608086880312156134a9575f80fd5b8535945060208601356134bb81613478565b93506134c960408701613391565b9250606086013567ffffffffffffffff8111156134e4575f80fd5b6134f08882890161334c565b969995985093965092949392505050565b5f60208284031215613511575f80fd5b8151610bef81613478565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b61ffff8181168382160290811690818114612a2c57612a2c61351c565b5f60208284031215613576575f80fd5b815160ff81168114610bef575f80fd5b5f60208284031215613596575f80fd5b5051919050565b80820281158282048414176108d1576108d161351c565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b5f826135ef576135ef6135b4565b500490565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b5f82825180855260208501945060208160051b830101602085015f5b838110156136f8577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0858403018852815180518085528060208301602087015e5f6020828701015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8301168601019450505060208201915060208801975060018101905061366a565b50909695505050505050565b602081525f610bef602083018461364e565b65ffffffffffff81811683821601908111156108d1576108d161351c565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b6fffffffffffffffffffffffffffffffff81811683821601908111156108d1576108d161351c565b65ffffffffffff82811682821603908111156108d1576108d161351c565b818103818111156108d1576108d161351c565b69ffffffffffffffffffff811681146108e9575f80fd5b5f602082840312156137e1575f80fd5b8135610bef816137ba565b808201808211156108d1576108d161351c565b6001815b600184111561383a5780850481111561381e5761381e61351c565b600184161561382c57908102905b60019390931c928002613803565b935093915050565b5f82613850575060016108d1565b8161385c57505f6108d1565b8160018114613872576002811461387c57613898565b60019150506108d1565b60ff84111561388d5761388d61351c565b50506001821b6108d1565b5060208310610133831016604e8410600b84101617156138bb575081810a6108d1565b6138e67fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff84846137ff565b807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048211156139185761391861351c565b029392505050565b5f610bef60ff841683613842565b5f610bef8383613842565b8082025f82127f8000000000000000000000000000000000000000000000000000000000000000841416156139705761397061351c565b81810583148215176108d1576108d161351c565b5f82613992576139926135b4565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83147f8000000000000000000000000000000000000000000000000000000000000000831416156139e6576139e661351c565b500590565b6040516060810167ffffffffffffffff81118282101715613a0e57613a0e6135f4565b60405290565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715613a5b57613a5b6135f4565b604052919050565b5f60808284031215613a73575f80fd5b6040516080810167ffffffffffffffff81118282101715613a9657613a966135f4565b806040525080915082518060070b8114613aae575f80fd5b81526020830151613abe816133ef565b60208201526040830151600381900b8114613ad7575f80fd5b6040820152606092830151920191909152919050565b5f60808284031215613afd575f80fd5b610bef8383613a63565b69ffffffffffffffffffff82811682821603908111156108d1576108d161351c565b5f805f805f60a08688031215613b3d575f80fd5b8551613b48816137ba565b60208701516040880151606089015160808a015193985091965094509250613b6f816137ba565b809150509295509295909350565b69ffffffffffffffffffff81811683821601908111156108d1576108d161351c565b67ffffffffffffffff82811682821603908111156108d1576108d161351c565b608081525f613bd1608083018761364e565b82810360208401528086518083526020830191506020880192505f5b81811015613c0b578351835260209384019390920191600101613bed565b5050809250505067ffffffffffffffff8416604083015267ffffffffffffffff8316606083015295945050505050565b5f60208284031215613c4b575f80fd5b815167ffffffffffffffff811115613c61575f80fd5b8201601f81018413613c71575f80fd5b805167ffffffffffffffff811115613c8b57613c8b6135f4565b613c9a60208260051b01613a14565b8082825260208201915060206101208402850101925086831115613cbc575f80fd5b6020840193505b82841015613d20576101208488031215613cdb575f80fd5b613ce36139eb565b84518152613cf48860208701613a63565b6020820152613d068860a08701613a63565b604082015282526101209390930192602090910190613cc3565b9695505050505050565b5f8160030b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffff800000008103613d5e57613d5e61351c565b5f039291505056fea2646970667358221220cc72a7860678e689e8e13604004bfe73690f6792da0aa1c5b21e812009daa0a064736f6c634300081a0033
Verified Source Code Full Match
Compiler: v0.8.26+commit.8a97fa7a
EVM: cancun
Optimization: Yes (20000 runs)
WstEthOracleMiddleware.sol 66 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.26;
import { IWstETH } from "../interfaces/IWstETH.sol";
import { PriceInfo } from "../interfaces/OracleMiddleware/IOracleMiddlewareTypes.sol";
import { IUsdnProtocolTypes as Types } from "../interfaces/UsdnProtocol/IUsdnProtocolTypes.sol";
import { OracleMiddleware } from "./OracleMiddleware.sol";
/**
* @title Middleware Implementation For WstETH Price
* @notice This contract is used to get the price of wstETH from the eth price oracle.
*/
contract WstEthOracleMiddleware is OracleMiddleware {
/// @notice The wstETH contract.
IWstETH internal immutable _wstEth;
/**
* @param pythContract The address of the Pyth contract.
* @param pythPriceID The ID of the ETH Pyth price feed.
* @param chainlinkPriceFeed The address of the ETH Chainlink price feed.
* @param wstETH The address of the wstETH contract.
* @param chainlinkTimeElapsedLimit The duration after which a Chainlink price is considered stale.
*/
constructor(
address pythContract,
bytes32 pythPriceID,
address chainlinkPriceFeed,
address wstETH,
uint256 chainlinkTimeElapsedLimit
) OracleMiddleware(pythContract, pythPriceID, chainlinkPriceFeed, chainlinkTimeElapsedLimit) {
_wstEth = IWstETH(wstETH);
}
/**
* @inheritdoc OracleMiddleware
* @notice Parses and validates `data`, returns the corresponding price data by applying eth/wstETH ratio.
* @dev The data format is specific to the middleware and is simply forwarded from the user transaction's calldata.
* Wsteth price is calculated as follows: `ethPrice x stEthPerToken / 1 ether`.
* A fee amounting to exactly {validationCost} (with the same `data` and `action`) must be sent or the transaction
* will revert.
* @param actionId A unique identifier for the current action. This identifier can be used to link an `Initiate`
* call with the corresponding `Validate` call.
* @param targetTimestamp The target timestamp for validating the price data. For validation actions, this is the
* timestamp of the initiation.
* @param action Type of action for which the price is requested. The middleware may use this to alter the
* validation of the price or the returned price.
* @param data The data to be used to communicate with oracles, the format varies from middleware to middleware and
* can be different depending on the action.
* @return result_ The price and timestamp as {IOracleMiddlewareTypes.PriceInfo}.
*/
function parseAndValidatePrice(
bytes32 actionId,
uint128 targetTimestamp,
Types.ProtocolAction action,
bytes calldata data
) public payable virtual override returns (PriceInfo memory) {
PriceInfo memory ethPrice = super.parseAndValidatePrice(actionId, targetTimestamp, action, data);
uint256 stEthPerToken = _wstEth.stEthPerToken();
return PriceInfo({
price: ethPrice.price * stEthPerToken / 1 ether,
neutralPrice: ethPrice.neutralPrice * stEthPerToken / 1 ether,
timestamp: ethPrice.timestamp
});
}
}
IWstETH.sol 61 lines
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
import { IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import { IERC20Permit } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol";
interface IWstETH is IERC20Metadata, IERC20Permit {
/**
* @notice Exchanges stETH to wstETH
* @param _stETHAmount The amount of stETH to wrap in exchange for wstETH
* @dev Requirements:
* - `_stETHAmount` must be non-zero
* - msg.sender must approve at least `_stETHAmount` stETH to this contract
* - msg.sender must have at least `_stETHAmount` of stETH
* User should first approve `_stETHAmount` to the WstETH contract
* @return Amount of wstETH user receives after wrap
*/
function wrap(uint256 _stETHAmount) external returns (uint256);
/**
* @notice Exchanges wstETH to stETH
* @param _wstETHAmount The amount of wstETH to unwrap in exchange for stETH
* @dev Requirements:
* - `_wstETHAmount` must be non-zero
* - msg.sender must have at least `_wstETHAmount` wstETH
* @return The amount of stETH user receives after unwrap
*/
function unwrap(uint256 _wstETHAmount) external returns (uint256);
/**
* @notice Get the amount of wstETH for a given amount of stETH
* @param _stETHAmount The amount of stETH
* @return The amount of wstETH for a given stETH amount
*/
function getWstETHByStETH(uint256 _stETHAmount) external view returns (uint256);
/**
* @notice Get the amount of stETH for a given amount of wstETH
* @param _wstETHAmount The amount of wstETH
* @return The amount of stETH for a given wstETH amount
*/
function getStETHByWstETH(uint256 _wstETHAmount) external view returns (uint256);
/**
* @notice Get the amount of stETH for a one wstETH
* @return The amount of stETH for 1 wstETH
*/
function stEthPerToken() external view returns (uint256);
/**
* @notice Get the amount of wstETH for a one stETH
* @return The amount of wstETH for a 1 stETH
*/
function tokensPerStEth() external view returns (uint256);
/**
* @notice Get the address of stETH
* @return The address of stETH
*/
function stETH() external view returns (address);
}
IOracleMiddlewareTypes.sol 59 lines
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
/**
* @notice The price and timestamp returned by the oracle middleware.
* @param price The validated asset price, potentially adjusted by the middleware.
* @param neutralPrice The neutral/average price of the asset.
* @param timestamp The timestamp of the price data.
*/
struct PriceInfo {
uint256 price;
uint256 neutralPrice;
uint256 timestamp;
}
/**
* @notice The price and timestamp returned by the Chainlink oracle.
* @param price The asset price formatted by the middleware.
* @param timestamp When the price was published on chain.
*/
struct ChainlinkPriceInfo {
int256 price;
uint256 timestamp;
}
/**
* @notice Representation of a Pyth price with a uint256 price.
* @param price The price of the asset.
* @param conf The confidence interval around the price (in dollars, absolute value).
* @param publishTime Unix timestamp describing when the price was published.
*/
struct FormattedPythPrice {
uint256 price;
uint256 conf;
uint256 publishTime;
}
/**
* @notice The price and timestamp returned by the Redstone oracle.
* @param price The asset price formatted by the middleware.
* @param timestamp The timestamp of the price data.
*/
struct RedstonePriceInfo {
uint256 price;
uint256 timestamp;
}
/**
* @notice The different confidence interval of a Pyth price.
* @dev Applied to the neutral price and available as `price`.
* @param Up Adjusted price at the upper bound of the confidence interval.
* @param Down Adjusted price at the lower bound of the confidence interval.
* @param None Neutral price without adjustment.
*/
enum ConfidenceInterval {
Up,
Down,
None
}
IUsdnProtocolTypes.sol 696 lines
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
import { IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import { HugeUint } from "@smardex-solidity-libraries-1/HugeUint.sol";
import { LibBitmap } from "solady/src/utils/LibBitmap.sol";
import { DoubleEndedQueue } from "../../libraries/DoubleEndedQueue.sol";
import { IBaseLiquidationRewardsManager } from "../LiquidationRewardsManager/IBaseLiquidationRewardsManager.sol";
import { IBaseOracleMiddleware } from "../OracleMiddleware/IBaseOracleMiddleware.sol";
import { IBaseRebalancer } from "../Rebalancer/IBaseRebalancer.sol";
import { IUsdn } from "../Usdn/IUsdn.sol";
interface IUsdnProtocolTypes {
/**
* @notice All possible action types for the protocol.
* @dev This is used for pending actions and to interact with the oracle middleware.
* @param None No particular action.
* @param Initialize The contract is being initialized.
* @param InitiateDeposit Initiating a `deposit` action.
* @param ValidateDeposit Validating a `deposit` action.
* @param InitiateWithdrawal Initiating a `withdraw` action.
* @param ValidateWithdrawal Validating a `withdraw` action.
* @param InitiateOpenPosition Initiating an `open` position action.
* @param ValidateOpenPosition Validating an `open` position action.
* @param InitiateClosePosition Initiating a `close` position action.
* @param ValidateClosePosition Validating a `close` position action.
* @param Liquidation The price is requested for a liquidation action.
*/
enum ProtocolAction {
None,
Initialize,
InitiateDeposit,
ValidateDeposit,
InitiateWithdrawal,
ValidateWithdrawal,
InitiateOpenPosition,
ValidateOpenPosition,
InitiateClosePosition,
ValidateClosePosition,
Liquidation
}
/**
* @notice The outcome of the call targeting a long position.
* @param Processed The call did what it was supposed to do.
* An initiate close has been completed / a pending action was validated.
* @param Liquidated The position has been liquidated by this call.
* @param PendingLiquidations The call cannot be completed because of pending liquidations.
* Try calling the {IUsdnProtocolActions.liquidate} function with a fresh price to unblock the situation.
*/
enum LongActionOutcome {
Processed,
Liquidated,
PendingLiquidations
}
/**
* @notice Classifies how far in its logic the {UsdnProtocolLongLibrary._triggerRebalancer} function made it to.
* @dev Used to estimate the gas spent by the function call to more accurately calculate liquidation rewards.
* @param None The rebalancer is not set.
* @param NoImbalance The protocol imbalance is not reached.
* @param PendingLiquidation The rebalancer position should be liquidated.
* @param NoCloseNoOpen The action neither closes nor opens a position.
* @param Closed The action only closes a position.
* @param Opened The action only opens a position.
* @param ClosedOpened The action closes and opens a position.
*/
enum RebalancerAction {
None,
NoImbalance,
PendingLiquidation,
NoCloseNoOpen,
Closed,
Opened,
ClosedOpened
}
/**
* @notice Information about a long user position.
* @param validated Whether the position was validated.
* @param timestamp The timestamp of the position start.
* @param user The user's address.
* @param totalExpo The total exposure of the position (0 for vault deposits). The product of the initial
* collateral and the initial leverage.
* @param amount The amount of initial collateral in the position.
*/
struct Position {
bool validated; // 1 byte
uint40 timestamp; // 5 bytes. Max 1_099_511_627_775 (36812-02-20 01:36:15)
address user; // 20 bytes
uint128 totalExpo; // 16 bytes. Max 340_282_366_920_938_463_463.374_607_431_768_211_455 ether
uint128 amount; // 16 bytes
}
/**
* @notice A pending action in the queue.
* @param action The action type.
* @param timestamp The timestamp of the initiate action.
* @param var0 See {DepositPendingAction}, {WithdrawalPendingAction} and {LongPendingAction}.
* @param to The target of the action.
* @param validator The address that is supposed to validate the action.
* @param securityDepositValue The security deposit of the pending action.
* @param var1 See {DepositPendingAction}, {WithdrawalPendingAction} and {LongPendingAction}.
* @param var2 See {DepositPendingAction}, {WithdrawalPendingAction} and {LongPendingAction}.
* @param var3 See {DepositPendingAction}, {WithdrawalPendingAction} and {LongPendingAction}.
* @param var4 See {DepositPendingAction}, {WithdrawalPendingAction} and {LongPendingAction}.
* @param var5 See {DepositPendingAction}, {WithdrawalPendingAction} and {LongPendingAction}.
* @param var6 See {DepositPendingAction}, {WithdrawalPendingAction} and {LongPendingAction}.
* @param var7 See {DepositPendingAction}, {WithdrawalPendingAction} and {LongPendingAction}.
*/
struct PendingAction {
ProtocolAction action; // 1 byte
uint40 timestamp; // 5 bytes
uint24 var0; // 3 bytes
address to; // 20 bytes
address validator; // 20 bytes
uint64 securityDepositValue; // 8 bytes
int24 var1; // 3 bytes
uint128 var2; // 16 bytes
uint128 var3; // 16 bytes
uint256 var4; // 32 bytes
uint256 var5; // 32 bytes
uint256 var6; // 32 bytes
uint256 var7; // 32 bytes
}
/**
* @notice A pending action in the queue for a vault deposit.
* @param action The action type.
* @param timestamp The timestamp of the initiate action.
* @param feeBps Fee for the deposit, in BPS.
* @param to The recipient of the funds.
* @param validator The address that is supposed to validate the action.
* @param securityDepositValue The security deposit of the pending action.
* @param _unused Unused field to align the struct to `PendingAction`.
* @param amount The amount of assets of the pending deposit.
* @param assetPrice The price of the asset at the time of the last update.
* @param totalExpo The total exposure at the time of the last update.
* @param balanceVault The balance of the vault at the time of the last update.
* @param balanceLong The balance of the long position at the time of the last update.
* @param usdnTotalShares The total supply of USDN shares at the time of the action.
*/
struct DepositPendingAction {
ProtocolAction action; // 1 byte
uint40 timestamp; // 5 bytes
uint24 feeBps; // 3 bytes
address to; // 20 bytes
address validator; // 20 bytes
uint64 securityDepositValue; // 8 bytes
uint24 _unused; // 3 bytes
uint128 amount; // 16 bytes
uint128 assetPrice; // 16 bytes
uint256 totalExpo; // 32 bytes
uint256 balanceVault; // 32 bytes
uint256 balanceLong; // 32 bytes
uint256 usdnTotalShares; // 32 bytes
}
/**
* @notice A pending action in the queue for a vault withdrawal.
* @param action The action type.
* @param timestamp The timestamp of the initiate action.
* @param feeBps Fee for the withdrawal, in BPS.
* @param to The recipient of the funds.
* @param validator The address that is supposed to validate the action.
* @param securityDepositValue The security deposit of the pending action.
* @param sharesLSB 3 least significant bytes of the withdrawal shares amount (uint152).
* @param sharesMSB 16 most significant bytes of the withdrawal shares amount (uint152).
* @param assetPrice The price of the asset at the time of the last update.
* @param totalExpo The total exposure at the time of the last update.
* @param balanceVault The balance of the vault at the time of the last update.
* @param balanceLong The balance of the long position at the time of the last update.
* @param usdnTotalShares The total shares supply of USDN at the time of the action.
*/
struct WithdrawalPendingAction {
ProtocolAction action; // 1 byte
uint40 timestamp; // 5 bytes
uint24 feeBps; // 3 bytes
address to; // 20 bytes
address validator; // 20 bytes
uint64 securityDepositValue; // 8 bytes
uint24 sharesLSB; // 3 bytes
uint128 sharesMSB; // 16 bytes
uint128 assetPrice; // 16 bytes
uint256 totalExpo; // 32 bytes
uint256 balanceVault; // 32 bytes
uint256 balanceLong; // 32 bytes
uint256 usdnTotalShares; // 32 bytes
}
/**
* @notice A pending action in the queue for a long position.
* @param action The action type.
* @param timestamp The timestamp of the initiate action.
* @param closeLiqPenalty The liquidation penalty of the tick (only used when closing a position).
* @param to The recipient of the position.
* @param validator The address that is supposed to validate the action.
* @param securityDepositValue The security deposit of the pending action.
* @param tick The tick of the position.
* @param closeAmount The portion of the initial position amount to close (only used when closing a position).
* @param closePosTotalExpo The total expo of the position (only used when closing a position).
* @param tickVersion The version of the tick.
* @param index The index of the position in the tick list.
* @param liqMultiplier A fixed precision representation of the liquidation multiplier (with
* `LIQUIDATION_MULTIPLIER_DECIMALS` decimals) used to calculate the effective price for a given tick number.
* @param closeBoundedPositionValue The amount that was removed from the long balance on
* {IUsdnProtocolActions.initiateClosePosition} (only used when closing a position).
*/
struct LongPendingAction {
ProtocolAction action; // 1 byte
uint40 timestamp; // 5 bytes
uint24 closeLiqPenalty; // 3 bytes
address to; // 20 bytes
address validator; // 20 bytes
uint64 securityDepositValue; // 8 bytes
int24 tick; // 3 bytes
uint128 closeAmount; // 16 bytes
uint128 closePosTotalExpo; // 16 bytes
uint256 tickVersion; // 32 bytes
uint256 index; // 32 bytes
uint256 liqMultiplier; // 32 bytes
uint256 closeBoundedPositionValue; // 32 bytes
}
/**
* @notice The data allowing to validate an actionable pending action.
* @param priceData An array of bytes, each representing the data to be forwarded to the oracle middleware to
* validate a pending action in the queue.
* @param rawIndices An array of raw indices in the pending actions queue, in the same order as the corresponding
* priceData.
*/
struct PreviousActionsData {
bytes[] priceData;
uint128[] rawIndices;
}
/**
* @notice Information of a liquidated tick.
* @param totalPositions The total number of positions in the tick.
* @param totalExpo The total expo of the tick.
* @param remainingCollateral The remaining collateral after liquidation.
* @param tickPrice The corresponding price.
* @param priceWithoutPenalty The price without the liquidation penalty.
*/
struct LiqTickInfo {
uint256 totalPositions;
uint256 totalExpo;
int256 remainingCollateral;
uint128 tickPrice;
uint128 priceWithoutPenalty;
}
/**
* @notice The effects of executed liquidations on the protocol.
* @param liquidatedPositions The total number of liquidated positions.
* @param remainingCollateral The remaining collateral after liquidation.
* @param newLongBalance The new balance of the long side.
* @param newVaultBalance The new balance of the vault side.
* @param isLiquidationPending Whether some ticks are still populated above the current price (left to liquidate).
* @param liquidatedTicks Information about the liquidated ticks.
*/
struct LiquidationsEffects {
uint256 liquidatedPositions;
int256 remainingCollateral;
uint256 newLongBalance;
uint256 newVaultBalance;
bool isLiquidationPending;
LiqTickInfo[] liquidatedTicks;
}
/**
* @notice Accumulator for tick data.
* @param totalExpo The sum of the total expo of each position in the tick.
* @param totalPos The number of positions in the tick.
* @param liquidationPenalty The liquidation penalty for the positions in the tick.
* @dev Since the liquidation penalty is a parameter that can be updated, we need to ensure that positions that get
* created with a given penalty, use this penalty throughout their lifecycle. As such, once a tick gets populated by
* a first position, it gets assigned the current liquidation penalty parameter value and can't use another value
* until it gets liquidated or all positions exit the tick.
*/
struct TickData {
uint256 totalExpo;
uint248 totalPos;
uint24 liquidationPenalty;
}
/**
* @notice The unique identifier for a long position.
* @param tick The tick of the position.
* @param tickVersion The version of the tick.
* @param index The index of the position in the tick list.
*/
struct PositionId {
int24 tick;
uint256 tickVersion;
uint256 index;
}
/**
* @notice Parameters for the internal {UsdnProtocolActionsLongLibrary._initiateOpenPosition} function.
* @param user The address of the user initiating the open position.
* @param to The address that will be the owner of the position.
* @param validator The address that is supposed to validate the action.
* @param amount The amount of assets to deposit.
* @param desiredLiqPrice The desired liquidation price, including the liquidation penalty.
* @param userMaxPrice The maximum price at which the position can be opened. The userMaxPrice is compared with the
* price after confidence interval, penalty, etc...
* @param userMaxLeverage The maximum leverage for the newly created position.
* @param deadline The deadline of the open position to be initiated.
* @param securityDepositValue The value of the security deposit for the newly created pending action.
* @param currentPriceData The current price data (used to calculate the temporary leverage and entry price,
* pending validation).
*/
struct InitiateOpenPositionParams {
address user;
address to;
address validator;
uint128 amount;
uint128 desiredLiqPrice;
uint128 userMaxPrice;
uint256 userMaxLeverage;
uint256 deadline;
uint64 securityDepositValue;
}
/**
* @notice Parameters for the internal {UsdnProtocolLongLibrary._prepareInitiateOpenPosition} function.
* @param validator The address that is supposed to validate the action.
* @param amount The amount of assets to deposit.
* @param desiredLiqPrice The desired liquidation price, including the liquidation penalty.
* @param userMaxPrice The maximum price at which the position can be opened. The userMaxPrice is compared with the
* price after confidence interval, penalty, etc...
* @param userMaxLeverage The maximum leverage for the newly created position.
* @param currentPriceData The current price data.
*/
struct PrepareInitiateOpenPositionParams {
address validator;
uint128 amount;
uint128 desiredLiqPrice;
uint256 userMaxPrice;
uint256 userMaxLeverage;
bytes currentPriceData;
}
/**
* @notice Parameters for the internal {UsdnProtocolActionsUtilsLibrary._prepareClosePositionData} function.
* @param to The recipient of the funds.
* @param validator The address that is supposed to validate the action.
* @param posId The unique identifier of the position.
* @param amountToClose The amount of collateral to remove from the position's amount.
* @param userMinPrice The minimum price at which the position can be closed.
* @param deadline The deadline until the position can be closed.
* @param currentPriceData The current price data.
* @param delegationSignature An EIP712 signature that proves the caller is authorized by the owner of the position
* to close it on their behalf.
* @param domainSeparatorV4 The domain separator v4.
*/
struct PrepareInitiateClosePositionParams {
address to;
address validator;
PositionId posId;
uint128 amountToClose;
uint256 userMinPrice;
uint256 deadline;
bytes currentPriceData;
bytes delegationSignature;
bytes32 domainSeparatorV4;
}
/**
* @notice Parameters for the internal {UsdnProtocolActionsLongLibrary._initiateClosePosition} function.
* @param to The recipient of the funds.
* @param validator The address that is supposed to validate the action.
* @param posId The unique identifier of the position.
* @param amountToClose The amount to close.
* @param userMinPrice The minimum price at which the position can be closed.
* @param deadline The deadline of the close position to be initiated.
* @param securityDepositValue The value of the security deposit for the newly created pending action.
* @param domainSeparatorV4 The domain separator v4 for EIP712 signature.
*/
struct InitiateClosePositionParams {
address to;
address payable validator;
uint256 deadline;
PositionId posId;
uint128 amountToClose;
uint256 userMinPrice;
uint64 securityDepositValue;
bytes32 domainSeparatorV4;
}
/**
* @dev Structure to hold the transient data during {UsdnProtocolActionsLongLibrary._initiateClosePosition}
* @param pos The position to close.
* @param liquidationPenalty The liquidation penalty.
* @param totalExpoToClose The total expo to close.
* @param lastPrice The price after the last balances update.
* @param tempPositionValue The bounded value of the position that was removed from the long balance.
* @param longTradingExpo The long trading expo.
* @param liqMulAcc The liquidation multiplier accumulator.
* @param isLiquidationPending Whether some ticks are still populated above the current price (left to liquidate).
*/
struct ClosePositionData {
Position pos;
uint24 liquidationPenalty;
uint128 totalExpoToClose;
uint128 lastPrice;
uint256 tempPositionValue;
uint256 longTradingExpo;
HugeUint.Uint512 liqMulAcc;
bool isLiquidationPending;
}
/**
* @dev Structure to hold the transient data during {UsdnProtocolActionsLongLibrary._validateOpenPosition}.
* @param action The long pending action.
* @param startPrice The new entry price of the position.
* @param lastPrice The price of the last balances update.
* @param tickHash The tick hash.
* @param pos The position object.
* @param liqPriceWithoutPenaltyNorFunding The liquidation price without penalty nor funding used to calculate the
* user leverage and the new total expo.
* @param liqPriceWithoutPenalty The new liquidation price without penalty.
* @param leverage The new leverage.
* @param oldPosValue The value of the position according to the old entry price and the _lastPrice.
* @param liquidationPenalty The liquidation penalty for the position's tick.
* @param isLiquidationPending Whether some ticks are still populated above the current price (left to liquidate).
*/
struct ValidateOpenPositionData {
LongPendingAction action;
uint128 startPrice;
uint128 lastPrice;
bytes32 tickHash;
Position pos;
uint128 liqPriceWithoutPenaltyNorFunding;
uint128 liqPriceWithoutPenalty;
uint256 leverage;
uint256 oldPosValue;
uint24 liquidationPenalty;
bool isLiquidationPending;
}
/**
* @dev Structure to hold the transient data during {UsdnProtocolActionsLongLibrary._initiateOpenPosition}.
* @param adjustedPrice The adjusted price with position fees applied.
* @param posId The unique identifier of the position.
* @param liquidationPenalty The liquidation penalty.
* @param positionTotalExpo The total expo of the position. The product of the initial collateral and the initial
* leverage.
* @param positionValue The value of the position, taking into account the position fee.
* @param liqMultiplier The liquidation multiplier represented with fixed precision.
* @param isLiquidationPending Whether some ticks are still populated above the current price (left to liquidate).
*/
struct InitiateOpenPositionData {
uint128 adjustedPrice;
PositionId posId;
uint24 liquidationPenalty;
uint128 positionTotalExpo;
uint256 positionValue;
uint256 liqMultiplier;
bool isLiquidationPending;
}
/**
* @notice Structure to hold the state of the protocol.
* @param totalExpo The long total expo.
* @param tradingExpo The long trading expo.
* @param longBalance The long balance.
* @param vaultBalance The vault balance.
* @param liqMultiplierAccumulator The liquidation multiplier accumulator.
*/
struct CachedProtocolState {
uint256 totalExpo;
uint256 tradingExpo;
uint256 longBalance;
uint256 vaultBalance;
HugeUint.Uint512 liqMultiplierAccumulator;
}
/**
* @notice Structure to hold transient data during the {UsdnProtocolActionsLongLibrary._calcRebalancerPositionTick}
* function.
* @param protocolMaxLeverage The protocol maximum leverage.
* @param longImbalanceTargetBps The long imbalance target in basis points.
* @param tradingExpoToFill The trading expo to fill.
* @param highestUsableTradingExpo The highest usable trading expo.
* @param currentLiqPenalty The current liquidation penalty.
* @param liqPriceWithoutPenalty The liquidation price without penalty.
*/
struct CalcRebalancerPositionTickData {
uint256 protocolMaxLeverage;
int256 longImbalanceTargetBps;
uint256 tradingExpoToFill;
uint256 highestUsableTradingExpo;
uint24 currentLiqPenalty;
uint128 liqPriceWithoutPenalty;
}
/**
* @notice Structure to hold the return values of the {UsdnProtocolActionsLongLibrary._calcRebalancerPositionTick}
* function.
* @param tick The tick of the rebalancer position, includes liquidation penalty.
* @param totalExpo The total expo of the rebalancer position.
* @param liquidationPenalty The liquidation penalty of the tick.
*/
struct RebalancerPositionData {
int24 tick;
uint128 totalExpo;
uint24 liquidationPenalty;
}
/**
* @notice Data structure for the {UsdnProtocolCoreLibrary._applyPnlAndFunding} function.
* @param tempLongBalance The new balance of the long side, could be negative (temporarily).
* @param tempVaultBalance The new balance of the vault side, could be negative (temporarily).
* @param lastPrice The last price.
*/
struct ApplyPnlAndFundingData {
int256 tempLongBalance;
int256 tempVaultBalance;
uint128 lastPrice;
}
/**
* @notice Data structure for tick to price conversion functions.
* @param tradingExpo The long side trading expo.
* @param accumulator The liquidation multiplier accumulator.
* @param tickSpacing The tick spacing.
*/
struct TickPriceConversionData {
uint256 tradingExpo;
HugeUint.Uint512 accumulator;
int24 tickSpacing;
}
/**
* @custom:storage-location erc7201:UsdnProtocol.storage.main.
* @notice Structure to hold the state of the protocol.
* @param _tickSpacing The liquidation tick spacing for storing long positions.
* A tick spacing of 1 is equivalent to a 0.01% increase in liquidation price between ticks. A tick spacing of
* 100 is equivalent to a ~1.005% increase in liquidation price between ticks.
* @param _asset The asset ERC20 contract.
* Assets with a blacklist are not supported because the protocol would be DoS if transfers revert.
* @param _assetDecimals The number of decimals used by the `_asset`.
* @param _priceFeedDecimals The price feed decimals (18).
* @param _usdn The USDN ERC20 contract.
* @param _sdex The SDEX ERC20 contract.
* @param _usdnMinDivisor The minimum divisor for USDN.
* @param _oracleMiddleware The oracle middleware contract.
* @param _liquidationRewardsManager The liquidation rewards manager contract.
* @param _rebalancer The rebalancer contract.
* @param _isRebalancer Whether an address is or has been a rebalancer.
* @param _minLeverage The minimum leverage for a position.
* @param _maxLeverage The maximum leverage for a position.
* @param _lowLatencyValidatorDeadline The deadline for a user to confirm their action with a low-latency oracle.
* After this deadline, any user can validate the action with the low-latency oracle until the
* OracleMiddleware's _lowLatencyDelay. This is an offset compared to the timestamp of the initiate action.
* @param _onChainValidatorDeadline The deadline for a user to confirm their action with an on-chain oracle.
* After this deadline, any user can validate the action with the on-chain oracle. This is an offset compared
* to the timestamp of the initiate action + the oracle middleware's _lowLatencyDelay.
* @param _safetyMarginBps Safety margin for the liquidation price of newly open positions, in basis points.
* @param _liquidationIteration The number of iterations to perform during the user's action (in tick).
* @param _protocolFeeBps The protocol fee in basis points.
* @param _rebalancerBonusBps Part of the remaining collateral that is given as a bonus to the Rebalancer upon
* liquidation of a tick, in basis points. The rest is sent to the Vault balance.
* @param _liquidationPenalty The liquidation penalty (in ticks).
* @param _EMAPeriod The moving average period of the funding rate.
* @param _fundingSF The scaling factor (SF) of the funding rate.
* @param _feeThreshold The threshold above which the fee will be sent.
* @param _openExpoImbalanceLimitBps The imbalance limit of the long expo for open actions (in basis points).
* As soon as the difference between the vault expo and the long expo exceeds this basis point limit in favor
* of long the open rebalancing mechanism is triggered, preventing the opening of a new long position.
* @param _withdrawalExpoImbalanceLimitBps The imbalance limit of the long expo for withdrawal actions (in basis
* points). As soon as the difference between vault expo and long expo exceeds this basis point limit in favor of
* long, the withdrawal rebalancing mechanism is triggered, preventing the withdrawal of the existing vault
* position.
* @param _depositExpoImbalanceLimitBps The imbalance limit of the vault expo for deposit actions (in basis points).
* As soon as the difference between the vault expo and the long expo exceeds this basis point limit in favor
* of the vault, the deposit vault rebalancing mechanism is triggered, preventing the opening of a new vault
* position.
* @param _closeExpoImbalanceLimitBps The imbalance limit of the vault expo for close actions (in basis points).
* As soon as the difference between the vault expo and the long expo exceeds this basis point limit in favor
* of the vault, the close rebalancing mechanism is triggered, preventing the close of an existing long position.
* @param _rebalancerCloseExpoImbalanceLimitBps The imbalance limit of the vault expo for close actions from the
* rebalancer (in basis points). As soon as the difference between the vault expo and the long expo exceeds this
* basis point limit in favor of the vault, the close rebalancing mechanism is triggered, preventing the close of an
* existing long position from the rebalancer contract.
* @param _longImbalanceTargetBps The target imbalance on the long side (in basis points)
* This value will be used to calculate how much of the missing trading expo the rebalancer position will try
* to compensate. A negative value means the rebalancer will compensate enough to go above the equilibrium. A
* positive value means the rebalancer will compensate but stay below the equilibrium.
* @param _positionFeeBps The position fee in basis points.
* @param _vaultFeeBps The fee for vault deposits and withdrawals, in basis points.
* @param _sdexRewardsRatioBps The ratio of SDEX rewards to send to the user (in basis points).
* @param _sdexBurnOnDepositRatio The ratio of USDN to SDEX tokens to burn on deposit.
* @param _feeCollector The fee collector's address.
* @param _securityDepositValue The deposit required for a new position.
* @param _targetUsdnPrice The nominal (target) price of USDN (with _priceFeedDecimals).
* @param _usdnRebaseThreshold The USDN price threshold to trigger a rebase (with _priceFeedDecimals).
* @param _minLongPosition The minimum long position size (with `_assetDecimals`).
* @param _lastFundingPerDay The funding rate calculated at the last update timestamp.
* @param _lastPrice The price of the asset during the last balances update (with price feed decimals).
* @param _lastUpdateTimestamp The timestamp of the last balances update.
* @param _pendingProtocolFee The pending protocol fee accumulator.
* @param _pendingActions The pending actions by the user (1 per user max).
* The value stored is an index into the `pendingActionsQueue` deque, shifted by one. A value of 0 means no
* pending action. Since the deque uses uint128 indices, the highest index will not overflow when adding one.
* @param _pendingActionsQueue The queue of pending actions.
* @param _balanceVault The balance of deposits (with `_assetDecimals`).
* @param _pendingBalanceVault The unreflected balance change due to pending vault actions (with `_assetDecimals`).
* @param _EMA The exponential moving average of the funding (0.0003 at initialization).
* @param _balanceLong The balance of long positions (with `_assetDecimals`).
* @param _totalExpo The total exposure of the long positions (with `_assetDecimals`).
* @param _liqMultiplierAccumulator The accumulator used to calculate the liquidation multiplier.
* This is the sum, for all ticks, of the total expo of positions inside the tick, multiplied by the
* unadjusted price of the tick which is `_tickData[tickHash].liquidationPenalty` below
* The unadjusted price is obtained with `TickMath.getPriceAtTick.
* @param _tickVersion The liquidation tick version.
* @param _longPositions The long positions per versioned tick (liquidation price).
* @param _tickData Accumulated data for a given tick and tick version.
* @param _highestPopulatedTick The highest tick with a position.
* @param _totalLongPositions Cache of the total long positions count.
* @param _tickBitmap The bitmap used to quickly find populated ticks.
* @param _protocolFallbackAddr The address of the fallback contract.
* @param _nonce The user EIP712 nonce.
*/
struct Storage {
// immutable
int24 _tickSpacing;
IERC20Metadata _asset;
uint8 _assetDecimals;
uint8 _priceFeedDecimals;
IUsdn _usdn;
IERC20Metadata _sdex;
uint256 _usdnMinDivisor;
// parameters
IBaseOracleMiddleware _oracleMiddleware;
IBaseLiquidationRewardsManager _liquidationRewardsManager;
IBaseRebalancer _rebalancer;
mapping(address => bool) _isRebalancer;
uint256 _minLeverage;
uint256 _maxLeverage;
uint128 _lowLatencyValidatorDeadline;
uint128 _onChainValidatorDeadline;
uint256 _safetyMarginBps;
uint16 _liquidationIteration;
uint16 _protocolFeeBps;
uint16 _rebalancerBonusBps;
uint24 _liquidationPenalty;
uint128 _EMAPeriod;
uint256 _fundingSF;
uint256 _feeThreshold;
int256 _openExpoImbalanceLimitBps;
int256 _withdrawalExpoImbalanceLimitBps;
int256 _depositExpoImbalanceLimitBps;
int256 _closeExpoImbalanceLimitBps;
int256 _rebalancerCloseExpoImbalanceLimitBps;
int256 _longImbalanceTargetBps;
uint16 _positionFeeBps;
uint16 _vaultFeeBps;
uint16 _sdexRewardsRatioBps;
uint32 _sdexBurnOnDepositRatio;
address _feeCollector;
uint64 _securityDepositValue;
uint128 _targetUsdnPrice;
uint128 _usdnRebaseThreshold;
uint256 _minLongPosition;
// state
int256 _lastFundingPerDay;
uint128 _lastPrice;
uint128 _lastUpdateTimestamp;
uint256 _pendingProtocolFee;
// pending actions queue
mapping(address => uint256) _pendingActions;
DoubleEndedQueue.Deque _pendingActionsQueue;
// vault
uint256 _balanceVault;
int256 _pendingBalanceVault;
// long positions
int256 _EMA;
uint256 _balanceLong;
uint256 _totalExpo;
HugeUint.Uint512 _liqMultiplierAccumulator;
mapping(int24 => uint256) _tickVersion;
mapping(bytes32 => Position[]) _longPositions;
mapping(bytes32 => TickData) _tickData;
int24 _highestPopulatedTick;
uint256 _totalLongPositions;
LibBitmap.Bitmap _tickBitmap;
// fallback
address _protocolFallbackAddr;
// EIP712
mapping(address => uint256) _nonce;
}
}
OracleMiddleware.sol 432 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.26;
import { AccessControlDefaultAdminRules } from
"@openzeppelin/contracts/access/extensions/AccessControlDefaultAdminRules.sol";
import { IBaseOracleMiddleware } from "../interfaces/OracleMiddleware/IBaseOracleMiddleware.sol";
import { IOracleMiddleware } from "../interfaces/OracleMiddleware/IOracleMiddleware.sol";
import {
ChainlinkPriceInfo,
ConfidenceInterval,
FormattedPythPrice,
PriceInfo
} from "../interfaces/OracleMiddleware/IOracleMiddlewareTypes.sol";
import { IUsdnProtocol } from "../interfaces/UsdnProtocol/IUsdnProtocol.sol";
import { IUsdnProtocolTypes as Types } from "../interfaces/UsdnProtocol/IUsdnProtocolTypes.sol";
import { ChainlinkOracle } from "./oracles/ChainlinkOracle.sol";
import { PythOracle } from "./oracles/PythOracle.sol";
/**
* @title Middleware Between Oracles And The USDN Protocol
* @notice This contract is used to get the price of an asset from different oracles.
* It is used by the USDN protocol to get the price of the USDN underlying asset.
*/
contract OracleMiddleware is IOracleMiddleware, PythOracle, ChainlinkOracle, AccessControlDefaultAdminRules {
/// @inheritdoc IOracleMiddleware
uint16 public constant BPS_DIVISOR = 10_000;
/// @inheritdoc IOracleMiddleware
uint16 public constant MAX_CONF_RATIO = BPS_DIVISOR * 2;
/// @notice The number of decimals for the returned price.
uint8 internal constant MIDDLEWARE_DECIMALS = 18;
/// @inheritdoc IOracleMiddleware
bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE");
/**
* @notice The delay (in seconds) between the moment an action is initiated and the timestamp of the
* price data used to validate that action.
*/
uint256 internal _validationDelay = 24 seconds;
/// @notice Ratio to applied to the Pyth confidence interval (in basis points).
uint16 internal _confRatioBps = 4000; // to divide by BPS_DIVISOR
/**
* @notice The amount of time during which a low latency oracle price validation is available.
* @dev This value should be greater than or equal to `_lowLatencyValidatorDeadline` of the USDN protocol.
*/
uint16 internal _lowLatencyDelay = 20 minutes;
/**
* @param pythContract Address of the Pyth contract.
* @param pythFeedId The Pyth price feed ID for the asset.
* @param chainlinkPriceFeed Address of the Chainlink price feed.
* @param chainlinkTimeElapsedLimit The duration after which a Chainlink price is considered stale.
*/
constructor(address pythContract, bytes32 pythFeedId, address chainlinkPriceFeed, uint256 chainlinkTimeElapsedLimit)
PythOracle(pythContract, pythFeedId)
ChainlinkOracle(chainlinkPriceFeed, chainlinkTimeElapsedLimit)
AccessControlDefaultAdminRules(0, msg.sender)
{
_grantRole(ADMIN_ROLE, msg.sender);
}
/* -------------------------------------------------------------------------- */
/* Public view functions */
/* -------------------------------------------------------------------------- */
/// @inheritdoc IBaseOracleMiddleware
function parseAndValidatePrice(bytes32, uint128 targetTimestamp, Types.ProtocolAction action, bytes calldata data)
public
payable
virtual
returns (PriceInfo memory price_)
{
if (action == Types.ProtocolAction.None) {
return
_getLowLatencyPrice(data, targetTimestamp, ConfidenceInterval.None, targetTimestamp + _lowLatencyDelay);
} else if (action == Types.ProtocolAction.Initialize) {
return _getInitiateActionPrice(data, ConfidenceInterval.None);
} else if (action == Types.ProtocolAction.ValidateDeposit) {
// use the lowest price in the confidence interval to ensure a minimum benefit for the user in case
// of price inaccuracies until low latency delay is exceeded then use chainlink specified roundId
return _getValidateActionPrice(data, targetTimestamp, ConfidenceInterval.Down);
} else if (action == Types.ProtocolAction.ValidateWithdrawal) {
// use the highest price in the confidence interval to ensure a minimum benefit for the user in case
// of price inaccuracies until low latency delay is exceeded then use chainlink specified roundId
return _getValidateActionPrice(data, targetTimestamp, ConfidenceInterval.Up);
} else if (action == Types.ProtocolAction.ValidateOpenPosition) {
// use the highest price in the confidence interval to ensure a minimum benefit for the user in case
// of price inaccuracies until low latency delay is exceeded then use chainlink specified roundId
return _getValidateActionPrice(data, targetTimestamp, ConfidenceInterval.Up);
} else if (action == Types.ProtocolAction.ValidateClosePosition) {
// use the lowest price in the confidence interval to ensure a minimum benefit for the user in case
// of price inaccuracies until low latency delay is exceeded then use chainlink specified roundId
return _getValidateActionPrice(data, targetTimestamp, ConfidenceInterval.Down);
} else if (action == Types.ProtocolAction.Liquidation) {
// special case, if we pass a timestamp of zero, then we accept all prices newer than
// `_pythRecentPriceDelay`
return _getLowLatencyPrice(data, 0, ConfidenceInterval.None, 0);
} else if (action == Types.ProtocolAction.InitiateDeposit) {
// if the user chooses to initiate with pyth, the neutral price will be used so no confidence is needed
return _getInitiateActionPrice(data, ConfidenceInterval.None);
} else if (action == Types.ProtocolAction.InitiateWithdrawal) {
// if the user chooses to initiate with pyth, the neutral price will be used so no confidence is needed
return _getInitiateActionPrice(data, ConfidenceInterval.None);
} else if (action == Types.ProtocolAction.InitiateOpenPosition) {
// if the user chooses to initiate with pyth, the neutral price will be used so no confidence is needed
return _getInitiateActionPrice(data, ConfidenceInterval.None);
} else if (action == Types.ProtocolAction.InitiateClosePosition) {
// if the user chooses to initiate with pyth, the neutral price will be used so no confidence is needed
return _getInitiateActionPrice(data, ConfidenceInterval.None);
}
}
/// @inheritdoc IBaseOracleMiddleware
function getValidationDelay() external view returns (uint256 delay_) {
return _validationDelay;
}
/// @inheritdoc IBaseOracleMiddleware
function getDecimals() external pure returns (uint8 decimals_) {
return MIDDLEWARE_DECIMALS;
}
/// @inheritdoc IOracleMiddleware
function getConfRatioBps() external view returns (uint16 ratio_) {
return _confRatioBps;
}
/// @inheritdoc IBaseOracleMiddleware
function getLowLatencyDelay() external view returns (uint16 delay_) {
return _lowLatencyDelay;
}
/// @inheritdoc IBaseOracleMiddleware
function validationCost(bytes calldata data, Types.ProtocolAction) public view virtual returns (uint256 result_) {
if (_isPythData(data)) {
result_ = _getPythUpdateFee(data);
}
}
/* -------------------------------------------------------------------------- */
/* Internal functions */
/* -------------------------------------------------------------------------- */
/**
* @notice Gets the price from the low-latency oracle (Pyth).
* @param data The signed price update data.
* @param actionTimestamp The timestamp of the action corresponding to the price. If zero, then we must accept all
* prices younger than {PythOracle._pythRecentPriceDelay}.
* @param dir The direction for the confidence interval adjusted price.
* @param targetLimit The most recent timestamp a price can have (can be zero if `actionTimestamp` is zero).
* @return price_ The price from the low-latency oracle, adjusted according to the confidence interval direction.
*/
function _getLowLatencyPrice(
bytes calldata data,
uint128 actionTimestamp,
ConfidenceInterval dir,
uint128 targetLimit
) internal virtual returns (PriceInfo memory price_) {
// if actionTimestamp is 0 we're performing a liquidation and we don't add the validation delay
if (actionTimestamp > 0) {
// add the validation delay to the action timestamp to get the timestamp of the price data used to
// validate
actionTimestamp += uint128(_validationDelay);
}
FormattedPythPrice memory pythPrice =
_getFormattedPythPrice(data, actionTimestamp, MIDDLEWARE_DECIMALS, targetLimit);
price_ = _adjustPythPrice(pythPrice, dir);
}
/**
* @notice Gets the price for an `initiate` action of the protocol.
* @dev If the data parameter is not empty, validate the price with {PythOracle}. Else, get the on-chain price from
* {ChainlinkOracle} and compare its timestamp with the latest seen Pyth price (cached). If Pyth is more recent, we
* return it. Otherwise, we return the Chainlink price. For the latter, we don't have a confidence interval, so both
* `neutralPrice` and `price` are equal.
* @param data An optional VAA from Pyth.
* @param dir The direction when applying the confidence interval (when using a Pyth price).
* @return price_ The price to use for the user action.
*/
function _getInitiateActionPrice(bytes calldata data, ConfidenceInterval dir)
internal
returns (PriceInfo memory price_)
{
// if data is not empty, use pyth
if (data.length > 0) {
// since we use this function for `initiate` type actions which pass `targetTimestamp = block.timestamp`,
// we should pass `0` to the function below to signal that we accept any recent price
return _getLowLatencyPrice(data, 0, dir, 0);
}
// chainlink calls do not require a fee
if (msg.value > 0) {
revert OracleMiddlewareIncorrectFee();
}
ChainlinkPriceInfo memory chainlinkOnChainPrice = _getFormattedChainlinkLatestPrice(MIDDLEWARE_DECIMALS);
// check if the cached pyth price is more recent and return it instead
FormattedPythPrice memory latestPythPrice = _getLatestStoredPythPrice(MIDDLEWARE_DECIMALS);
if (chainlinkOnChainPrice.timestamp <= latestPythPrice.publishTime) {
// we use the same price age limit as for chainlink here
if (latestPythPrice.publishTime < block.timestamp - _timeElapsedLimit) {
revert OracleMiddlewarePriceTooOld(latestPythPrice.publishTime);
}
return _adjustPythPrice(latestPythPrice, dir);
}
// if the price equals PRICE_TOO_OLD then the tolerated time elapsed for price validity was exceeded, revert
if (chainlinkOnChainPrice.price == PRICE_TOO_OLD) {
revert OracleMiddlewarePriceTooOld(chainlinkOnChainPrice.timestamp);
}
// if the price is negative or zero, revert
if (chainlinkOnChainPrice.price <= 0) {
revert OracleMiddlewareWrongPrice(chainlinkOnChainPrice.price);
}
price_ = PriceInfo({
price: uint256(chainlinkOnChainPrice.price),
neutralPrice: uint256(chainlinkOnChainPrice.price),
timestamp: chainlinkOnChainPrice.timestamp
});
}
/**
* @notice Applies the confidence interval in the `dir` direction, scaled by the configured {_confRatioBps}.
* @param pythPrice The formatted Pyth price object.
* @param dir The direction of the confidence interval to apply.
* @return price_ The adjusted price according to the confidence interval and confidence ratio.
*/
function _adjustPythPrice(FormattedPythPrice memory pythPrice, ConfidenceInterval dir)
internal
view
returns (PriceInfo memory price_)
{
if (dir == ConfidenceInterval.Down) {
uint256 adjust = (pythPrice.conf * _confRatioBps) / BPS_DIVISOR;
if (adjust >= pythPrice.price) {
// avoid underflow or zero price due to confidence interval adjustment
price_.price = 1;
} else {
// strictly positive
unchecked {
price_.price = pythPrice.price - adjust;
}
}
} else if (dir == ConfidenceInterval.Up) {
price_.price = pythPrice.price + ((pythPrice.conf * _confRatioBps) / BPS_DIVISOR);
} else {
price_.price = pythPrice.price;
}
price_.timestamp = pythPrice.publishTime;
price_.neutralPrice = pythPrice.price;
}
/**
* @notice Gets the price for a validate action of the protocol.
* @dev If the low latency delay is not exceeded, validate the price with the low-latency oracle(s).
* Else, get the specified roundId on-chain price from Chainlink. In case of chainlink price,
* we don't have a confidence interval and so both `neutralPrice` and `price` are equal.
* @param data An optional VAA from Pyth or a chainlink roundId (abi-encoded uint80).
* @param targetTimestamp The timestamp of the initiate action.
* @param dir The direction for applying the confidence interval (in case we use a Pyth price).
* @return price_ The price to use for the user action.
*/
function _getValidateActionPrice(bytes calldata data, uint128 targetTimestamp, ConfidenceInterval dir)
internal
returns (PriceInfo memory price_)
{
uint128 targetLimit = targetTimestamp + _lowLatencyDelay;
if (block.timestamp <= targetLimit) {
return _getLowLatencyPrice(data, targetTimestamp, dir, targetLimit);
}
// chainlink calls do not require a fee
if (msg.value > 0) {
revert OracleMiddlewareIncorrectFee();
}
uint80 validateRoundId = abi.decode(data, (uint80));
// check that the round ID is valid and get its price data
ChainlinkPriceInfo memory chainlinkOnChainPrice = _validateChainlinkRoundId(targetLimit, validateRoundId);
price_ = PriceInfo({
price: uint256(chainlinkOnChainPrice.price),
neutralPrice: uint256(chainlinkOnChainPrice.price),
timestamp: chainlinkOnChainPrice.timestamp
});
}
/**
* @notice Checks that the given round ID is valid and returns its corresponding price data.
* @dev Round IDs are not necessarily consecutive, so additional computing can be necessary to find
* the previous round ID.
* @param targetLimit The timestamp of the initiate action + {_lowLatencyDelay}.
* @param roundId The round ID to validate.
* @return providedRoundPrice_ The price data of the provided round ID.
*/
function _validateChainlinkRoundId(uint128 targetLimit, uint80 roundId)
internal
view
returns (ChainlinkPriceInfo memory providedRoundPrice_)
{
providedRoundPrice_ = _getFormattedChainlinkPrice(MIDDLEWARE_DECIMALS, roundId);
if (providedRoundPrice_.price <= 0) {
revert OracleMiddlewareWrongPrice(providedRoundPrice_.price);
}
(,,, uint256 previousRoundTimestamp,) = _priceFeed.getRoundData(roundId - 1);
// if the provided round's timestamp is 0, it's possible the aggregator recently changed and there is no data
// available for the previous round ID in the aggregator. In that case, we accept the given round ID as the
// sole reference with additional checks to make sure it is not too far from the target timestamp
if (previousRoundTimestamp == 0) {
// calculate the provided round's phase ID
uint80 roundPhaseId = roundId >> 64;
// calculate the first valid round ID for this phase
uint80 firstRoundId = (roundPhaseId << 64) + 1;
// the provided round ID must be the first round ID of the phase, if not, revert
if (firstRoundId != roundId) {
revert OracleMiddlewareInvalidRoundId();
}
// make sure that the provided round ID is not newer than it should be
if (providedRoundPrice_.timestamp > targetLimit + _timeElapsedLimit) {
revert OracleMiddlewareInvalidRoundId();
}
} else if (previousRoundTimestamp > targetLimit) {
// previous round should precede targetLimit
revert OracleMiddlewareInvalidRoundId();
}
if (providedRoundPrice_.timestamp <= targetLimit) {
revert OracleMiddlewareInvalidRoundId();
}
}
/**
* @notice Checks if the passed calldata corresponds to a Pyth message.
* @param data The calldata pointer to the message.
* @return isPythData_ Whether the data is a valid Pyth message or not.
*/
function _isPythData(bytes calldata data) internal pure returns (bool isPythData_) {
if (data.length <= 32) {
return false;
}
// check the first 4 bytes of the data to identify a pyth message
uint32 magic;
assembly {
magic := shr(224, calldataload(data.offset))
}
// Pyth magic stands for PNAU (Pyth Network Accumulator Update)
return magic == 0x504e4155;
}
/* -------------------------------------------------------------------------- */
/* Privileged functions */
/* -------------------------------------------------------------------------- */
/// @inheritdoc IOracleMiddleware
function setValidationDelay(uint256 newValidationDelay) external onlyRole(ADMIN_ROLE) {
_validationDelay = newValidationDelay;
emit ValidationDelayUpdated(newValidationDelay);
}
/// @inheritdoc IOracleMiddleware
function setChainlinkTimeElapsedLimit(uint256 newTimeElapsedLimit) external onlyRole(ADMIN_ROLE) {
_timeElapsedLimit = newTimeElapsedLimit;
emit TimeElapsedLimitUpdated(newTimeElapsedLimit);
}
/// @inheritdoc IOracleMiddleware
function setPythRecentPriceDelay(uint64 newDelay) external onlyRole(ADMIN_ROLE) {
if (newDelay < 10 seconds) {
revert OracleMiddlewareInvalidRecentPriceDelay(newDelay);
}
if (newDelay > 10 minutes) {
revert OracleMiddlewareInvalidRecentPriceDelay(newDelay);
}
_pythRecentPriceDelay = newDelay;
emit PythRecentPriceDelayUpdated(newDelay);
}
/// @inheritdoc IOracleMiddleware
function setConfRatio(uint16 newConfRatio) external onlyRole(ADMIN_ROLE) {
// confidence ratio limit check
if (newConfRatio > MAX_CONF_RATIO) {
revert OracleMiddlewareConfRatioTooHigh();
}
_confRatioBps = newConfRatio;
emit ConfRatioUpdated(newConfRatio);
}
/// @inheritdoc IOracleMiddleware
function setLowLatencyDelay(uint16 newLowLatencyDelay, IUsdnProtocol usdnProtocol) external onlyRole(ADMIN_ROLE) {
if (newLowLatencyDelay > 90 minutes) {
revert OracleMiddlewareInvalidLowLatencyDelay();
}
if (newLowLatencyDelay < usdnProtocol.getLowLatencyValidatorDeadline()) {
revert OracleMiddlewareInvalidLowLatencyDelay();
}
_lowLatencyDelay = newLowLatencyDelay;
emit LowLatencyDelayUpdated(newLowLatencyDelay);
}
/// @inheritdoc IOracleMiddleware
function withdrawEther(address to) external onlyRole(ADMIN_ROLE) {
if (to == address(0)) {
revert OracleMiddlewareTransferToZeroAddress();
}
// slither-disable-next-line arbitrary-send-eth
(bool success,) = payable(to).call{ value: address(this).balance }("");
if (!success) {
revert OracleMiddlewareTransferFailed(to);
}
}
}
IERC20Metadata.sol 26 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/extensions/IERC20Metadata.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
/**
* @dev Interface for the optional metadata functions from the ERC-20 standard.
*/
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);
}
IERC20Permit.sol 90 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/extensions/IERC20Permit.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC-20 Permit extension allowing approvals to be made via signatures, as defined in
* https://eips.ethereum.org/EIPS/eip-2612[ERC-2612].
*
* Adds the {permit} method, which can be used to change an account's ERC-20 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.
*
* ==== Security Considerations
*
* There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
* expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
* considered as an intention to spend the allowance in any specific way. The second is that because permits have
* built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
* take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
* generally recommended is:
*
* ```solidity
* function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
* try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
* doThing(..., value);
* }
*
* function doThing(..., uint256 value) public {
* token.safeTransferFrom(msg.sender, address(this), value);
* ...
* }
* ```
*
* Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
* `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
* {SafeERC20-safeTransferFrom}).
*
* Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
* contracts should have entry points that don't rely on permit.
*/
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].
*
* CAUTION: See Security Considerations above.
*/
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);
}
HugeUint.sol 428 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
/**
* @notice A library for manipulating uint512 quantities.
* @dev The 512-bit unsigned integers are represented as two uint256 "limbs", a `hi` limb for the most significant bits,
* and a `lo` limb for the least-significant bits. The resulting uint512 quantity is obtained with `hi * 2^256 + lo`.
*/
library HugeUint {
/// @notice Indicates that the division failed because the divisor is zero or the result overflows a uint256.
error HugeUintDivisionFailed();
/// @notice Indicates that the addition overflowed a uint512.
error HugeUintAddOverflow();
/// @notice Indicates that the subtraction underflowed.
error HugeUintSubUnderflow();
/// @notice Indicates that the multiplication overflowed a uint512.
error HugeUintMulOverflow();
/**
* @notice A 512-bit integer represented as two 256-bit limbs.
* @dev The integer value can be reconstructed as `hi * 2^256 + lo`.
* @param hi The most-significant bits (higher limb) of the integer.
* @param lo The least-significant bits (lower limb) of the integer.
*/
struct Uint512 {
uint256 hi;
uint256 lo;
}
/**
* @notice Wraps a uint256 into a {Uint512} integer.
* @param x A uint256 integer.
* @return The same value as a 512-bit integer.
*/
function wrap(uint256 x) internal pure returns (Uint512 memory) {
return Uint512({ hi: 0, lo: x });
}
/**
* @notice Calculates the sum `a + b` of two 512-bit unsigned integers.
* @dev This function will revert if the result overflows a uint512.
* @param a The first operand.
* @param b The second operand.
* @return res_ The sum of `a` and `b`.
*/
function add(Uint512 memory a, Uint512 memory b) internal pure returns (Uint512 memory res_) {
(res_.lo, res_.hi) = _add(a.lo, a.hi, b.lo, b.hi);
// check for overflow, i.e. if the result is less than b
if (res_.hi < b.hi || (res_.hi == b.hi && res_.lo < b.lo)) {
revert HugeUintAddOverflow();
}
}
/**
* @notice Calculates the difference `a - b` of two 512-bit unsigned integers.
* @dev This function will revert if `b > a`.
* @param a The first operand.
* @param b The second operand.
* @return res_ The difference `a - b`.
*/
function sub(Uint512 memory a, Uint512 memory b) internal pure returns (Uint512 memory res_) {
// check for underflow
if (a.hi < b.hi || (a.hi == b.hi && a.lo < b.lo)) {
revert HugeUintSubUnderflow();
}
(res_.lo, res_.hi) = _sub(a.lo, a.hi, b.lo, b.hi);
}
/**
* @notice Calculates the product `a * b` of two 256-bit unsigned integers using the Chinese remainder theorem.
* @param a The first operand.
* @param b The second operand.
* @return res_ The product `a * b` of the operands as an unsigned 512-bit integer.
*/
function mul(uint256 a, uint256 b) internal pure returns (Uint512 memory res_) {
(res_.lo, res_.hi) = _mul256(a, b);
}
/**
* @notice Calculates the product `a * b` of a 512-bit unsigned integer and a 256-bit unsigned integer.
* @dev This function reverts if the result overflows a uint512.
* @param a The first operand.
* @param b The second operand.
* @return res_ The product `a * b` of the operands as an unsigned 512-bit integer.
*/
function mul(Uint512 memory a, uint256 b) internal pure returns (Uint512 memory res_) {
if ((a.hi == 0 && a.lo == 0) || b == 0) {
return res_;
}
(res_.lo, res_.hi) = _mul256(a.lo, b);
unchecked {
uint256 p = a.hi * b;
if (p / b != a.hi) {
revert HugeUintMulOverflow();
}
res_.hi += p;
if (res_.hi < p) {
revert HugeUintMulOverflow();
}
}
}
/**
* @notice Calculates the division `floor(a / b)` of a 512-bit unsigned integer by an unsigned 256-bit integer.
* @dev The call will revert if the result doesn't fit inside a uint256 or if the denominator is zero.
* @param a The numerator as a 512-bit unsigned integer.
* @param b The denominator as a 256-bit unsigned integer.
* @return res_ The division `floor(a / b)` of the operands as an unsigned 256-bit integer.
*/
function div(Uint512 memory a, uint256 b) internal pure returns (uint256 res_) {
// make sure the output fits inside a uint256, also prevents b == 0
if (b <= a.hi) {
revert HugeUintDivisionFailed();
}
// if the numerator is smaller than the denominator, the result is zero
if (a.hi == 0 && a.lo < b) {
return 0;
}
// the first operand fits in 256 bits, we can use the Solidity division operator
if (a.hi == 0) {
unchecked {
return a.lo / b;
}
}
res_ = _div256(a.lo, a.hi, b);
}
/**
* @notice Computes the division `floor(a/b)` of two 512-bit integers, knowing the result fits inside a uint256.
* @dev Credits chfast (Apache 2.0 License): <https://github.com/chfast/intx>.
* This function will revert if the second operand is zero or if the result doesn't fit inside a uint256.
* @param a The numerator as a 512-bit integer.
* @param b The denominator as a 512-bit integer.
* @return res_ The quotient floor(a/b).
*/
function div(Uint512 memory a, Uint512 memory b) internal pure returns (uint256 res_) {
res_ = _div(a.lo, a.hi, b.lo, b.hi);
}
/**
* @notice Calculates the sum `a + b` of two 512-bit unsigned integers.
* @dev Credits Remco Bloemen (MIT license): <https://2π.com/17/512-bit-division>.
* The result is not checked for overflow, the caller must ensure that the result fits inside a uint512.
* @param a0 The low limb of the first operand.
* @param a1 The high limb of the first operand.
* @param b0 The low limb of the second operand.
* @param b1 The high limb of the second operand.
* @return lo_ The low limb of the result of `a + b`.
* @return hi_ The high limb of the result of `a + b`.
*/
function _add(uint256 a0, uint256 a1, uint256 b0, uint256 b1) internal pure returns (uint256 lo_, uint256 hi_) {
assembly {
lo_ := add(a0, b0)
hi_ := add(add(a1, b1), lt(lo_, a0))
}
}
/**
* @notice Calculates the difference `a - b` of two 512-bit unsigned integers.
* @dev Credits Remco Bloemen (MIT license): <https://2π.com/17/512-bit-division>.
* The result is not checked for underflow, the caller must ensure that the second operand is less than or equal to
* the first operand.
* @param a0 The low limb of the first operand.
* @param a1 The high limb of the first operand.
* @param b0 The low limb of the second operand.
* @param b1 The high limb of the second operand.
* @return lo_ The low limb of the result of `a - b`.
* @return hi_ The high limb of the result of `a - b`.
*/
function _sub(uint256 a0, uint256 a1, uint256 b0, uint256 b1) internal pure returns (uint256 lo_, uint256 hi_) {
assembly {
lo_ := sub(a0, b0)
hi_ := sub(sub(a1, b1), lt(a0, b0))
}
}
/**
* @notice Calculates the product `a * b` of two 256-bit unsigned integers using the Chinese remainder theorem.
* @dev Credits Remco Bloemen (MIT license): <https://2π.com/17/chinese-remainder-theorem>
* and Solady (MIT license): <https://github.com/Vectorized/solady>.
* @param a The first operand.
* @param b The second operand.
* @return lo_ The low limb of the result of `a * b`.
* @return hi_ The high limb of the result of `a * b`.
*/
function _mul256(uint256 a, uint256 b) internal pure returns (uint256 lo_, uint256 hi_) {
assembly {
lo_ := mul(a, b)
let mm := mulmod(a, b, not(0)) // (a * b) % uint256.max
hi_ := sub(mm, add(lo_, lt(mm, lo_)))
}
}
/**
* @notice Calculates the division `floor(a / b)` of a 512-bit unsigned integer by an unsigned 256-bit integer.
* @dev Credits Solady (MIT license): <https://github.com/Vectorized/solady>.
* The caller must ensure that the result fits inside a uint256 and that the division is non-zero.
* For performance reasons, the caller should ensure that the numerator high limb (hi) is non-zero.
* @param a0 The low limb of the numerator.
* @param a1 The high limb of the numerator.
* @param b The denominator as a 256-bit unsigned integer.
* @return res_ The division `floor(a / b)` of the operands as an unsigned 256-bit integer.
*/
function _div256(uint256 a0, uint256 a1, uint256 b) internal pure returns (uint256 res_) {
uint256 r;
assembly {
// to make the division exact, we find out the remainder of the division of a by b
r := mulmod(a1, not(0), b) // (a1 * uint256.max) % b
r := addmod(r, a1, b) // (r + a1) % b
r := addmod(r, a0, b) // (r + a0) % b
// `t` is the least significant bit of `b`
// always greater or equal to 1
let t := and(b, sub(0, b))
// divide `b` by `t`, which is a power of two
b := div(b, t)
// invert `b mod 2**256`
// now that `b` is an odd number, it has an inverse
// modulo `2**256` such that `b * inv = 1 mod 2**256`
// compute the inverse by starting with a seed that is
// correct for four bits. That is, `b * inv = 1 mod 2**4`
let inv := xor(2, mul(3, b))
// now use 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
inv := mul(inv, sub(2, mul(b, inv))) // inverse mod 2**8
inv := mul(inv, sub(2, mul(b, inv))) // inverse mod 2**16
inv := mul(inv, sub(2, mul(b, inv))) // inverse mod 2**32
inv := mul(inv, sub(2, mul(b, inv))) // inverse mod 2**64
inv := mul(inv, sub(2, mul(b, inv))) // inverse mod 2**128
res_ :=
mul(
// divide [a1 a0] by the factors of two
// shift in bits from `a1` into `a0`
// for this we need to flip `t` such that it is `2**256 / t`
or(mul(sub(a1, gt(r, a0)), add(div(sub(0, t), t), 1)), div(sub(a0, r), t)),
// inverse mod 2**256
mul(inv, sub(2, mul(b, inv)))
)
}
}
/**
* @notice Computes the division of a 768-bit integer `a` by a 512-bit integer `b`, knowing the reciprocal of `b`.
* @dev Credits chfast (Apache 2.0 License): <https://github.com/chfast/intx>.
* @param a0 The LSB of the numerator.
* @param a1 The middle limb of the numerator.
* @param a2 The MSB of the numerator.
* @param b0 The low limb of the divisor.
* @param b1 The high limb of the divisor.
* @param v The reciprocal `v` as defined in `_reciprocal_2`.
* @return The quotient floor(a/b).
*/
function _div_2(uint256 a0, uint256 a1, uint256 a2, uint256 b0, uint256 b1, uint256 v)
internal
pure
returns (uint256)
{
(uint256 q0, uint256 q1) = _mul256(v, a2);
(q0, q1) = _add(q0, q1, a1, a2);
(uint256 t0, uint256 t1) = _mul256(b0, q1);
uint256 r1;
assembly {
r1 := sub(a1, mul(q1, b1))
}
uint256 r0;
(r0, r1) = _sub(a0, r1, t0, t1);
(r0, r1) = _sub(r0, r1, b0, b1);
assembly {
q1 := add(q1, 1)
}
if (r1 >= q0) {
assembly {
q1 := sub(q1, 1)
}
(r0, r1) = _add(r0, r1, b0, b1);
}
if (r1 > b1 || (r1 == b1 && r0 >= b0)) {
assembly {
q1 := add(q1, 1)
}
// we don't care about the remainder
// (r0, r1) = _sub(r0, r1, b0, b1);
}
return q1;
}
/**
* @notice Computes the division floor(a/b) of two 512-bit integers, knowing the result fits inside a uint256.
* @dev Credits chfast (Apache 2.0 License): <https://github.com/chfast/intx>.
* @param a0 LSB of the numerator.
* @param a1 MSB of the numerator.
* @param b0 LSB of the divisor.
* @param b1 MSB of the divisor.
* @return res_ The quotient floor(a/b).
*/
function _div(uint256 a0, uint256 a1, uint256 b0, uint256 b1) internal pure returns (uint256 res_) {
if (b1 == 0) {
// prevent division by zero
if (b0 == 0) {
revert HugeUintDivisionFailed();
}
// if both operands fit inside a uint256, we can use the Solidity division operator
if (a1 == 0) {
unchecked {
return a0 / b0;
}
}
// if the result fits inside a uint256, we can use the `div(Uint512,uint256)` function
if (b0 > a1) {
return _div256(a0, a1, b0);
}
revert HugeUintDivisionFailed();
}
// if the numerator is smaller than the denominator, the result is zero
if (a1 < b1 || (a1 == b1 && a0 < b0)) {
return 0;
}
// division algo
uint256 lsh = _clz(b1);
if (lsh == 0) {
// numerator is equal or larger than the denominator, and the denominator is at least 0b1000...
// the result is necessarily 1
return 1;
}
uint256 bn_lo;
uint256 bn_hi;
uint256 an_lo;
uint256 an_hi;
uint256 an_ex;
assembly {
let rsh := sub(256, lsh)
bn_lo := shl(lsh, b0)
bn_hi := or(shl(lsh, b1), shr(rsh, b0))
an_lo := shl(lsh, a0)
an_hi := or(shl(lsh, a1), shr(rsh, a0))
an_ex := shr(rsh, a1)
}
uint256 v = _reciprocal_2(bn_lo, bn_hi);
res_ = _div_2(an_lo, an_hi, an_ex, bn_lo, bn_hi, v);
}
/**
* @notice Computes the reciprocal `v = floor((2^512-1) / d) - 2^256`.
* @dev The input must be normalized (d >= 2^255).
* @param d The input value.
* @return v_ The reciprocal of d.
*/
function _reciprocal(uint256 d) internal pure returns (uint256 v_) {
if (d & 0x8000000000000000000000000000000000000000000000000000000000000000 == 0) {
revert HugeUintDivisionFailed();
}
v_ = _div256(type(uint256).max, type(uint256).max - d, d);
}
/**
* @notice Computes the reciprocal `v = floor((2^768-1) / d) - 2^256`, where d is a uint512 integer.
* @dev Credits chfast (Apache 2.0 License): <https://github.com/chfast/intx>.
* @param d0 LSB of the input.
* @param d1 MSB of the input.
* @return v_ The reciprocal of d.
*/
function _reciprocal_2(uint256 d0, uint256 d1) internal pure returns (uint256 v_) {
v_ = _reciprocal(d1);
uint256 p;
assembly {
p := mul(d1, v_)
p := add(p, d0)
if lt(p, d0) {
// carry out
v_ := sub(v_, 1)
if iszero(lt(p, d1)) {
v_ := sub(v_, 1)
p := sub(p, d1)
}
p := sub(p, d1)
}
}
(uint256 t0, uint256 t1) = _mul256(v_, d0);
assembly {
p := add(p, t1)
if lt(p, t1) {
// carry out
v_ := sub(v_, 1)
if and(iszero(lt(p, d1)), or(gt(p, d1), iszero(lt(t0, d0)))) {
// if (<p, t0> >= <d1, d0>)
v_ := sub(v_, 1)
}
}
}
}
/**
* @notice Counts the number of consecutive zero bits, starting from the left.
* @dev Credits Solady (MIT license): <https://github.com/Vectorized/solady>.
* @param x An unsigned integer.
* @return n_ The number of zeroes starting from the most significant bit.
*/
function _clz(uint256 x) internal pure returns (uint256 n_) {
if (x == 0) {
return 256;
}
assembly {
n_ := shl(7, lt(0xffffffffffffffffffffffffffffffff, x))
n_ := or(n_, shl(6, lt(0xffffffffffffffff, shr(n_, x))))
n_ := or(n_, shl(5, lt(0xffffffff, shr(n_, x))))
n_ := or(n_, shl(4, lt(0xffff, shr(n_, x))))
n_ := or(n_, shl(3, lt(0xff, shr(n_, x))))
n_ :=
add(
xor(
n_,
byte(
and(0x1f, shr(shr(n_, x), 0x8421084210842108cc6318c6db6d54be)),
0xf8f9f9faf9fdfafbf9fdfcfdfafbfcfef9fafdfafcfcfbfefafafcfbffffffff
)
),
iszero(x)
)
}
}
}
LibBitmap.sol 236 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
import {LibBit} from "./LibBit.sol";
/// @notice Library for storage of packed unsigned booleans.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibBitmap.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/LibBitmap.sol)
/// @author Modified from Solidity-Bits (https://github.com/estarriolvetch/solidity-bits/blob/main/contracts/BitMaps.sol)
library LibBitmap {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CONSTANTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The constant returned when a bitmap scan does not find a result.
uint256 internal constant NOT_FOUND = type(uint256).max;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* STRUCTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev A bitmap in storage.
struct Bitmap {
mapping(uint256 => uint256) map;
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the boolean value of the bit at `index` in `bitmap`.
function get(Bitmap storage bitmap, uint256 index) internal view returns (bool isSet) {
// It is better to set `isSet` to either 0 or 1, than zero vs non-zero.
// Both cost the same amount of gas, but the former allows the returned value
// to be reused without cleaning the upper bits.
uint256 b = (bitmap.map[index >> 8] >> (index & 0xff)) & 1;
/// @solidity memory-safe-assembly
assembly {
isSet := b
}
}
/// @dev Updates the bit at `index` in `bitmap` to true.
function set(Bitmap storage bitmap, uint256 index) internal {
bitmap.map[index >> 8] |= (1 << (index & 0xff));
}
/// @dev Updates the bit at `index` in `bitmap` to false.
function unset(Bitmap storage bitmap, uint256 index) internal {
bitmap.map[index >> 8] &= ~(1 << (index & 0xff));
}
/// @dev Flips the bit at `index` in `bitmap`.
/// Returns the boolean result of the flipped bit.
function toggle(Bitmap storage bitmap, uint256 index) internal returns (bool newIsSet) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x20, bitmap.slot)
mstore(0x00, shr(8, index))
let storageSlot := keccak256(0x00, 0x40)
let shift := and(index, 0xff)
let storageValue := xor(sload(storageSlot), shl(shift, 1))
// It makes sense to return the `newIsSet`,
// as it allow us to skip an additional warm `sload`,
// and it costs minimal gas (about 15),
// which may be optimized away if the returned value is unused.
newIsSet := and(1, shr(shift, storageValue))
sstore(storageSlot, storageValue)
}
}
/// @dev Updates the bit at `index` in `bitmap` to `shouldSet`.
function setTo(Bitmap storage bitmap, uint256 index, bool shouldSet) internal {
/// @solidity memory-safe-assembly
assembly {
mstore(0x20, bitmap.slot)
mstore(0x00, shr(8, index))
let storageSlot := keccak256(0x00, 0x40)
let storageValue := sload(storageSlot)
let shift := and(index, 0xff)
sstore(
storageSlot,
// Unsets the bit at `shift` via `and`, then sets its new value via `or`.
or(and(storageValue, not(shl(shift, 1))), shl(shift, iszero(iszero(shouldSet))))
)
}
}
/// @dev Consecutively sets `amount` of bits starting from the bit at `start`.
function setBatch(Bitmap storage bitmap, uint256 start, uint256 amount) internal {
/// @solidity memory-safe-assembly
assembly {
let max := not(0)
let shift := and(start, 0xff)
mstore(0x20, bitmap.slot)
mstore(0x00, shr(8, start))
if iszero(lt(add(shift, amount), 257)) {
let storageSlot := keccak256(0x00, 0x40)
sstore(storageSlot, or(sload(storageSlot), shl(shift, max)))
let bucket := add(mload(0x00), 1)
let bucketEnd := add(mload(0x00), shr(8, add(amount, shift)))
amount := and(add(amount, shift), 0xff)
shift := 0
for {} iszero(eq(bucket, bucketEnd)) { bucket := add(bucket, 1) } {
mstore(0x00, bucket)
sstore(keccak256(0x00, 0x40), max)
}
mstore(0x00, bucket)
}
let storageSlot := keccak256(0x00, 0x40)
sstore(storageSlot, or(sload(storageSlot), shl(shift, shr(sub(256, amount), max))))
}
}
/// @dev Consecutively unsets `amount` of bits starting from the bit at `start`.
function unsetBatch(Bitmap storage bitmap, uint256 start, uint256 amount) internal {
/// @solidity memory-safe-assembly
assembly {
let shift := and(start, 0xff)
mstore(0x20, bitmap.slot)
mstore(0x00, shr(8, start))
if iszero(lt(add(shift, amount), 257)) {
let storageSlot := keccak256(0x00, 0x40)
sstore(storageSlot, and(sload(storageSlot), not(shl(shift, not(0)))))
let bucket := add(mload(0x00), 1)
let bucketEnd := add(mload(0x00), shr(8, add(amount, shift)))
amount := and(add(amount, shift), 0xff)
shift := 0
for {} iszero(eq(bucket, bucketEnd)) { bucket := add(bucket, 1) } {
mstore(0x00, bucket)
sstore(keccak256(0x00, 0x40), 0)
}
mstore(0x00, bucket)
}
let storageSlot := keccak256(0x00, 0x40)
sstore(
storageSlot, and(sload(storageSlot), not(shl(shift, shr(sub(256, amount), not(0)))))
)
}
}
/// @dev Returns number of set bits within a range by
/// scanning `amount` of bits starting from the bit at `start`.
function popCount(Bitmap storage bitmap, uint256 start, uint256 amount)
internal
view
returns (uint256 count)
{
unchecked {
uint256 bucket = start >> 8;
uint256 shift = start & 0xff;
if (!(amount + shift < 257)) {
count = LibBit.popCount(bitmap.map[bucket] >> shift);
uint256 bucketEnd = bucket + ((amount + shift) >> 8);
amount = (amount + shift) & 0xff;
shift = 0;
for (++bucket; bucket != bucketEnd; ++bucket) {
count += LibBit.popCount(bitmap.map[bucket]);
}
}
count += LibBit.popCount((bitmap.map[bucket] >> shift) << (256 - amount));
}
}
/// @dev Returns the index of the most significant set bit in `[0..upTo]`.
/// If no set bit is found, returns `NOT_FOUND`.
function findLastSet(Bitmap storage bitmap, uint256 upTo)
internal
view
returns (uint256 setBitIndex)
{
setBitIndex = NOT_FOUND;
uint256 bucket = upTo >> 8;
uint256 bits;
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, bucket)
mstore(0x20, bitmap.slot)
let offset := and(0xff, not(upTo)) // `256 - (255 & upTo) - 1`.
bits := shr(offset, shl(offset, sload(keccak256(0x00, 0x40))))
if iszero(or(bits, iszero(bucket))) {
for {} 1 {} {
bucket := add(bucket, setBitIndex) // `sub(bucket, 1)`.
mstore(0x00, bucket)
bits := sload(keccak256(0x00, 0x40))
if or(bits, iszero(bucket)) { break }
}
}
}
if (bits != 0) {
setBitIndex = (bucket << 8) | LibBit.fls(bits);
/// @solidity memory-safe-assembly
assembly {
setBitIndex := or(setBitIndex, sub(0, gt(setBitIndex, upTo)))
}
}
}
/// @dev Returns the index of the least significant unset bit in `[begin..upTo]`.
/// If no unset bit is found, returns `NOT_FOUND`.
function findFirstUnset(Bitmap storage bitmap, uint256 begin, uint256 upTo)
internal
view
returns (uint256 unsetBitIndex)
{
unsetBitIndex = NOT_FOUND;
uint256 bucket = begin >> 8;
uint256 negBits;
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, bucket)
mstore(0x20, bitmap.slot)
let offset := and(0xff, begin)
negBits := shl(offset, shr(offset, not(sload(keccak256(0x00, 0x40)))))
if iszero(negBits) {
let lastBucket := shr(8, upTo)
for {} 1 {} {
bucket := add(bucket, 1)
mstore(0x00, bucket)
negBits := not(sload(keccak256(0x00, 0x40)))
if or(negBits, gt(bucket, lastBucket)) { break }
}
if gt(bucket, lastBucket) {
negBits := shl(and(0xff, not(upTo)), shr(and(0xff, not(upTo)), negBits))
}
}
}
if (negBits != 0) {
uint256 r = (bucket << 8) | LibBit.ffs(negBits);
/// @solidity memory-safe-assembly
assembly {
unsetBitIndex := or(r, sub(0, or(gt(r, upTo), lt(r, begin))))
}
}
}
}
DoubleEndedQueue.sol 247 lines
// SPDX-License-Identifier: MIT
// based on the OpenZeppelin implementation
pragma solidity ^0.8.20;
import { IUsdnProtocolTypes as Types } from "../interfaces/UsdnProtocol/IUsdnProtocolTypes.sol";
/**
* @notice A sequence of items with the ability to efficiently push and pop items (i.e. insert and remove) on both ends
* of the sequence (called front and back).
* @dev Storage use is optimized, and all operations are O(1) constant time.
*
* The struct is called `Deque` and holds {IUsdnProtocolTypes.PendingAction}'s. This data structure can only be used in
* storage, and not in memory.
*/
library DoubleEndedQueue {
/// @dev An operation (e.g. {front}) couldn't be completed due to the queue being empty.
error QueueEmpty();
/// @dev A push operation couldn't be completed due to the queue being full.
error QueueFull();
/// @dev An operation (e.g. {atRaw}) couldn't be completed due to an index being out of bounds.
error QueueOutOfBounds();
/**
* @dev Indices are 128 bits so begin and end are packed in a single storage slot for efficient access.
*
* Struct members have an underscore prefix indicating that they are "private" and should not be read or written to
* directly. Use the functions provided below instead. Modifying the struct manually may violate assumptions and
* lead to unexpected behavior.
*
* The first item is at `data[begin]` and the last item is at `data[end - 1]`. This range can wrap around.
* @param _begin The index of the first item in the queue.
* @param _end The index of the item after the last item in the queue.
* @param _data The items in the queue.
*/
struct Deque {
uint128 _begin;
uint128 _end;
mapping(uint128 index => Types.PendingAction) _data;
}
/**
* @dev Inserts an item at the end of the queue.
* Reverts with {QueueFull} if the queue is full.
* @param deque The queue.
* @param value The item to insert.
* @return backIndex_ The raw index of the inserted item.
*/
function pushBack(Deque storage deque, Types.PendingAction memory value) external returns (uint128 backIndex_) {
unchecked {
backIndex_ = deque._end;
if (backIndex_ + 1 == deque._begin) {
revert QueueFull();
}
deque._data[backIndex_] = value;
deque._end = backIndex_ + 1;
}
}
/**
* @dev Removes the item at the end of the queue and returns it.
* Reverts with {QueueEmpty} if the queue is empty.
* @param deque The queue.
* @return value_ The removed item.
*/
function popBack(Deque storage deque) public returns (Types.PendingAction memory value_) {
unchecked {
uint128 backIndex = deque._end;
if (backIndex == deque._begin) {
revert QueueEmpty();
}
--backIndex;
value_ = deque._data[backIndex];
delete deque._data[backIndex];
deque._end = backIndex;
}
}
/**
* @dev Inserts an item at the beginning of the queue.
* Reverts with {QueueFull} if the queue is full.
* @param deque The queue.
* @param value The item to insert.
* @return frontIndex_ The raw index of the inserted item.
*/
function pushFront(Deque storage deque, Types.PendingAction memory value) external returns (uint128 frontIndex_) {
unchecked {
frontIndex_ = deque._begin - 1;
if (frontIndex_ == deque._end) {
revert QueueFull();
}
deque._data[frontIndex_] = value;
deque._begin = frontIndex_;
}
}
/**
* @dev Removes the item at the beginning of the queue and returns it.
* Reverts with {QueueEmpty} if the queue is empty.
* @param deque The queue.
* @return value_ The removed item.
*/
function popFront(Deque storage deque) public returns (Types.PendingAction memory value_) {
unchecked {
uint128 frontIndex = deque._begin;
if (frontIndex == deque._end) {
revert QueueEmpty();
}
value_ = deque._data[frontIndex];
delete deque._data[frontIndex];
deque._begin = frontIndex + 1;
}
}
/**
* @dev Returns the item at the beginning of the queue.
* Reverts with {QueueEmpty} if the queue is empty.
* @param deque The queue.
* @return value_ The item at the front of the queue.
* @return rawIndex_ The raw index of the returned item.
*/
function front(Deque storage deque) external view returns (Types.PendingAction memory value_, uint128 rawIndex_) {
if (empty(deque)) {
revert QueueEmpty();
}
rawIndex_ = deque._begin;
value_ = deque._data[rawIndex_];
}
/**
* @dev Returns the item at the end of the queue.
* Reverts with {QueueEmpty} if the queue is empty.
* @param deque The queue.
* @return value_ The item at the back of the queue.
* @return rawIndex_ The raw index of the returned item.
*/
function back(Deque storage deque) external view returns (Types.PendingAction memory value_, uint128 rawIndex_) {
if (empty(deque)) {
revert QueueEmpty();
}
unchecked {
rawIndex_ = deque._end - 1;
value_ = deque._data[rawIndex_];
}
}
/**
* @dev Returns the item at a position in the queue given by `index`, with the first item at 0 and the last item at
* `length(deque) - 1`.
* Reverts with {QueueOutOfBounds} if the index is out of bounds.
* @param deque The queue.
* @param index The index of the item to return.
* @return value_ The item at the given index.
* @return rawIndex_ The raw index of the item.
*/
function at(Deque storage deque, uint256 index)
external
view
returns (Types.PendingAction memory value_, uint128 rawIndex_)
{
if (index >= length(deque)) {
revert QueueOutOfBounds();
}
// by construction, length is a uint128, so the check above ensures that
// the index can be safely downcast to a uint128
unchecked {
rawIndex_ = deque._begin + uint128(index);
value_ = deque._data[rawIndex_];
}
}
/**
* @dev Returns the item at a position in the queue given by `rawIndex`, indexing into the underlying storage array
* directly.
* Reverts with {QueueOutOfBounds} if the index is out of bounds.
* @param deque The queue.
* @param rawIndex The index of the item to return.
* @return value_ The item at the given index.
*/
function atRaw(Deque storage deque, uint128 rawIndex) external view returns (Types.PendingAction memory value_) {
if (!isValid(deque, rawIndex)) {
revert QueueOutOfBounds();
}
value_ = deque._data[rawIndex];
}
/**
* @dev Deletes the item at a position in the queue given by `rawIndex`, indexing into the underlying storage array
* directly. If clearing the front or back item, then the bounds are updated. Otherwise, the values are simply set
* to zero and the queue's begin and end indices are not updated.
* @param deque The queue.
* @param rawIndex The index of the item to delete.
*/
function clearAt(Deque storage deque, uint128 rawIndex) external {
uint128 backIndex = deque._end;
unchecked {
backIndex--;
}
if (rawIndex == deque._begin) {
popFront(deque); // reverts if empty
} else if (rawIndex == backIndex) {
popBack(deque); // reverts if empty
} else {
// we don't care to revert if this is not a valid index, since we're just clearing it
delete deque._data[rawIndex];
}
}
/**
* @dev Checks if the raw index is valid (in bounds).
* @param deque The queue.
* @param rawIndex The raw index to check.
* @return valid_ Whether the raw index is valid.
*/
function isValid(Deque storage deque, uint128 rawIndex) public view returns (bool valid_) {
if (deque._begin > deque._end) {
// here the values are split at the beginning and end of the range, so invalid indices are in the middle
if (rawIndex < deque._begin && rawIndex >= deque._end) {
return false;
}
} else if (rawIndex < deque._begin || rawIndex >= deque._end) {
return false;
}
valid_ = true;
}
/**
* @dev Returns the number of items in the queue.
* @param deque The queue.
* @return length_ The number of items in the queue.
*/
function length(Deque storage deque) public view returns (uint256 length_) {
unchecked {
length_ = uint256(deque._end - deque._begin);
}
}
/**
* @dev Returns true if the queue is empty.
* @param deque The queue.
* @return empty_ True if the queue is empty.
*/
function empty(Deque storage deque) internal view returns (bool empty_) {
empty_ = deque._end == deque._begin;
}
}
IBaseLiquidationRewardsManager.sol 33 lines
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
import { IUsdnProtocolTypes as Types } from "../UsdnProtocol/IUsdnProtocolTypes.sol";
/**
* @title IBaseLiquidationRewardsManager
* @notice This interface exposes the only function used by the UsdnProtocol.
* @dev Future implementations of the rewards manager must implement this interface without modifications.
*/
interface IBaseLiquidationRewardsManager {
/**
* @notice Computes the amount of assets to reward a liquidator.
* @param liquidatedTicks Information about the liquidated ticks.
* @param currentPrice The current price of the asset.
* @param rebased Indicates whether a USDN rebase was performed.
* @param rebalancerAction The action performed by the {UsdnProtocolLongLibrary._triggerRebalancer} function.
* @param action The type of protocol action that triggered the liquidation.
* @param rebaseCallbackResult The result of the rebase callback, if any.
* @param priceData The oracle price data, if any. This can be used to differentiate rewards based on the oracle
* used to provide the liquidation price.
* @return assetRewards_ The amount of asset tokens to reward the liquidator.
*/
function getLiquidationRewards(
Types.LiqTickInfo[] calldata liquidatedTicks,
uint256 currentPrice,
bool rebased,
Types.RebalancerAction rebalancerAction,
Types.ProtocolAction action,
bytes calldata rebaseCallbackResult,
bytes calldata priceData
) external view returns (uint256 assetRewards_);
}
IBaseOracleMiddleware.sol 63 lines
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
import { IUsdnProtocolTypes as Types } from "../UsdnProtocol/IUsdnProtocolTypes.sol";
import { PriceInfo } from "./IOracleMiddlewareTypes.sol";
/**
* @title Base Oracle Middleware interface
* @notice This interface exposes the only functions used or required by the USDN Protocol.
* @dev Any current or future implementation of the oracle middleware must be compatible with
* this interface without any modification.
*/
interface IBaseOracleMiddleware {
/**
* @notice Parse and validate `data` and returns the corresponding price data.
* @dev The data format is specific to the middleware and is simply forwarded from the user transaction's calldata.
* A fee amounting to exactly {validationCost} (with the same `data` and `action`) must be sent or the transaction
* will revert.
* @param actionId A unique identifier for the current action. This identifier can be used to link an `Initiate`
* call with the corresponding `Validate` call.
* @param targetTimestamp The target timestamp for validating the price data. For validation actions, this is the
* timestamp of the initiation.
* @param action Type of action for which the price is requested. The middleware may use this to alter the
* validation of the price or the returned price.
* @param data The data to be used to communicate with oracles, the format varies from middleware to middleware and
* can be different depending on the action.
* @return result_ The price and timestamp as {IOracleMiddlewareTypes.PriceInfo}.
*/
function parseAndValidatePrice(
bytes32 actionId,
uint128 targetTimestamp,
Types.ProtocolAction action,
bytes calldata data
) external payable returns (PriceInfo memory result_);
/**
* @notice Gets the required delay (in seconds) between the moment an action is initiated and the timestamp of the
* price data used to validate that action.
* @return delay_ The validation delay.
*/
function getValidationDelay() external view returns (uint256 delay_);
/**
* @notice Gets The maximum amount of time (in seconds) after initiation during which a low-latency price oracle can
* be used for validation.
* @return delay_ The maximum delay for low-latency validation.
*/
function getLowLatencyDelay() external view returns (uint16 delay_);
/**
* @notice Gets the number of decimals for the price.
* @return decimals_ The number of decimals.
*/
function getDecimals() external view returns (uint8 decimals_);
/**
* @notice Returns the cost of one price validation for the given action (in native token).
* @param data Price data for which to get the fee.
* @param action Type of the action for which the price is requested.
* @return cost_ The cost of one price validation (in native token).
*/
function validationCost(bytes calldata data, Types.ProtocolAction action) external view returns (uint256 cost_);
}
IBaseRebalancer.sol 51 lines
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
import { IUsdnProtocolTypes as Types } from "../UsdnProtocol/IUsdnProtocolTypes.sol";
import { IRebalancerTypes } from "./IRebalancerTypes.sol";
interface IBaseRebalancer {
/**
* @notice Returns the necessary data for the USDN protocol to update the position.
* @return pendingAssets_ The amount of assets that are pending inclusion in the protocol.
* @return maxLeverage_ The maximum leverage of the rebalancer.
* @return currentPosId_ The ID of the current position (`tick` == `NO_POSITION_TICK` if no position).
*/
function getCurrentStateData()
external
view
returns (uint128 pendingAssets_, uint256 maxLeverage_, Types.PositionId memory currentPosId_);
/**
* @notice Returns the minimum amount of assets a user can deposit in the rebalancer.
* @return minAssetDeposit_ The minimum amount of assets that can be deposited by a user.
*/
function getMinAssetDeposit() external view returns (uint256 minAssetDeposit_);
/**
* @notice Returns the data regarding the assets deposited by the provided user.
* @param user The address of the user.
* @return data_ The data regarding the assets deposited by the provided user.
*/
function getUserDepositData(address user) external view returns (IRebalancerTypes.UserDeposit memory data_);
/**
* @notice Indicates that the previous version of the position was closed and a new one was opened.
* @dev If `previousPosValue` equals 0, it means the previous version got liquidated.
* @param newPosId The position ID of the new position.
* @param previousPosValue The amount of assets left in the previous position.
*/
function updatePosition(Types.PositionId calldata newPosId, uint128 previousPosValue) external;
/* -------------------------------------------------------------------------- */
/* Admin */
/* -------------------------------------------------------------------------- */
/**
* @notice Sets the minimum amount of assets to be deposited by a user.
* @dev The new minimum amount must be greater than or equal to the minimum long position of the USDN protocol.
* This function can only be called by the owner or the USDN protocol.
* @param minAssetDeposit The new minimum amount of assets to be deposited.
*/
function setMinAssetDeposit(uint256 minAssetDeposit) external;
}
IUsdn.sol 201 lines
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import { IERC20Permit } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol";
import { IRebaseCallback } from "./IRebaseCallback.sol";
import { IUsdnErrors } from "./IUsdnErrors.sol";
import { IUsdnEvents } from "./IUsdnEvents.sol";
/**
* @title USDN token interface
* @notice Implements the ERC-20 token standard as well as the EIP-2612 permit extension. Additional functions related
* to the specifics of this token are included below.
*/
interface IUsdn is IERC20, IERC20Metadata, IERC20Permit, IUsdnEvents, IUsdnErrors {
/**
* @notice Returns the total number of shares in existence.
* @return shares_ The number of shares.
*/
function totalShares() external view returns (uint256 shares_);
/**
* @notice Returns the number of shares owned by `account`.
* @param account The account to query.
* @return shares_ The number of shares.
*/
function sharesOf(address account) external view returns (uint256 shares_);
/**
* @notice Transfers a given amount of shares from the `msg.sender` to `to`.
* @param to Recipient of the shares.
* @param value Number of shares to transfer.
* @return success_ Indicates whether the transfer was successfully executed.
*/
function transferShares(address to, uint256 value) external returns (bool success_);
/**
* @notice Transfers a given amount of shares from the `from` to `to`.
* @dev There should be sufficient allowance for the spender. Be mindful of the rebase logic. The allowance is in
* tokens. So, after a rebase, the same amount of shares will be worth a higher amount of tokens. In that case,
* the allowance of the initial approval will not be enough to transfer the new amount of tokens. This can
* also happen when your transaction is in the mempool and the rebase happens before your transaction. Also note
* that the amount of tokens deduced from the allowance is rounded up, so the `convertToTokensRoundUp` function
* should be used when converting shares into an allowance value.
* @param from The owner of the shares.
* @param to Recipient of the shares.
* @param value Number of shares to transfer.
* @return success_ Indicates whether the transfer was successfully executed.
*/
function transferSharesFrom(address from, address to, uint256 value) external returns (bool success_);
/**
* @notice Mints new shares, providing a token value.
* @dev Caller must have the MINTER_ROLE.
* @param to Account to receive the new shares.
* @param amount Amount of tokens to mint, is internally converted to the proper shares amounts.
*/
function mint(address to, uint256 amount) external;
/**
* @notice Mints new shares, providing a share value.
* @dev Caller must have the MINTER_ROLE.
* @param to Account to receive the new shares.
* @param amount Amount of shares to mint.
* @return mintedTokens_ Amount of tokens that were minted (informational).
*/
function mintShares(address to, uint256 amount) external returns (uint256 mintedTokens_);
/**
* @notice Destroys a `value` amount of tokens from the caller, reducing the total supply.
* @param value Amount of tokens to burn, is internally converted to the proper shares amounts.
*/
function burn(uint256 value) external;
/**
* @notice Destroys a `value` amount of tokens from `account`, deducting from the caller's allowance.
* @param account Account to burn tokens from.
* @param value Amount of tokens to burn, is internally converted to the proper shares amounts.
*/
function burnFrom(address account, uint256 value) external;
/**
* @notice Destroys a `value` amount of shares from the caller, reducing the total supply.
* @param value Amount of shares to burn.
*/
function burnShares(uint256 value) external;
/**
* @notice Destroys a `value` amount of shares from `account`, deducting from the caller's allowance.
* @dev There should be sufficient allowance for the spender. Be mindful of the rebase logic. The allowance is in
* tokens. So, after a rebase, the same amount of shares will be worth a higher amount of tokens. In that case,
* the allowance of the initial approval will not be enough to transfer the new amount of tokens. This can
* also happen when your transaction is in the mempool and the rebase happens before your transaction. Also note
* that the amount of tokens deduced from the allowance is rounded up, so the `convertToTokensRoundUp` function
* should be used when converting shares into an allowance value.
* @param account Account to burn shares from.
* @param value Amount of shares to burn.
*/
function burnSharesFrom(address account, uint256 value) external;
/**
* @notice Converts a number of tokens to the corresponding amount of shares.
* @dev The conversion reverts with `UsdnMaxTokensExceeded` if the corresponding amount of shares overflows.
* @param amountTokens The amount of tokens to convert to shares.
* @return shares_ The corresponding amount of shares.
*/
function convertToShares(uint256 amountTokens) external view returns (uint256 shares_);
/**
* @notice Converts a number of shares to the corresponding amount of tokens.
* @dev The conversion never overflows as we are performing a division. The conversion rounds to the nearest amount
* of tokens that minimizes the error when converting back to shares.
* @param amountShares The amount of shares to convert to tokens.
* @return tokens_ The corresponding amount of tokens.
*/
function convertToTokens(uint256 amountShares) external view returns (uint256 tokens_);
/**
* @notice Converts a number of shares to the corresponding amount of tokens, rounding up.
* @dev Use this function to determine the amount of a token approval, as we always round up when deducting from
* a token transfer allowance.
* @param amountShares The amount of shares to convert to tokens.
* @return tokens_ The corresponding amount of tokens, rounded up.
*/
function convertToTokensRoundUp(uint256 amountShares) external view returns (uint256 tokens_);
/**
* @notice Returns the current maximum tokens supply, given the current divisor.
* @dev This function is used to check if a conversion operation would overflow.
* @return maxTokens_ The maximum number of tokens that can exist.
*/
function maxTokens() external view returns (uint256 maxTokens_);
/**
* @notice Decreases the global divisor, which effectively grows all balances and the total supply.
* @dev If the provided divisor is larger than or equal to the current divisor value, no rebase will happen
* If the new divisor is smaller than `MIN_DIVISOR`, the value will be clamped to `MIN_DIVISOR`.
* Caller must have the `REBASER_ROLE`.
* @param newDivisor The new divisor, should be strictly smaller than the current one and greater or equal to
* `MIN_DIVISOR`.
* @return rebased_ Whether a rebase happened.
* @return oldDivisor_ The previous value of the divisor.
* @return callbackResult_ The result of the callback, if a rebase happened and a callback handler is defined.
*/
function rebase(uint256 newDivisor)
external
returns (bool rebased_, uint256 oldDivisor_, bytes memory callbackResult_);
/**
* @notice Sets the rebase handler address.
* @dev Emits a `RebaseHandlerUpdated` event.
* If set to the zero address, no handler will be called after a rebase.
* Caller must have the `DEFAULT_ADMIN_ROLE`.
* @param newHandler The new handler address.
*/
function setRebaseHandler(IRebaseCallback newHandler) external;
/* -------------------------------------------------------------------------- */
/* Dev view functions */
/* -------------------------------------------------------------------------- */
/**
* @notice Gets the current value of the divisor that converts between tokens and shares.
* @return divisor_ The current divisor.
*/
function divisor() external view returns (uint256 divisor_);
/**
* @notice Gets the rebase handler address, which is called whenever a rebase happens.
* @return rebaseHandler_ The rebase handler address.
*/
function rebaseHandler() external view returns (IRebaseCallback rebaseHandler_);
/**
* @notice Gets the minter role signature.
* @return minter_role_ The role signature.
*/
function MINTER_ROLE() external pure returns (bytes32 minter_role_);
/**
* @notice Gets the rebaser role signature.
* @return rebaser_role_ The role signature.
*/
function REBASER_ROLE() external pure returns (bytes32 rebaser_role_);
/**
* @notice Gets the maximum value of the divisor, which is also the initial value.
* @return maxDivisor_ The maximum divisor.
*/
function MAX_DIVISOR() external pure returns (uint256 maxDivisor_);
/**
* @notice Gets the minimum acceptable value of the divisor.
* @dev The minimum divisor that can be set. This corresponds to a growth of 1B times. Technically, 1e5 would still
* work without precision errors.
* @return minDivisor_ The minimum divisor.
*/
function MIN_DIVISOR() external pure returns (uint256 minDivisor_);
}
AccessControlDefaultAdminRules.sol 396 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/extensions/AccessControlDefaultAdminRules.sol)
pragma solidity ^0.8.20;
import {IAccessControlDefaultAdminRules} from "./IAccessControlDefaultAdminRules.sol";
import {AccessControl, IAccessControl} from "../AccessControl.sol";
import {SafeCast} from "../../utils/math/SafeCast.sol";
import {Math} from "../../utils/math/Math.sol";
import {IERC5313} from "../../interfaces/IERC5313.sol";
/**
* @dev Extension of {AccessControl} that allows specifying special rules to manage
* the `DEFAULT_ADMIN_ROLE` holder, which is a sensitive role with special permissions
* over other roles that may potentially have privileged rights in the system.
*
* If a specific role doesn't have an admin role assigned, the holder of the
* `DEFAULT_ADMIN_ROLE` will have the ability to grant it and revoke it.
*
* This contract implements the following risk mitigations on top of {AccessControl}:
*
* * Only one account holds the `DEFAULT_ADMIN_ROLE` since deployment until it's potentially renounced.
* * Enforces a 2-step process to transfer the `DEFAULT_ADMIN_ROLE` to another account.
* * Enforces a configurable delay between the two steps, with the ability to cancel before the transfer is accepted.
* * The delay can be changed by scheduling, see {changeDefaultAdminDelay}.
* * It is not possible to use another role to manage the `DEFAULT_ADMIN_ROLE`.
*
* Example usage:
*
* ```solidity
* contract MyToken is AccessControlDefaultAdminRules {
* constructor() AccessControlDefaultAdminRules(
* 3 days,
* msg.sender // Explicit initial `DEFAULT_ADMIN_ROLE` holder
* ) {}
* }
* ```
*/
abstract contract AccessControlDefaultAdminRules is IAccessControlDefaultAdminRules, IERC5313, AccessControl {
// pending admin pair read/written together frequently
address private _pendingDefaultAdmin;
uint48 private _pendingDefaultAdminSchedule; // 0 == unset
uint48 private _currentDelay;
address private _currentDefaultAdmin;
// pending delay pair read/written together frequently
uint48 private _pendingDelay;
uint48 private _pendingDelaySchedule; // 0 == unset
/**
* @dev Sets the initial values for {defaultAdminDelay} and {defaultAdmin} address.
*/
constructor(uint48 initialDelay, address initialDefaultAdmin) {
if (initialDefaultAdmin == address(0)) {
revert AccessControlInvalidDefaultAdmin(address(0));
}
_currentDelay = initialDelay;
_grantRole(DEFAULT_ADMIN_ROLE, initialDefaultAdmin);
}
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IAccessControlDefaultAdminRules).interfaceId || super.supportsInterface(interfaceId);
}
/**
* @dev See {IERC5313-owner}.
*/
function owner() public view virtual returns (address) {
return defaultAdmin();
}
///
/// Override AccessControl role management
///
/**
* @dev See {AccessControl-grantRole}. Reverts for `DEFAULT_ADMIN_ROLE`.
*/
function grantRole(bytes32 role, address account) public virtual override(AccessControl, IAccessControl) {
if (role == DEFAULT_ADMIN_ROLE) {
revert AccessControlEnforcedDefaultAdminRules();
}
super.grantRole(role, account);
}
/**
* @dev See {AccessControl-revokeRole}. Reverts for `DEFAULT_ADMIN_ROLE`.
*/
function revokeRole(bytes32 role, address account) public virtual override(AccessControl, IAccessControl) {
if (role == DEFAULT_ADMIN_ROLE) {
revert AccessControlEnforcedDefaultAdminRules();
}
super.revokeRole(role, account);
}
/**
* @dev See {AccessControl-renounceRole}.
*
* For the `DEFAULT_ADMIN_ROLE`, it only allows renouncing in two steps by first calling
* {beginDefaultAdminTransfer} to the `address(0)`, so it's required that the {pendingDefaultAdmin} schedule
* has also passed when calling this function.
*
* After its execution, it will not be possible to call `onlyRole(DEFAULT_ADMIN_ROLE)` functions.
*
* NOTE: Renouncing `DEFAULT_ADMIN_ROLE` will leave the contract without a {defaultAdmin},
* thereby disabling any functionality that is only available for it, and the possibility of reassigning a
* non-administrated role.
*/
function renounceRole(bytes32 role, address account) public virtual override(AccessControl, IAccessControl) {
if (role == DEFAULT_ADMIN_ROLE && account == defaultAdmin()) {
(address newDefaultAdmin, uint48 schedule) = pendingDefaultAdmin();
if (newDefaultAdmin != address(0) || !_isScheduleSet(schedule) || !_hasSchedulePassed(schedule)) {
revert AccessControlEnforcedDefaultAdminDelay(schedule);
}
delete _pendingDefaultAdminSchedule;
}
super.renounceRole(role, account);
}
/**
* @dev See {AccessControl-_grantRole}.
*
* For `DEFAULT_ADMIN_ROLE`, it only allows granting if there isn't already a {defaultAdmin} or if the
* role has been previously renounced.
*
* NOTE: Exposing this function through another mechanism may make the `DEFAULT_ADMIN_ROLE`
* assignable again. Make sure to guarantee this is the expected behavior in your implementation.
*/
function _grantRole(bytes32 role, address account) internal virtual override returns (bool) {
if (role == DEFAULT_ADMIN_ROLE) {
if (defaultAdmin() != address(0)) {
revert AccessControlEnforcedDefaultAdminRules();
}
_currentDefaultAdmin = account;
}
return super._grantRole(role, account);
}
/**
* @dev See {AccessControl-_revokeRole}.
*/
function _revokeRole(bytes32 role, address account) internal virtual override returns (bool) {
if (role == DEFAULT_ADMIN_ROLE && account == defaultAdmin()) {
delete _currentDefaultAdmin;
}
return super._revokeRole(role, account);
}
/**
* @dev See {AccessControl-_setRoleAdmin}. Reverts for `DEFAULT_ADMIN_ROLE`.
*/
function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual override {
if (role == DEFAULT_ADMIN_ROLE) {
revert AccessControlEnforcedDefaultAdminRules();
}
super._setRoleAdmin(role, adminRole);
}
///
/// AccessControlDefaultAdminRules accessors
///
/**
* @inheritdoc IAccessControlDefaultAdminRules
*/
function defaultAdmin() public view virtual returns (address) {
return _currentDefaultAdmin;
}
/**
* @inheritdoc IAccessControlDefaultAdminRules
*/
function pendingDefaultAdmin() public view virtual returns (address newAdmin, uint48 schedule) {
return (_pendingDefaultAdmin, _pendingDefaultAdminSchedule);
}
/**
* @inheritdoc IAccessControlDefaultAdminRules
*/
function defaultAdminDelay() public view virtual returns (uint48) {
uint48 schedule = _pendingDelaySchedule;
return (_isScheduleSet(schedule) && _hasSchedulePassed(schedule)) ? _pendingDelay : _currentDelay;
}
/**
* @inheritdoc IAccessControlDefaultAdminRules
*/
function pendingDefaultAdminDelay() public view virtual returns (uint48 newDelay, uint48 schedule) {
schedule = _pendingDelaySchedule;
return (_isScheduleSet(schedule) && !_hasSchedulePassed(schedule)) ? (_pendingDelay, schedule) : (0, 0);
}
/**
* @inheritdoc IAccessControlDefaultAdminRules
*/
function defaultAdminDelayIncreaseWait() public view virtual returns (uint48) {
return 5 days;
}
///
/// AccessControlDefaultAdminRules public and internal setters for defaultAdmin/pendingDefaultAdmin
///
/**
* @inheritdoc IAccessControlDefaultAdminRules
*/
function beginDefaultAdminTransfer(address newAdmin) public virtual onlyRole(DEFAULT_ADMIN_ROLE) {
_beginDefaultAdminTransfer(newAdmin);
}
/**
* @dev See {beginDefaultAdminTransfer}.
*
* Internal function without access restriction.
*/
function _beginDefaultAdminTransfer(address newAdmin) internal virtual {
uint48 newSchedule = SafeCast.toUint48(block.timestamp) + defaultAdminDelay();
_setPendingDefaultAdmin(newAdmin, newSchedule);
emit DefaultAdminTransferScheduled(newAdmin, newSchedule);
}
/**
* @inheritdoc IAccessControlDefaultAdminRules
*/
function cancelDefaultAdminTransfer() public virtual onlyRole(DEFAULT_ADMIN_ROLE) {
_cancelDefaultAdminTransfer();
}
/**
* @dev See {cancelDefaultAdminTransfer}.
*
* Internal function without access restriction.
*/
function _cancelDefaultAdminTransfer() internal virtual {
_setPendingDefaultAdmin(address(0), 0);
}
/**
* @inheritdoc IAccessControlDefaultAdminRules
*/
function acceptDefaultAdminTransfer() public virtual {
(address newDefaultAdmin, ) = pendingDefaultAdmin();
if (_msgSender() != newDefaultAdmin) {
// Enforce newDefaultAdmin explicit acceptance.
revert AccessControlInvalidDefaultAdmin(_msgSender());
}
_acceptDefaultAdminTransfer();
}
/**
* @dev See {acceptDefaultAdminTransfer}.
*
* Internal function without access restriction.
*/
function _acceptDefaultAdminTransfer() internal virtual {
(address newAdmin, uint48 schedule) = pendingDefaultAdmin();
if (!_isScheduleSet(schedule) || !_hasSchedulePassed(schedule)) {
revert AccessControlEnforcedDefaultAdminDelay(schedule);
}
_revokeRole(DEFAULT_ADMIN_ROLE, defaultAdmin());
_grantRole(DEFAULT_ADMIN_ROLE, newAdmin);
delete _pendingDefaultAdmin;
delete _pendingDefaultAdminSchedule;
}
///
/// AccessControlDefaultAdminRules public and internal setters for defaultAdminDelay/pendingDefaultAdminDelay
///
/**
* @inheritdoc IAccessControlDefaultAdminRules
*/
function changeDefaultAdminDelay(uint48 newDelay) public virtual onlyRole(DEFAULT_ADMIN_ROLE) {
_changeDefaultAdminDelay(newDelay);
}
/**
* @dev See {changeDefaultAdminDelay}.
*
* Internal function without access restriction.
*/
function _changeDefaultAdminDelay(uint48 newDelay) internal virtual {
uint48 newSchedule = SafeCast.toUint48(block.timestamp) + _delayChangeWait(newDelay);
_setPendingDelay(newDelay, newSchedule);
emit DefaultAdminDelayChangeScheduled(newDelay, newSchedule);
}
/**
* @inheritdoc IAccessControlDefaultAdminRules
*/
function rollbackDefaultAdminDelay() public virtual onlyRole(DEFAULT_ADMIN_ROLE) {
_rollbackDefaultAdminDelay();
}
/**
* @dev See {rollbackDefaultAdminDelay}.
*
* Internal function without access restriction.
*/
function _rollbackDefaultAdminDelay() internal virtual {
_setPendingDelay(0, 0);
}
/**
* @dev Returns the amount of seconds to wait after the `newDelay` will
* become the new {defaultAdminDelay}.
*
* The value returned guarantees that if the delay is reduced, it will go into effect
* after a wait that honors the previously set delay.
*
* See {defaultAdminDelayIncreaseWait}.
*/
function _delayChangeWait(uint48 newDelay) internal view virtual returns (uint48) {
uint48 currentDelay = defaultAdminDelay();
// When increasing the delay, we schedule the delay change to occur after a period of "new delay" has passed, up
// to a maximum given by defaultAdminDelayIncreaseWait, by default 5 days. For example, if increasing from 1 day
// to 3 days, the new delay will come into effect after 3 days. If increasing from 1 day to 10 days, the new
// delay will come into effect after 5 days. The 5 day wait period is intended to be able to fix an error like
// using milliseconds instead of seconds.
//
// When decreasing the delay, we wait the difference between "current delay" and "new delay". This guarantees
// that an admin transfer cannot be made faster than "current delay" at the time the delay change is scheduled.
// For example, if decreasing from 10 days to 3 days, the new delay will come into effect after 7 days.
return
newDelay > currentDelay
? uint48(Math.min(newDelay, defaultAdminDelayIncreaseWait())) // no need to safecast, both inputs are uint48
: currentDelay - newDelay;
}
///
/// Private setters
///
/**
* @dev Setter of the tuple for pending admin and its schedule.
*
* May emit a DefaultAdminTransferCanceled event.
*/
function _setPendingDefaultAdmin(address newAdmin, uint48 newSchedule) private {
(, uint48 oldSchedule) = pendingDefaultAdmin();
_pendingDefaultAdmin = newAdmin;
_pendingDefaultAdminSchedule = newSchedule;
// An `oldSchedule` from `pendingDefaultAdmin()` is only set if it hasn't been accepted.
if (_isScheduleSet(oldSchedule)) {
// Emit for implicit cancellations when another default admin was scheduled.
emit DefaultAdminTransferCanceled();
}
}
/**
* @dev Setter of the tuple for pending delay and its schedule.
*
* May emit a DefaultAdminDelayChangeCanceled event.
*/
function _setPendingDelay(uint48 newDelay, uint48 newSchedule) private {
uint48 oldSchedule = _pendingDelaySchedule;
if (_isScheduleSet(oldSchedule)) {
if (_hasSchedulePassed(oldSchedule)) {
// Materialize a virtual delay
_currentDelay = _pendingDelay;
} else {
// Emit for implicit cancellations when another delay was scheduled.
emit DefaultAdminDelayChangeCanceled();
}
}
_pendingDelay = newDelay;
_pendingDelaySchedule = newSchedule;
}
///
/// Private helpers
///
/**
* @dev Defines if an `schedule` is considered set. For consistency purposes.
*/
function _isScheduleSet(uint48 schedule) private pure returns (bool) {
return schedule != 0;
}
/**
* @dev Defines if an `schedule` is considered passed. For consistency purposes.
*/
function _hasSchedulePassed(uint48 schedule) private view returns (bool) {
return schedule < block.timestamp;
}
}
IOracleMiddleware.sol 105 lines
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
import { IUsdnProtocol } from "../UsdnProtocol/IUsdnProtocol.sol";
import { IBaseOracleMiddleware } from "./IBaseOracleMiddleware.sol";
import { IChainlinkOracle } from "./IChainlinkOracle.sol";
import { IOracleMiddlewareErrors } from "./IOracleMiddlewareErrors.sol";
import { IOracleMiddlewareEvents } from "./IOracleMiddlewareEvents.sol";
import { IPythOracle } from "./IPythOracle.sol";
/**
* @notice The oracle middleware is a contract that is used by the USDN protocol to validate price data.
* Using a middleware allows the protocol to later upgrade to a new oracle logic without having to modify
* the protocol's contracts.
*/
interface IOracleMiddleware is
IChainlinkOracle,
IPythOracle,
IBaseOracleMiddleware,
IOracleMiddlewareErrors,
IOracleMiddlewareEvents
{
/* -------------------------------------------------------------------------- */
/* Roles */
/* -------------------------------------------------------------------------- */
/**
* @notice Gets the admin role's signature.
* @return role_ Get the role signature.
*/
function ADMIN_ROLE() external pure returns (bytes32 role_);
/* -------------------------------------------------------------------------- */
/* Constants */
/* -------------------------------------------------------------------------- */
/**
* @notice Gets the denominator for the variables using basis points as a unit.
* @return denominator_ The BPS divisor.
*/
function BPS_DIVISOR() external pure returns (uint16 denominator_);
/**
* @notice Gets the maximum value for `_confRatioBps`.
* @return ratio_ The max allowed confidence ratio.
*/
function MAX_CONF_RATIO() external pure returns (uint16 ratio_);
/* -------------------------------------------------------------------------- */
/* Generic features */
/* -------------------------------------------------------------------------- */
/**
* @notice Gets the confidence ratio.
* @dev This ratio is used to apply a specific portion of the confidence interval provided by an oracle, which is
* used to adjust the precision of predictions or estimations.
* @return ratio_ The confidence ratio (in basis points).
*/
function getConfRatioBps() external view returns (uint16 ratio_);
/* -------------------------------------------------------------------------- */
/* Owner features */
/* -------------------------------------------------------------------------- */
/**
* @notice Sets the confidence ratio.
* @dev The new value should be lower than {MAX_CONF_RATIO}.
* @param newConfRatio the new confidence ratio.
*/
function setConfRatio(uint16 newConfRatio) external;
/**
* @notice Sets the elapsed time tolerated before we consider the price from Chainlink invalid.
* @param newTimeElapsedLimit The new time elapsed limit.
*/
function setChainlinkTimeElapsedLimit(uint256 newTimeElapsedLimit) external;
/**
* @notice Sets the amount of time after which we do not consider a price as recent.
* @param newDelay The maximum age of a price to be considered recent.
*/
function setPythRecentPriceDelay(uint64 newDelay) external;
/**
* @notice Sets the validation delay (in seconds) between an action timestamp and the price
* data timestamp used to validate that action.
* @param newValidationDelay The new validation delay.
*/
function setValidationDelay(uint256 newValidationDelay) external;
/**
* @notice Sets the new low latency delay.
* @param newLowLatencyDelay The new low latency delay.
* @param usdnProtocol The address of the USDN protocol.
*/
function setLowLatencyDelay(uint16 newLowLatencyDelay, IUsdnProtocol usdnProtocol) external;
/**
* @notice Withdraws the ether balance of this contract.
* @dev This contract can receive funds but is not designed to hold them.
* So this function can be used if there's an error and funds remain after a call.
* @param to The address to send the ether to.
*/
function withdrawEther(address to) external;
}
IUsdnProtocol.sol 21 lines
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
import { IUsdnProtocolFallback } from "./IUsdnProtocolFallback.sol";
import { IUsdnProtocolImpl } from "./IUsdnProtocolImpl.sol";
/**
* @title IUsdnProtocol
* @notice Interface for the USDN protocol and fallback.
*/
interface IUsdnProtocol is IUsdnProtocolImpl, IUsdnProtocolFallback {
/**
* @notice Upgrades the protocol to a new implementation (check
* [UUPSUpgradeable](https://docs.openzeppelin.com/contracts/5.x/api/proxy#UUPSUpgradeable)).
* @dev This function should be called by the role with the PROXY_UPGRADE_ROLE.
* @param newImplementation The address of the new implementation.
* @param data The data to call when upgrading to the new implementation. Passing in empty data skips the
* delegatecall to `newImplementation`.
*/
function upgradeToAndCall(address newImplementation, bytes calldata data) external payable;
}
ChainlinkOracle.sol 111 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.26;
import { AggregatorV3Interface } from "@chainlink/contracts/src/v0.8/shared/interfaces/AggregatorV3Interface.sol";
import { IChainlinkOracle } from "../../interfaces/OracleMiddleware/IChainlinkOracle.sol";
import { IOracleMiddlewareErrors } from "../../interfaces/OracleMiddleware/IOracleMiddlewareErrors.sol";
import { ChainlinkPriceInfo } from "../../interfaces/OracleMiddleware/IOracleMiddlewareTypes.sol";
/**
* @title The Contract Communicating With the Chainlink Oracle Price Feed Contracts.
* @notice This contract is used to get the price of an asset from Chainlink. It is used by the USDN protocol to get the
* price of the USDN underlying asset, and by the LiquidationRewardsManager to get the price of the gas.
*/
abstract contract ChainlinkOracle is IChainlinkOracle, IOracleMiddlewareErrors {
/// @inheritdoc IChainlinkOracle
int256 public constant PRICE_TOO_OLD = type(int256).min;
/// @notice The Chainlink price feed aggregator contract.
AggregatorV3Interface internal immutable _priceFeed;
/// @notice Tolerated elapsed time until we consider the data too old.
// slither-disable-next-line immutable-states
uint256 internal _timeElapsedLimit;
/**
* @param chainlinkPriceFeed Address of the price feed.
* @param timeElapsedLimit Tolerated elapsed time before the data is considered invalid.
*/
constructor(address chainlinkPriceFeed, uint256 timeElapsedLimit) {
_priceFeed = AggregatorV3Interface(chainlinkPriceFeed);
_timeElapsedLimit = timeElapsedLimit;
}
/// @inheritdoc IChainlinkOracle
function getChainlinkDecimals() public view returns (uint256 decimals_) {
return _priceFeed.decimals();
}
/// @inheritdoc IChainlinkOracle
function getPriceFeed() public view returns (AggregatorV3Interface priceFeed_) {
return _priceFeed;
}
/// @inheritdoc IChainlinkOracle
function getChainlinkTimeElapsedLimit() external view returns (uint256 limit_) {
return _timeElapsedLimit;
}
/**
* @notice Gets the latest price of the asset from Chainlink.
* @dev If the price is too old, the returned price will be equal to `PRICE_TOO_OLD`.
* @return price_ The price of the asset.
*/
function _getChainlinkLatestPrice() internal view virtual returns (ChainlinkPriceInfo memory price_) {
(, int256 price,, uint256 timestamp,) = _priceFeed.latestRoundData();
if (timestamp < block.timestamp - _timeElapsedLimit) {
price = PRICE_TOO_OLD;
}
price_ = ChainlinkPriceInfo({ price: price, timestamp: timestamp });
}
/**
* @notice Gets the latest price of the asset from Chainlink, formatted to the specified number of decimals.
* @param middlewareDecimals The number of decimals to format the price to.
* @return formattedPrice_ The formatted price of the asset.
*/
function _getFormattedChainlinkLatestPrice(uint256 middlewareDecimals)
internal
view
returns (ChainlinkPriceInfo memory formattedPrice_)
{
uint8 oracleDecimals = _priceFeed.decimals();
formattedPrice_ = _getChainlinkLatestPrice();
if (formattedPrice_.price == PRICE_TOO_OLD) {
return formattedPrice_;
}
formattedPrice_.price = formattedPrice_.price * int256(10 ** middlewareDecimals) / int256(10 ** oracleDecimals);
}
/**
* @notice Gets the price of the asset at the specified round ID, formatted to the specified number of decimals.
* @param middlewareDecimals The number of decimals to format the price to.
* @param roundId The targeted round ID.
* @return formattedPrice_ The formatted price of the asset.
*/
function _getFormattedChainlinkPrice(uint256 middlewareDecimals, uint80 roundId)
internal
view
returns (ChainlinkPriceInfo memory formattedPrice_)
{
uint8 oracleDecimals = _priceFeed.decimals();
formattedPrice_ = _getChainlinkPrice(roundId);
if (formattedPrice_.price <= 0) {
return formattedPrice_;
}
formattedPrice_.price = formattedPrice_.price * int256(10 ** middlewareDecimals) / int256(10 ** oracleDecimals);
}
/**
* @notice Gets the price of the asset at the specified round ID.
* @param roundId The Chainlink roundId price.
* @return price_ The price of the asset.
*/
function _getChainlinkPrice(uint80 roundId) internal view virtual returns (ChainlinkPriceInfo memory price_) {
(, price_.price,, price_.timestamp,) = _priceFeed.getRoundData(roundId);
}
}
PythOracle.sol 191 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.26;
import { IPyth } from "@pythnetwork/pyth-sdk-solidity/IPyth.sol";
import { PythStructs } from "@pythnetwork/pyth-sdk-solidity/PythStructs.sol";
import { IOracleMiddlewareErrors } from "../../interfaces/OracleMiddleware/IOracleMiddlewareErrors.sol";
import { FormattedPythPrice } from "../../interfaces/OracleMiddleware/IOracleMiddlewareTypes.sol";
import { IPythOracle } from "../../interfaces/OracleMiddleware/IPythOracle.sol";
/**
* @title Contract To Communicate With The Pyth Oracle
* @notice This contract is used to get the price of the asset that corresponds to the stored feed ID.
* @dev Is implemented by the {OracleMiddleware} contract.
*/
abstract contract PythOracle is IPythOracle, IOracleMiddlewareErrors {
/// @notice The ID of the Pyth price feed.
bytes32 internal immutable _pythFeedId;
/// @notice The address of the Pyth contract.
IPyth internal immutable _pyth;
/// @notice The maximum age of a recent price to be considered valid.
uint64 internal _pythRecentPriceDelay = 45 seconds;
/**
* @param pythAddress The address of the Pyth contract.
* @param pythFeedId The ID of the Pyth price feed.
*/
constructor(address pythAddress, bytes32 pythFeedId) {
_pyth = IPyth(pythAddress);
_pythFeedId = pythFeedId;
}
/// @inheritdoc IPythOracle
function getPyth() external view returns (IPyth) {
return _pyth;
}
/// @inheritdoc IPythOracle
function getPythFeedId() external view returns (bytes32) {
return _pythFeedId;
}
/// @inheritdoc IPythOracle
function getPythRecentPriceDelay() external view returns (uint64) {
return _pythRecentPriceDelay;
}
/**
* @notice Gets the price of the asset from the stored Pyth price feed.
* @param priceUpdateData The data required to update the price feed.
* @param targetTimestamp The timestamp of the price in the given `priceUpdateData`.
* If zero, then we accept all recent prices.
* @param targetLimit The most recent timestamp a price can have.
* Can be zero if `targetTimestamp` is zero.
* @return price_ The raw price of the asset returned by Pyth.
*/
function _getPythPrice(bytes calldata priceUpdateData, uint128 targetTimestamp, uint128 targetLimit)
internal
returns (PythStructs.Price memory)
{
// parse the price feed update and get the price feed
bytes32[] memory feedIds = new bytes32[](1);
feedIds[0] = _pythFeedId;
bytes[] memory pricesUpdateData = new bytes[](1);
pricesUpdateData[0] = priceUpdateData;
uint256 pythFee = _pyth.getUpdateFee(pricesUpdateData);
// sanity check on the fee requested by Pyth
if (pythFee > 0.01 ether) {
revert OracleMiddlewarePythFeeSafeguard(pythFee);
}
if (msg.value != pythFee) {
revert OracleMiddlewareIncorrectFee();
}
PythStructs.PriceFeed[] memory priceFeeds;
if (targetTimestamp == 0) {
// we want to validate that the price is recent
// we don't enforce that the price update is the first one in a given second
// slither-disable-next-line arbitrary-send-eth
priceFeeds = _pyth.parsePriceFeedUpdates{ value: pythFee }(
pricesUpdateData, feedIds, uint64(block.timestamp) - _pythRecentPriceDelay, uint64(block.timestamp)
);
} else {
// we want to validate that the price is exactly at `targetTimestamp` (first in the second) or the next
// available price in the future, as identified by the prevPublishTime being strictly less than
// targetTimestamp
// we add a sanity check that this price update cannot be too late (more than `_lowLatencyDelay` seconds
// late) compared to the desired targetTimestamp
priceFeeds = _pyth.parsePriceFeedUpdatesUnique{ value: pythFee }(
pricesUpdateData, feedIds, uint64(targetTimestamp), uint64(targetLimit)
);
}
if (priceFeeds[0].price.price <= 0) {
revert OracleMiddlewareWrongPrice(priceFeeds[0].price.price);
}
return priceFeeds[0].price;
}
/**
* @notice Gets the price of the asset from Pyth, formatted to the specified number of decimals.
* @param priceUpdateData The data required to update the price feed.
* @param targetTimestamp The timestamp of the price in the given `priceUpdateData`.
* If zero, then we accept all recent prices.
* @param middlewareDecimals The number of decimals to format the price to.
* @param targetLimit The most recent timestamp a price can have.
* Can be zero if `targetTimestamp` is zero.
* @return price_ The Pyth price formatted with `middlewareDecimals`.
*/
function _getFormattedPythPrice(
bytes calldata priceUpdateData,
uint128 targetTimestamp,
uint256 middlewareDecimals,
uint128 targetLimit
) internal returns (FormattedPythPrice memory price_) {
// this call checks that the price is strictly positive
PythStructs.Price memory pythPrice = _getPythPrice(priceUpdateData, targetTimestamp, targetLimit);
if (pythPrice.expo > 0) {
revert OracleMiddlewarePythPositiveExponent(pythPrice.expo);
}
price_ = _formatPythPrice(pythPrice, middlewareDecimals);
}
/**
* @notice Formats a Pyth price object to normalize to the specified number of decimals.
* @param pythPrice A Pyth price object.
* @param middlewareDecimals The number of decimals to format the price to.
* @return price_ The Pyth price formatted with `middlewareDecimals`.
*/
function _formatPythPrice(PythStructs.Price memory pythPrice, uint256 middlewareDecimals)
internal
pure
returns (FormattedPythPrice memory price_)
{
uint256 pythDecimals = uint32(-pythPrice.expo);
price_ = FormattedPythPrice({
price: uint256(int256(pythPrice.price)) * 10 ** middlewareDecimals / 10 ** pythDecimals,
conf: uint256(pythPrice.conf) * 10 ** middlewareDecimals / 10 ** pythDecimals,
publishTime: pythPrice.publishTime
});
}
/**
* @notice Gets the fee required to update the price feed.
* @param priceUpdateData The data required to update the price feed.
* @return updateFee_ The fee required to update the price feed.
*/
function _getPythUpdateFee(bytes calldata priceUpdateData) internal view returns (uint256) {
bytes[] memory pricesUpdateData = new bytes[](1);
pricesUpdateData[0] = priceUpdateData;
return _pyth.getUpdateFee(pricesUpdateData);
}
/**
* @notice Gets the latest seen (cached) price from the Pyth contract.
* @param middlewareDecimals The number of decimals for the returned price.
* @return price_ The formatted cached Pyth price, or all-zero values if there was no valid Pyth price on-chain.
*/
function _getLatestStoredPythPrice(uint256 middlewareDecimals)
internal
view
returns (FormattedPythPrice memory price_)
{
// we use getPriceUnsafe to get the latest price without reverting, no matter how old
PythStructs.Price memory pythPrice;
// if the proxy implementation changes, this can revert
try _pyth.getPriceUnsafe(_pythFeedId) returns (PythStructs.Price memory unsafePrice_) {
pythPrice = unsafePrice_;
} catch { }
// negative or zero prices are considered invalid, we return zero
if (pythPrice.price <= 0) {
return price_;
}
if (pythPrice.expo > 0) {
revert OracleMiddlewarePythPositiveExponent(pythPrice.expo);
}
price_ = _formatPythPrice(pythPrice, middlewareDecimals);
}
}
IERC20.sol 79 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC-20 standard as defined in the ERC.
*/
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 value of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the value of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves a `value` amount of 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 value) 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 a `value` amount of tokens 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 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the
* allowance mechanism. `value` 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 value) external returns (bool);
}
LibBit.sol 180 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Library for bit twiddling and boolean operations.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibBit.sol)
/// @author Inspired by (https://graphics.stanford.edu/~seander/bithacks.html)
library LibBit {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* BIT TWIDDLING OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Find last set.
/// Returns the index of the most significant bit of `x`,
/// counting from the least significant bit position.
/// If `x` is zero, returns 256.
function fls(uint256 x) internal pure returns (uint256 r) {
/// @solidity memory-safe-assembly
assembly {
r := or(shl(8, iszero(x)), shl(7, lt(0xffffffffffffffffffffffffffffffff, x)))
r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x))))
r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
r := or(r, shl(4, lt(0xffff, shr(r, x))))
r := or(r, shl(3, lt(0xff, shr(r, x))))
// forgefmt: disable-next-item
r := or(r, byte(and(0x1f, shr(shr(r, x), 0x8421084210842108cc6318c6db6d54be)),
0x0706060506020504060203020504030106050205030304010505030400000000))
}
}
/// @dev Count leading zeros.
/// Returns the number of zeros preceding the most significant one bit.
/// If `x` is zero, returns 256.
function clz(uint256 x) internal pure returns (uint256 r) {
/// @solidity memory-safe-assembly
assembly {
r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x))
r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x))))
r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
r := or(r, shl(4, lt(0xffff, shr(r, x))))
r := or(r, shl(3, lt(0xff, shr(r, x))))
// forgefmt: disable-next-item
r := add(xor(r, byte(and(0x1f, shr(shr(r, x), 0x8421084210842108cc6318c6db6d54be)),
0xf8f9f9faf9fdfafbf9fdfcfdfafbfcfef9fafdfafcfcfbfefafafcfbffffffff)), iszero(x))
}
}
/// @dev Find first set.
/// Returns the index of the least significant bit of `x`,
/// counting from the least significant bit position.
/// If `x` is zero, returns 256.
/// Equivalent to `ctz` (count trailing zeros), which gives
/// the number of zeros following the least significant one bit.
function ffs(uint256 x) internal pure returns (uint256 r) {
/// @solidity memory-safe-assembly
assembly {
// Isolate the least significant bit.
x := and(x, add(not(x), 1))
// For the upper 3 bits of the result, use a De Bruijn-like lookup.
// Credit to adhusson: https://blog.adhusson.com/cheap-find-first-set-evm/
// forgefmt: disable-next-item
r := shl(5, shr(252, shl(shl(2, shr(250, mul(x,
0xb6db6db6ddddddddd34d34d349249249210842108c6318c639ce739cffffffff))),
0x8040405543005266443200005020610674053026020000107506200176117077)))
// For the lower 5 bits of the result, use a De Bruijn lookup.
// forgefmt: disable-next-item
r := or(r, byte(and(div(0xd76453e0, shr(r, x)), 0x1f),
0x001f0d1e100c1d070f090b19131c1706010e11080a1a141802121b1503160405))
}
}
/// @dev Returns the number of set bits in `x`.
function popCount(uint256 x) internal pure returns (uint256 c) {
/// @solidity memory-safe-assembly
assembly {
let max := not(0)
let isMax := eq(x, max)
x := sub(x, and(shr(1, x), div(max, 3)))
x := add(and(x, div(max, 5)), and(shr(2, x), div(max, 5)))
x := and(add(x, shr(4, x)), div(max, 17))
c := or(shl(8, isMax), shr(248, mul(x, div(max, 255))))
}
}
/// @dev Returns whether `x` is a power of 2.
function isPo2(uint256 x) internal pure returns (bool result) {
/// @solidity memory-safe-assembly
assembly {
// Equivalent to `x && !(x & (x - 1))`.
result := iszero(add(and(x, sub(x, 1)), iszero(x)))
}
}
/// @dev Returns `x` reversed at the bit level.
function reverseBits(uint256 x) internal pure returns (uint256 r) {
uint256 m0 = 0x0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f;
uint256 m1 = m0 ^ (m0 << 2);
uint256 m2 = m1 ^ (m1 << 1);
r = reverseBytes(x);
r = (m2 & (r >> 1)) | ((m2 & r) << 1);
r = (m1 & (r >> 2)) | ((m1 & r) << 2);
r = (m0 & (r >> 4)) | ((m0 & r) << 4);
}
/// @dev Returns `x` reversed at the byte level.
function reverseBytes(uint256 x) internal pure returns (uint256 r) {
unchecked {
// Computing masks on-the-fly reduces bytecode size by about 200 bytes.
uint256 m0 = 0x100000000000000000000000000000001 * (~toUint(x == uint256(0)) >> 192);
uint256 m1 = m0 ^ (m0 << 32);
uint256 m2 = m1 ^ (m1 << 16);
uint256 m3 = m2 ^ (m2 << 8);
r = (m3 & (x >> 8)) | ((m3 & x) << 8);
r = (m2 & (r >> 16)) | ((m2 & r) << 16);
r = (m1 & (r >> 32)) | ((m1 & r) << 32);
r = (m0 & (r >> 64)) | ((m0 & r) << 64);
r = (r >> 128) | (r << 128);
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* BOOLEAN OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
// A Solidity bool on the stack or memory is represented as a 256-bit word.
// Non-zero values are true, zero is false.
// A clean bool is either 0 (false) or 1 (true) under the hood.
// Usually, if not always, the bool result of a regular Solidity expression,
// or the argument of a public/external function will be a clean bool.
// You can usually use the raw variants for more performance.
// If uncertain, test (best with exact compiler settings).
// Or use the non-raw variants (compiler can sometimes optimize out the double `iszero`s).
/// @dev Returns `x & y`. Inputs must be clean.
function rawAnd(bool x, bool y) internal pure returns (bool z) {
/// @solidity memory-safe-assembly
assembly {
z := and(x, y)
}
}
/// @dev Returns `x & y`.
function and(bool x, bool y) internal pure returns (bool z) {
/// @solidity memory-safe-assembly
assembly {
z := and(iszero(iszero(x)), iszero(iszero(y)))
}
}
/// @dev Returns `x | y`. Inputs must be clean.
function rawOr(bool x, bool y) internal pure returns (bool z) {
/// @solidity memory-safe-assembly
assembly {
z := or(x, y)
}
}
/// @dev Returns `x | y`.
function or(bool x, bool y) internal pure returns (bool z) {
/// @solidity memory-safe-assembly
assembly {
z := or(iszero(iszero(x)), iszero(iszero(y)))
}
}
/// @dev Returns 1 if `b` is true, else 0. Input must be clean.
function rawToUint(bool b) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := b
}
}
/// @dev Returns 1 if `b` is true, else 0.
function toUint(bool b) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := iszero(iszero(b))
}
}
}
IRebalancerTypes.sol 59 lines
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
/**
* @title Rebalancer Types
* @notice Defines all custom types used by the Rebalancer contract.
*/
interface IRebalancerTypes {
/**
* @notice Represents the deposit data of a user.
* @dev A value of zero for `initiateTimestamp` indicates that the deposit or withdrawal has been validated.
* @param initiateTimestamp The timestamp when the deposit or withdrawal was initiated.
* @param amount The amount of assets deposited by the user.
* @param entryPositionVersion The version of the position the user entered.
*/
struct UserDeposit {
uint40 initiateTimestamp;
uint88 amount; // maximum 309'485'009 tokens with 18 decimals
uint128 entryPositionVersion;
}
/**
* @notice Represents data for a specific version of a position.
* @dev The difference between `amount` here and the amount saved in the USDN protocol is the liquidation bonus.
* @param amount The amount of assets used as collateral to open the position.
* @param tick The tick of the position.
* @param tickVersion The version of the tick.
* @param index The index of the position in the tick list.
* @param entryAccMultiplier The accumulated PnL multiplier of all positions up to this one.
*/
struct PositionData {
uint128 amount;
int24 tick;
uint256 tickVersion;
uint256 index;
uint256 entryAccMultiplier;
}
/**
* @notice Defines parameters related to the validation process for rebalancer deposits and withdrawals.
* @dev If `validationDeadline` has passed, the user must wait until the cooldown duration has elapsed. Then, for
* deposit actions, the user must retrieve its funds using {IRebalancer.resetDepositAssets}. For withdrawal actions,
* the user can simply initiate a new withdrawal.
* @param validationDelay The minimum duration in seconds between an initiate action and the corresponding validate
* action.
* @param validationDeadline The maximum duration in seconds between an initiate action and the corresponding
* validate action.
* @param actionCooldown The duration in seconds from the initiate action during which the user can't interact with
* the rebalancer if the `validationDeadline` is exceeded.
* @param closeDelay The Duration in seconds from the last rebalancer long position opening during which the user
* can't perform an {IRebalancer.initiateClosePosition}.
*/
struct TimeLimits {
uint64 validationDelay;
uint64 validationDeadline;
uint64 actionCooldown;
uint64 closeDelay;
}
}
IRebaseCallback.sol 12 lines
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
interface IRebaseCallback {
/**
* @notice Called by the USDN token after a rebase has happened.
* @param oldDivisor The value of the divisor before the rebase.
* @param newDivisor The value of the divisor after the rebase (necessarily smaller than `oldDivisor`).
* @return result_ Arbitrary data that will be forwarded to the caller of `rebase`.
*/
function rebaseCallback(uint256 oldDivisor, uint256 newDivisor) external returns (bytes memory result_);
}
IUsdnErrors.sol 25 lines
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
/**
* @title Errors for the USDN token contract
* @notice Defines all custom errors emitted by the USDN token contract.
*/
interface IUsdnErrors {
/**
* @dev The amount of tokens exceeds the maximum allowed limit.
* @param value The invalid token value.
*/
error UsdnMaxTokensExceeded(uint256 value);
/**
* @dev The sender's share balance is insufficient.
* @param sender The sender's address.
* @param balance The current share balance of the sender.
* @param needed The required amount of shares for the transfer.
*/
error UsdnInsufficientSharesBalance(address sender, uint256 balance, uint256 needed);
/// @dev The divisor value in storage is invalid (< 1).
error UsdnInvalidDivisor();
}
IUsdnEvents.sol 24 lines
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
import { IRebaseCallback } from "./IRebaseCallback.sol";
/**
* @title Events for the USDN token contract
* @notice Defines all custom events emitted by the USDN token contract.
*/
interface IUsdnEvents {
/**
* @notice The divisor was updated, emitted during a rebase.
* @param oldDivisor The divisor value before the rebase.
* @param newDivisor The new divisor value.
*/
event Rebase(uint256 oldDivisor, uint256 newDivisor);
/**
* @notice The rebase handler address was updated.
* @dev The rebase handler is a contract that is called when a rebase occurs.
* @param newHandler The address of the new rebase handler contract.
*/
event RebaseHandlerUpdated(IRebaseCallback newHandler);
}
IAccessControlDefaultAdminRules.sol 192 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (access/extensions/IAccessControlDefaultAdminRules.sol)
pragma solidity ^0.8.20;
import {IAccessControl} from "../IAccessControl.sol";
/**
* @dev External interface of AccessControlDefaultAdminRules declared to support ERC-165 detection.
*/
interface IAccessControlDefaultAdminRules is IAccessControl {
/**
* @dev The new default admin is not a valid default admin.
*/
error AccessControlInvalidDefaultAdmin(address defaultAdmin);
/**
* @dev At least one of the following rules was violated:
*
* - The `DEFAULT_ADMIN_ROLE` must only be managed by itself.
* - The `DEFAULT_ADMIN_ROLE` must only be held by one account at the time.
* - Any `DEFAULT_ADMIN_ROLE` transfer must be in two delayed steps.
*/
error AccessControlEnforcedDefaultAdminRules();
/**
* @dev The delay for transferring the default admin delay is enforced and
* the operation must wait until `schedule`.
*
* NOTE: `schedule` can be 0 indicating there's no transfer scheduled.
*/
error AccessControlEnforcedDefaultAdminDelay(uint48 schedule);
/**
* @dev Emitted when a {defaultAdmin} transfer is started, setting `newAdmin` as the next
* address to become the {defaultAdmin} by calling {acceptDefaultAdminTransfer} only after `acceptSchedule`
* passes.
*/
event DefaultAdminTransferScheduled(address indexed newAdmin, uint48 acceptSchedule);
/**
* @dev Emitted when a {pendingDefaultAdmin} is reset if it was never accepted, regardless of its schedule.
*/
event DefaultAdminTransferCanceled();
/**
* @dev Emitted when a {defaultAdminDelay} change is started, setting `newDelay` as the next
* delay to be applied between default admin transfer after `effectSchedule` has passed.
*/
event DefaultAdminDelayChangeScheduled(uint48 newDelay, uint48 effectSchedule);
/**
* @dev Emitted when a {pendingDefaultAdminDelay} is reset if its schedule didn't pass.
*/
event DefaultAdminDelayChangeCanceled();
/**
* @dev Returns the address of the current `DEFAULT_ADMIN_ROLE` holder.
*/
function defaultAdmin() external view returns (address);
/**
* @dev Returns a tuple of a `newAdmin` and an accept schedule.
*
* After the `schedule` passes, the `newAdmin` will be able to accept the {defaultAdmin} role
* by calling {acceptDefaultAdminTransfer}, completing the role transfer.
*
* A zero value only in `acceptSchedule` indicates no pending admin transfer.
*
* NOTE: A zero address `newAdmin` means that {defaultAdmin} is being renounced.
*/
function pendingDefaultAdmin() external view returns (address newAdmin, uint48 acceptSchedule);
/**
* @dev Returns the delay required to schedule the acceptance of a {defaultAdmin} transfer started.
*
* This delay will be added to the current timestamp when calling {beginDefaultAdminTransfer} to set
* the acceptance schedule.
*
* NOTE: If a delay change has been scheduled, it will take effect as soon as the schedule passes, making this
* function returns the new delay. See {changeDefaultAdminDelay}.
*/
function defaultAdminDelay() external view returns (uint48);
/**
* @dev Returns a tuple of `newDelay` and an effect schedule.
*
* After the `schedule` passes, the `newDelay` will get into effect immediately for every
* new {defaultAdmin} transfer started with {beginDefaultAdminTransfer}.
*
* A zero value only in `effectSchedule` indicates no pending delay change.
*
* NOTE: A zero value only for `newDelay` means that the next {defaultAdminDelay}
* will be zero after the effect schedule.
*/
function pendingDefaultAdminDelay() external view returns (uint48 newDelay, uint48 effectSchedule);
/**
* @dev Starts a {defaultAdmin} transfer by setting a {pendingDefaultAdmin} scheduled for acceptance
* after the current timestamp plus a {defaultAdminDelay}.
*
* Requirements:
*
* - Only can be called by the current {defaultAdmin}.
*
* Emits a DefaultAdminRoleChangeStarted event.
*/
function beginDefaultAdminTransfer(address newAdmin) external;
/**
* @dev Cancels a {defaultAdmin} transfer previously started with {beginDefaultAdminTransfer}.
*
* A {pendingDefaultAdmin} not yet accepted can also be cancelled with this function.
*
* Requirements:
*
* - Only can be called by the current {defaultAdmin}.
*
* May emit a DefaultAdminTransferCanceled event.
*/
function cancelDefaultAdminTransfer() external;
/**
* @dev Completes a {defaultAdmin} transfer previously started with {beginDefaultAdminTransfer}.
*
* After calling the function:
*
* - `DEFAULT_ADMIN_ROLE` should be granted to the caller.
* - `DEFAULT_ADMIN_ROLE` should be revoked from the previous holder.
* - {pendingDefaultAdmin} should be reset to zero values.
*
* Requirements:
*
* - Only can be called by the {pendingDefaultAdmin}'s `newAdmin`.
* - The {pendingDefaultAdmin}'s `acceptSchedule` should've passed.
*/
function acceptDefaultAdminTransfer() external;
/**
* @dev Initiates a {defaultAdminDelay} update by setting a {pendingDefaultAdminDelay} scheduled for getting
* into effect after the current timestamp plus a {defaultAdminDelay}.
*
* This function guarantees that any call to {beginDefaultAdminTransfer} done between the timestamp this
* method is called and the {pendingDefaultAdminDelay} effect schedule will use the current {defaultAdminDelay}
* set before calling.
*
* The {pendingDefaultAdminDelay}'s effect schedule is defined in a way that waiting until the schedule and then
* calling {beginDefaultAdminTransfer} with the new delay will take at least the same as another {defaultAdmin}
* complete transfer (including acceptance).
*
* The schedule is designed for two scenarios:
*
* - When the delay is changed for a larger one the schedule is `block.timestamp + newDelay` capped by
* {defaultAdminDelayIncreaseWait}.
* - When the delay is changed for a shorter one, the schedule is `block.timestamp + (current delay - new delay)`.
*
* A {pendingDefaultAdminDelay} that never got into effect will be canceled in favor of a new scheduled change.
*
* Requirements:
*
* - Only can be called by the current {defaultAdmin}.
*
* Emits a DefaultAdminDelayChangeScheduled event and may emit a DefaultAdminDelayChangeCanceled event.
*/
function changeDefaultAdminDelay(uint48 newDelay) external;
/**
* @dev Cancels a scheduled {defaultAdminDelay} change.
*
* Requirements:
*
* - Only can be called by the current {defaultAdmin}.
*
* May emit a DefaultAdminDelayChangeCanceled event.
*/
function rollbackDefaultAdminDelay() external;
/**
* @dev Maximum time in seconds for an increase to {defaultAdminDelay} (that is scheduled using {changeDefaultAdminDelay})
* to take effect. Default to 5 days.
*
* When the {defaultAdminDelay} is scheduled to be increased, it goes into effect after the new delay has passed with
* the purpose of giving enough time for reverting any accidental change (i.e. using milliseconds instead of seconds)
* that may lock the contract. However, to avoid excessive schedules, the wait is capped by this function and it can
* be overrode for a custom {defaultAdminDelay} increase scheduling.
*
* IMPORTANT: Make sure to add a reasonable amount of time while overriding this value, otherwise,
* there's a risk of setting a high new delay that goes into effect almost immediately without the
* possibility of human intervention in the case of an input error (eg. set milliseconds instead of seconds).
*/
function defaultAdminDelayIncreaseWait() external view returns (uint48);
}
AccessControl.sol 209 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/AccessControl.sol)
pragma solidity ^0.8.20;
import {IAccessControl} from "./IAccessControl.sol";
import {Context} from "../utils/Context.sol";
import {ERC165} from "../utils/introspection/ERC165.sol";
/**
* @dev Contract module that allows children to implement role-based access
* control mechanisms. This is a lightweight version that doesn't allow enumerating role
* members except through off-chain means by accessing the contract event logs. Some
* applications may benefit from on-chain enumerability, for those cases see
* {AccessControlEnumerable}.
*
* Roles are referred to by their `bytes32` identifier. These should be exposed
* in the external API and be unique. The best way to achieve this is by
* using `public constant` hash digests:
*
* ```solidity
* bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
* ```
*
* Roles can be used to represent a set of permissions. To restrict access to a
* function call, use {hasRole}:
*
* ```solidity
* function foo() public {
* require(hasRole(MY_ROLE, msg.sender));
* ...
* }
* ```
*
* Roles can be granted and revoked dynamically via the {grantRole} and
* {revokeRole} functions. Each role has an associated admin role, and only
* accounts that have a role's admin role can call {grantRole} and {revokeRole}.
*
* By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
* that only accounts with this role will be able to grant or revoke other
* roles. More complex role relationships can be created by using
* {_setRoleAdmin}.
*
* WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
* grant and revoke this role. Extra precautions should be taken to secure
* accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules}
* to enforce additional security measures for this role.
*/
abstract contract AccessControl is Context, IAccessControl, ERC165 {
struct RoleData {
mapping(address account => bool) hasRole;
bytes32 adminRole;
}
mapping(bytes32 role => RoleData) private _roles;
bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
/**
* @dev Modifier that checks that an account has a specific role. Reverts
* with an {AccessControlUnauthorizedAccount} error including the required role.
*/
modifier onlyRole(bytes32 role) {
_checkRole(role);
_;
}
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
}
/**
* @dev Returns `true` if `account` has been granted `role`.
*/
function hasRole(bytes32 role, address account) public view virtual returns (bool) {
return _roles[role].hasRole[account];
}
/**
* @dev Reverts with an {AccessControlUnauthorizedAccount} error if `_msgSender()`
* is missing `role`. Overriding this function changes the behavior of the {onlyRole} modifier.
*/
function _checkRole(bytes32 role) internal view virtual {
_checkRole(role, _msgSender());
}
/**
* @dev Reverts with an {AccessControlUnauthorizedAccount} error if `account`
* is missing `role`.
*/
function _checkRole(bytes32 role, address account) internal view virtual {
if (!hasRole(role, account)) {
revert AccessControlUnauthorizedAccount(account, role);
}
}
/**
* @dev Returns the admin role that controls `role`. See {grantRole} and
* {revokeRole}.
*
* To change a role's admin, use {_setRoleAdmin}.
*/
function getRoleAdmin(bytes32 role) public view virtual returns (bytes32) {
return _roles[role].adminRole;
}
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*
* May emit a {RoleGranted} event.
*/
function grantRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
_grantRole(role, account);
}
/**
* @dev Revokes `role` from `account`.
*
* If `account` had been granted `role`, emits a {RoleRevoked} event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*
* May emit a {RoleRevoked} event.
*/
function revokeRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
_revokeRole(role, account);
}
/**
* @dev Revokes `role` from the calling account.
*
* Roles are often managed via {grantRole} and {revokeRole}: this function's
* purpose is to provide a mechanism for accounts to lose their privileges
* if they are compromised (such as when a trusted device is misplaced).
*
* If the calling account had been revoked `role`, emits a {RoleRevoked}
* event.
*
* Requirements:
*
* - the caller must be `callerConfirmation`.
*
* May emit a {RoleRevoked} event.
*/
function renounceRole(bytes32 role, address callerConfirmation) public virtual {
if (callerConfirmation != _msgSender()) {
revert AccessControlBadConfirmation();
}
_revokeRole(role, callerConfirmation);
}
/**
* @dev Sets `adminRole` as ``role``'s admin role.
*
* Emits a {RoleAdminChanged} event.
*/
function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
bytes32 previousAdminRole = getRoleAdmin(role);
_roles[role].adminRole = adminRole;
emit RoleAdminChanged(role, previousAdminRole, adminRole);
}
/**
* @dev Attempts to grant `role` to `account` and returns a boolean indicating if `role` was granted.
*
* Internal function without access restriction.
*
* May emit a {RoleGranted} event.
*/
function _grantRole(bytes32 role, address account) internal virtual returns (bool) {
if (!hasRole(role, account)) {
_roles[role].hasRole[account] = true;
emit RoleGranted(role, account, _msgSender());
return true;
} else {
return false;
}
}
/**
* @dev Attempts to revoke `role` to `account` and returns a boolean indicating if `role` was revoked.
*
* Internal function without access restriction.
*
* May emit a {RoleRevoked} event.
*/
function _revokeRole(bytes32 role, address account) internal virtual returns (bool) {
if (hasRole(role, account)) {
_roles[role].hasRole[account] = false;
emit RoleRevoked(role, account, _msgSender());
return true;
} else {
return false;
}
}
}
SafeCast.sol 1162 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/math/SafeCast.sol)
// This file was procedurally generated from scripts/generate/templates/SafeCast.js.
pragma solidity ^0.8.20;
/**
* @dev Wrappers over Solidity's uintXX/intXX/bool 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.
*/
library SafeCast {
/**
* @dev Value doesn't fit in an uint of `bits` size.
*/
error SafeCastOverflowedUintDowncast(uint8 bits, uint256 value);
/**
* @dev An int value doesn't fit in an uint of `bits` size.
*/
error SafeCastOverflowedIntToUint(int256 value);
/**
* @dev Value doesn't fit in an int of `bits` size.
*/
error SafeCastOverflowedIntDowncast(uint8 bits, int256 value);
/**
* @dev An uint value doesn't fit in an int of `bits` size.
*/
error SafeCastOverflowedUintToInt(uint256 value);
/**
* @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
*/
function toUint248(uint256 value) internal pure returns (uint248) {
if (value > type(uint248).max) {
revert SafeCastOverflowedUintDowncast(248, value);
}
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
*/
function toUint240(uint256 value) internal pure returns (uint240) {
if (value > type(uint240).max) {
revert SafeCastOverflowedUintDowncast(240, value);
}
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
*/
function toUint232(uint256 value) internal pure returns (uint232) {
if (value > type(uint232).max) {
revert SafeCastOverflowedUintDowncast(232, value);
}
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
*/
function toUint224(uint256 value) internal pure returns (uint224) {
if (value > type(uint224).max) {
revert SafeCastOverflowedUintDowncast(224, value);
}
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
*/
function toUint216(uint256 value) internal pure returns (uint216) {
if (value > type(uint216).max) {
revert SafeCastOverflowedUintDowncast(216, value);
}
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
*/
function toUint208(uint256 value) internal pure returns (uint208) {
if (value > type(uint208).max) {
revert SafeCastOverflowedUintDowncast(208, value);
}
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
*/
function toUint200(uint256 value) internal pure returns (uint200) {
if (value > type(uint200).max) {
revert SafeCastOverflowedUintDowncast(200, value);
}
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
*/
function toUint192(uint256 value) internal pure returns (uint192) {
if (value > type(uint192).max) {
revert SafeCastOverflowedUintDowncast(192, value);
}
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
*/
function toUint184(uint256 value) internal pure returns (uint184) {
if (value > type(uint184).max) {
revert SafeCastOverflowedUintDowncast(184, value);
}
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
*/
function toUint176(uint256 value) internal pure returns (uint176) {
if (value > type(uint176).max) {
revert SafeCastOverflowedUintDowncast(176, value);
}
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
*/
function toUint168(uint256 value) internal pure returns (uint168) {
if (value > type(uint168).max) {
revert SafeCastOverflowedUintDowncast(168, value);
}
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
*/
function toUint160(uint256 value) internal pure returns (uint160) {
if (value > type(uint160).max) {
revert SafeCastOverflowedUintDowncast(160, value);
}
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
*/
function toUint152(uint256 value) internal pure returns (uint152) {
if (value > type(uint152).max) {
revert SafeCastOverflowedUintDowncast(152, value);
}
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
*/
function toUint144(uint256 value) internal pure returns (uint144) {
if (value > type(uint144).max) {
revert SafeCastOverflowedUintDowncast(144, value);
}
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
*/
function toUint136(uint256 value) internal pure returns (uint136) {
if (value > type(uint136).max) {
revert SafeCastOverflowedUintDowncast(136, value);
}
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
*/
function toUint128(uint256 value) internal pure returns (uint128) {
if (value > type(uint128).max) {
revert SafeCastOverflowedUintDowncast(128, value);
}
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
*/
function toUint120(uint256 value) internal pure returns (uint120) {
if (value > type(uint120).max) {
revert SafeCastOverflowedUintDowncast(120, value);
}
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
*/
function toUint112(uint256 value) internal pure returns (uint112) {
if (value > type(uint112).max) {
revert SafeCastOverflowedUintDowncast(112, value);
}
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
*/
function toUint104(uint256 value) internal pure returns (uint104) {
if (value > type(uint104).max) {
revert SafeCastOverflowedUintDowncast(104, value);
}
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
*/
function toUint96(uint256 value) internal pure returns (uint96) {
if (value > type(uint96).max) {
revert SafeCastOverflowedUintDowncast(96, value);
}
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
*/
function toUint88(uint256 value) internal pure returns (uint88) {
if (value > type(uint88).max) {
revert SafeCastOverflowedUintDowncast(88, value);
}
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
*/
function toUint80(uint256 value) internal pure returns (uint80) {
if (value > type(uint80).max) {
revert SafeCastOverflowedUintDowncast(80, value);
}
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
*/
function toUint72(uint256 value) internal pure returns (uint72) {
if (value > type(uint72).max) {
revert SafeCastOverflowedUintDowncast(72, value);
}
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
*/
function toUint64(uint256 value) internal pure returns (uint64) {
if (value > type(uint64).max) {
revert SafeCastOverflowedUintDowncast(64, value);
}
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
*/
function toUint56(uint256 value) internal pure returns (uint56) {
if (value > type(uint56).max) {
revert SafeCastOverflowedUintDowncast(56, value);
}
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
*/
function toUint48(uint256 value) internal pure returns (uint48) {
if (value > type(uint48).max) {
revert SafeCastOverflowedUintDowncast(48, value);
}
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
*/
function toUint40(uint256 value) internal pure returns (uint40) {
if (value > type(uint40).max) {
revert SafeCastOverflowedUintDowncast(40, value);
}
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
*/
function toUint32(uint256 value) internal pure returns (uint32) {
if (value > type(uint32).max) {
revert SafeCastOverflowedUintDowncast(32, value);
}
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
*/
function toUint24(uint256 value) internal pure returns (uint24) {
if (value > type(uint24).max) {
revert SafeCastOverflowedUintDowncast(24, value);
}
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
*/
function toUint16(uint256 value) internal pure returns (uint16) {
if (value > type(uint16).max) {
revert SafeCastOverflowedUintDowncast(16, value);
}
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
*/
function toUint8(uint256 value) internal pure returns (uint8) {
if (value > type(uint8).max) {
revert SafeCastOverflowedUintDowncast(8, value);
}
return uint8(value);
}
/**
* @dev Converts a signed int256 into an unsigned uint256.
*
* Requirements:
*
* - input must be greater than or equal to 0.
*/
function toUint256(int256 value) internal pure returns (uint256) {
if (value < 0) {
revert SafeCastOverflowedIntToUint(value);
}
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
*/
function toInt248(int256 value) internal pure returns (int248 downcasted) {
downcasted = int248(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(248, value);
}
}
/**
* @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
*/
function toInt240(int256 value) internal pure returns (int240 downcasted) {
downcasted = int240(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(240, value);
}
}
/**
* @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
*/
function toInt232(int256 value) internal pure returns (int232 downcasted) {
downcasted = int232(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(232, value);
}
}
/**
* @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
*/
function toInt224(int256 value) internal pure returns (int224 downcasted) {
downcasted = int224(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(224, value);
}
}
/**
* @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
*/
function toInt216(int256 value) internal pure returns (int216 downcasted) {
downcasted = int216(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(216, value);
}
}
/**
* @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
*/
function toInt208(int256 value) internal pure returns (int208 downcasted) {
downcasted = int208(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(208, value);
}
}
/**
* @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
*/
function toInt200(int256 value) internal pure returns (int200 downcasted) {
downcasted = int200(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(200, value);
}
}
/**
* @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
*/
function toInt192(int256 value) internal pure returns (int192 downcasted) {
downcasted = int192(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(192, value);
}
}
/**
* @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
*/
function toInt184(int256 value) internal pure returns (int184 downcasted) {
downcasted = int184(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(184, value);
}
}
/**
* @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
*/
function toInt176(int256 value) internal pure returns (int176 downcasted) {
downcasted = int176(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(176, value);
}
}
/**
* @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
*/
function toInt168(int256 value) internal pure returns (int168 downcasted) {
downcasted = int168(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(168, value);
}
}
/**
* @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
*/
function toInt160(int256 value) internal pure returns (int160 downcasted) {
downcasted = int160(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(160, value);
}
}
/**
* @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
*/
function toInt152(int256 value) internal pure returns (int152 downcasted) {
downcasted = int152(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(152, value);
}
}
/**
* @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
*/
function toInt144(int256 value) internal pure returns (int144 downcasted) {
downcasted = int144(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(144, value);
}
}
/**
* @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
*/
function toInt136(int256 value) internal pure returns (int136 downcasted) {
downcasted = int136(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(136, value);
}
}
/**
* @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
*/
function toInt128(int256 value) internal pure returns (int128 downcasted) {
downcasted = int128(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(128, value);
}
}
/**
* @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
*/
function toInt120(int256 value) internal pure returns (int120 downcasted) {
downcasted = int120(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(120, value);
}
}
/**
* @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
*/
function toInt112(int256 value) internal pure returns (int112 downcasted) {
downcasted = int112(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(112, value);
}
}
/**
* @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
*/
function toInt104(int256 value) internal pure returns (int104 downcasted) {
downcasted = int104(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(104, value);
}
}
/**
* @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
*/
function toInt96(int256 value) internal pure returns (int96 downcasted) {
downcasted = int96(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(96, value);
}
}
/**
* @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
*/
function toInt88(int256 value) internal pure returns (int88 downcasted) {
downcasted = int88(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(88, value);
}
}
/**
* @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
*/
function toInt80(int256 value) internal pure returns (int80 downcasted) {
downcasted = int80(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(80, value);
}
}
/**
* @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
*/
function toInt72(int256 value) internal pure returns (int72 downcasted) {
downcasted = int72(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(72, value);
}
}
/**
* @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
*/
function toInt64(int256 value) internal pure returns (int64 downcasted) {
downcasted = int64(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(64, value);
}
}
/**
* @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
*/
function toInt56(int256 value) internal pure returns (int56 downcasted) {
downcasted = int56(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(56, value);
}
}
/**
* @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
*/
function toInt48(int256 value) internal pure returns (int48 downcasted) {
downcasted = int48(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(48, value);
}
}
/**
* @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
*/
function toInt40(int256 value) internal pure returns (int40 downcasted) {
downcasted = int40(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(40, value);
}
}
/**
* @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
*/
function toInt32(int256 value) internal pure returns (int32 downcasted) {
downcasted = int32(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(32, value);
}
}
/**
* @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
*/
function toInt24(int256 value) internal pure returns (int24 downcasted) {
downcasted = int24(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(24, value);
}
}
/**
* @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
*/
function toInt16(int256 value) internal pure returns (int16 downcasted) {
downcasted = int16(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(16, value);
}
}
/**
* @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
*/
function toInt8(int256 value) internal pure returns (int8 downcasted) {
downcasted = int8(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(8, value);
}
}
/**
* @dev Converts an unsigned uint256 into a signed int256.
*
* Requirements:
*
* - input must be less than or equal to maxInt256.
*/
function toInt256(uint256 value) internal pure returns (int256) {
// Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive
if (value > uint256(type(int256).max)) {
revert SafeCastOverflowedUintToInt(value);
}
return int256(value);
}
/**
* @dev Cast a boolean (false or true) to a uint256 (0 or 1) with no jump.
*/
function toUint(bool b) internal pure returns (uint256 u) {
assembly ("memory-safe") {
u := iszero(iszero(b))
}
}
}
Math.sol 685 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/math/Math.sol)
pragma solidity ^0.8.20;
import {Panic} from "../Panic.sol";
import {SafeCast} from "./SafeCast.sol";
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
enum Rounding {
Floor, // Toward negative infinity
Ceil, // Toward positive infinity
Trunc, // Toward zero
Expand // Away from zero
}
/**
* @dev Returns the addition of two unsigned integers, with an success flag (no overflow).
*/
function tryAdd(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
unchecked {
uint256 c = a + b;
if (c < a) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the subtraction of two unsigned integers, with an success flag (no overflow).
*/
function trySub(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
unchecked {
if (b > a) return (false, 0);
return (true, a - b);
}
}
/**
* @dev Returns the multiplication of two unsigned integers, with an success flag (no overflow).
*/
function tryMul(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
unchecked {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) return (true, 0);
uint256 c = a * b;
if (c / a != b) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the division of two unsigned integers, with a success flag (no division by zero).
*/
function tryDiv(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
unchecked {
if (b == 0) return (false, 0);
return (true, a / b);
}
}
/**
* @dev Returns the remainder of dividing two unsigned integers, with a success flag (no division by zero).
*/
function tryMod(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
unchecked {
if (b == 0) return (false, 0);
return (true, a % b);
}
}
/**
* @dev Branchless ternary evaluation for `a ? b : c`. Gas costs are constant.
*
* IMPORTANT: This function may reduce bytecode size and consume less gas when used standalone.
* However, the compiler may optimize Solidity ternary operations (i.e. `a ? b : c`) to only compute
* one branch when needed, making this function more expensive.
*/
function ternary(bool condition, uint256 a, uint256 b) internal pure returns (uint256) {
unchecked {
// branchless ternary works because:
// b ^ (a ^ b) == a
// b ^ 0 == b
return b ^ ((a ^ b) * SafeCast.toUint(condition));
}
}
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return ternary(a > b, a, b);
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return ternary(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 towards infinity instead
* of rounding towards zero.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
if (b == 0) {
// Guarantee the same behavior as in a regular Solidity division.
Panic.panic(Panic.DIVISION_BY_ZERO);
}
// The following calculation ensures accurate ceiling division without overflow.
// Since a is non-zero, (a - 1) / b will not overflow.
// The largest possible result occurs when (a - 1) / b is type(uint256).max,
// but the largest value we can obtain is type(uint256).max - 1, which happens
// when a = type(uint256).max and b = 1.
unchecked {
return SafeCast.toUint(a > 0) * ((a - 1) / b + 1);
}
}
/**
* @dev Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
* denominator == 0.
*
* 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²⁵⁶ and mod 2²⁵⁶ - 1, then use
// the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2²⁵⁶ + prod0.
uint256 prod0 = x * y; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(x, y, not(0))
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²⁵⁶. Also prevents denominator == 0.
if (denominator <= prod1) {
Panic.panic(ternary(denominator == 0, Panic.DIVISION_BY_ZERO, Panic.UNDER_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.
uint256 twos = denominator & (0 - denominator);
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²⁵⁶ / 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²⁵⁶. Now that denominator is an odd number, it has an inverse modulo 2²⁵⁶ such
// that denominator * inv ≡ 1 mod 2²⁵⁶. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv ≡ 1 mod 2⁴.
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⁸
inverse *= 2 - denominator * inverse; // inverse mod 2¹⁶
inverse *= 2 - denominator * inverse; // inverse mod 2³²
inverse *= 2 - denominator * inverse; // inverse mod 2⁶⁴
inverse *= 2 - denominator * inverse; // inverse mod 2¹²⁸
inverse *= 2 - denominator * inverse; // inverse mod 2²⁵⁶
// 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²⁵⁶. Since the preconditions guarantee that the outcome is
// less than 2²⁵⁶, 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;
}
}
/**
* @dev 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) {
return mulDiv(x, y, denominator) + SafeCast.toUint(unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0);
}
/**
* @dev Calculate the modular multiplicative inverse of a number in Z/nZ.
*
* If n is a prime, then Z/nZ is a field. In that case all elements are inversible, except 0.
* If n is not a prime, then Z/nZ is not a field, and some elements might not be inversible.
*
* If the input value is not inversible, 0 is returned.
*
* NOTE: If you know for sure that n is (big) a prime, it may be cheaper to use Fermat's little theorem and get the
* inverse using `Math.modExp(a, n - 2, n)`. See {invModPrime}.
*/
function invMod(uint256 a, uint256 n) internal pure returns (uint256) {
unchecked {
if (n == 0) return 0;
// The inverse modulo is calculated using the Extended Euclidean Algorithm (iterative version)
// Used to compute integers x and y such that: ax + ny = gcd(a, n).
// When the gcd is 1, then the inverse of a modulo n exists and it's x.
// ax + ny = 1
// ax = 1 + (-y)n
// ax ≡ 1 (mod n) # x is the inverse of a modulo n
// If the remainder is 0 the gcd is n right away.
uint256 remainder = a % n;
uint256 gcd = n;
// Therefore the initial coefficients are:
// ax + ny = gcd(a, n) = n
// 0a + 1n = n
int256 x = 0;
int256 y = 1;
while (remainder != 0) {
uint256 quotient = gcd / remainder;
(gcd, remainder) = (
// The old remainder is the next gcd to try.
remainder,
// Compute the next remainder.
// Can't overflow given that (a % gcd) * (gcd // (a % gcd)) <= gcd
// where gcd is at most n (capped to type(uint256).max)
gcd - remainder * quotient
);
(x, y) = (
// Increment the coefficient of a.
y,
// Decrement the coefficient of n.
// Can overflow, but the result is casted to uint256 so that the
// next value of y is "wrapped around" to a value between 0 and n - 1.
x - y * int256(quotient)
);
}
if (gcd != 1) return 0; // No inverse exists.
return ternary(x < 0, n - uint256(-x), uint256(x)); // Wrap the result if it's negative.
}
}
/**
* @dev Variant of {invMod}. More efficient, but only works if `p` is known to be a prime greater than `2`.
*
* From https://en.wikipedia.org/wiki/Fermat%27s_little_theorem[Fermat's little theorem], we know that if p is
* prime, then `a**(p-1) ≡ 1 mod p`. As a consequence, we have `a * a**(p-2) ≡ 1 mod p`, which means that
* `a**(p-2)` is the modular multiplicative inverse of a in Fp.
*
* NOTE: this function does NOT check that `p` is a prime greater than `2`.
*/
function invModPrime(uint256 a, uint256 p) internal view returns (uint256) {
unchecked {
return Math.modExp(a, p - 2, p);
}
}
/**
* @dev Returns the modular exponentiation of the specified base, exponent and modulus (b ** e % m)
*
* Requirements:
* - modulus can't be zero
* - underlying staticcall to precompile must succeed
*
* IMPORTANT: The result is only valid if the underlying call succeeds. When using this function, make
* sure the chain you're using it on supports the precompiled contract for modular exponentiation
* at address 0x05 as specified in https://eips.ethereum.org/EIPS/eip-198[EIP-198]. Otherwise,
* the underlying function will succeed given the lack of a revert, but the result may be incorrectly
* interpreted as 0.
*/
function modExp(uint256 b, uint256 e, uint256 m) internal view returns (uint256) {
(bool success, uint256 result) = tryModExp(b, e, m);
if (!success) {
Panic.panic(Panic.DIVISION_BY_ZERO);
}
return result;
}
/**
* @dev Returns the modular exponentiation of the specified base, exponent and modulus (b ** e % m).
* It includes a success flag indicating if the operation succeeded. Operation will be marked as failed if trying
* to operate modulo 0 or if the underlying precompile reverted.
*
* IMPORTANT: The result is only valid if the success flag is true. When using this function, make sure the chain
* you're using it on supports the precompiled contract for modular exponentiation at address 0x05 as specified in
* https://eips.ethereum.org/EIPS/eip-198[EIP-198]. Otherwise, the underlying function will succeed given the lack
* of a revert, but the result may be incorrectly interpreted as 0.
*/
function tryModExp(uint256 b, uint256 e, uint256 m) internal view returns (bool success, uint256 result) {
if (m == 0) return (false, 0);
assembly ("memory-safe") {
let ptr := mload(0x40)
// | Offset | Content | Content (Hex) |
// |-----------|------------|--------------------------------------------------------------------|
// | 0x00:0x1f | size of b | 0x0000000000000000000000000000000000000000000000000000000000000020 |
// | 0x20:0x3f | size of e | 0x0000000000000000000000000000000000000000000000000000000000000020 |
// | 0x40:0x5f | size of m | 0x0000000000000000000000000000000000000000000000000000000000000020 |
// | 0x60:0x7f | value of b | 0x<.............................................................b> |
// | 0x80:0x9f | value of e | 0x<.............................................................e> |
// | 0xa0:0xbf | value of m | 0x<.............................................................m> |
mstore(ptr, 0x20)
mstore(add(ptr, 0x20), 0x20)
mstore(add(ptr, 0x40), 0x20)
mstore(add(ptr, 0x60), b)
mstore(add(ptr, 0x80), e)
mstore(add(ptr, 0xa0), m)
// Given the result < m, it's guaranteed to fit in 32 bytes,
// so we can use the memory scratch space located at offset 0.
success := staticcall(gas(), 0x05, ptr, 0xc0, 0x00, 0x20)
result := mload(0x00)
}
}
/**
* @dev Variant of {modExp} that supports inputs of arbitrary length.
*/
function modExp(bytes memory b, bytes memory e, bytes memory m) internal view returns (bytes memory) {
(bool success, bytes memory result) = tryModExp(b, e, m);
if (!success) {
Panic.panic(Panic.DIVISION_BY_ZERO);
}
return result;
}
/**
* @dev Variant of {tryModExp} that supports inputs of arbitrary length.
*/
function tryModExp(
bytes memory b,
bytes memory e,
bytes memory m
) internal view returns (bool success, bytes memory result) {
if (_zeroBytes(m)) return (false, new bytes(0));
uint256 mLen = m.length;
// Encode call args in result and move the free memory pointer
result = abi.encodePacked(b.length, e.length, mLen, b, e, m);
assembly ("memory-safe") {
let dataPtr := add(result, 0x20)
// Write result on top of args to avoid allocating extra memory.
success := staticcall(gas(), 0x05, dataPtr, mload(result), dataPtr, mLen)
// Overwrite the length.
// result.length > returndatasize() is guaranteed because returndatasize() == m.length
mstore(result, mLen)
// Set the memory pointer after the returned data.
mstore(0x40, add(dataPtr, mLen))
}
}
/**
* @dev Returns whether the provided byte array is zero.
*/
function _zeroBytes(bytes memory byteArray) private pure returns (bool) {
for (uint256 i = 0; i < byteArray.length; ++i) {
if (byteArray[i] != 0) {
return false;
}
}
return true;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded
* towards zero.
*
* This method is based on Newton's method for computing square roots; the algorithm is restricted to only
* using integer operations.
*/
function sqrt(uint256 a) internal pure returns (uint256) {
unchecked {
// Take care of easy edge cases when a == 0 or a == 1
if (a <= 1) {
return a;
}
// In this function, we use Newton's method to get a root of `f(x) := x² - a`. It involves building a
// sequence x_n that converges toward sqrt(a). For each iteration x_n, we also define the error between
// the current value as `ε_n = | x_n - sqrt(a) |`.
//
// For our first estimation, we consider `e` the smallest power of 2 which is bigger than the square root
// of the target. (i.e. `2**(e-1) ≤ sqrt(a) < 2**e`). We know that `e ≤ 128` because `(2¹²⁸)² = 2²⁵⁶` is
// bigger than any uint256.
//
// By noticing that
// `2**(e-1) ≤ sqrt(a) < 2**e → (2**(e-1))² ≤ a < (2**e)² → 2**(2*e-2) ≤ a < 2**(2*e)`
// we can deduce that `e - 1` is `log2(a) / 2`. We can thus compute `x_n = 2**(e-1)` using a method similar
// to the msb function.
uint256 aa = a;
uint256 xn = 1;
if (aa >= (1 << 128)) {
aa >>= 128;
xn <<= 64;
}
if (aa >= (1 << 64)) {
aa >>= 64;
xn <<= 32;
}
if (aa >= (1 << 32)) {
aa >>= 32;
xn <<= 16;
}
if (aa >= (1 << 16)) {
aa >>= 16;
xn <<= 8;
}
if (aa >= (1 << 8)) {
aa >>= 8;
xn <<= 4;
}
if (aa >= (1 << 4)) {
aa >>= 4;
xn <<= 2;
}
if (aa >= (1 << 2)) {
xn <<= 1;
}
// We now have x_n such that `x_n = 2**(e-1) ≤ sqrt(a) < 2**e = 2 * x_n`. This implies ε_n ≤ 2**(e-1).
//
// We can refine our estimation by noticing that the middle of that interval minimizes the error.
// If we move x_n to equal 2**(e-1) + 2**(e-2), then we reduce the error to ε_n ≤ 2**(e-2).
// This is going to be our x_0 (and ε_0)
xn = (3 * xn) >> 1; // ε_0 := | x_0 - sqrt(a) | ≤ 2**(e-2)
// From here, Newton's method give us:
// x_{n+1} = (x_n + a / x_n) / 2
//
// One should note that:
// x_{n+1}² - a = ((x_n + a / x_n) / 2)² - a
// = ((x_n² + a) / (2 * x_n))² - a
// = (x_n⁴ + 2 * a * x_n² + a²) / (4 * x_n²) - a
// = (x_n⁴ + 2 * a * x_n² + a² - 4 * a * x_n²) / (4 * x_n²)
// = (x_n⁴ - 2 * a * x_n² + a²) / (4 * x_n²)
// = (x_n² - a)² / (2 * x_n)²
// = ((x_n² - a) / (2 * x_n))²
// ≥ 0
// Which proves that for all n ≥ 1, sqrt(a) ≤ x_n
//
// This gives us the proof of quadratic convergence of the sequence:
// ε_{n+1} = | x_{n+1} - sqrt(a) |
// = | (x_n + a / x_n) / 2 - sqrt(a) |
// = | (x_n² + a - 2*x_n*sqrt(a)) / (2 * x_n) |
// = | (x_n - sqrt(a))² / (2 * x_n) |
// = | ε_n² / (2 * x_n) |
// = ε_n² / | (2 * x_n) |
//
// For the first iteration, we have a special case where x_0 is known:
// ε_1 = ε_0² / | (2 * x_0) |
// ≤ (2**(e-2))² / (2 * (2**(e-1) + 2**(e-2)))
// ≤ 2**(2*e-4) / (3 * 2**(e-1))
// ≤ 2**(e-3) / 3
// ≤ 2**(e-3-log2(3))
// ≤ 2**(e-4.5)
//
// For the following iterations, we use the fact that, 2**(e-1) ≤ sqrt(a) ≤ x_n:
// ε_{n+1} = ε_n² / | (2 * x_n) |
// ≤ (2**(e-k))² / (2 * 2**(e-1))
// ≤ 2**(2*e-2*k) / 2**e
// ≤ 2**(e-2*k)
xn = (xn + a / xn) >> 1; // ε_1 := | x_1 - sqrt(a) | ≤ 2**(e-4.5) -- special case, see above
xn = (xn + a / xn) >> 1; // ε_2 := | x_2 - sqrt(a) | ≤ 2**(e-9) -- general case with k = 4.5
xn = (xn + a / xn) >> 1; // ε_3 := | x_3 - sqrt(a) | ≤ 2**(e-18) -- general case with k = 9
xn = (xn + a / xn) >> 1; // ε_4 := | x_4 - sqrt(a) | ≤ 2**(e-36) -- general case with k = 18
xn = (xn + a / xn) >> 1; // ε_5 := | x_5 - sqrt(a) | ≤ 2**(e-72) -- general case with k = 36
xn = (xn + a / xn) >> 1; // ε_6 := | x_6 - sqrt(a) | ≤ 2**(e-144) -- general case with k = 72
// Because e ≤ 128 (as discussed during the first estimation phase), we know have reached a precision
// ε_6 ≤ 2**(e-144) < 1. Given we're operating on integers, then we can ensure that xn is now either
// sqrt(a) or sqrt(a) + 1.
return xn - SafeCast.toUint(xn > a / xn);
}
}
/**
* @dev 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 + SafeCast.toUint(unsignedRoundsUp(rounding) && result * result < a);
}
}
/**
* @dev Return the log in base 2 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
uint256 exp;
unchecked {
exp = 128 * SafeCast.toUint(value > (1 << 128) - 1);
value >>= exp;
result += exp;
exp = 64 * SafeCast.toUint(value > (1 << 64) - 1);
value >>= exp;
result += exp;
exp = 32 * SafeCast.toUint(value > (1 << 32) - 1);
value >>= exp;
result += exp;
exp = 16 * SafeCast.toUint(value > (1 << 16) - 1);
value >>= exp;
result += exp;
exp = 8 * SafeCast.toUint(value > (1 << 8) - 1);
value >>= exp;
result += exp;
exp = 4 * SafeCast.toUint(value > (1 << 4) - 1);
value >>= exp;
result += exp;
exp = 2 * SafeCast.toUint(value > (1 << 2) - 1);
value >>= exp;
result += exp;
result += SafeCast.toUint(value > 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 + SafeCast.toUint(unsignedRoundsUp(rounding) && 1 << result < value);
}
}
/**
* @dev Return the log in base 10 of a positive value rounded towards zero.
* 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 + SafeCast.toUint(unsignedRoundsUp(rounding) && 10 ** result < value);
}
}
/**
* @dev Return the log in base 256 of a positive value rounded towards zero.
* 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;
uint256 isGt;
unchecked {
isGt = SafeCast.toUint(value > (1 << 128) - 1);
value >>= isGt * 128;
result += isGt * 16;
isGt = SafeCast.toUint(value > (1 << 64) - 1);
value >>= isGt * 64;
result += isGt * 8;
isGt = SafeCast.toUint(value > (1 << 32) - 1);
value >>= isGt * 32;
result += isGt * 4;
isGt = SafeCast.toUint(value > (1 << 16) - 1);
value >>= isGt * 16;
result += isGt * 2;
result += SafeCast.toUint(value > (1 << 8) - 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 + SafeCast.toUint(unsignedRoundsUp(rounding) && 1 << (result << 3) < value);
}
}
/**
* @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
*/
function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
return uint8(rounding) % 2 == 1;
}
}
IERC5313.sol 16 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC5313.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface for the Light Contract Ownership Standard.
*
* A standardized minimal interface required to identify an account that controls a contract
*/
interface IERC5313 {
/**
* @dev Gets the address of the owner.
*/
function owner() external view returns (address);
}
IChainlinkOracle.sol 30 lines
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
import { AggregatorV3Interface } from "@chainlink/contracts/src/v0.8/shared/interfaces/AggregatorV3Interface.sol";
interface IChainlinkOracle {
/**
* @notice The sentinel value returned instead of the price if the data from the oracle is too old.
* @return sentinelValue_ The sentinel value for "price too old".
*/
function PRICE_TOO_OLD() external pure returns (int256 sentinelValue_);
/**
* @notice Gets the number of decimals of the asset from Chainlink.
* @return decimals_ The number of decimals of the asset.
*/
function getChainlinkDecimals() external view returns (uint256 decimals_);
/**
* @notice Gets the Chainlink price feed aggregator contract address.
* @return priceFeed_ The address of the Chainlink price feed contract.
*/
function getPriceFeed() external view returns (AggregatorV3Interface priceFeed_);
/**
* @notice Gets the duration after which the Chainlink data is considered stale or invalid.
* @return limit_ The price validity duration.
*/
function getChainlinkTimeElapsedLimit() external view returns (uint256 limit_);
}
IOracleMiddlewareErrors.sol 71 lines
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
/**
* @title Errors For The Middleware And Oracle Related Contracts
* @notice Defines all the custom errors thrown by the contracts related to the OracleMiddleware contract.
*/
interface IOracleMiddlewareErrors {
/**
* @notice The price returned by an oracle is negative.
* @param price The price returned by the oracle.
*/
error OracleMiddlewareWrongPrice(int256 price);
/**
* @notice The price returned by an oracle is too old.
* @param timestamp The timestamp of the price given by the oracle.
*/
error OracleMiddlewarePriceTooOld(uint256 timestamp);
/**
* @notice The price returned by an oracle is too recent.
* @param timestamp The timestamp of the price given by the oracle.
*/
error OracleMiddlewarePriceTooRecent(uint256 timestamp);
/**
* @notice The Pyth price reported a positive exponent (negative decimals).
* @param expo The price exponent.
*/
error OracleMiddlewarePythPositiveExponent(int32 expo);
/// @notice Indicates that the confidence ratio is too high.
error OracleMiddlewareConfRatioTooHigh();
/// @notice Indicates that an incorrect amount of tokens was provided to cover the cost of price validation.
error OracleMiddlewareIncorrectFee();
/**
* @notice The validation fee returned by the Pyth contract exceeded the safeguard value.
* @param fee The required fee returned by Pyth.
*/
error OracleMiddlewarePythFeeSafeguard(uint256 fee);
/// @notice The Redstone price is to divergent from the Chainlink price.
error OracleMiddlewareRedstoneSafeguard();
/**
* @notice The withdrawal of the Ether in the contract failed.
* @param to The address of the intended recipient.
*/
error OracleMiddlewareTransferFailed(address to);
/// @notice The recipient of a transfer is the zero address.
error OracleMiddlewareTransferToZeroAddress();
/**
* @notice The recent price delay is outside of the limits.
* @param newDelay The delay that was provided.
*/
error OracleMiddlewareInvalidRecentPriceDelay(uint64 newDelay);
/// @dev The new penalty is invalid.
error OracleMiddlewareInvalidPenaltyBps();
/// @notice The provided Chainlink round ID is invalid.
error OracleMiddlewareInvalidRoundId();
/// @notice The new low latency delay is invalid.
error OracleMiddlewareInvalidLowLatencyDelay();
}
IOracleMiddlewareEvents.sol 50 lines
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
/**
* @title Events For The Middleware And Oracle Related Contracts
* @notice Defines all the custom events emitted by the contracts related to the OracleMiddleware contract.
*/
interface IOracleMiddlewareEvents {
/**
* @notice The time elapsed limit was updated.
* @param newTimeElapsedLimit The new limit.
*/
event TimeElapsedLimitUpdated(uint256 newTimeElapsedLimit);
/**
* @notice The validation delay was updated.
* @param newValidationDelay The new validation delay.
*/
event ValidationDelayUpdated(uint256 newValidationDelay);
/**
* @notice The recent price delay for Pyth was updated.
* @param newDelay The new recent price delay.
*/
event PythRecentPriceDelayUpdated(uint64 newDelay);
/**
* @notice The recent price delay for Redstone was updated.
* @param newDelay The new recent price delay.
*/
event RedstoneRecentPriceDelayUpdated(uint48 newDelay);
/**
* @notice The confidence ratio was updated.
* @param newConfRatio The new confidence ratio.
*/
event ConfRatioUpdated(uint256 newConfRatio);
/**
* @notice The penalty for Redstone prices was updated.
* @param newPenaltyBps The new penalty.
*/
event PenaltyBpsUpdated(uint16 newPenaltyBps);
/**
* @notice The low latency delay was updated.
* @param newLowLatencyDelay The new low latency delay.
*/
event LowLatencyDelayUpdated(uint16 newLowLatencyDelay);
}
IPythOracle.sol 24 lines
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
import { IPyth } from "@pythnetwork/pyth-sdk-solidity/IPyth.sol";
interface IPythOracle {
/**
* @notice Gets the Pyth contract address.
* @return pyth_ The Pyth contract address.
*/
function getPyth() external view returns (IPyth pyth_);
/**
* @notice Gets the ID of the price feed queried by this contract.
* @return feedId_ The Pyth price feed ID.
*/
function getPythFeedId() external view returns (bytes32 feedId_);
/**
* @notice Gets the recent price delay.
* @return recentPriceDelay_ The maximum age of a recent price to be considered valid.
*/
function getPythRecentPriceDelay() external view returns (uint64 recentPriceDelay_);
}
IUsdnProtocolFallback.sol 754 lines
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
import { IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import { HugeUint } from "@smardex-solidity-libraries-1/HugeUint.sol";
import { IBaseLiquidationRewardsManager } from "../LiquidationRewardsManager/IBaseLiquidationRewardsManager.sol";
import { IBaseOracleMiddleware } from "../OracleMiddleware/IBaseOracleMiddleware.sol";
import { IBaseRebalancer } from "../Rebalancer/IBaseRebalancer.sol";
import { IUsdn } from "../Usdn/IUsdn.sol";
import { IUsdnProtocolTypes } from "./IUsdnProtocolTypes.sol";
/**
* @title IUsdnProtocolFallback
* @notice Interface for the USDN protocol fallback functions
*/
interface IUsdnProtocolFallback is IUsdnProtocolTypes {
/**
* @notice Retrieves the list of pending actions that must be validated by the next user action in the protocol.
* @dev If this function returns a non-empty list of pending actions, then the next user action MUST include the
* corresponding list of price update data and raw indices as the last parameter. The user that processes those
* pending actions will receive the corresponding security deposit.
* @param currentUser The address of the user that will submit the price signatures for third-party actions
* validations. This is used to filter out their actions from the returned list.
* @param lookAhead Additionally to pending actions which are actionable at this moment `block.timestamp`, the
* function will also return pending actions which will be actionable `lookAhead` seconds later. It is recommended
* to use a non-zero value in order to account for the interval where the validation transaction will be pending. A
* value of 30 seconds should already account for most situations and avoid reverts in case an action becomes
* actionable after a user submits their transaction.
* @param maxIter The maximum number of iterations when looking through the queue to find actionable pending
* actions. This value will be clamped to [MIN_ACTIONABLE_PENDING_ACTIONS_ITER,_pendingActionsQueue.length()].
* @return actions_ The pending actions if any, otherwise an empty array.
* @return rawIndices_ The raw indices of the actionable pending actions in the queue if any, otherwise an empty
* array. Each entry corresponds to the action in the `actions_` array, at the same index.
*/
function getActionablePendingActions(address currentUser, uint256 lookAhead, uint256 maxIter)
external
view
returns (PendingAction[] memory actions_, uint128[] memory rawIndices_);
/**
* @notice Retrieves the pending action with `user` as the given validator.
* @param user The user's address.
* @return action_ The pending action if any, otherwise a struct with all fields set to zero and
* `ProtocolAction.None`.
*/
function getUserPendingAction(address user) external view returns (PendingAction memory action_);
/**
* @notice Computes the hash generated from the given tick number and version.
* @param tick The tick number.
* @param version The tick version.
* @return hash_ The hash of the given tick number and version.
*/
function tickHash(int24 tick, uint256 version) external pure returns (bytes32 hash_);
/**
* @notice Computes the liquidation price of the given tick number, taking into account the effects of funding.
* @dev Uses the values from storage for the various variables. Note that ticks that are
* not a multiple of the tick spacing cannot contain a long position.
* @param tick The tick number.
* @return price_ The liquidation price.
*/
function getEffectivePriceForTick(int24 tick) external view returns (uint128 price_);
/**
* @notice Computes the liquidation price of the given tick number, taking into account the effects of funding.
* @dev Uses the given values instead of the ones from the storage. Note that ticks that are not a multiple of the
* tick spacing cannot contain a long position.
* @param tick The tick number.
* @param assetPrice The current/projected price of the asset.
* @param longTradingExpo The trading exposure of the long side (total expo - balance long).
* @param accumulator The liquidation multiplier accumulator.
* @return price_ The liquidation price.
*/
function getEffectivePriceForTick(
int24 tick,
uint256 assetPrice,
uint256 longTradingExpo,
HugeUint.Uint512 memory accumulator
) external view returns (uint128 price_);
/**
* @notice Computes an estimate of the amount of assets received when withdrawing.
* @dev The result is a rough estimate and does not take into account rebases and liquidations.
* @param usdnShares The amount of USDN shares to use in the withdrawal.
* @param price The current/projected price of the asset.
* @param timestamp The The timestamp corresponding to `price`.
* @return assetExpected_ The expected amount of assets to be received.
*/
function previewWithdraw(uint256 usdnShares, uint128 price, uint128 timestamp)
external
view
returns (uint256 assetExpected_);
/**
* @notice Computes an estimate of USDN tokens to be minted and SDEX tokens to be burned when depositing.
* @dev The result is a rough estimate and does not take into account rebases and liquidations.
* @param amount The amount of assets to deposit.
* @param price The current/projected price of the asset.
* @param timestamp The timestamp corresponding to `price`.
* @return usdnSharesExpected_ The amount of USDN shares to be minted.
* @return sdexToBurn_ The amount of SDEX tokens to be burned.
*/
function previewDeposit(uint256 amount, uint128 price, uint128 timestamp)
external
view
returns (uint256 usdnSharesExpected_, uint256 sdexToBurn_);
/**
* @notice Refunds the security deposit to the given validator if it has a liquidated initiated long position.
* @dev The security deposit is always sent to the validator even if the pending action is actionable.
* @param validator The address of the validator (must be payable as it will receive some native currency).
*/
function refundSecurityDeposit(address payable validator) external;
/// @notice Sends the accumulated SDEX token fees to the dead address. This function can be called by anyone.
function burnSdex() external;
/* -------------------------------------------------------------------------- */
/* Admin functions */
/* -------------------------------------------------------------------------- */
/**
* @notice Removes a stuck pending action and performs the minimal amount of cleanup necessary.
* @dev This function can only be called by the owner of the protocol, it serves as an escape hatch if a
* pending action ever gets stuck due to something internal reverting unexpectedly.
* It will not refund any fees or burned SDEX.
* @param validator The address of the validator of the stuck pending action.
* @param to Where the retrieved funds should be sent (security deposit, assets, usdn). Must be payable.
*/
function removeBlockedPendingAction(address validator, address payable to) external;
/**
* @notice Removes a stuck pending action with no cleanup.
* @dev This function can only be called by the owner of the protocol, it serves as an escape hatch if a
* pending action ever gets stuck due to something internal reverting unexpectedly.
* Always try to use `removeBlockedPendingAction` first, and only call this function if the other one fails.
* It will not refund any fees or burned SDEX.
* @param validator The address of the validator of the stuck pending action.
* @param to Where the retrieved funds should be sent (security deposit, assets, usdn). Must be payable.
*/
function removeBlockedPendingActionNoCleanup(address validator, address payable to) external;
/**
* @notice Removes a stuck pending action and performs the minimal amount of cleanup necessary.
* @dev This function can only be called by the owner of the protocol, it serves as an escape hatch if a
* pending action ever gets stuck due to something internal reverting unexpectedly.
* It will not refund any fees or burned SDEX.
* @param rawIndex The raw index of the stuck pending action.
* @param to Where the retrieved funds should be sent (security deposit, assets, usdn). Must be payable.
*/
function removeBlockedPendingAction(uint128 rawIndex, address payable to) external;
/**
* @notice Removes a stuck pending action with no cleanup.
* @dev This function can only be called by the owner of the protocol, it serves as an escape hatch if a
* pending action ever gets stuck due to something internal reverting unexpectedly.
* Always try to use `removeBlockedPendingAction` first, and only call this function if the other one fails.
* It will not refund any fees or burned SDEX.
* @param rawIndex The raw index of the stuck pending action.
* @param to Where the retrieved funds should be sent (security deposit, assets, usdn). Must be payable.
*/
function removeBlockedPendingActionNoCleanup(uint128 rawIndex, address payable to) external;
/* -------------------------------------------------------------------------- */
/* Immutables getters */
/* -------------------------------------------------------------------------- */
/**
* @notice The number of ticks between usable ticks. Only tick numbers that are a multiple of the tick spacing can
* be used for storing long positions.
* @dev A tick spacing of 1 is equivalent to a 0.01% increase in price between ticks. A tick spacing of 100 is.
* equivalent to a ~1.005% increase in price between ticks.
* @return tickSpacing_ The tick spacing.
*/
function getTickSpacing() external view returns (int24 tickSpacing_);
/**
* @notice Gets the address of the protocol's underlying asset (ERC20 token).
* @return asset_ The address of the asset token.
*/
function getAsset() external view returns (IERC20Metadata asset_);
/**
* @notice Gets the address of the SDEX ERC20 token.
* @return sdex_ The address of the SDEX token.
*/
function getSdex() external view returns (IERC20Metadata sdex_);
/**
* @notice Gets the number of decimals of the asset's price feed.
* @return decimals_ The number of decimals of the asset's price feed.
*/
function getPriceFeedDecimals() external view returns (uint8 decimals_);
/**
* @notice Gets the number of decimals of the underlying asset token.
* @return decimals_ The number of decimals of the asset token.
*/
function getAssetDecimals() external view returns (uint8 decimals_);
/**
* @notice Gets the address of the USDN ERC20 token.
* @return usdn_ The address of USDN ERC20 token.
*/
function getUsdn() external view returns (IUsdn usdn_);
/**
* @notice Gets the `MIN_DIVISOR` constant of the USDN token.
* @dev Check the USDN contract for more information.
* @return minDivisor_ The `MIN_DIVISOR` constant of the USDN token.
*/
function getUsdnMinDivisor() external view returns (uint256 minDivisor_);
/* -------------------------------------------------------------------------- */
/* Parameters getters */
/* -------------------------------------------------------------------------- */
/**
* @notice Gets the oracle middleware contract.
* @return oracleMiddleware_ The address of the oracle middleware contract.
*/
function getOracleMiddleware() external view returns (IBaseOracleMiddleware oracleMiddleware_);
/**
* @notice Gets the liquidation rewards manager contract.
* @return liquidationRewardsManager_ The address of the liquidation rewards manager contract.
*/
function getLiquidationRewardsManager()
external
view
returns (IBaseLiquidationRewardsManager liquidationRewardsManager_);
/**
* @notice Gets the rebalancer contract.
* @return rebalancer_ The address of the rebalancer contract.
*/
function getRebalancer() external view returns (IBaseRebalancer rebalancer_);
/**
* @notice Gets the lowest leverage that can be used to open a long position.
* @return minLeverage_ The minimum leverage (with `LEVERAGE_DECIMALS` decimals).
*/
function getMinLeverage() external view returns (uint256 minLeverage_);
/**
* @notice Gets the highest leverage that can be used to open a long position.
* @dev A position can have a leverage a bit higher than this value under specific conditions involving
* a change to the liquidation penalty setting.
* @return maxLeverage_ The maximum leverage value (with `LEVERAGE_DECIMALS` decimals).
*/
function getMaxLeverage() external view returns (uint256 maxLeverage_);
/**
* @notice Gets the deadline of the exclusivity period for the validator of a pending action with a low-latency
* oracle.
* @dev After this deadline, any user can validate the action with the low-latency oracle until the
* OracleMiddleware's `_lowLatencyDelay`, and retrieve the security deposit for the pending action.
* @return deadline_ The low-latency validation deadline of a validator (in seconds).
*/
function getLowLatencyValidatorDeadline() external view returns (uint128 deadline_);
/**
* @notice Gets the deadline of the exclusivity period for the validator to confirm their action with the on-chain
* oracle.
* @dev After this deadline, any user can validate the pending action with the on-chain oracle and retrieve its
* security deposit.
* @return deadline_ The on-chain validation deadline of a validator (in seconds)
*/
function getOnChainValidatorDeadline() external view returns (uint128 deadline_);
/**
* @notice Gets the liquidation penalty applied to the liquidation price when opening a position.
* @return liquidationPenalty_ The liquidation penalty (in ticks).
*/
function getLiquidationPenalty() external view returns (uint24 liquidationPenalty_);
/**
* @notice Gets the safety margin for the liquidation price of newly open positions.
* @return safetyMarginBps_ The safety margin (in basis points).
*/
function getSafetyMarginBps() external view returns (uint256 safetyMarginBps_);
/**
* @notice Gets the number of tick liquidations to perform when attempting to
* liquidate positions during user actions.
* @return iterations_ The number of iterations for liquidations during user actions.
*/
function getLiquidationIteration() external view returns (uint16 iterations_);
/**
* @notice Gets the time frame for the EMA calculations.
* @dev The EMA is set to the last funding rate when the time elapsed between 2 actions is greater than this value.
* @return period_ The time frame of the EMA (in seconds).
*/
function getEMAPeriod() external view returns (uint128 period_);
/**
* @notice Gets the scaling factor (SF) of the funding rate.
* @return scalingFactor_ The scaling factor (with `FUNDING_SF_DECIMALS` decimals).
*/
function getFundingSF() external view returns (uint256 scalingFactor_);
/**
* @notice Gets the fee taken by the protocol during the application of funding.
* @return feeBps_ The fee applied to the funding (in basis points).
*/
function getProtocolFeeBps() external view returns (uint16 feeBps_);
/**
* @notice Gets the fee applied when a long position is opened or closed.
* @return feeBps_ The fee applied to a long position (in basis points).
*/
function getPositionFeeBps() external view returns (uint16 feeBps_);
/**
* @notice Gets the fee applied during a vault deposit or withdrawal.
* @return feeBps_ The fee applied to a vault action (in basis points).
*/
function getVaultFeeBps() external view returns (uint16 feeBps_);
/**
* @notice Gets the rewards ratio given to the caller when burning SDEX tokens.
* @return rewardsBps_ The rewards ratio (in basis points).
*/
function getSdexRewardsRatioBps() external view returns (uint16 rewardsBps_);
/**
* @notice Gets the part of the remaining collateral given as a bonus to the Rebalancer upon liquidation of a tick.
* @return bonusBps_ The fraction of the remaining collateral for the Rebalancer bonus (in basis points).
*/
function getRebalancerBonusBps() external view returns (uint16 bonusBps_);
/**
* @notice Gets the ratio of SDEX tokens to burn per minted USDN.
* @return ratio_ The ratio (to be divided by SDEX_BURN_ON_DEPOSIT_DIVISOR).
*/
function getSdexBurnOnDepositRatio() external view returns (uint32 ratio_);
/**
* @notice Gets the amount of native tokens used as security deposit when opening a new position.
* @return securityDeposit_ The amount of assets to use as a security deposit (in ether).
*/
function getSecurityDepositValue() external view returns (uint64 securityDeposit_);
/**
* @notice Gets the threshold to reach to send accumulated fees to the fee collector.
* @return threshold_ The amount of accumulated fees to reach (in `_assetDecimals`).
*/
function getFeeThreshold() external view returns (uint256 threshold_);
/**
* @notice Gets the address of the fee collector.
* @return feeCollector_ The address of the fee collector.
*/
function getFeeCollector() external view returns (address feeCollector_);
/**
* @notice Returns the amount of time to wait before an action can be validated.
* @dev This is also the amount of time to add to the initiate action timestamp to fetch the correct price data to
* validate said action with a low-latency oracle.
* @return delay_ The validation delay (in seconds).
*/
function getMiddlewareValidationDelay() external view returns (uint256 delay_);
/**
* @notice Gets the expo imbalance limit when depositing assets (in basis points).
* @return depositExpoImbalanceLimitBps_ The deposit expo imbalance limit.
*/
function getDepositExpoImbalanceLimitBps() external view returns (int256 depositExpoImbalanceLimitBps_);
/**
* @notice Gets the expo imbalance limit when withdrawing assets (in basis points).
* @return withdrawalExpoImbalanceLimitBps_ The withdrawal expo imbalance limit.
*/
function getWithdrawalExpoImbalanceLimitBps() external view returns (int256 withdrawalExpoImbalanceLimitBps_);
/**
* @notice Gets the expo imbalance limit when opening a position (in basis points).
* @return openExpoImbalanceLimitBps_ The open expo imbalance limit.
*/
function getOpenExpoImbalanceLimitBps() external view returns (int256 openExpoImbalanceLimitBps_);
/**
* @notice Gets the expo imbalance limit when closing a position (in basis points).
* @return closeExpoImbalanceLimitBps_ The close expo imbalance limit.
*/
function getCloseExpoImbalanceLimitBps() external view returns (int256 closeExpoImbalanceLimitBps_);
/**
* @notice Returns the limit of the imbalance in bps to close the rebalancer position.
* @return rebalancerCloseExpoImbalanceLimitBps_ The limit of the imbalance in bps to close the rebalancer position.
*/
function getRebalancerCloseExpoImbalanceLimitBps()
external
view
returns (int256 rebalancerCloseExpoImbalanceLimitBps_);
/**
* @notice Returns the imbalance desired on the long side after the creation of a rebalancer position.
* @dev The creation of the rebalancer position aims for this target but does not guarantee reaching it.
* @return targetLongImbalance_ The target long imbalance.
*/
function getLongImbalanceTargetBps() external view returns (int256 targetLongImbalance_);
/**
* @notice Gets the nominal (target) price of USDN.
* @return price_ The price of the USDN token after a rebase (in `_priceFeedDecimals`).
*/
function getTargetUsdnPrice() external view returns (uint128 price_);
/**
* @notice Gets the USDN token price above which a rebase should occur.
* @return threshold_ The rebase threshold (in `_priceFeedDecimals`).
*/
function getUsdnRebaseThreshold() external view returns (uint128 threshold_);
/**
* @notice Gets the minimum collateral amount when opening a long position.
* @return minLongPosition_ The minimum amount (with `_assetDecimals`).
*/
function getMinLongPosition() external view returns (uint256 minLongPosition_);
/* -------------------------------------------------------------------------- */
/* State getters */
/* -------------------------------------------------------------------------- */
/**
* @notice Gets the value of the funding rate at the last timestamp (`getLastUpdateTimestamp`).
* @return lastFunding_ The last value of the funding rate (per day) with `FUNDING_RATE_DECIMALS` decimals.
*/
function getLastFundingPerDay() external view returns (int256 lastFunding_);
/**
* @notice Gets the neutral price of the asset used during the last update of the vault and long balances.
* @return lastPrice_ The most recent known price of the asset (in `_priceFeedDecimals`).
*/
function getLastPrice() external view returns (uint128 lastPrice_);
/**
* @notice Gets the timestamp of the last time a fresh price was provided.
* @return lastTimestamp_ The timestamp of the last update.
*/
function getLastUpdateTimestamp() external view returns (uint128 lastTimestamp_);
/**
* @notice Gets the fees that were accumulated by the contract and are yet to be sent
* to the fee collector (in `_assetDecimals`).
* @return protocolFees_ The amount of accumulated fees still in the contract.
*/
function getPendingProtocolFee() external view returns (uint256 protocolFees_);
/**
* @notice Gets the amount of assets backing the USDN token.
* @return balanceVault_ The amount of assets on the vault side (in `_assetDecimals`).
*/
function getBalanceVault() external view returns (uint256 balanceVault_);
/**
* @notice Gets the pending balance updates due to pending vault actions.
* @return pendingBalanceVault_ The unreflected balance change due to pending vault actions (in `_assetDecimals`).
*/
function getPendingBalanceVault() external view returns (int256 pendingBalanceVault_);
/**
* @notice Gets the exponential moving average of the funding rate per day.
* @return ema_ The exponential moving average of the funding rate per day.
*/
function getEMA() external view returns (int256 ema_);
/**
* @notice Gets the summed value of all the currently open long positions at `_lastUpdateTimestamp`.
* @return balanceLong_ The balance of the long side (in `_assetDecimals`).
*/
function getBalanceLong() external view returns (uint256 balanceLong_);
/**
* @notice Gets the total exposure of all currently open long positions.
* @return totalExpo_ The total exposure of the longs (in `_assetDecimals`).
*/
function getTotalExpo() external view returns (uint256 totalExpo_);
/**
* @notice Gets the accumulator used to calculate the liquidation multiplier.
* @return accumulator_ The liquidation multiplier accumulator.
*/
function getLiqMultiplierAccumulator() external view returns (HugeUint.Uint512 memory accumulator_);
/**
* @notice Gets the current version of the given tick.
* @param tick The tick number.
* @return tickVersion_ The version of the tick.
*/
function getTickVersion(int24 tick) external view returns (uint256 tickVersion_);
/**
* @notice Gets the tick data for the current tick version.
* @param tick The tick number.
* @return tickData_ The tick data.
*/
function getTickData(int24 tick) external view returns (TickData memory tickData_);
/**
* @notice Gets the long position at the provided tick and index.
* @param tick The tick number.
* @param index The position index.
* @return position_ The long position.
*/
function getCurrentLongPosition(int24 tick, uint256 index) external view returns (Position memory position_);
/**
* @notice Gets the highest tick that has an open position.
* @return tick_ The highest populated tick.
*/
function getHighestPopulatedTick() external view returns (int24 tick_);
/**
* @notice Gets the total number of long positions currently open.
* @return totalLongPositions_ The number of long positions.
*/
function getTotalLongPositions() external view returns (uint256 totalLongPositions_);
/**
* @notice Gets the address of the fallback contract.
* @return fallback_ The address of the fallback contract.
*/
function getFallbackAddress() external view returns (address fallback_);
/**
* @notice Gets the pause status of the USDN protocol.
* @return isPaused_ True if it's paused, false otherwise.
*/
function isPaused() external view returns (bool isPaused_);
/**
* @notice Gets the nonce a user can use to generate a delegation signature.
* @dev This is to prevent replay attacks when using an eip712 delegation signature.
* @param user The address of the user.
* @return nonce_ The user's nonce.
*/
function getNonce(address user) external view returns (uint256 nonce_);
/* -------------------------------------------------------------------------- */
/* Setters */
/* -------------------------------------------------------------------------- */
/**
* @notice Replaces the OracleMiddleware contract with a new implementation.
* @dev Cannot be the 0 address.
* @param newOracleMiddleware The address of the new contract.
*/
function setOracleMiddleware(IBaseOracleMiddleware newOracleMiddleware) external;
/**
* @notice Sets the fee collector address.
* @dev Cannot be the zero address.
* @param newFeeCollector The address of the fee collector.
*/
function setFeeCollector(address newFeeCollector) external;
/**
* @notice Replaces the LiquidationRewardsManager contract with a new implementation.
* @dev Cannot be the 0 address.
* @param newLiquidationRewardsManager The address of the new contract.
*/
function setLiquidationRewardsManager(IBaseLiquidationRewardsManager newLiquidationRewardsManager) external;
/**
* @notice Replaces the Rebalancer contract with a new implementation.
* @param newRebalancer The address of the new contract.
*/
function setRebalancer(IBaseRebalancer newRebalancer) external;
/**
* @notice Sets the new deadlines of the exclusivity period for the validator to confirm its action and get its
* security deposit back.
* @param newLowLatencyValidatorDeadline The new exclusivity deadline for low-latency validation (offset from
* initiate timestamp).
* @param newOnChainValidatorDeadline The new exclusivity deadline for on-chain validation (offset from initiate
* timestamp + oracle middleware's low latency delay).
*/
function setValidatorDeadlines(uint128 newLowLatencyValidatorDeadline, uint128 newOnChainValidatorDeadline)
external;
/**
* @notice Sets the minimum long position size.
* @dev This value is used to prevent users from opening positions that are too small and not worth liquidating.
* @param newMinLongPosition The new minimum long position size (with `_assetDecimals`).
*/
function setMinLongPosition(uint256 newMinLongPosition) external;
/**
* @notice Sets the new minimum leverage for a position.
* @param newMinLeverage The new minimum leverage.
*/
function setMinLeverage(uint256 newMinLeverage) external;
/**
* @notice Sets the new maximum leverage for a position.
* @param newMaxLeverage The new maximum leverage.
*/
function setMaxLeverage(uint256 newMaxLeverage) external;
/**
* @notice Sets the new liquidation penalty (in ticks).
* @param newLiquidationPenalty The new liquidation penalty.
*/
function setLiquidationPenalty(uint24 newLiquidationPenalty) external;
/**
* @notice Sets the new exponential moving average period of the funding rate.
* @param newEMAPeriod The new EMA period.
*/
function setEMAPeriod(uint128 newEMAPeriod) external;
/**
* @notice Sets the new scaling factor (SF) of the funding rate.
* @param newFundingSF The new scaling factor (SF) of the funding rate.
*/
function setFundingSF(uint256 newFundingSF) external;
/**
* @notice Sets the protocol fee.
* @dev Fees are charged when the funding is applied (Example: 50 bps -> 0.5%).
* @param newFeeBps The fee to be charged (in basis points).
*/
function setProtocolFeeBps(uint16 newFeeBps) external;
/**
* @notice Sets the position fee.
* @param newPositionFee The new position fee (in basis points).
*/
function setPositionFeeBps(uint16 newPositionFee) external;
/**
* @notice Sets the vault fee.
* @param newVaultFee The new vault fee (in basis points).
*/
function setVaultFeeBps(uint16 newVaultFee) external;
/**
* @notice Sets the rewards ratio given to the caller when burning SDEX tokens.
* @param newRewardsBps The new rewards ratio (in basis points).
*/
function setSdexRewardsRatioBps(uint16 newRewardsBps) external;
/**
* @notice Sets the rebalancer bonus.
* @param newBonus The bonus (in basis points).
*/
function setRebalancerBonusBps(uint16 newBonus) external;
/**
* @notice Sets the ratio of SDEX tokens to burn per minted USDN.
* @param newRatio The new ratio.
*/
function setSdexBurnOnDepositRatio(uint32 newRatio) external;
/**
* @notice Sets the security deposit value.
* @dev The maximum value of the security deposit is 2^64 - 1 = 18446744073709551615 = 18.4 ethers.
* @param securityDepositValue The security deposit value.
* This value cannot be greater than MAX_SECURITY_DEPOSIT.
*/
function setSecurityDepositValue(uint64 securityDepositValue) external;
/**
* @notice Sets the imbalance limits (in basis point).
* @dev `newLongImbalanceTargetBps` needs to be lower than `newCloseLimitBps` and
* higher than the additive inverse of `newWithdrawalLimitBps`.
* @param newOpenLimitBps The new open limit.
* @param newDepositLimitBps The new deposit limit.
* @param newWithdrawalLimitBps The new withdrawal limit.
* @param newCloseLimitBps The new close limit.
* @param newRebalancerCloseLimitBps The new rebalancer close limit.
* @param newLongImbalanceTargetBps The new target imbalance limit for the long side.
* A positive value will target below equilibrium, a negative one will target above equilibrium.
* If negative, the rebalancerCloseLimit will be useless since the minimum value is 1.
*/
function setExpoImbalanceLimits(
uint256 newOpenLimitBps,
uint256 newDepositLimitBps,
uint256 newWithdrawalLimitBps,
uint256 newCloseLimitBps,
uint256 newRebalancerCloseLimitBps,
int256 newLongImbalanceTargetBps
) external;
/**
* @notice Sets the new safety margin for the liquidation price of newly open positions.
* @param newSafetyMarginBps The new safety margin (in basis points).
*/
function setSafetyMarginBps(uint256 newSafetyMarginBps) external;
/**
* @notice Sets the new number of liquidations iteration for user actions.
* @param newLiquidationIteration The new number of liquidation iteration.
*/
function setLiquidationIteration(uint16 newLiquidationIteration) external;
/**
* @notice Sets the minimum amount of fees to be collected before they can be withdrawn.
* @param newFeeThreshold The minimum amount of fees to be collected before they can be withdrawn.
*/
function setFeeThreshold(uint256 newFeeThreshold) external;
/**
* @notice Sets the target USDN price.
* @dev When a rebase of USDN occurs, it will bring the price back down to this value.
* @param newPrice The new target price (with `_priceFeedDecimals`).
* This value cannot be greater than `_usdnRebaseThreshold`.
*/
function setTargetUsdnPrice(uint128 newPrice) external;
/**
* @notice Sets the USDN rebase threshold.
* @dev When the price of USDN exceeds this value, a rebase will be triggered.
* @param newThreshold The new threshold value (with `_priceFeedDecimals`).
* This value cannot be smaller than `_targetUsdnPrice` or greater than uint128(2 * 10 ** s._priceFeedDecimals)
*/
function setUsdnRebaseThreshold(uint128 newThreshold) external;
/**
* @notice Pauses related USDN protocol functions.
* @dev Pauses simultaneously all initiate/validate, refundSecurityDeposit and transferPositionOwnership functions.
* Before pausing, this function will call `_applyPnlAndFunding` with `_lastPrice` and the current timestamp.
* This is done to stop the funding rate from accumulating while the protocol is paused. Be sure to call {unpause}
* to update `_lastUpdateTimestamp` when unpausing.
*/
function pause() external;
/**
* @notice Pauses related USDN protocol functions without applying PnLs and the funding.
* @dev Pauses simultaneously all initiate/validate, refundSecurityDeposit and transferPositionOwnership functions.
* This safe version will not call `_applyPnlAndFunding` before pausing.
*/
function pauseSafe() external;
/**
* @notice Unpauses related USDN protocol functions.
* @dev Unpauses simultaneously all initiate/validate, refundSecurityDeposit and transferPositionOwnership
* functions. This function will set `_lastUpdateTimestamp` to the current timestamp to prevent any funding during
* the pause. Only meant to be called after a {pause} call.
*/
function unpause() external;
/**
* @notice Unpauses related USDN protocol functions without updating `_lastUpdateTimestamp`.
* @dev Unpauses simultaneously all initiate/validate, refundSecurityDeposit and transferPositionOwnership
* functions. This safe version will not set `_lastUpdateTimestamp` to the current timestamp.
*/
function unpauseSafe() external;
}
IUsdnProtocolImpl.sol 54 lines
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
import { IAccessControlDefaultAdminRules } from
"@openzeppelin/contracts/access/extensions/IAccessControlDefaultAdminRules.sol";
import { IERC5267 } from "@openzeppelin/contracts/interfaces/IERC5267.sol";
import { IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import { IBaseLiquidationRewardsManager } from "../LiquidationRewardsManager/IBaseLiquidationRewardsManager.sol";
import { IBaseOracleMiddleware } from "../OracleMiddleware/IBaseOracleMiddleware.sol";
import { IUsdn } from "../Usdn/IUsdn.sol";
import { IUsdnProtocolActions } from "./IUsdnProtocolActions.sol";
import { IUsdnProtocolCore } from "./IUsdnProtocolCore.sol";
import { IUsdnProtocolFallback } from "./IUsdnProtocolFallback.sol";
import { IUsdnProtocolLong } from "./IUsdnProtocolLong.sol";
import { IUsdnProtocolVault } from "./IUsdnProtocolVault.sol";
/**
* @title IUsdnProtocolImpl
* @notice Interface for the implementation of the USDN protocol (completed with {IUsdnProtocolFallback})
*/
interface IUsdnProtocolImpl is
IUsdnProtocolActions,
IUsdnProtocolVault,
IUsdnProtocolLong,
IUsdnProtocolCore,
IAccessControlDefaultAdminRules,
IERC5267
{
/**
* @notice Initializes the protocol's storage with the given values.
* @dev This function should be called on deployment when creating the proxy.
* It can only be called once.
* @param usdn The USDN ERC20 contract address (must have a total supply of 0).
* @param sdex The SDEX ERC20 contract address.
* @param asset The ERC20 contract address of the token held in the vault.
* @param oracleMiddleware The oracle middleware contract address.
* @param liquidationRewardsManager The liquidation rewards manager contract address.
* @param tickSpacing The number of ticks between usable ticks.
* @param feeCollector The address that will receive the protocol fees.
* @param protocolFallback The address of the contract that contains the remaining functions of the protocol.
* Any call with a function signature not present in this contract will be delegated to the fallback contract.
*/
function initializeStorage(
IUsdn usdn,
IERC20Metadata sdex,
IERC20Metadata asset,
IBaseOracleMiddleware oracleMiddleware,
IBaseLiquidationRewardsManager liquidationRewardsManager,
int24 tickSpacing,
address feeCollector,
IUsdnProtocolFallback protocolFallback
) external;
}
AggregatorV3Interface.sol 20 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// solhint-disable-next-line interface-starts-with-i
interface AggregatorV3Interface {
function decimals() external view returns (uint8);
function description() external view returns (string memory);
function version() external view returns (uint256);
function getRoundData(
uint80 _roundId
) external view returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound);
function latestRoundData()
external
view
returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound);
}
IPyth.sol 160 lines
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;
import "./PythStructs.sol";
import "./IPythEvents.sol";
/// @title Consume prices from the Pyth Network (https://pyth.network/).
/// @dev Please refer to the guidance at https://docs.pyth.network/documentation/pythnet-price-feeds/best-practices for how to consume prices safely.
/// @author Pyth Data Association
interface IPyth is IPythEvents {
/// @notice Returns the period (in seconds) that a price feed is considered valid since its publish time
function getValidTimePeriod() external view returns (uint validTimePeriod);
/// @notice Returns the price and confidence interval.
/// @dev Reverts if the price has not been updated within the last `getValidTimePeriod()` seconds.
/// @param id The Pyth Price Feed ID of which to fetch the price and confidence interval.
/// @return price - please read the documentation of PythStructs.Price to understand how to use this safely.
function getPrice(
bytes32 id
) external view returns (PythStructs.Price memory price);
/// @notice Returns the exponentially-weighted moving average price and confidence interval.
/// @dev Reverts if the EMA price is not available.
/// @param id The Pyth Price Feed ID of which to fetch the EMA price and confidence interval.
/// @return price - please read the documentation of PythStructs.Price to understand how to use this safely.
function getEmaPrice(
bytes32 id
) external view returns (PythStructs.Price memory price);
/// @notice Returns the price of a price feed without any sanity checks.
/// @dev This function returns the most recent price update in this contract without any recency checks.
/// This function is unsafe as the returned price update may be arbitrarily far in the past.
///
/// Users of this function should check the `publishTime` in the price to ensure that the returned price is
/// sufficiently recent for their application. If you are considering using this function, it may be
/// safer / easier to use either `getPrice` or `getPriceNoOlderThan`.
/// @return price - please read the documentation of PythStructs.Price to understand how to use this safely.
function getPriceUnsafe(
bytes32 id
) external view returns (PythStructs.Price memory price);
/// @notice Returns the price that is no older than `age` seconds of the current time.
/// @dev This function is a sanity-checked version of `getPriceUnsafe` which is useful in
/// applications that require a sufficiently-recent price. Reverts if the price wasn't updated sufficiently
/// recently.
/// @return price - please read the documentation of PythStructs.Price to understand how to use this safely.
function getPriceNoOlderThan(
bytes32 id,
uint age
) external view returns (PythStructs.Price memory price);
/// @notice Returns the exponentially-weighted moving average price of a price feed without any sanity checks.
/// @dev This function returns the same price as `getEmaPrice` in the case where the price is available.
/// However, if the price is not recent this function returns the latest available price.
///
/// The returned price can be from arbitrarily far in the past; this function makes no guarantees that
/// the returned price is recent or useful for any particular application.
///
/// Users of this function should check the `publishTime` in the price to ensure that the returned price is
/// sufficiently recent for their application. If you are considering using this function, it may be
/// safer / easier to use either `getEmaPrice` or `getEmaPriceNoOlderThan`.
/// @return price - please read the documentation of PythStructs.Price to understand how to use this safely.
function getEmaPriceUnsafe(
bytes32 id
) external view returns (PythStructs.Price memory price);
/// @notice Returns the exponentially-weighted moving average price that is no older than `age` seconds
/// of the current time.
/// @dev This function is a sanity-checked version of `getEmaPriceUnsafe` which is useful in
/// applications that require a sufficiently-recent price. Reverts if the price wasn't updated sufficiently
/// recently.
/// @return price - please read the documentation of PythStructs.Price to understand how to use this safely.
function getEmaPriceNoOlderThan(
bytes32 id,
uint age
) external view returns (PythStructs.Price memory price);
/// @notice Update price feeds with given update messages.
/// This method requires the caller to pay a fee in wei; the required fee can be computed by calling
/// `getUpdateFee` with the length of the `updateData` array.
/// Prices will be updated if they are more recent than the current stored prices.
/// The call will succeed even if the update is not the most recent.
/// @dev Reverts if the transferred fee is not sufficient or the updateData is invalid.
/// @param updateData Array of price update data.
function updatePriceFeeds(bytes[] calldata updateData) external payable;
/// @notice Wrapper around updatePriceFeeds that rejects fast if a price update is not necessary. A price update is
/// necessary if the current on-chain publishTime is older than the given publishTime. It relies solely on the
/// given `publishTimes` for the price feeds and does not read the actual price update publish time within `updateData`.
///
/// This method requires the caller to pay a fee in wei; the required fee can be computed by calling
/// `getUpdateFee` with the length of the `updateData` array.
///
/// `priceIds` and `publishTimes` are two arrays with the same size that correspond to senders known publishTime
/// of each priceId when calling this method. If all of price feeds within `priceIds` have updated and have
/// a newer or equal publish time than the given publish time, it will reject the transaction to save gas.
/// Otherwise, it calls updatePriceFeeds method to update the prices.
///
/// @dev Reverts if update is not needed or the transferred fee is not sufficient or the updateData is invalid.
/// @param updateData Array of price update data.
/// @param priceIds Array of price ids.
/// @param publishTimes Array of publishTimes. `publishTimes[i]` corresponds to known `publishTime` of `priceIds[i]`
function updatePriceFeedsIfNecessary(
bytes[] calldata updateData,
bytes32[] calldata priceIds,
uint64[] calldata publishTimes
) external payable;
/// @notice Returns the required fee to update an array of price updates.
/// @param updateData Array of price update data.
/// @return feeAmount The required fee in Wei.
function getUpdateFee(
bytes[] calldata updateData
) external view returns (uint feeAmount);
/// @notice Parse `updateData` and return price feeds of the given `priceIds` if they are all published
/// within `minPublishTime` and `maxPublishTime`.
///
/// You can use this method if you want to use a Pyth price at a fixed time and not the most recent price;
/// otherwise, please consider using `updatePriceFeeds`. This method may store the price updates on-chain, if they
/// are more recent than the current stored prices.
///
/// This method requires the caller to pay a fee in wei; the required fee can be computed by calling
/// `getUpdateFee` with the length of the `updateData` array.
///
///
/// @dev Reverts if the transferred fee is not sufficient or the updateData is invalid or there is
/// no update for any of the given `priceIds` within the given time range.
/// @param updateData Array of price update data.
/// @param priceIds Array of price ids.
/// @param minPublishTime minimum acceptable publishTime for the given `priceIds`.
/// @param maxPublishTime maximum acceptable publishTime for the given `priceIds`.
/// @return priceFeeds Array of the price feeds corresponding to the given `priceIds` (with the same order).
function parsePriceFeedUpdates(
bytes[] calldata updateData,
bytes32[] calldata priceIds,
uint64 minPublishTime,
uint64 maxPublishTime
) external payable returns (PythStructs.PriceFeed[] memory priceFeeds);
/// @notice Similar to `parsePriceFeedUpdates` but ensures the updates returned are
/// the first updates published in minPublishTime. That is, if there are multiple updates for a given timestamp,
/// this method will return the first update. This method may store the price updates on-chain, if they
/// are more recent than the current stored prices.
///
///
/// @dev Reverts if the transferred fee is not sufficient or the updateData is invalid or there is
/// no update for any of the given `priceIds` within the given time range and uniqueness condition.
/// @param updateData Array of price update data.
/// @param priceIds Array of price ids.
/// @param minPublishTime minimum acceptable publishTime for the given `priceIds`.
/// @param maxPublishTime maximum acceptable publishTime for the given `priceIds`.
/// @return priceFeeds Array of the price feeds corresponding to the given `priceIds` (with the same order).
function parsePriceFeedUpdatesUnique(
bytes[] calldata updateData,
bytes32[] calldata priceIds,
uint64 minPublishTime,
uint64 maxPublishTime
) external payable returns (PythStructs.PriceFeed[] memory priceFeeds);
}
PythStructs.sol 33 lines
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;
contract PythStructs {
// A price with a degree of uncertainty, represented as a price +- a confidence interval.
//
// The confidence interval roughly corresponds to the standard error of a normal distribution.
// Both the price and confidence are stored in a fixed-point numeric representation,
// `x * (10^expo)`, where `expo` is the exponent.
//
// Please refer to the documentation at https://docs.pyth.network/documentation/pythnet-price-feeds/best-practices for how
// to how this price safely.
struct Price {
// Price
int64 price;
// Confidence interval around the price
uint64 conf;
// Price exponent
int32 expo;
// Unix timestamp describing when the price was published
uint publishTime;
}
// PriceFeed represents a current aggregate price from pyth publisher feeds.
struct PriceFeed {
// The price ID.
bytes32 id;
// Latest available price
Price price;
// Latest available exponentially-weighted moving average price
Price emaPrice;
}
}
IAccessControl.sol 98 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (access/IAccessControl.sol)
pragma solidity ^0.8.20;
/**
* @dev External interface of AccessControl declared to support ERC-165 detection.
*/
interface IAccessControl {
/**
* @dev The `account` is missing a role.
*/
error AccessControlUnauthorizedAccount(address account, bytes32 neededRole);
/**
* @dev The caller of a function is not the expected one.
*
* NOTE: Don't confuse with {AccessControlUnauthorizedAccount}.
*/
error AccessControlBadConfirmation();
/**
* @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
*
* `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
* {RoleAdminChanged} not being emitted signaling this.
*/
event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
/**
* @dev Emitted when `account` is granted `role`.
*
* `sender` is the account that originated the contract call. This account bears the admin role (for the granted role).
* Expected in cases where the role was granted using the internal {AccessControl-_grantRole}.
*/
event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
/**
* @dev Emitted when `account` is revoked `role`.
*
* `sender` is the account that originated the contract call:
* - if using `revokeRole`, it is the admin role bearer
* - if using `renounceRole`, it is the role bearer (i.e. `account`)
*/
event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
/**
* @dev Returns `true` if `account` has been granted `role`.
*/
function hasRole(bytes32 role, address account) external view returns (bool);
/**
* @dev Returns the admin role that controls `role`. See {grantRole} and
* {revokeRole}.
*
* To change a role's admin, use {AccessControl-_setRoleAdmin}.
*/
function getRoleAdmin(bytes32 role) external view returns (bytes32);
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/
function grantRole(bytes32 role, address account) external;
/**
* @dev Revokes `role` from `account`.
*
* If `account` had been granted `role`, emits a {RoleRevoked} event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/
function revokeRole(bytes32 role, address account) external;
/**
* @dev Revokes `role` from the calling account.
*
* Roles are often managed via {grantRole} and {revokeRole}: this function's
* purpose is to provide a mechanism for accounts to lose their privileges
* if they are compromised (such as when a trusted device is misplaced).
*
* If the calling account had been granted `role`, emits a {RoleRevoked}
* event.
*
* Requirements:
*
* - the caller must be `callerConfirmation`.
*/
function renounceRole(bytes32 role, address callerConfirmation) external;
}
Context.sol 28 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
pragma solidity ^0.8.20;
/**
* @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;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}
ERC165.sol 27 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/ERC165.sol)
pragma solidity ^0.8.20;
import {IERC165} from "./IERC165.sol";
/**
* @dev Implementation of the {IERC165} interface.
*
* Contracts that want to implement ERC-165 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);
* }
* ```
*/
abstract contract ERC165 is IERC165 {
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
return interfaceId == type(IERC165).interfaceId;
}
}
Panic.sol 57 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/Panic.sol)
pragma solidity ^0.8.20;
/**
* @dev Helper library for emitting standardized panic codes.
*
* ```solidity
* contract Example {
* using Panic for uint256;
*
* // Use any of the declared internal constants
* function foo() { Panic.GENERIC.panic(); }
*
* // Alternatively
* function foo() { Panic.panic(Panic.GENERIC); }
* }
* ```
*
* Follows the list from https://github.com/ethereum/solidity/blob/v0.8.24/libsolutil/ErrorCodes.h[libsolutil].
*
* _Available since v5.1._
*/
// slither-disable-next-line unused-state
library Panic {
/// @dev generic / unspecified error
uint256 internal constant GENERIC = 0x00;
/// @dev used by the assert() builtin
uint256 internal constant ASSERT = 0x01;
/// @dev arithmetic underflow or overflow
uint256 internal constant UNDER_OVERFLOW = 0x11;
/// @dev division or modulo by zero
uint256 internal constant DIVISION_BY_ZERO = 0x12;
/// @dev enum conversion error
uint256 internal constant ENUM_CONVERSION_ERROR = 0x21;
/// @dev invalid encoding in storage
uint256 internal constant STORAGE_ENCODING_ERROR = 0x22;
/// @dev empty array pop
uint256 internal constant EMPTY_ARRAY_POP = 0x31;
/// @dev array out of bounds access
uint256 internal constant ARRAY_OUT_OF_BOUNDS = 0x32;
/// @dev resource error (too large allocation or too large array)
uint256 internal constant RESOURCE_ERROR = 0x41;
/// @dev calling invalid internal function
uint256 internal constant INVALID_INTERNAL_FUNCTION = 0x51;
/// @dev Reverts with a panic code. Recommended to use with
/// the internal constants with predefined codes.
function panic(uint256 code) internal pure {
assembly ("memory-safe") {
mstore(0x00, 0x4e487b71)
mstore(0x20, code)
revert(0x1c, 0x24)
}
}
}
IERC5267.sol 28 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC5267.sol)
pragma solidity ^0.8.20;
interface IERC5267 {
/**
* @dev MAY be emitted to signal that the domain could have changed.
*/
event EIP712DomainChanged();
/**
* @dev returns the fields and values that describe the domain separator used by this contract for EIP-712
* signature.
*/
function eip712Domain()
external
view
returns (
bytes1 fields,
string memory name,
string memory version,
uint256 chainId,
address verifyingContract,
bytes32 salt,
uint256[] memory extensions
);
}
IUsdnProtocolActions.sol 263 lines
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
import { IUsdnProtocolTypes } from "./IUsdnProtocolTypes.sol";
/**
* @title IUsdnProtocolActions
* @notice Interface for the USDN Protocol Actions.
*/
interface IUsdnProtocolActions is IUsdnProtocolTypes {
/**
* @notice Initiates an open position action.
* @dev Consult the current oracle middleware for price data format and possible oracle fee.
* Requires `_securityDepositValue` to be included in the transaction value. In case of pending liquidations, this
* function will not initiate the position (`isInitiated_` would be false).
* The user's input for price and leverage is not guaranteed due to the price difference between the initiate and
* validate actions.
* @param amount The amount of assets to deposit.
* @param desiredLiqPrice The desired liquidation price, including the penalty.
* @param userMaxPrice The user's wanted maximum price at which the position can be opened.
* @param userMaxLeverage The user's wanted maximum leverage for the new position.
* @param to The address that will owns of the position.
* @param validator The address that is supposed to validate the opening and receive the security deposit. If not
* an EOA, it must be a contract that implements a `receive` function.
* @param deadline The deadline for initiating the open position.
* @param currentPriceData The price data used for temporary leverage and entry price computations.
* @param previousActionsData The data needed to validate actionable pending actions.
* @return isInitiated_ Whether the position was successfully initiated. If false, the security deposit was refunded
* @return posId_ The unique position identifier. If the position was not initiated, the tick number will be
* `NO_POSITION_TICK`.
*/
function initiateOpenPosition(
uint128 amount,
uint128 desiredLiqPrice,
uint128 userMaxPrice,
uint256 userMaxLeverage,
address to,
address payable validator,
uint256 deadline,
bytes calldata currentPriceData,
PreviousActionsData calldata previousActionsData
) external payable returns (bool isInitiated_, PositionId memory posId_);
/**
* @notice Validates a pending open position action.
* @dev Consult the current oracle middleware for price data format and possible oracle fee.
* It is possible for this operation to change the tick, tick version and index of the position, in which case we emit
* the `LiquidationPriceUpdated` event.
* This function always sends the security deposit to the validator. So users wanting to earn the corresponding
* security deposit must use `validateActionablePendingActions`.
* In case liquidations are pending (`outcome_ == LongActionOutcome.PendingLiquidations`), the pending action will
* not be removed from the queue, and the user will have to try again.
* In case the position was liquidated by this call (`outcome_ == LongActionOutcome.Liquidated`), this function will
* refund the security deposit and remove the pending action from the queue.
* @param validator The address associated with the pending open position. If not an EOA, it must be a contract that
* implements a `receive` function.
* @param openPriceData The price data for the pending open position.
* @param previousActionsData The data needed to validate actionable pending actions.
* @return outcome_ The effect on the pending action (processed, liquidated, or pending liquidations).
* @return posId_ The position ID after validation (or `NO_POSITION_TICK` if liquidated).
*/
function validateOpenPosition(
address payable validator,
bytes calldata openPriceData,
PreviousActionsData calldata previousActionsData
) external payable returns (LongActionOutcome outcome_, PositionId memory posId_);
/**
* @notice Initiates a close position action.
* @dev Consult the current oracle middleware for price data format and possible oracle fee.
* Requires `_securityDepositValue` to be included in the transaction value.
* If the current tick version is greater than the tick version of the position (when it was opened), then the
* position has been liquidated and the transaction will revert.
* In case liquidations are pending (`outcome_ == LongActionOutcome.PendingLiquidations`), the pending action will
* not be removed from the queue, and the user will have to try again.
* In case the position was liquidated by this call (`outcome_ == LongActionOutcome.Liquidated`), this function will
* refund the security deposit and remove the pending action from the queue.
* The user's input for the price is not guaranteed due to the price difference between the initiate and validate
* actions.
* @param posId The unique identifier of the position to close.
* @param amountToClose The amount of collateral to remove.
* @param userMinPrice The user's wanted minimum price for closing the position.
* @param to The address that will receive the assets.
* @param validator The address that is supposed to validate the closing and receive the security deposit. If not an
* EOA, it must be a contract that implements a `receive` function.
* @param deadline The deadline for initiating the close position.
* @param currentPriceData The price data for temporary calculations.
* @param previousActionsData The data needed to validate actionable pending actions.
* @param delegationSignature Optional EIP712 signature for delegated action.
* @return outcome_ The effect on the pending action (processed, liquidated, or pending liquidations).
*/
function initiateClosePosition(
PositionId calldata posId,
uint128 amountToClose,
uint256 userMinPrice,
address to,
address payable validator,
uint256 deadline,
bytes calldata currentPriceData,
PreviousActionsData calldata previousActionsData,
bytes calldata delegationSignature
) external payable returns (LongActionOutcome outcome_);
/**
* @notice Validates a pending close position action.
* @dev Consult the current oracle middleware for price data format and possible oracle fee.
* This function calculates the final exit price, determines the profit of the long position, and performs the
* payout.
* This function always sends the security deposit to the validator. So users wanting to earn the corresponding
* security deposit must use `validateActionablePendingActions`.
* In case liquidations are pending (`outcome_ == LongActionOutcome.PendingLiquidations`),
* the pending action will not be removed from the queue, and the user will have to try again.
* In case the position was liquidated by this call (`outcome_ == LongActionOutcome.Liquidated`),
* this function will refund the security deposit and remove the pending action from the queue.
* @param validator The address associated with the pending close position. If not an EOA, it must be a contract
* that implements a `receive` function.
* @param closePriceData The price data for the pending close position action.
* @param previousActionsData The data required to validate actionable pending actions.
* @return outcome_ The outcome of the action (processed, liquidated, or pending liquidations).
*/
function validateClosePosition(
address payable validator,
bytes calldata closePriceData,
PreviousActionsData calldata previousActionsData
) external payable returns (LongActionOutcome outcome_);
/**
* @notice Initiates a deposit of assets into the vault to mint USDN.
* @dev Consult the current oracle middleware for price data format and possible oracle fee.
* Requires `_securityDepositValue` to be included in the transaction value.
* In case liquidations are pending, this function might not initiate the deposit, and `success_` would be false.
* The user's input for the shares is not guaranteed due to the price difference between the initiate and validate
* actions.
* @param amount The amount of assets to deposit.
* @param sharesOutMin The minimum amount of USDN shares to receive.
* @param to The address that will receive the USDN tokens.
* @param validator The address that is supposed to validate the deposit and receive the security deposit. If not an
* EOA, it must be a contract that implements a `receive` function.
* @param deadline The deadline for initiating the deposit.
* @param currentPriceData The current price data.
* @param previousActionsData The data required to validate actionable pending actions.
* @return success_ Indicates whether the deposit was successfully initiated.
*/
function initiateDeposit(
uint128 amount,
uint256 sharesOutMin,
address to,
address payable validator,
uint256 deadline,
bytes calldata currentPriceData,
PreviousActionsData calldata previousActionsData
) external payable returns (bool success_);
/**
* @notice Validates a pending deposit action.
* @dev Consult the current oracle middleware for price data format and possible oracle fee.
* This function always sends the security deposit to the validator. So users wanting to earn the corresponding
* security deposit must use `validateActionablePendingActions`.
* If liquidations are pending, the validation may fail, and `success_` would be false.
* @param validator The address associated with the pending deposit action. If not an EOA, it must be a contract
* that implements a `receive` function.
* @param depositPriceData The price data for the pending deposit action.
* @param previousActionsData The data required to validate actionable pending actions.
* @return success_ Indicates whether the deposit was successfully validated.
*/
function validateDeposit(
address payable validator,
bytes calldata depositPriceData,
PreviousActionsData calldata previousActionsData
) external payable returns (bool success_);
/**
* @notice Initiates a withdrawal of assets from the vault using USDN tokens.
* @dev Consult the current oracle middleware for price data format and possible oracle fee.
* Requires `_securityDepositValue` to be included in the transaction value.
* Note that in case liquidations are pending, this function might not initiate the withdrawal, and `success_` would
* be false.
* The user's input for the minimum amount is not guaranteed due to the price difference between the initiate and
* validate actions.
* @param usdnShares The amount of USDN shares to burn.
* @param amountOutMin The minimum amount of assets to receive.
* @param to The address that will receive the assets.
* @param validator The address that is supposed to validate the withdrawal and receive the security deposit. If not
* an EOA, it must be a contract that implements a `receive` function.
* @param deadline The deadline for initiating the withdrawal.
* @param currentPriceData The current price data.
* @param previousActionsData The data required to validate actionable pending actions.
* @return success_ Indicates whether the withdrawal was successfully initiated.
*/
function initiateWithdrawal(
uint152 usdnShares,
uint256 amountOutMin,
address to,
address payable validator,
uint256 deadline,
bytes calldata currentPriceData,
PreviousActionsData calldata previousActionsData
) external payable returns (bool success_);
/**
* @notice Validates a pending withdrawal action.
* @dev Consult the current oracle middleware for price data format and possible oracle fee.
* This function always sends the security deposit to the validator. So users wanting to earn the corresponding
* security deposit must use `validateActionablePendingActions`.
* In case liquidations are pending, this function might not validate the withdrawal, and `success_` would be false.
* @param validator The address associated with the pending withdrawal action. If not an EOA, it must be a contract
* that implements a `receive` function.
* @param withdrawalPriceData The price data for the pending withdrawal action.
* @param previousActionsData The data required to validate actionable pending actions.
* @return success_ Indicates whether the withdrawal was successfully validated.
*/
function validateWithdrawal(
address payable validator,
bytes calldata withdrawalPriceData,
PreviousActionsData calldata previousActionsData
) external payable returns (bool success_);
/**
* @notice Liquidates positions based on the provided asset price.
* @dev Consult the current oracle middleware for price data format and possible oracle fee.
* Each tick is liquidated in constant time. The tick version is incremented for each liquidated tick.
* @param currentPriceData The price data.
* @return liquidatedTicks_ Information about the liquidated ticks.
*/
function liquidate(bytes calldata currentPriceData)
external
payable
returns (LiqTickInfo[] memory liquidatedTicks_);
/**
* @notice Manually validates actionable pending actions.
* @dev Consult the current oracle middleware for price data format and possible oracle fee.
* The timestamp for each pending action is calculated by adding the `OracleMiddleware.validationDelay` to its
* initiation timestamp.
* @param previousActionsData The data required to validate actionable pending actions.
* @param maxValidations The maximum number of actionable pending actions to validate. At least one validation will
* be performed.
* @return validatedActions_ The number of successfully validated actions.
*/
function validateActionablePendingActions(PreviousActionsData calldata previousActionsData, uint256 maxValidations)
external
payable
returns (uint256 validatedActions_);
/**
* @notice Transfers the ownership of a position to another address.
* @dev This function reverts if the caller is not the position owner, if the position does not exist, or if the new
* owner's address is the zero address.
* If the new owner is a contract that implements the `IOwnershipCallback` interface, its `ownershipCallback`
* function will be invoked after the transfer.
* @param posId The unique identifier of the position.
* @param newOwner The address of the new position owner.
* @param delegationSignature An optional EIP712 signature to authorize the transfer on the owner's behalf.
*/
function transferPositionOwnership(PositionId calldata posId, address newOwner, bytes calldata delegationSignature)
external;
/**
* @notice Retrieves the domain separator used in EIP-712 signatures.
* @return domainSeparatorV4_ The domain separator compliant with EIP-712.
*/
function domainSeparatorV4() external view returns (bytes32 domainSeparatorV4_);
}
IUsdnProtocolCore.sol 39 lines
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
/**
* @title IUsdnProtocolCore
* @notice Interface for the core layer of the USDN protocol.
*/
interface IUsdnProtocolCore {
/**
* @notice Computes the predicted funding value since the last state update for the specified timestamp.
* @dev The funding value, when multiplied by the long trading exposure, represents the asset balance to be
* transferred to the vault side, or to the long side if the value is negative.
* Reverts with `UsdnProtocolTimestampTooOld` if the given timestamp is older than the last state update.
* @param timestamp The timestamp to use for the computation.
* @return funding_ The funding magnitude (with `FUNDING_RATE_DECIMALS` decimals) since the last update timestamp.
* @return fundingPerDay_ The funding rate per day (with `FUNDING_RATE_DECIMALS` decimals).
* @return oldLongExpo_ The long trading exposure recorded at the last state update.
*/
function funding(uint128 timestamp)
external
view
returns (int256 funding_, int256 fundingPerDay_, int256 oldLongExpo_);
/**
* @notice Initializes the protocol by making an initial deposit and creating the first long position.
* @dev This function can only be called once. No other user actions can be performed until the protocol
* is initialized.
* @param depositAmount The amount of assets to deposit.
* @param longAmount The amount of assets for the long position.
* @param desiredLiqPrice The desired liquidation price for the long position, excluding the liquidation penalty.
* @param currentPriceData The encoded current price data.
*/
function initialize(
uint128 depositAmount,
uint128 longAmount,
uint128 desiredLiqPrice,
bytes calldata currentPriceData
) external payable;
}
IUsdnProtocolLong.sol 126 lines
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
import { HugeUint } from "@smardex-solidity-libraries-1/HugeUint.sol";
import { IUsdnProtocolTypes } from "./IUsdnProtocolTypes.sol";
/**
* @title IUsdnProtocolLong
* @notice Interface for the long side layer of the USDN protocol.
*/
interface IUsdnProtocolLong is IUsdnProtocolTypes {
/**
* @notice Gets the value of the lowest usable tick, taking into account the tick spacing.
* @dev Note that the effective minimum tick of a newly open long position also depends on the minimum allowed
* leverage value and the current value of the liquidation price multiplier.
* @return tick_ The lowest usable tick.
*/
function minTick() external view returns (int24 tick_);
/**
* @notice Gets the liquidation price from a desired one by taking into account the tick rounding.
* @param desiredLiqPriceWithoutPenalty The desired liquidation price without the penalty.
* @param assetPrice The current price of the asset.
* @param longTradingExpo The trading exposition of the long side.
* @param accumulator The liquidation multiplier accumulator.
* @param tickSpacing The tick spacing.
* @param liquidationPenalty The liquidation penalty set on the tick.
* @return liqPrice_ The new liquidation price without the penalty.
*/
function getLiqPriceFromDesiredLiqPrice(
uint128 desiredLiqPriceWithoutPenalty,
uint256 assetPrice,
uint256 longTradingExpo,
HugeUint.Uint512 memory accumulator,
int24 tickSpacing,
uint24 liquidationPenalty
) external view returns (uint128 liqPrice_);
/**
* @notice Gets the value of a long position when the asset price is equal to the given price, at the given
* timestamp.
* @dev If the current price is smaller than the liquidation price of the position without the liquidation penalty,
* then the value of the position is negative.
* @param posId The unique position identifier.
* @param price The asset price.
* @param timestamp The timestamp of the price.
* @return value_ The position value in assets.
*/
function getPositionValue(PositionId calldata posId, uint128 price, uint128 timestamp)
external
view
returns (int256 value_);
/**
* @notice Gets the tick number corresponding to a given price, accounting for funding effects.
* @dev Uses the stored parameters for calculation.
* @param price The asset price.
* @return tick_ The tick number, a multiple of the tick spacing.
*/
function getEffectiveTickForPrice(uint128 price) external view returns (int24 tick_);
/**
* @notice Gets the tick number corresponding to a given price, accounting for funding effects.
* @param price The asset price.
* @param assetPrice The current price of the asset.
* @param longTradingExpo The trading exposition of the long side.
* @param accumulator The liquidation multiplier accumulator.
* @param tickSpacing The tick spacing.
* @return tick_ The tick number, a multiple of the tick spacing.
*/
function getEffectiveTickForPrice(
uint128 price,
uint256 assetPrice,
uint256 longTradingExpo,
HugeUint.Uint512 memory accumulator,
int24 tickSpacing
) external view returns (int24 tick_);
/**
* @notice Retrieves the liquidation penalty assigned to the given tick if there are positions in it, otherwise
* retrieve the current setting value from storage.
* @param tick The tick number.
* @return liquidationPenalty_ The liquidation penalty, in tick spacing units.
*/
function getTickLiquidationPenalty(int24 tick) external view returns (uint24 liquidationPenalty_);
/**
* @notice Gets a long position identified by its tick, tick version and index.
* @param posId The unique position identifier.
* @return pos_ The position data.
* @return liquidationPenalty_ The liquidation penalty for that position.
*/
function getLongPosition(PositionId calldata posId)
external
view
returns (Position memory pos_, uint24 liquidationPenalty_);
/**
* @notice Gets the predicted value of the long balance for the given asset price and timestamp.
* @dev The effects of the funding and any PnL of the long positions since the last contract state
* update is taken into account, as well as the fees. If the provided timestamp is older than the last state
* update, the function reverts with `UsdnProtocolTimestampTooOld`. The value cannot be below 0.
* @param currentPrice The given asset price.
* @param timestamp The timestamp corresponding to the given price.
* @return available_ The long balance value in assets.
*/
function longAssetAvailableWithFunding(uint128 currentPrice, uint128 timestamp)
external
view
returns (uint256 available_);
/**
* @notice Gets the predicted value of the long trading exposure for the given asset price and timestamp.
* @dev The effects of the funding and any profit or loss of the long positions since the last contract state
* update is taken into account. If the provided timestamp is older than the last state update, the function reverts
* with `UsdnProtocolTimestampTooOld`. The value cannot be below 0.
* @param currentPrice The given asset price.
* @param timestamp The timestamp corresponding to the given price.
* @return expo_ The long trading exposure value in assets.
*/
function longTradingExpoWithFunding(uint128 currentPrice, uint128 timestamp)
external
view
returns (uint256 expo_);
}
IUsdnProtocolVault.sol 41 lines
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
/**
* @title IUsdnProtocolVault
* @notice Interface for the vault layer of the USDN protocol.
*/
interface IUsdnProtocolVault {
/**
* @notice Calculates the predicted USDN token price based on the given asset price and timestamp.
* @dev The effects of the funding and the PnL of the long positions since the last contract state update are taken
* into account.
* @param currentPrice The current or predicted asset price.
* @param timestamp The timestamp corresponding to `currentPrice`.
* @return price_ The predicted USDN token price.
*/
function usdnPrice(uint128 currentPrice, uint128 timestamp) external view returns (uint256 price_);
/**
* @notice Calculates the USDN token price based on the given asset price at the current timestamp.
* @dev The effects of the funding and the PnL of the long positions since the last contract state update are taken
* into account.
* @param currentPrice The asset price at `block.timestamp`.
* @return price_ The calculated USDN token price.
*/
function usdnPrice(uint128 currentPrice) external view returns (uint256 price_);
/**
* @notice Gets the amount of assets in the vault for the given asset price and timestamp.
* @dev The effects of the funding, the PnL of the long positions and the accumulated fees since the last contract
* state update are taken into account, but not liquidations. If the provided timestamp is older than the last
* state update, the function reverts with `UsdnProtocolTimestampTooOld`.
* @param currentPrice The current or predicted asset price.
* @param timestamp The timestamp corresponding to `currentPrice` (must not be earlier than `_lastUpdateTimestamp`).
* @return available_ The available vault balance (cannot be less than 0).
*/
function vaultAssetAvailableWithFunding(uint128 currentPrice, uint128 timestamp)
external
view
returns (uint256 available_);
}
IPythEvents.sol 18 lines
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;
/// @title IPythEvents contains the events that Pyth contract emits.
/// @dev This interface can be used for listening to the updates for off-chain and testing purposes.
interface IPythEvents {
/// @dev Emitted when the price feed with `id` has received a fresh update.
/// @param id The Pyth Price Feed ID.
/// @param publishTime Publish time of the given price update.
/// @param price Price of the given price update.
/// @param conf Confidence interval of the given price update.
event PriceFeedUpdate(
bytes32 indexed id,
uint64 publishTime,
int64 price,
uint64 conf
);
}
IERC165.sol 25 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/IERC165.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC-165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[ERC].
*
* 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[ERC 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);
}
Read Contract
ADMIN_ROLE 0x75b238fc → bytes32
BPS_DIVISOR 0x191fe1ed → uint16
DEFAULT_ADMIN_ROLE 0xa217fddf → bytes32
MAX_CONF_RATIO 0x98793ff9 → uint16
PRICE_TOO_OLD 0xaf2d32bb → int256
defaultAdmin 0x84ef8ffc → address
defaultAdminDelay 0xcc8463c8 → uint48
defaultAdminDelayIncreaseWait 0x022d63fb → uint48
getChainlinkDecimals 0xab8b2f34 → uint256
getChainlinkTimeElapsedLimit 0xbe7756dd → uint256
getConfRatioBps 0x94218074 → uint16
getDecimals 0xf0141d84 → uint8
getLowLatencyDelay 0xc9371327 → uint16
getPriceFeed 0x9e87a5cd → address
getPyth 0x78399b0b → address
getPythFeedId 0xb222ec71 → bytes32
getPythRecentPriceDelay 0xb1529fbf → uint64
getRoleAdmin 0x248a9ca3 → bytes32
getValidationDelay 0x831bcbf3 → uint256
hasRole 0x91d14854 → bool
owner 0x8da5cb5b → address
pendingDefaultAdmin 0xcf6eefb7 → address, uint48
pendingDefaultAdminDelay 0xa1eda53c → uint48, uint48
supportsInterface 0x01ffc9a7 → bool
validationCost 0x3f5bd8a9 → uint256
Write Contract 15 functions
These functions modify contract state and require a wallet transaction to execute.
acceptDefaultAdminTransfer 0xcefc1429
No parameters
beginDefaultAdminTransfer 0x634e93da
address newAdmin
cancelDefaultAdminTransfer 0xd602b9fd
No parameters
changeDefaultAdminDelay 0x649a5ec7
uint48 newDelay
grantRole 0x2f2ff15d
bytes32 role
address account
parseAndValidatePrice 0xf211abcc
bytes32 actionId
uint128 targetTimestamp
uint8 action
bytes data
returns: tuple
renounceRole 0x36568abe
bytes32 role
address account
revokeRole 0xd547741f
bytes32 role
address account
rollbackDefaultAdminDelay 0x0aa6220b
No parameters
setChainlinkTimeElapsedLimit 0xc4fa8bf4
uint256 newTimeElapsedLimit
setConfRatio 0x93f22b9d
uint16 newConfRatio
setLowLatencyDelay 0x0b6313a6
uint16 newLowLatencyDelay
address usdnProtocol
setPythRecentPriceDelay 0x63174c7c
uint64 newDelay
setValidationDelay 0x986e1890
uint256 newValidationDelay
withdrawEther 0xaf933b57
address to
Recent Transactions
No transactions found for this address