Cryo Explorer Ethereum Mainnet

Address Contract Verified

Address 0xe6C5dc8508939B1c417f83B7fed3149ef22cF3Ff
Balance 0 ETH
Nonce 1
Code Size 19372 bytes
Indexed Transactions 0
External Etherscan · Sourcify

Contract Bytecode

19372 bytes
0x608060405234801561001057600080fd5b50600436106103625760003560e01c80639a6b27cf116101c8578063bffe348611610104578063d805b074116100a2578063ebbc49651161007c578063ebbc4965146108a8578063f1bffe27146108b0578063f8d89898146108c3578063fbfa77cf146108d657600080fd5b8063d805b0741461085f578063daeccc7914610872578063dd50e2d3146108a057600080fd5b8063cf6a9a94116100de578063cf6a9a94146107cc578063d015740b146107f3578063d2ae9a0d14610823578063d49dbdeb1461083657600080fd5b8063bffe348614610744578063c415b95c14610796578063cbc9af88146107b957600080fd5b8063b1f8100d11610171578063b9d4e8791161014b578063b9d4e879146106ef578063baaaa7b11461070b578063bfcc8c411461071e578063bfccf0ec1461073157600080fd5b8063b1f8100d146106a2578063b2016bd4146106b5578063b54e0e1f146106dc57600080fd5b8063a42dce80116101a2578063a42dce8014610671578063a7229fd914610684578063af1454d31461069757600080fd5b80639a6b27cf146106505780639e6289771461053a578063a08105701461066057600080fd5b806359b97ddb116102a2578063757bb8131161024057806386dd05181161021a57806386dd0518146105e85780638da5cb5b146105fb57806390421bf61461060e5780639672093e1461063557600080fd5b8063757bb8131461057c57806380f51c12146105b257806385a3a589146105d557600080fd5b8063708891651161027c578063708891651461053a5780637180c8ca14610543578063724593c01461055657806372637c181461056957600080fd5b806359b97ddb14610517578063633854ce1461051f5780636a68c7181461053257600080fd5b8063167860a71161030f5780632f6a2967116102e95780632f6a29671461045257806346fbf68e1461049f57806351e163c0146104cb57806357b17a52146104de57600080fd5b8063167860a7146104245780632b968958146104375780632d97c13d1461043f57600080fd5b80630a5517ca116103405780630a5517ca146103ba5780630ef616cf146103c957806312481ad6146103f457600080fd5b806301ffc9a71461036757806305a8b8301461038f57806306bb50bf146103a4575b600080fd5b61037a61037536600461401f565b6108fc565b60405190151581526020015b60405180910390f35b6103a261039d36600461404f565b610995565b005b6103ac610a22565b604051908152602001610386565b6103ac67016345785d8a000081565b6007546103dc906001600160a01b031681565b6040516001600160a01b039091168152602001610386565b60085461040e9065010000000000900464ffffffffff1681565b60405164ffffffffff9091168152602001610386565b6103a2610432366004614084565b610b99565b6103a2610de6565b6103a261044d3660046140e0565b610eed565b600754610482907401000000000000000000000000000000000000000090046bffffffffffffffffffffffff1681565b6040516bffffffffffffffffffffffff9091168152602001610386565b61037a6104ad3660046140e0565b6001600160a01b031660009081526003602052604090205460ff1690565b6006546103dc906001600160a01b031681565b6006546105049074010000000000000000000000000000000000000000900461ffff1681565b60405161ffff9091168152602001610386565b6103ac610ff1565b6103a261052d3660046140fd565b611072565b6103a26110c9565b61050461014a81565b6103a2610551366004614124565b6112c3565b6103ac61056436600461415d565b61138b565b6103a261057736600461417f565b6113a8565b61058f61058a36600461404f565b6114a1565b604080519384526001600160a01b03909216602084015290820152606001610386565b61037a6105c03660046140e0565b60036020526000908152604090205460ff1681565b6103a26105e33660046141c8565b6114f4565b6103a26105f636600461421a565b6115ab565b6000546103dc906001600160a01b031681565b60065461037a90760100000000000000000000000000000000000000000000900460ff1681565b61063d6117ca565b604051600f9190910b8152602001610386565b600454610100900460ff1661037a565b60085461040e9064ffffffffff1681565b6103a261067f3660046140e0565b6117dc565b6103a261069236600461424b565b6118d4565b60045460ff1661037a565b6103a26106b03660046140e0565b6119eb565b6103dc7f0000000000000000000000000ab87046fbb341d058f17cbc4c1133f25a20a52f81565b6103a26106ea36600461428c565b611afa565b600854610504906a0100000000000000000000900461ffff1681565b6103a26107193660046142b0565b611c09565b6103a261072c3660046140e0565b611c7d565b6103a261073f3660046142c8565b611e54565b6107776107523660046140e0565b600960205260009081526040902080546001909101546001600160a01b039091169082565b604080516001600160a01b039093168352602083019190915201610386565b6008546103dc906c0100000000000000000000000090046001600160a01b031681565b6103a26107c736600461428c565b612019565b6103dc7f000000000000000000000000db591ea2e5db886da872654d58f6cc584b68e7cc81565b600554610482907401000000000000000000000000000000000000000090046bffffffffffffffffffffffff1681565b6103a26108313660046143ce565b61211e565b61083e612395565b6040516fffffffffffffffffffffffffffffffff9091168152602001610386565b6103a261086d36600461448b565b612439565b61037a6108803660046144d5565b600160209081526000928352604080842090915290825290205460ff1681565b6103ac6124c4565b6103a261257f565b6103a26108be36600461450a565b61261a565b6005546103dc906001600160a01b031681565b7f0000000000000000000000001db1591540d7a6062be0837ca3c808add28844f66103dc565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167fcd77a74500000000000000000000000000000000000000000000000000000000148061098f57507fffffffff0000000000000000000000000000000000000000000000000000000082167f01ffc9a700000000000000000000000000000000000000000000000000000000145b92915050565b336001600160a01b037f0000000000000000000000001db1591540d7a6062be0837ca3c808add28844f616146109de57604051633006171960e21b815260040160405180910390fd5b610a1d610a18846109f9856109f16124c4565b8660016126eb565b6001600160a01b0387166000908152600960205260409020919061276b565b612796565b505050565b6005546006546007546040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000936001600160a01b0390811693811692831515929116908115159085906370a0823190602401602060405180830381865afa158015610a9c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ac09190614527565b95508215610adf57610ad28430612847565b610adc908761456f565b95505b8015610b91576040517f70a082310000000000000000000000000000000000000000000000000000000081526001600160a01b0383811660048301528616906370a0823190602401602060405180830381865afa158015610b44573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b689190614527565b610b72908761456f565b95508215610b9157610b848483612847565b610b8e908761456f565b95505b505050505090565b336001600160a01b037f0000000000000000000000001db1591540d7a6062be0837ca3c808add28844f61614610be257604051633006171960e21b815260040160405180910390fd5b6000610bed87612963565b90506000610bfa82614582565b90506000610c098260006129b6565b90506000610c1c8884600f0b8888612cad565b90508051600014610cd5576040517fb64c393a0000000000000000000000000000000000000000000000000000000081526001600160a01b037f000000000000000000000000db591ea2e5db886da872654d58f6cc584b68e7cc169063b64c393a90610c8e9084903090600401614610565b6060604051808303816000875af1158015610cad573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610cd1919061463b565b5050505b506040517f2cf05b400000000000000000000000000000000000000000000000000000000081526060906001600160a01b037f000000000000000000000000db591ea2e5db886da872654d58f6cc584b68e7cc1690632cf05b4090610d4490879030908c908790600401614669565b6020604051808303816000875af1158015610d63573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d8791906146b7565b50604080518b8152602081018b90526001600160a01b03891681830152600f84900b606082015290517f7c1a089fe19cc45f63d2ae7ab5689183827e444bae9731f3e09b8709f7d82d239181900360800190a150505050505050505050565b610e14336000357fffffffff0000000000000000000000000000000000000000000000000000000016612d49565b610e3157604051633006171960e21b815260040160405180910390fd5b6002546001600160a01b031661dead14610e8b576002546040517f8e4c8aa60000000000000000000000000000000000000000000000000000000081526001600160a01b0390911660048201526024015b60405180910390fd5b600080546040516001600160a01b03909116907f5cd6b24c0149d980c82592262b3a81294b39f8f6e3c004126aaf0828c787d554908390a3600080547fffffffffffffffffffffffff0000000000000000000000000000000000000000169055565b610f1b336000357fffffffff0000000000000000000000000000000000000000000000000000000016612d49565b610f3857604051633006171960e21b815260040160405180910390fd5b6001600160a01b038116610f83576040517f8e4c8aa60000000000000000000000000000000000000000000000000000000081526001600160a01b0382166004820152602401610e82565b6040516001600160a01b038216907f673779832598d6a388768ee342f8de96fdd5c39a468a6955377cf1405b9652b990600090a2600780547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0392909216919091179055565b600080611045610fff612395565b6005546fffffffffffffffffffffffffffffffff91909116907401000000000000000000000000000000000000000090046bffffffffffffffffffffffff166001612db0565b90506000611051610a22565b905080821161106157600061106b565b61106b81836146e9565b9250505090565b6110a0336000357fffffffff0000000000000000000000000000000000000000000000000000000016612d49565b6110bd57604051633006171960e21b815260040160405180910390fd5b6110c681612e1c565b50565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000907f0000000000000000000000001db1591540d7a6062be0837ca3c808add28844f66001600160a01b0316906370a0823190602401602060405180830381865afa158015611149573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061116d9190614527565b60085490915060009081906111969084906a0100000000000000000000900461ffff1683613120565b60408051838152602081018390529294509092507f072b053b1a85b7b9a1d87d61c12e8e8f01a28b7b85ebc240d47662353b036609910160405180910390a1801561122557600854611225906001600160a01b037f0000000000000000000000001db1591540d7a6062be0837ca3c808add28844f68116916c0100000000000000000000000090041683613150565b8115610a1d576040517f42966c68000000000000000000000000000000000000000000000000000000008152600481018390527f0000000000000000000000001db1591540d7a6062be0837ca3c808add28844f66001600160a01b0316906342966c6890602401600060405180830381600087803b1580156112a657600080fd5b505af11580156112ba573d6000803e3d6000fd5b50505050505050565b6112f1336000357fffffffff0000000000000000000000000000000000000000000000000000000016612d49565b61130e57604051633006171960e21b815260040160405180910390fd5b6001600160a01b03821660008181526003602090815260409182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001685151590811790915591519182527fa11b5803b8a35081b8f993e0dee5bc30301a3d83f644e5ab2ff39f972f0a807f910160405180910390a25050565b60006113a1836113996124c4565b8460006126eb565b9392505050565b6113d6336000357fffffffff0000000000000000000000000000000000000000000000000000000016612d49565b6113f357604051633006171960e21b815260040160405180910390fd5b600880547fffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000001664ffffffffff8416908117909155600780546001600160a01b0316740100000000000000000000000000000000000000006bffffffffffffffffffffffff8516908102919091179091556040805192835260208301919091527f8a337a68484dadae2ebe98e2be78818c2acdf428e878ec55b3b6e0c0efdf77b3910160405180910390a15050565b60008060006114b0858561138b565b6001600160a01b0396871660009081526009602090815260409182902082518084019093528054909916808352600190990154910181905290979095509350505050565b336001600160a01b037f0000000000000000000000001db1591540d7a6062be0837ca3c808add28844f6161461153d57604051633006171960e21b815260040160405180910390fd5b60006115476124c4565b6001600160a01b03871660009081526009602052604090209091506115a390610a18908861157889868860016126eb565b6001600160a01b03891660009081526009602052604090208961159e8a898b60016126eb565b613217565b505050505050565b336001600160a01b037f0000000000000000000000001db1591540d7a6062be0837ca3c808add28844f616146115f457604051633006171960e21b815260040160405180910390fd5b600061160284878585612cad565b905060607f000000000000000000000000db591ea2e5db886da872654d58f6cc584b68e7cc6001600160a01b03166305d6f8ad61163e89612963565b30846040518463ffffffff1660e01b815260040161165e939291906146fc565b600060405180830381600087803b15801561167857600080fd5b505af115801561168c573d6000803e3d6000fd5b505050508151600014611747576040517fb64c393a0000000000000000000000000000000000000000000000000000000081526001600160a01b037f000000000000000000000000db591ea2e5db886da872654d58f6cc584b68e7cc169063b64c393a906117009085903090600401614610565b6060604051808303816000875af115801561171f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611743919061463b565b5050505b60006117546000886129b6565b60055490915061176e906001600160a01b03168789613150565b60408051898152602081018990526001600160a01b03881681830152600f83900b606082015290517fdd59a648fc60c2ec0babc22331309da32b7e70ee36ca53045ed97937e425483d9181900360800190a15050505050505050565b60006117d76000806129b6565b905090565b61180a336000357fffffffff0000000000000000000000000000000000000000000000000000000016612d49565b61182757604051633006171960e21b815260040160405180910390fd5b6001600160a01b03811661186a576040517f8e4c8aa600000000000000000000000000000000000000000000000000000000815260006004820152602401610e82565b6040516001600160a01b038216907f12e1d17016b94668449f97876f4a8d5cc2c19f314db337418894734037cc19d490600090a2600880546001600160a01b039092166c01000000000000000000000000026bffffffffffffffffffffffff909216919091179055565b611902336000357fffffffff0000000000000000000000000000000000000000000000000000000016612d49565b61191f57604051633006171960e21b815260040160405180910390fd5b6005546001600160a01b038481169116148061194857506006546001600160a01b038481169116145b1561198a576040517f961c9a4f0000000000000000000000000000000000000000000000000000000081526001600160a01b0384166004820152602401610e82565b826001600160a01b0316826001600160a01b03167f879f92dded0f26b83c3e00b12e0395dc72cfc3077343d1854ed6988edd1f9096836040516119cf91815260200190565b60405180910390a3610a1d6001600160a01b0384168383613150565b611a19336000357fffffffff0000000000000000000000000000000000000000000000000000000016612d49565b611a3657604051633006171960e21b815260040160405180910390fd5b6001600160a01b038116611a81576040517f8e4c8aa60000000000000000000000000000000000000000000000000000000081526001600160a01b0382166004820152602401610e82565b600254600080546040516001600160a01b03808616948116939216917f64420d4a41c6ed4de2bccbf33192eea18e576c5b23c79c3a722d4e9534c2e8d891a4600280547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0392909216919091179055565b611b28336000357fffffffff0000000000000000000000000000000000000000000000000000000016612d49565b611b4557604051633006171960e21b815260040160405180910390fd5b61014a61ffff82161115611b85576040517fd252903400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60405161ffff821681527f990129c5b5310a81f94ed9049364e93ff504a3dbd9ef24503b64537effd4e2129060200160405180910390a16006805461ffff90921674010000000000000000000000000000000000000000027fffffffffffffffffffff0000ffffffffffffffffffffffffffffffffffffffff909216919091179055565b3360009081526003602052604090205460ff16611c3957604051633006171960e21b815260040160405180910390fd5b7f803ee193075547dae36361498f3de5e399cdb29b7e0c7b680533f3da8b733a1781604051611c689190614736565b60405180910390a1806004610a1d8282614766565b611cab336000357fffffffff0000000000000000000000000000000000000000000000000000000016612d49565b611cc857604051633006171960e21b815260040160405180910390fd5b60045460ff161580611ce25750600454610100900460ff16155b15611d19576040517fbc871ce500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6005546006546001600160a01b039182169116611d58827f000000000000000000000000db591ea2e5db886da872654d58f6cc584b68e7cc60006132c3565b6001600160a01b03811615611d7c57611d7c6001600160a01b0383168260006132c3565b60007f000000000000000000000000db591ea2e5db886da872654d58f6cc584b68e7cc6001600160a01b031663f8d898986040518163ffffffff1660e01b8152600401602060405180830381865afa158015611ddc573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e0091906147e7565b905083611e0d828261342b565b806001600160a01b0316826001600160a01b03167f66b1e3550aca8bbae3dd0fc223b9fe2675b387ebc059caef17a7400ecdc2e1d960405160405180910390a35050505050565b611e82336000357fffffffff0000000000000000000000000000000000000000000000000000000016612d49565b611e9f57604051633006171960e21b815260040160405180910390fd5b6001600160a01b038316611eea576040517f8e4c8aa60000000000000000000000000000000000000000000000000000000081526001600160a01b0384166004820152602401610e82565b604080518082019091526000808252602082015260005b8281101561201257838382818110611f1b57611f1b614804565b905060400201803603810190611f319190614833565b91508160200151151582600001517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916866001600160a01b03167ff5736e75de2c751f775d4c5ed517289f77074f8c337f451ba4c0c3ed1dd7f9ad60405160405180910390a46020828101516001600160a01b03871660009081526001808452604080832087517fffffffff0000000000000000000000000000000000000000000000000000000016845290945292902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001691151591909117905501611f01565b5050505050565b612047336000357fffffffff0000000000000000000000000000000000000000000000000000000016612d49565b61206457604051633006171960e21b815260040160405180910390fd5b61014a61ffff821611156120a4576040517fd252903400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60405161ffff821681527fceb20f7f0b19335681096ee1eaa9bb2a6ef5a9a69ba48b6b488e7b7eff2ef04d9060200160405180910390a16008805461ffff9092166a0100000000000000000000027fffffffffffffffffffffffffffffffffffffffff0000ffffffffffffffffffff909216919091179055565b61214c336000357fffffffff0000000000000000000000000000000000000000000000000000000016612d49565b61216957604051633006171960e21b815260040160405180910390fd5b6007547401000000000000000000000000000000000000000090046bffffffffffffffffffffffff168211156121cb576040517fbdff37b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6008546121eb9064ffffffffff8082169165010000000000900416614890565b64ffffffffff1642101561222b576040517f0965fe8300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600880547fffffffffffffffffffffffffffffffffffffffffffff0000000000ffffffffff16650100000000004264ffffffffff16021790556007546006546005546001600160a01b039283169291821691166000821561228c578261228e565b815b90506122a46001600160a01b0382168588613150565b806001600160a01b03167f19c64d15f93886cd5726e45942ed1c77b389ba8bd5cb129c8f2e8a42404bc8fe876040516122df91815260200190565b60405180910390a26040517fee534a3f0000000000000000000000000000000000000000000000000000000081526001600160a01b0385169063ee534a3f906123529084908a907f0000000000000000000000001db1591540d7a6062be0837ca3c808add28844f6908b9060040161491c565b6020604051808303816000875af1158015612371573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112ba9190614527565b6040517f5472214b0000000000000000000000000000000000000000000000000000000081523060048201526000907f000000000000000000000000db591ea2e5db886da872654d58f6cc584b68e7cc6001600160a01b031690635472214b90602401602060405180830381865afa158015612415573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117d791906146b7565b336001600160a01b037f0000000000000000000000001db1591540d7a6062be0837ca3c808add28844f6161461248257604051633006171960e21b815260040160405180910390fd5b6124be610a18858361249e876124966124c4565b8860016126eb565b6001600160a01b038916600090815260096020526040902092919061369a565b50505050565b6040517f030c3c580000000000000000000000000000000000000000000000000000000081523060048201526000907f000000000000000000000000db591ea2e5db886da872654d58f6cc584b68e7cc6001600160a01b03169063030c3c5890602401602060405180830381865afa158015612544573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061256891906146b7565b6fffffffffffffffffffffffffffffffff16905090565b6002546001600160a01b031633146125aa57604051633006171960e21b815260040160405180910390fd5b6000805460405133926001600160a01b03909216917f5cd6b24c0149d980c82592262b3a81294b39f8f6e3c004126aaf0828c787d55491a3600080547fffffffffffffffffffffffff00000000000000000000000000000000000000009081163317909155600280549091169055565b612648336000357fffffffff0000000000000000000000000000000000000000000000000000000016612d49565b61266557604051633006171960e21b815260040160405180910390fd5b60068054821515760100000000000000000000000000000000000000000000027fffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffff9091161790556040517f4b87e76680987bd1ad4e6874dfd845441cd40583a7ec9482105acf19e64c6bbd906126e090831515815260200190565b60405180910390a150565b6000826000036126fd57506000612763565b82851115612737576040517fd252903400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6127448486856000613779565b905081801561275a575067016345785d8a000081105b15612763575060005b949350505050565b825460018401546060916127639161279191879187916001600160a01b0316908761380c565b61384d565b8051156110c6576040517fb64c393a0000000000000000000000000000000000000000000000000000000081526001600160a01b037f000000000000000000000000db591ea2e5db886da872654d58f6cc584b68e7cc169063b64c393a906128049084903090600401614610565b6060604051808303816000875af1158015612823573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124be919061463b565b6040517f70a082310000000000000000000000000000000000000000000000000000000081526001600160a01b03828116600483015260009182918516906370a0823190602401602060405180830381865afa1580156128ab573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128cf9190614527565b9050801561295c576040517f4cdad506000000000000000000000000000000000000000000000000000000008152600481018290526001600160a01b03851690634cdad50690602401602060405180830381865afa158015612935573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129599190614527565b91505b5092915050565b60006fffffffffffffffffffffffffffffffff8211156129b2576040517fe0fb6a7c00000000000000000000000000000000000000000000000000000000815260048101839052602401610e82565b5090565b6040517fb611328a000000000000000000000000000000000000000000000000000000008152306004820152600f83900b60248201526000907f000000000000000000000000db591ea2e5db886da872654d58f6cc584b68e7cc6001600160a01b03169063b611328a90604401602060405180830381865afa158015612a40573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a64919061494e565b9050600081600f0b1315612b7057600654760100000000000000000000000000000000000000000000900460ff1615612a9f5750600061098f565b6040517fa8f18f250000000000000000000000000000000000000000000000000000000081526fffffffffffffffffffffffffffffffff82166004820152306024820181905260448201527f000000000000000000000000db591ea2e5db886da872654d58f6cc584b68e7cc6001600160a01b03169063a8f18f25906064016020604051808303816000875af1158015612b3d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b6191906146b7565b50612b6b82612e1c565b61098f565b600081600f0b121561098f576000612b8782614582565b600554909150600090612bd5906fffffffffffffffffffffffffffffffff8416907401000000000000000000000000000000000000000090046bffffffffffffffffffffffff166001612db0565b9050612be9612be4828661456f565b612e1c565b6040517f25e6b5e80000000000000000000000000000000000000000000000000000000081526fffffffffffffffffffffffffffffffff831660048201523060248201527f000000000000000000000000db591ea2e5db886da872654d58f6cc584b68e7cc6001600160a01b0316906325e6b5e8906044016020604051808303816000875af1158015612c80573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ca491906146b7565b50505092915050565b6001600160a01b03808516600090815260096020526040902080546060921615612d4057600085612cdc6124c4565b612ce69190614971565b90506000811215612d23576040517fd252903400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612d3c87612d3487848860016126eb565b84919061276b565b9250505b50949350505050565b600080546001600160a01b03848116911614806113a15750506001600160a01b039190911660009081526001602090815260408083207fffffffff000000000000000000000000000000000000000000000000000000009094168352929052205460ff1690565b600082600103612dc15750826113a1565b6000826001811115612dd557612dd5614999565b03612df157828481612de957612de96149c8565b0490506113a1565b8315612e1257826001850381612e0957612e096149c8565b04600101612763565b5060009392505050565b6006546001600160a01b031680612e31575050565b6005546040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000916001600160a01b0316906370a0823190602401602060405180830381865afa158015612e93573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612eb79190614527565b9050600083821115612ff257506040517f402d267d000000000000000000000000000000000000000000000000000000008152306004820152838203906000906001600160a01b0385169063402d267d90602401602060405180830381865afa158015612f28573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612f4c9190614527565b905080821115612f5a578091505b8115612fec576040517f6e553f65000000000000000000000000000000000000000000000000000000008152600481018390523060248201526001600160a01b03851690636e553f65906044016020604051808303816000875af1158015612fc6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612fea9190614527565b505b506124be565b506040517fce96cb77000000000000000000000000000000000000000000000000000000008152306004820152818403906000906001600160a01b0385169063ce96cb7790602401602060405180830381865afa158015613057573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061307b9190614527565b905080821115613089578091505b8115612012576040517fb460af9400000000000000000000000000000000000000000000000000000000815260048101839052306024820181905260448201526001600160a01b0385169063b460af94906064016020604051808303816000875af11580156130fc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115a39190614527565b6000808360000361313657508390506000613148565b613141858585613928565b9150508084035b935093915050565b6040516001600160a01b038316602482015260448101829052610a1d9084907fa9059cbb00000000000000000000000000000000000000000000000000000000906064015b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009093169290921790915261394c565b6060826001600160a01b0316866001600160a01b03160361326f576040517f8e4c8aa60000000000000000000000000000000000000000000000000000000081526001600160a01b0384166004820152602401610e82565b865460018801546132b891613294918a918a916001600160a01b03909116908a61380c565b855460018701546132b391889188916001600160a01b0316908861380c565b613a4e565b979650505050505050565b80158061335657506040517fdd62ed3e0000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b03838116602483015284169063dd62ed3e90604401602060405180830381865afa158015613330573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133549190614527565b155b6133e2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603660248201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60448201527f20746f206e6f6e2d7a65726f20616c6c6f77616e6365000000000000000000006064820152608401610e82565b6040516001600160a01b038316602482015260448101829052610a1d9084907f095ea7b30000000000000000000000000000000000000000000000000000000090606401613195565b6000826001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa15801561346b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061348f91906149f7565b905060128160ff1611156134da576040517f961c9a4f0000000000000000000000000000000000000000000000000000000081526001600160a01b0384166004820152602401610e82565b600580547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b03851617905561351b60ff821660126146e9565b61352690600a614afe565b600580546bffffffffffffffffffffffff9290921674010000000000000000000000000000000000000000026001600160a01b03928316179055600680548483167fffffffffffffffffffffffff0000000000000000000000000000000000000000919091161790556135be9084167f000000000000000000000000db591ea2e5db886da872654d58f6cc584b68e7cc6000196132c3565b6001600160a01b03821615610a1d57826001600160a01b0316826001600160a01b03166338d52e0f6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613615573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061363991906147e7565b6001600160a01b031614613684576040517f961c9a4f0000000000000000000000000000000000000000000000000000000081526001600160a01b0383166004820152602401610e82565b610a1d6001600160a01b038416836000196132c3565b60606136a4613fa9565b6136ac613fa9565b865460018801546001600160a01b039182169187168290036136dc576136d5898984848a61380c565b9350613762565b6001600160a01b038216156136fb576136f88883836000613bfd565b93505b6001600160a01b0387166137125760009550613722565b61371f8888600089613bfd565b92505b88547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b03881617895585811461376257600189018690555b61376c8484613a4e565b9998505050505050505050565b6000613786858585613c80565b9050600182600181111561379c5761379c614999565b036127635782806137af576137af6149c8565b84860915612763576000198110156137c957600101612763565b6040517f63a05778000000000000000000000000000000000000000000000000000000008152600481018690526024810185905260448101849052606401610e82565b613814613fa9565b6001600160a01b03841661382757600091505b8183146138445761383a85858585613bfd565b6001870183905590505b95945050505050565b80516060906001600160a01b0316156139235760408051600180825281830190925290816020015b60408051808201909152600080825260208201528152602001906001900390816138755790505090508160200151816000815181106138b6576138b6614804565b60200260200101819052508160200151600001516001600160a01b031682600001516001600160a01b03167fab331859c86987039e21afcbe4329f38052610222bdc17b3990febf710fbb71484602001516020015160405161391a91815260200190565b60405180910390a35b919050565b600061271083810390841061393e576000613844565b613844858261271086613779565b60006139a1826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316613d6d9092919063ffffffff16565b90508051600014806139c25750808060200190518101906139c29190614b0a565b610a1d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152608401610e82565b81516060906001600160a01b031615801590613a73575081516001600160a01b031615155b15613bc4576040805160028082526060820190925290816020015b6040805180820190915260008082526020820152815260200190600190039081613a8e5790505090508260200151826020015182600081518110613ad457613ad4614804565b6020026020010183600181518110613aee57613aee614804565b6020026020010182905282905250508260200151600001516001600160a01b031683600001516001600160a01b03167fab331859c86987039e21afcbe4329f38052610222bdc17b3990febf710fbb714856020015160200151604051613b5691815260200190565b60405180910390a38160200151600001516001600160a01b031682600001516001600160a01b03167fab331859c86987039e21afcbe4329f38052610222bdc17b3990febf710fbb714846020015160200151604051613bb791815260200190565b60405180910390a361098f565b82516001600160a01b031615613be457613bdd8361384d565b905061098f565b81516001600160a01b03161561098f57613bdd8261384d565b613c05613fa9565b6001600160a01b03841615612763576000613c1f84613d7c565b613c2884613d7c565b613c329190614b27565b90508015612d40576040518060400160405280876001600160a01b031681526020016040518060400160405280886001600160a01b0316815260200184815250815250915050949350505050565b6000808060001985870985870292508281108382030391505080600003613cba57838281613cb057613cb06149c8565b04925050506113a1565b838110613d04576040517f63a05778000000000000000000000000000000000000000000000000000000008152600481018790526024810186905260448101859052606401610e82565b60008486880960026001871981018816978890046003810283188082028403028082028403028082028403028082028403028082028403029081029092039091026000889003889004909101858311909403939093029303949094049190911702949350505050565b60606127638484600085613ddb565b60007f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8211156129b2576040517fe0fb6a7c00000000000000000000000000000000000000000000000000000000815260048101839052602401610e82565b606082471015613e6d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f60448201527f722063616c6c00000000000000000000000000000000000000000000000000006064820152608401610e82565b600080866001600160a01b03168587604051613e899190614b47565b60006040518083038185875af1925050503d8060008114613ec6576040519150601f19603f3d011682016040523d82523d6000602084013e613ecb565b606091505b5091509150612d3c8783838760608315613f60578251600003613f59576001600160a01b0385163b613f59576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610e82565b5081612763565b6127638383815115613f755781518083602001fd5b806040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610e829190614b63565b604051806040016040528060006001600160a01b03168152602001613fea604051806040016040528060006001600160a01b03168152602001600081525090565b905290565b80357fffffffff000000000000000000000000000000000000000000000000000000008116811461392357600080fd5b60006020828403121561403157600080fd5b6113a182613fef565b6001600160a01b03811681146110c657600080fd5b60008060006060848603121561406457600080fd5b833561406f8161403a565b95602085013595506040909401359392505050565b60008060008060008060c0878903121561409d57600080fd5b863595506020870135945060408701356140b68161403a565b935060608701356140c68161403a565b9598949750929560808101359460a0909101359350915050565b6000602082840312156140f257600080fd5b81356113a18161403a565b60006020828403121561410f57600080fd5b5035919050565b80151581146110c657600080fd5b6000806040838503121561413757600080fd5b82356141428161403a565b9150602083013561415281614116565b809150509250929050565b6000806040838503121561417057600080fd5b50508035926020909101359150565b6000806040838503121561419257600080fd5b823564ffffffffff811681146141a757600080fd5b915060208301356bffffffffffffffffffffffff8116811461415257600080fd5b600080600080600060a086880312156141e057600080fd5b85356141eb8161403a565b94506020860135935060408601356142028161403a565b94979396509394606081013594506080013592915050565b600080600080600060a0868803121561423257600080fd5b853594506020860135935060408601356142028161403a565b60008060006060848603121561426057600080fd5b833561426b8161403a565b9250602084013561427b8161403a565b929592945050506040919091013590565b60006020828403121561429e57600080fd5b813561ffff811681146113a157600080fd5b6000604082840312156142c257600080fd5b50919050565b6000806000604084860312156142dd57600080fd5b83356142e88161403a565b9250602084013567ffffffffffffffff8082111561430557600080fd5b818601915086601f83011261431957600080fd5b81358181111561432857600080fd5b8760208260061b850101111561433d57600080fd5b6020830194508093505050509250925092565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff811182821017156143c6576143c6614350565b604052919050565b600080604083850312156143e157600080fd5b8235915060208084013567ffffffffffffffff8082111561440157600080fd5b818601915086601f83011261441557600080fd5b81358181111561442757614427614350565b614457847fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8401160161437f565b9150808252878482850101111561446d57600080fd5b80848401858401376000848284010152508093505050509250929050565b600080600080608085870312156144a157600080fd5b84356144ac8161403a565b9350602085013592506040850135915060608501356144ca8161403a565b939692955090935050565b600080604083850312156144e857600080fd5b82356144f38161403a565b915061450160208401613fef565b90509250929050565b60006020828403121561451c57600080fd5b81356113a181614116565b60006020828403121561453957600080fd5b5051919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b8082018082111561098f5761098f614540565b600081600f0b7fffffffffffffffffffffffffffffffff8000000000000000000000000000000081036145b7576145b7614540565b60000392915050565b60008151808452602080850194506020840160005b8381101561460557815180516001600160a01b0316885283015183880152604090960195908201906001016145d5565b509495945050505050565b60408152600061462360408301856145c0565b90506001600160a01b03831660208301529392505050565b60008060006060848603121561465057600080fd5b8351925060208401519150604084015190509250925092565b6fffffffffffffffffffffffffffffffff8516815260006001600160a01b038086166020840152808516604084015250608060608301526146ad60808301846145c0565b9695505050505050565b6000602082840312156146c957600080fd5b81516fffffffffffffffffffffffffffffffff811681146113a157600080fd5b8181038181111561098f5761098f614540565b6fffffffffffffffffffffffffffffffff841681526001600160a01b038316602082015260606040820152600061384460608301846145c0565b60408101823561474581614116565b15158252602083013561475781614116565b80151560208401525092915050565b813561477181614116565b81547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00811691151560ff16918217835560208401356147af81614116565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00009190911690911790151560081b61ff001617905550565b6000602082840312156147f957600080fd5b81516113a18161403a565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60006040828403121561484557600080fd5b6040516040810181811067ffffffffffffffff8211171561486857614868614350565b60405261487483613fef565b8152602083013561488481614116565b60208201529392505050565b64ffffffffff81811683821601908082111561295c5761295c614540565b60005b838110156148c95781810151838201526020016148b1565b50506000910152565b600081518084526148ea8160208601602086016148ae565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b60006001600160a01b038087168352856020840152808516604084015250608060608301526146ad60808301846148d2565b60006020828403121561496057600080fd5b815180600f0b81146113a157600080fd5b808201828112600083128015821682158216171561499157614991614540565b505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b600060208284031215614a0957600080fd5b815160ff811681146113a157600080fd5b600181815b80851115614a55578160001904821115614a3b57614a3b614540565b80851615614a4857918102915b93841c9390800290614a1f565b509250929050565b600082614a6c5750600161098f565b81614a795750600061098f565b8160018114614a8f5760028114614a9957614ab5565b600191505061098f565b60ff841115614aaa57614aaa614540565b50506001821b61098f565b5060208310610133831016604e8410600b8410161715614ad8575081810a61098f565b614ae28383614a1a565b8060001904821115614af657614af6614540565b029392505050565b60006113a18383614a5d565b600060208284031215614b1c57600080fd5b81516113a181614116565b818103600083128015838313168383128216171561295c5761295c614540565b60008251614b598184602087016148ae565b9190910192915050565b6020815260006113a160208301846148d256fea2646970667358221220abd956a0e78a597c05cd5cd231a38201dc8ac5dc0f04fcfbd9409316bbe55ee664736f6c63430008160033

Verified Source Code Full Match

Compiler: v0.8.22+commit.4fc1097e EVM: paris Optimization: Yes (9999 runs)
OwnableOFT.sol 29 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

abstract contract OwnableOFT {
    modifier onlyOFTOwner() virtual;
}

// @dev Example to use OpenZeppelin's Ownable:
/**
    import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
    contract OZOwnableOFT is OwnableOFT, Ownable {
        modifier onlyOFTOwner() override {
            _checkOwner();
            _;
        }
    }
*/

// @dev Example to use Origami's OnlyElevatedAccess:
/**
    import { OrigamiElevatedAccess } from "contracts/common/access/OrigamiElevatedAccess.sol";

    contract OrigamiOwnableOFT is OwnableOFT, OrigamiElevatedAccess {
        modifier onlyOFTOwner() override {
            if (!isElevatedAccess(msg.sender, msg.sig)) revert CommonEventsAndErrors.InvalidAccess();
            _;
        }
    }
*/
IOAppCore.sol 52 lines
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.20;

import { ILayerZeroEndpointV2 } from "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol";

/**
 * @title IOAppCore
 */
interface IOAppCore {
    // Custom error messages
    error OnlyPeer(uint32 eid, bytes32 sender);
    error NoPeer(uint32 eid);
    error InvalidEndpointCall();
    error InvalidDelegate();

    // Event emitted when a peer (OApp) is set for a corresponding endpoint
    event PeerSet(uint32 eid, bytes32 peer);

    /**
     * @notice Retrieves the OApp version information.
     * @return senderVersion The version of the OAppSender.sol contract.
     * @return receiverVersion The version of the OAppReceiver.sol contract.
     */
    function oAppVersion() external view returns (uint64 senderVersion, uint64 receiverVersion);

    /**
     * @notice Retrieves the LayerZero endpoint associated with the OApp.
     * @return iEndpoint The LayerZero endpoint as an interface.
     */
    function endpoint() external view returns (ILayerZeroEndpointV2 iEndpoint);

    /**
     * @notice Retrieves the peer (OApp) associated with a corresponding endpoint.
     * @param _eid The endpoint ID.
     * @return peer The peer address (OApp instance) associated with the corresponding endpoint.
     */
    function peers(uint32 _eid) external view returns (bytes32 peer);

    /**
     * @notice Sets the peer address (OApp instance) for a corresponding endpoint.
     * @param _eid The endpoint ID.
     * @param _peer The address of the peer to be associated with the corresponding endpoint.
     */
    function setPeer(uint32 _eid, bytes32 _peer) external;

    /**
     * @notice Sets the delegate address for the OApp Core.
     * @param _delegate The address of the delegate to be set.
     */
    function setDelegate(address _delegate) external;
}
OAppCore.sol 83 lines
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.20;

import { OwnableOFT } from "../access/OwnableOFT.sol";
import { IOAppCore, ILayerZeroEndpointV2 } from "./interfaces/IOAppCore.sol";

/**
 * @title OAppCore
 * @dev Abstract contract implementing the IOAppCore interface with basic OApp configurations.
 */
abstract contract OAppCore is IOAppCore, OwnableOFT {
    // The LayerZero endpoint associated with the given OApp
    ILayerZeroEndpointV2 public immutable endpoint;

    // Mapping to store peers associated with corresponding endpoints
    mapping(uint32 eid => bytes32 peer) public peers;

    /**
     * @dev Constructor to initialize the OAppCore with the provided endpoint and delegate.
     * @param _endpoint The address of the LOCAL Layer Zero endpoint.
     * @param _delegate The delegate capable of making OApp configurations inside of the endpoint.
     *
     * @dev The delegate typically should be set as the owner of the contract.
     */
    constructor(address _endpoint, address _delegate) {
        endpoint = ILayerZeroEndpointV2(_endpoint);

        if (_delegate == address(0)) revert InvalidDelegate();
        endpoint.setDelegate(_delegate);
    }

    /**
     * @notice Sets the peer address (OApp instance) for a corresponding endpoint.
     * @param _eid The endpoint ID.
     * @param _peer The address of the peer to be associated with the corresponding endpoint.
     *
     * @dev Only the owner/admin of the OApp can call this function.
     * @dev Indicates that the peer is trusted to send LayerZero messages to this OApp.
     * @dev Set this to bytes32(0) to remove the peer address.
     * @dev Peer is a bytes32 to accommodate non-evm chains.
     */
    function setPeer(uint32 _eid, bytes32 _peer) public virtual onlyOFTOwner {
        _setPeer(_eid, _peer);
    }

    /**
     * @notice Sets the peer address (OApp instance) for a corresponding endpoint.
     * @param _eid The endpoint ID.
     * @param _peer The address of the peer to be associated with the corresponding endpoint.
     *
     * @dev Indicates that the peer is trusted to send LayerZero messages to this OApp.
     * @dev Set this to bytes32(0) to remove the peer address.
     * @dev Peer is a bytes32 to accommodate non-evm chains.
     */
    function _setPeer(uint32 _eid, bytes32 _peer) internal virtual {
        peers[_eid] = _peer;
        emit PeerSet(_eid, _peer);
    }

    /**
     * @notice Internal function to get the peer address associated with a specific endpoint; reverts if NOT set.
     * ie. the peer is set to bytes32(0).
     * @param _eid The endpoint ID.
     * @return peer The address of the peer associated with the specified endpoint.
     */
    function _getPeerOrRevert(uint32 _eid) internal view virtual returns (bytes32) {
        bytes32 peer = peers[_eid];
        if (peer == bytes32(0)) revert NoPeer(_eid);
        return peer;
    }

    /**
     * @notice Sets the delegate address for the OApp.
     * @param _delegate The address of the delegate to be set.
     *
     * @dev Only the owner/admin of the OApp can call this function.
     * @dev Provides the ability for a delegate to set configs, on behalf of the OApp, directly on the Endpoint contract.
     */
    function setDelegate(address _delegate) public onlyOFTOwner {
        endpoint.setDelegate(_delegate);
    }
}
OAppSender.sol 124 lines
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.20;

import { SafeERC20, IERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import { MessagingParams, MessagingFee, MessagingReceipt } from "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol";
import { OAppCore } from "./OAppCore.sol";

/**
 * @title OAppSender
 * @dev Abstract contract implementing the OAppSender functionality for sending messages to a LayerZero endpoint.
 */
abstract contract OAppSender is OAppCore {
    using SafeERC20 for IERC20;

    // Custom error messages
    error NotEnoughNative(uint256 msgValue);
    error LzTokenUnavailable();

    // @dev The version of the OAppSender implementation.
    // @dev Version is bumped when changes are made to this contract.
    uint64 internal constant SENDER_VERSION = 1;

    /**
     * @notice Retrieves the OApp version information.
     * @return senderVersion The version of the OAppSender.sol contract.
     * @return receiverVersion The version of the OAppReceiver.sol contract.
     *
     * @dev Providing 0 as the default for OAppReceiver version. Indicates that the OAppReceiver is not implemented.
     * ie. this is a SEND only OApp.
     * @dev If the OApp uses both OAppSender and OAppReceiver, then this needs to be override returning the correct versions
     */
    function oAppVersion() public view virtual returns (uint64 senderVersion, uint64 receiverVersion) {
        return (SENDER_VERSION, 0);
    }

    /**
     * @dev Internal function to interact with the LayerZero EndpointV2.quote() for fee calculation.
     * @param _dstEid The destination endpoint ID.
     * @param _message The message payload.
     * @param _options Additional options for the message.
     * @param _payInLzToken Flag indicating whether to pay the fee in LZ tokens.
     * @return fee The calculated MessagingFee for the message.
     *      - nativeFee: The native fee for the message.
     *      - lzTokenFee: The LZ token fee for the message.
     */
    function _quote(
        uint32 _dstEid,
        bytes memory _message,
        bytes memory _options,
        bool _payInLzToken
    ) internal view virtual returns (MessagingFee memory fee) {
        return
            endpoint.quote(
                MessagingParams(_dstEid, _getPeerOrRevert(_dstEid), _message, _options, _payInLzToken),
                address(this)
            );
    }

    /**
     * @dev Internal function to interact with the LayerZero EndpointV2.send() for sending a message.
     * @param _dstEid The destination endpoint ID.
     * @param _message The message payload.
     * @param _options Additional options for the message.
     * @param _fee The calculated LayerZero fee for the message.
     *      - nativeFee: The native fee.
     *      - lzTokenFee: The lzToken fee.
     * @param _refundAddress The address to receive any excess fee values sent to the endpoint.
     * @return receipt The receipt for the sent message.
     *      - guid: The unique identifier for the sent message.
     *      - nonce: The nonce of the sent message.
     *      - fee: The LayerZero fee incurred for the message.
     */
    function _lzSend(
        uint32 _dstEid,
        bytes memory _message,
        bytes memory _options,
        MessagingFee memory _fee,
        address _refundAddress
    ) internal virtual returns (MessagingReceipt memory receipt) {
        // @dev Push corresponding fees to the endpoint, any excess is sent back to the _refundAddress from the endpoint.
        uint256 messageValue = _payNative(_fee.nativeFee);
        if (_fee.lzTokenFee > 0) _payLzToken(_fee.lzTokenFee);

        return
            // solhint-disable-next-line check-send-result
            endpoint.send{ value: messageValue }(
                MessagingParams(_dstEid, _getPeerOrRevert(_dstEid), _message, _options, _fee.lzTokenFee > 0),
                _refundAddress
            );
    }

    /**
     * @dev Internal function to pay the native fee associated with the message.
     * @param _nativeFee The native fee to be paid.
     * @return nativeFee The amount of native currency paid.
     *
     * @dev If the OApp needs to initiate MULTIPLE LayerZero messages in a single transaction,
     * this will need to be overridden because msg.value would contain multiple lzFees.
     * @dev Should be overridden in the event the LayerZero endpoint requires a different native currency.
     * @dev Some EVMs use an ERC20 as a method for paying transactions/gasFees.
     * @dev The endpoint is EITHER/OR, ie. it will NOT support both types of native payment at a time.
     */
    function _payNative(uint256 _nativeFee) internal virtual returns (uint256 nativeFee) {
        if (msg.value != _nativeFee) revert NotEnoughNative(msg.value);
        return _nativeFee;
    }

    /**
     * @dev Internal function to pay the LZ token fee associated with the message.
     * @param _lzTokenFee The LZ token fee to be paid.
     *
     * @dev If the caller is trying to pay in the specified lzToken, then the lzTokenFee is passed to the endpoint.
     * @dev Any excess sent, is passed back to the specified _refundAddress in the _lzSend().
     */
    function _payLzToken(uint256 _lzTokenFee) internal virtual {
        // @dev Cannot cache the token because it is not immutable in the endpoint.
        address lzToken = endpoint.lzToken();
        if (lzToken == address(0)) revert LzTokenUnavailable();

        // Pay LZ token fee by sending tokens to the endpoint.
        IERC20(lzToken).safeTransferFrom(msg.sender, address(endpoint), _lzTokenFee);
    }
}
IOFT.sol 149 lines
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.20;

import { MessagingReceipt, MessagingFee } from "../../oapp/OAppSender.sol";

/**
 * @dev Struct representing token parameters for the OFT send() operation.
 */
struct SendParam {
    uint32 dstEid; // Destination endpoint ID.
    bytes32 to; // Recipient address.
    uint256 amountLD; // Amount to send in local decimals.
    uint256 minAmountLD; // Minimum amount to send in local decimals.
    bytes extraOptions; // Additional options supplied by the caller to be used in the LayerZero message.
    bytes composeMsg; // The composed message for the send() operation.
    bytes oftCmd; // The OFT command to be executed, unused in default OFT implementations.
}

/**
 * @dev Struct representing OFT limit information.
 * @dev These amounts can change dynamically and are up the the specific oft implementation.
 */
struct OFTLimit {
    uint256 minAmountLD; // Minimum amount in local decimals that can be sent to the recipient.
    uint256 maxAmountLD; // Maximum amount in local decimals that can be sent to the recipient.
}

/**
 * @dev Struct representing OFT receipt information.
 */
struct OFTReceipt {
    uint256 amountSentLD; // Amount of tokens ACTUALLY debited from the sender in local decimals.
    // @dev In non-default implementations, the amountReceivedLD COULD differ from this value.
    uint256 amountReceivedLD; // Amount of tokens to be received on the remote side.
}

/**
 * @dev Struct representing OFT fee details.
 * @dev Future proof mechanism to provide a standardized way to communicate fees to things like a UI.
 */
struct OFTFeeDetail {
    int256 feeAmountLD; // Amount of the fee in local decimals.
    string description; // Description of the fee.
}

/**
 * @title IOFT
 * @dev Interface for the OftChain (OFT) token.
 * @dev Does not inherit ERC20 to accommodate usage by OFTAdapter as well.
 * @dev This specific interface ID is '0x02e49c2c'.
 */
interface IOFT {
    // Custom error messages
    error InvalidLocalDecimals();
    error SlippageExceeded(uint256 amountLD, uint256 minAmountLD);

    // Events
    event OFTSent(
        bytes32 indexed guid, // GUID of the OFT message.
        uint32 dstEid, // Destination Endpoint ID.
        address indexed fromAddress, // Address of the sender on the src chain.
        uint256 amountSentLD, // Amount of tokens sent in local decimals.
        uint256 amountReceivedLD // Amount of tokens received in local decimals.
    );
    event OFTReceived(
        bytes32 indexed guid, // GUID of the OFT message.
        uint32 srcEid, // Source Endpoint ID.
        address indexed toAddress, // Address of the recipient on the dst chain.
        uint256 amountReceivedLD // Amount of tokens received in local decimals.
    );

    /**
     * @notice Retrieves interfaceID and the version of the OFT.
     * @return interfaceId The interface ID.
     * @return version The version.
     *
     * @dev interfaceId: This specific interface ID is '0x02e49c2c'.
     * @dev version: Indicates a cross-chain compatible msg encoding with other OFTs.
     * @dev If a new feature is added to the OFT cross-chain msg encoding, the version will be incremented.
     * ie. localOFT version(x,1) CAN send messages to remoteOFT version(x,1)
     */
    function oftVersion() external view returns (bytes4 interfaceId, uint64 version);

    /**
     * @notice Retrieves the address of the token associated with the OFT.
     * @return token The address of the ERC20 token implementation.
     */
    function token() external view returns (address);

    /**
     * @notice Indicates whether the OFT contract requires approval of the 'token()' to send.
     * @return requiresApproval Needs approval of the underlying token implementation.
     *
     * @dev Allows things like wallet implementers to determine integration requirements,
     * without understanding the underlying token implementation.
     */
    function approvalRequired() external view returns (bool);

    /**
     * @notice Retrieves the shared decimals of the OFT.
     * @return sharedDecimals The shared decimals of the OFT.
     */
    function sharedDecimals() external view returns (uint8);

    /**
     * @notice Provides a quote for OFT-related operations.
     * @param _sendParam The parameters for the send operation.
     * @return limit The OFT limit information.
     * @return oftFeeDetails The details of OFT fees.
     * @return receipt The OFT receipt information.
     */
    function quoteOFT(
        SendParam calldata _sendParam
    ) external view returns (OFTLimit memory, OFTFeeDetail[] memory oftFeeDetails, OFTReceipt memory);

    /**
     * @notice Provides a quote for the send() operation.
     * @param _sendParam The parameters for the send() operation.
     * @param _payInLzToken Flag indicating whether the caller is paying in the LZ token.
     * @return fee The calculated LayerZero messaging fee from the send() operation.
     *
     * @dev MessagingFee: LayerZero msg fee
     *  - nativeFee: The native fee.
     *  - lzTokenFee: The lzToken fee.
     */
    function quoteSend(SendParam calldata _sendParam, bool _payInLzToken) external view returns (MessagingFee memory);

    /**
     * @notice Executes the send() operation.
     * @param _sendParam The parameters for the send operation.
     * @param _fee The fee information supplied by the caller.
     *      - nativeFee: The native fee.
     *      - lzTokenFee: The lzToken fee.
     * @param _refundAddress The address to receive any excess funds from fees etc. on the src.
     * @return receipt The LayerZero messaging receipt from the send() operation.
     * @return oftReceipt The OFT receipt information.
     *
     * @dev MessagingReceipt: LayerZero msg receipt
     *  - guid: The unique identifier for the sent message.
     *  - nonce: The nonce of the sent message.
     *  - fee: The LayerZero fee incurred for the message.
     */
    function send(
        SendParam calldata _sendParam,
        MessagingFee calldata _fee,
        address _refundAddress
    ) external payable returns (MessagingReceipt memory, OFTReceipt memory);
}
ILayerZeroEndpointV2.sol 89 lines
// SPDX-License-Identifier: MIT

pragma solidity >=0.8.0;

import { IMessageLibManager } from "./IMessageLibManager.sol";
import { IMessagingComposer } from "./IMessagingComposer.sol";
import { IMessagingChannel } from "./IMessagingChannel.sol";
import { IMessagingContext } from "./IMessagingContext.sol";

struct MessagingParams {
    uint32 dstEid;
    bytes32 receiver;
    bytes message;
    bytes options;
    bool payInLzToken;
}

struct MessagingReceipt {
    bytes32 guid;
    uint64 nonce;
    MessagingFee fee;
}

struct MessagingFee {
    uint256 nativeFee;
    uint256 lzTokenFee;
}

struct Origin {
    uint32 srcEid;
    bytes32 sender;
    uint64 nonce;
}

interface ILayerZeroEndpointV2 is IMessageLibManager, IMessagingComposer, IMessagingChannel, IMessagingContext {
    event PacketSent(bytes encodedPayload, bytes options, address sendLibrary);

    event PacketVerified(Origin origin, address receiver, bytes32 payloadHash);

    event PacketDelivered(Origin origin, address receiver);

    event LzReceiveAlert(
        address indexed receiver,
        address indexed executor,
        Origin origin,
        bytes32 guid,
        uint256 gas,
        uint256 value,
        bytes message,
        bytes extraData,
        bytes reason
    );

    event LzTokenSet(address token);

    event DelegateSet(address sender, address delegate);

    function quote(MessagingParams calldata _params, address _sender) external view returns (MessagingFee memory);

    function send(
        MessagingParams calldata _params,
        address _refundAddress
    ) external payable returns (MessagingReceipt memory);

    function verify(Origin calldata _origin, address _receiver, bytes32 _payloadHash) external;

    function verifiable(Origin calldata _origin, address _receiver) external view returns (bool);

    function initializable(Origin calldata _origin, address _receiver) external view returns (bool);

    function lzReceive(
        Origin calldata _origin,
        address _receiver,
        bytes32 _guid,
        bytes calldata _message,
        bytes calldata _extraData
    ) external payable;

    // oapp can burn messages partially by calling this function with its own business logic if messages are verified in order
    function clear(address _oapp, Origin calldata _origin, bytes32 _guid, bytes calldata _message) external;

    function setLzToken(address _lzToken) external;

    function lzToken() external view returns (address);

    function nativeToken() external view returns (address);

    function setDelegate(address _delegate) external;
}
IMessageLibManager.sol 70 lines
// SPDX-License-Identifier: MIT

pragma solidity >=0.8.0;

struct SetConfigParam {
    uint32 eid;
    uint32 configType;
    bytes config;
}

interface IMessageLibManager {
    struct Timeout {
        address lib;
        uint256 expiry;
    }

    event LibraryRegistered(address newLib);
    event DefaultSendLibrarySet(uint32 eid, address newLib);
    event DefaultReceiveLibrarySet(uint32 eid, address newLib);
    event DefaultReceiveLibraryTimeoutSet(uint32 eid, address oldLib, uint256 expiry);
    event SendLibrarySet(address sender, uint32 eid, address newLib);
    event ReceiveLibrarySet(address receiver, uint32 eid, address newLib);
    event ReceiveLibraryTimeoutSet(address receiver, uint32 eid, address oldLib, uint256 timeout);

    function registerLibrary(address _lib) external;

    function isRegisteredLibrary(address _lib) external view returns (bool);

    function getRegisteredLibraries() external view returns (address[] memory);

    function setDefaultSendLibrary(uint32 _eid, address _newLib) external;

    function defaultSendLibrary(uint32 _eid) external view returns (address);

    function setDefaultReceiveLibrary(uint32 _eid, address _newLib, uint256 _timeout) external;

    function defaultReceiveLibrary(uint32 _eid) external view returns (address);

    function setDefaultReceiveLibraryTimeout(uint32 _eid, address _lib, uint256 _expiry) external;

    function defaultReceiveLibraryTimeout(uint32 _eid) external view returns (address lib, uint256 expiry);

    function isSupportedEid(uint32 _eid) external view returns (bool);

    function isValidReceiveLibrary(address _receiver, uint32 _eid, address _lib) external view returns (bool);

    /// ------------------- OApp interfaces -------------------
    function setSendLibrary(address _oapp, uint32 _eid, address _newLib) external;

    function getSendLibrary(address _sender, uint32 _eid) external view returns (address lib);

    function isDefaultSendLibrary(address _sender, uint32 _eid) external view returns (bool);

    function setReceiveLibrary(address _oapp, uint32 _eid, address _newLib, uint256 _gracePeriod) external;

    function getReceiveLibrary(address _receiver, uint32 _eid) external view returns (address lib, bool isDefault);

    function setReceiveLibraryTimeout(address _oapp, uint32 _eid, address _lib, uint256 _gracePeriod) external;

    function receiveLibraryTimeout(address _receiver, uint32 _eid) external view returns (address lib, uint256 expiry);

    function setConfig(address _oapp, address _lib, SetConfigParam[] calldata _params) external;

    function getConfig(
        address _oapp,
        address _lib,
        uint32 _eid,
        uint32 _configType
    ) external view returns (bytes memory config);
}
IMessagingChannel.sol 34 lines
// SPDX-License-Identifier: MIT

pragma solidity >=0.8.0;

interface IMessagingChannel {
    event InboundNonceSkipped(uint32 srcEid, bytes32 sender, address receiver, uint64 nonce);
    event PacketNilified(uint32 srcEid, bytes32 sender, address receiver, uint64 nonce, bytes32 payloadHash);
    event PacketBurnt(uint32 srcEid, bytes32 sender, address receiver, uint64 nonce, bytes32 payloadHash);

    function eid() external view returns (uint32);

    // this is an emergency function if a message cannot be verified for some reasons
    // required to provide _nextNonce to avoid race condition
    function skip(address _oapp, uint32 _srcEid, bytes32 _sender, uint64 _nonce) external;

    function nilify(address _oapp, uint32 _srcEid, bytes32 _sender, uint64 _nonce, bytes32 _payloadHash) external;

    function burn(address _oapp, uint32 _srcEid, bytes32 _sender, uint64 _nonce, bytes32 _payloadHash) external;

    function nextGuid(address _sender, uint32 _dstEid, bytes32 _receiver) external view returns (bytes32);

    function inboundNonce(address _receiver, uint32 _srcEid, bytes32 _sender) external view returns (uint64);

    function outboundNonce(address _sender, uint32 _dstEid, bytes32 _receiver) external view returns (uint64);

    function inboundPayloadHash(
        address _receiver,
        uint32 _srcEid,
        bytes32 _sender,
        uint64 _nonce
    ) external view returns (bytes32);

    function lazyInboundNonce(address _receiver, uint32 _srcEid, bytes32 _sender) external view returns (uint64);
}
IMessagingComposer.sol 38 lines
// SPDX-License-Identifier: MIT

pragma solidity >=0.8.0;

interface IMessagingComposer {
    event ComposeSent(address from, address to, bytes32 guid, uint16 index, bytes message);
    event ComposeDelivered(address from, address to, bytes32 guid, uint16 index);
    event LzComposeAlert(
        address indexed from,
        address indexed to,
        address indexed executor,
        bytes32 guid,
        uint16 index,
        uint256 gas,
        uint256 value,
        bytes message,
        bytes extraData,
        bytes reason
    );

    function composeQueue(
        address _from,
        address _to,
        bytes32 _guid,
        uint16 _index
    ) external view returns (bytes32 messageHash);

    function sendCompose(address _to, bytes32 _guid, uint16 _index, bytes calldata _message) external;

    function lzCompose(
        address _from,
        address _to,
        bytes32 _guid,
        uint16 _index,
        bytes calldata _message,
        bytes calldata _extraData
    ) external payable;
}
IMessagingContext.sol 9 lines
// SPDX-License-Identifier: MIT

pragma solidity >=0.8.0;

interface IMessagingContext {
    function isSendingMessage() external view returns (bool);

    function getSendContext() external view returns (uint32 dstEid, address sender);
}
IERC165.sol 6 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (interfaces/IERC165.sol)

pragma solidity ^0.8.0;

import "../utils/introspection/IERC165.sol";
IERC4626.sol 232 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (interfaces/IERC4626.sol)

pragma solidity ^0.8.0;

import "../token/ERC20/IERC20.sol";
import "../token/ERC20/extensions/IERC20Metadata.sol";

/**
 * @dev Interface of the ERC4626 "Tokenized Vault Standard", as defined in
 * https://eips.ethereum.org/EIPS/eip-4626[ERC-4626].
 *
 * _Available since v4.7._
 */
interface IERC4626 is IERC20, IERC20Metadata {
    event Deposit(address indexed sender, address indexed owner, uint256 assets, uint256 shares);

    event Withdraw(
        address indexed sender,
        address indexed receiver,
        address indexed owner,
        uint256 assets,
        uint256 shares
    );

    /**
     * @dev Returns the address of the underlying token used for the Vault for accounting, depositing, and withdrawing.
     *
     * - MUST be an ERC-20 token contract.
     * - MUST NOT revert.
     */
    function asset() external view returns (address assetTokenAddress);

    /**
     * @dev Returns the total amount of the underlying asset that is “managed” by Vault.
     *
     * - SHOULD include any compounding that occurs from yield.
     * - MUST be inclusive of any fees that are charged against assets in the Vault.
     * - MUST NOT revert.
     */
    function totalAssets() external view returns (uint256 totalManagedAssets);

    /**
     * @dev Returns the amount of shares that the Vault would exchange for the amount of assets provided, in an ideal
     * scenario where all the conditions are met.
     *
     * - MUST NOT be inclusive of any fees that are charged against assets in the Vault.
     * - MUST NOT show any variations depending on the caller.
     * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.
     * - MUST NOT revert.
     *
     * NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the
     * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and
     * from.
     */
    function convertToShares(uint256 assets) external view returns (uint256 shares);

    /**
     * @dev Returns the amount of assets that the Vault would exchange for the amount of shares provided, in an ideal
     * scenario where all the conditions are met.
     *
     * - MUST NOT be inclusive of any fees that are charged against assets in the Vault.
     * - MUST NOT show any variations depending on the caller.
     * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.
     * - MUST NOT revert.
     *
     * NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the
     * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and
     * from.
     */
    function convertToAssets(uint256 shares) external view returns (uint256 assets);

    /**
     * @dev Returns the maximum amount of the underlying asset that can be deposited into the Vault for the receiver,
     * through a deposit call.
     *
     * - MUST return a limited value if receiver is subject to some deposit limit.
     * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of assets that may be deposited.
     * - MUST NOT revert.
     */
    function maxDeposit(address receiver) external view returns (uint256 maxAssets);

    /**
     * @dev Allows an on-chain or off-chain user to simulate the effects of their deposit at the current block, given
     * current on-chain conditions.
     *
     * - MUST return as close to and no more than the exact amount of Vault shares that would be minted in a deposit
     *   call in the same transaction. I.e. deposit should return the same or more shares as previewDeposit if called
     *   in the same transaction.
     * - MUST NOT account for deposit limits like those returned from maxDeposit and should always act as though the
     *   deposit would be accepted, regardless if the user has enough tokens approved, etc.
     * - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.
     * - MUST NOT revert.
     *
     * NOTE: any unfavorable discrepancy between convertToShares and previewDeposit SHOULD be considered slippage in
     * share price or some other type of condition, meaning the depositor will lose assets by depositing.
     */
    function previewDeposit(uint256 assets) external view returns (uint256 shares);

    /**
     * @dev Mints shares Vault shares to receiver by depositing exactly amount of underlying tokens.
     *
     * - MUST emit the Deposit event.
     * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
     *   deposit execution, and are accounted for during deposit.
     * - MUST revert if all of assets cannot be deposited (due to deposit limit being reached, slippage, the user not
     *   approving enough underlying tokens to the Vault contract, etc).
     *
     * NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.
     */
    function deposit(uint256 assets, address receiver) external returns (uint256 shares);

    /**
     * @dev Returns the maximum amount of the Vault shares that can be minted for the receiver, through a mint call.
     * - MUST return a limited value if receiver is subject to some mint limit.
     * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of shares that may be minted.
     * - MUST NOT revert.
     */
    function maxMint(address receiver) external view returns (uint256 maxShares);

    /**
     * @dev Allows an on-chain or off-chain user to simulate the effects of their mint at the current block, given
     * current on-chain conditions.
     *
     * - MUST return as close to and no fewer than the exact amount of assets that would be deposited in a mint call
     *   in the same transaction. I.e. mint should return the same or fewer assets as previewMint if called in the
     *   same transaction.
     * - MUST NOT account for mint limits like those returned from maxMint and should always act as though the mint
     *   would be accepted, regardless if the user has enough tokens approved, etc.
     * - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.
     * - MUST NOT revert.
     *
     * NOTE: any unfavorable discrepancy between convertToAssets and previewMint SHOULD be considered slippage in
     * share price or some other type of condition, meaning the depositor will lose assets by minting.
     */
    function previewMint(uint256 shares) external view returns (uint256 assets);

    /**
     * @dev Mints exactly shares Vault shares to receiver by depositing amount of underlying tokens.
     *
     * - MUST emit the Deposit event.
     * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the mint
     *   execution, and are accounted for during mint.
     * - MUST revert if all of shares cannot be minted (due to deposit limit being reached, slippage, the user not
     *   approving enough underlying tokens to the Vault contract, etc).
     *
     * NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.
     */
    function mint(uint256 shares, address receiver) external returns (uint256 assets);

    /**
     * @dev Returns the maximum amount of the underlying asset that can be withdrawn from the owner balance in the
     * Vault, through a withdraw call.
     *
     * - MUST return a limited value if owner is subject to some withdrawal limit or timelock.
     * - MUST NOT revert.
     */
    function maxWithdraw(address owner) external view returns (uint256 maxAssets);

    /**
     * @dev Allows an on-chain or off-chain user to simulate the effects of their withdrawal at the current block,
     * given current on-chain conditions.
     *
     * - MUST return as close to and no fewer than the exact amount of Vault shares that would be burned in a withdraw
     *   call in the same transaction. I.e. withdraw should return the same or fewer shares as previewWithdraw if
     *   called
     *   in the same transaction.
     * - MUST NOT account for withdrawal limits like those returned from maxWithdraw and should always act as though
     *   the withdrawal would be accepted, regardless if the user has enough shares, etc.
     * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.
     * - MUST NOT revert.
     *
     * NOTE: any unfavorable discrepancy between convertToShares and previewWithdraw SHOULD be considered slippage in
     * share price or some other type of condition, meaning the depositor will lose assets by depositing.
     */
    function previewWithdraw(uint256 assets) external view returns (uint256 shares);

    /**
     * @dev Burns shares from owner and sends exactly assets of underlying tokens to receiver.
     *
     * - MUST emit the Withdraw event.
     * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
     *   withdraw execution, and are accounted for during withdraw.
     * - MUST revert if all of assets cannot be withdrawn (due to withdrawal limit being reached, slippage, the owner
     *   not having enough shares, etc).
     *
     * Note that some implementations will require pre-requesting to the Vault before a withdrawal may be performed.
     * Those methods should be performed separately.
     */
    function withdraw(uint256 assets, address receiver, address owner) external returns (uint256 shares);

    /**
     * @dev Returns the maximum amount of Vault shares that can be redeemed from the owner balance in the Vault,
     * through a redeem call.
     *
     * - MUST return a limited value if owner is subject to some withdrawal limit or timelock.
     * - MUST return balanceOf(owner) if owner is not subject to any withdrawal limit or timelock.
     * - MUST NOT revert.
     */
    function maxRedeem(address owner) external view returns (uint256 maxShares);

    /**
     * @dev Allows an on-chain or off-chain user to simulate the effects of their redeemption at the current block,
     * given current on-chain conditions.
     *
     * - MUST return as close to and no more than the exact amount of assets that would be withdrawn in a redeem call
     *   in the same transaction. I.e. redeem should return the same or more assets as previewRedeem if called in the
     *   same transaction.
     * - MUST NOT account for redemption limits like those returned from maxRedeem and should always act as though the
     *   redemption would be accepted, regardless if the user has enough shares, etc.
     * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.
     * - MUST NOT revert.
     *
     * NOTE: any unfavorable discrepancy between convertToAssets and previewRedeem SHOULD be considered slippage in
     * share price or some other type of condition, meaning the depositor will lose assets by redeeming.
     */
    function previewRedeem(uint256 shares) external view returns (uint256 assets);

    /**
     * @dev Burns exactly shares from owner and sends assets of underlying tokens to receiver.
     *
     * - MUST emit the Withdraw event.
     * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
     *   redeem execution, and are accounted for during redeem.
     * - MUST revert if all of shares cannot be redeemed (due to withdrawal limit being reached, slippage, the owner
     *   not having enough shares, etc).
     *
     * NOTE: some implementations will require pre-requesting to the Vault before a withdrawal may be performed.
     * Those methods should be performed separately.
     */
    function redeem(uint256 shares, address receiver, address owner) external returns (uint256 assets);
}
IERC5267.sol 28 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (interfaces/IERC5267.sol)

pragma solidity ^0.8.0;

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
        );
}
ReentrancyGuard.sol 77 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (security/ReentrancyGuard.sol)

pragma solidity ^0.8.0;

/**
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
 * available, which can be applied to functions to make sure there are no nested
 * (reentrant) calls to them.
 *
 * Note that because there is a single `nonReentrant` guard, functions marked as
 * `nonReentrant` may not call one another. This can be worked around by making
 * those functions `private`, and then adding `external` `nonReentrant` entry
 * points to them.
 *
 * TIP: If you would like to learn more about reentrancy and alternative ways
 * to protect against it, check out our blog post
 * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
 */
abstract contract ReentrancyGuard {
    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.

    // The values being non-zero value makes deployment a bit more expensive,
    // but in exchange the refund on every call to nonReentrant will be lower in
    // amount. Since refunds are capped to a percentage of the total
    // transaction's gas, it is best to keep them low in cases like this one, to
    // increase the likelihood of the full refund coming into effect.
    uint256 private constant _NOT_ENTERED = 1;
    uint256 private constant _ENTERED = 2;

    uint256 private _status;

    constructor() {
        _status = _NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and making it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        _nonReentrantBefore();
        _;
        _nonReentrantAfter();
    }

    function _nonReentrantBefore() private {
        // On the first call to nonReentrant, _status will be _NOT_ENTERED
        require(_status != _ENTERED, "ReentrancyGuard: reentrant call");

        // Any calls to nonReentrant after this point will fail
        _status = _ENTERED;
    }

    function _nonReentrantAfter() private {
        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        _status = _NOT_ENTERED;
    }

    /**
     * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
     * `nonReentrant` function in the call stack.
     */
    function _reentrancyGuardEntered() internal view returns (bool) {
        return _status == _ENTERED;
    }
}
ERC20.sol 365 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/ERC20.sol)

pragma solidity ^0.8.0;

import "./IERC20.sol";
import "./extensions/IERC20Metadata.sol";
import "../../utils/Context.sol";

/**
 * @dev Implementation of the {IERC20} interface.
 *
 * This implementation is agnostic to the way tokens are created. This means
 * that a supply mechanism has to be added in a derived contract using {_mint}.
 * For a generic mechanism see {ERC20PresetMinterPauser}.
 *
 * TIP: For a detailed writeup see our guide
 * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
 * to implement supply mechanisms].
 *
 * The default value of {decimals} is 18. To change this, you should override
 * this function so it returns a different value.
 *
 * We have followed general OpenZeppelin Contracts guidelines: functions revert
 * instead returning `false` on failure. This behavior is nonetheless
 * conventional and does not conflict with the expectations of ERC20
 * applications.
 *
 * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
 * This allows applications to reconstruct the allowance for all accounts just
 * by listening to said events. Other implementations of the EIP may not emit
 * these events, as it isn't required by the specification.
 *
 * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
 * functions have been added to mitigate the well-known issues around setting
 * allowances. See {IERC20-approve}.
 */
contract ERC20 is Context, IERC20, IERC20Metadata {
    mapping(address => uint256) private _balances;

    mapping(address => mapping(address => uint256)) private _allowances;

    uint256 private _totalSupply;

    string private _name;
    string private _symbol;

    /**
     * @dev Sets the values for {name} and {symbol}.
     *
     * All two of these values are immutable: they can only be set once during
     * construction.
     */
    constructor(string memory name_, string memory symbol_) {
        _name = name_;
        _symbol = symbol_;
    }

    /**
     * @dev Returns the name of the token.
     */
    function name() public view virtual override returns (string memory) {
        return _name;
    }

    /**
     * @dev Returns the symbol of the token, usually a shorter version of the
     * name.
     */
    function symbol() public view virtual override returns (string memory) {
        return _symbol;
    }

    /**
     * @dev Returns the number of decimals used to get its user representation.
     * For example, if `decimals` equals `2`, a balance of `505` tokens should
     * be displayed to a user as `5.05` (`505 / 10 ** 2`).
     *
     * Tokens usually opt for a value of 18, imitating the relationship between
     * Ether and Wei. This is the default value returned by this function, unless
     * it's overridden.
     *
     * NOTE: This information is only used for _display_ purposes: it in
     * no way affects any of the arithmetic of the contract, including
     * {IERC20-balanceOf} and {IERC20-transfer}.
     */
    function decimals() public view virtual override returns (uint8) {
        return 18;
    }

    /**
     * @dev See {IERC20-totalSupply}.
     */
    function totalSupply() public view virtual override returns (uint256) {
        return _totalSupply;
    }

    /**
     * @dev See {IERC20-balanceOf}.
     */
    function balanceOf(address account) public view virtual override returns (uint256) {
        return _balances[account];
    }

    /**
     * @dev See {IERC20-transfer}.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - the caller must have a balance of at least `amount`.
     */
    function transfer(address to, uint256 amount) public virtual override returns (bool) {
        address owner = _msgSender();
        _transfer(owner, to, amount);
        return true;
    }

    /**
     * @dev See {IERC20-allowance}.
     */
    function allowance(address owner, address spender) public view virtual override returns (uint256) {
        return _allowances[owner][spender];
    }

    /**
     * @dev See {IERC20-approve}.
     *
     * NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on
     * `transferFrom`. This is semantically equivalent to an infinite approval.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function approve(address spender, uint256 amount) public virtual override returns (bool) {
        address owner = _msgSender();
        _approve(owner, spender, amount);
        return true;
    }

    /**
     * @dev See {IERC20-transferFrom}.
     *
     * Emits an {Approval} event indicating the updated allowance. This is not
     * required by the EIP. See the note at the beginning of {ERC20}.
     *
     * NOTE: Does not update the allowance if the current allowance
     * is the maximum `uint256`.
     *
     * Requirements:
     *
     * - `from` and `to` cannot be the zero address.
     * - `from` must have a balance of at least `amount`.
     * - the caller must have allowance for ``from``'s tokens of at least
     * `amount`.
     */
    function transferFrom(address from, address to, uint256 amount) public virtual override returns (bool) {
        address spender = _msgSender();
        _spendAllowance(from, spender, amount);
        _transfer(from, to, amount);
        return true;
    }

    /**
     * @dev Atomically increases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
        address owner = _msgSender();
        _approve(owner, spender, allowance(owner, spender) + addedValue);
        return true;
    }

    /**
     * @dev Atomically decreases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `spender` must have allowance for the caller of at least
     * `subtractedValue`.
     */
    function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
        address owner = _msgSender();
        uint256 currentAllowance = allowance(owner, spender);
        require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
        unchecked {
            _approve(owner, spender, currentAllowance - subtractedValue);
        }

        return true;
    }

    /**
     * @dev Moves `amount` of tokens from `from` to `to`.
     *
     * This internal function is equivalent to {transfer}, and can be used to
     * e.g. implement automatic token fees, slashing mechanisms, etc.
     *
     * Emits a {Transfer} event.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `from` must have a balance of at least `amount`.
     */
    function _transfer(address from, address to, uint256 amount) internal virtual {
        require(from != address(0), "ERC20: transfer from the zero address");
        require(to != address(0), "ERC20: transfer to the zero address");

        _beforeTokenTransfer(from, to, amount);

        uint256 fromBalance = _balances[from];
        require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
        unchecked {
            _balances[from] = fromBalance - amount;
            // Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by
            // decrementing then incrementing.
            _balances[to] += amount;
        }

        emit Transfer(from, to, amount);

        _afterTokenTransfer(from, to, amount);
    }

    /** @dev Creates `amount` tokens and assigns them to `account`, increasing
     * the total supply.
     *
     * Emits a {Transfer} event with `from` set to the zero address.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     */
    function _mint(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: mint to the zero address");

        _beforeTokenTransfer(address(0), account, amount);

        _totalSupply += amount;
        unchecked {
            // Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above.
            _balances[account] += amount;
        }
        emit Transfer(address(0), account, amount);

        _afterTokenTransfer(address(0), account, amount);
    }

    /**
     * @dev Destroys `amount` tokens from `account`, reducing the
     * total supply.
     *
     * Emits a {Transfer} event with `to` set to the zero address.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     * - `account` must have at least `amount` tokens.
     */
    function _burn(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: burn from the zero address");

        _beforeTokenTransfer(account, address(0), amount);

        uint256 accountBalance = _balances[account];
        require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
        unchecked {
            _balances[account] = accountBalance - amount;
            // Overflow not possible: amount <= accountBalance <= totalSupply.
            _totalSupply -= amount;
        }

        emit Transfer(account, address(0), amount);

        _afterTokenTransfer(account, address(0), amount);
    }

    /**
     * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
     *
     * This internal function is equivalent to `approve`, and can be used to
     * e.g. set automatic allowances for certain subsystems, etc.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `owner` cannot be the zero address.
     * - `spender` cannot be the zero address.
     */
    function _approve(address owner, address spender, uint256 amount) internal virtual {
        require(owner != address(0), "ERC20: approve from the zero address");
        require(spender != address(0), "ERC20: approve to the zero address");

        _allowances[owner][spender] = amount;
        emit Approval(owner, spender, amount);
    }

    /**
     * @dev Updates `owner` s allowance for `spender` based on spent `amount`.
     *
     * Does not update the allowance amount in case of infinite allowance.
     * Revert if not enough allowance is available.
     *
     * Might emit an {Approval} event.
     */
    function _spendAllowance(address owner, address spender, uint256 amount) internal virtual {
        uint256 currentAllowance = allowance(owner, spender);
        if (currentAllowance != type(uint256).max) {
            require(currentAllowance >= amount, "ERC20: insufficient allowance");
            unchecked {
                _approve(owner, spender, currentAllowance - amount);
            }
        }
    }

    /**
     * @dev Hook that is called before any transfer of tokens. This includes
     * minting and burning.
     *
     * Calling conditions:
     *
     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * will be transferred to `to`.
     * - when `from` is zero, `amount` tokens will be minted for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
     * - `from` and `to` are never both zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual {}

    /**
     * @dev Hook that is called after any transfer of tokens. This includes
     * minting and burning.
     *
     * Calling conditions:
     *
     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * has been transferred to `to`.
     * - when `from` is zero, `amount` tokens have been minted for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens have been burned.
     * - `from` and `to` are never both zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _afterTokenTransfer(address from, address to, uint256 amount) internal virtual {}
}
ERC20Permit.sol 95 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/extensions/ERC20Permit.sol)

pragma solidity ^0.8.0;

import "./IERC20Permit.sol";
import "../ERC20.sol";
import "../../../utils/cryptography/ECDSA.sol";
import "../../../utils/cryptography/EIP712.sol";
import "../../../utils/Counters.sol";

/**
 * @dev Implementation of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
 * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
 *
 * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
 * presenting a message signed by the account. By not relying on `{IERC20-approve}`, the token holder account doesn't
 * need to send a transaction, and thus is not required to hold Ether at all.
 *
 * _Available since v3.4._
 */
abstract contract ERC20Permit is ERC20, IERC20Permit, EIP712 {
    using Counters for Counters.Counter;

    mapping(address => Counters.Counter) private _nonces;

    // solhint-disable-next-line var-name-mixedcase
    bytes32 private constant _PERMIT_TYPEHASH =
        keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
    /**
     * @dev In previous versions `_PERMIT_TYPEHASH` was declared as `immutable`.
     * However, to ensure consistency with the upgradeable transpiler, we will continue
     * to reserve a slot.
     * @custom:oz-renamed-from _PERMIT_TYPEHASH
     */
    // solhint-disable-next-line var-name-mixedcase
    bytes32 private _PERMIT_TYPEHASH_DEPRECATED_SLOT;

    /**
     * @dev Initializes the {EIP712} domain separator using the `name` parameter, and setting `version` to `"1"`.
     *
     * It's a good idea to use the same `name` that is defined as the ERC20 token name.
     */
    constructor(string memory name) EIP712(name, "1") {}

    /**
     * @dev See {IERC20Permit-permit}.
     */
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) public virtual override {
        require(block.timestamp <= deadline, "ERC20Permit: expired deadline");

        bytes32 structHash = keccak256(abi.encode(_PERMIT_TYPEHASH, owner, spender, value, _useNonce(owner), deadline));

        bytes32 hash = _hashTypedDataV4(structHash);

        address signer = ECDSA.recover(hash, v, r, s);
        require(signer == owner, "ERC20Permit: invalid signature");

        _approve(owner, spender, value);
    }

    /**
     * @dev See {IERC20Permit-nonces}.
     */
    function nonces(address owner) public view virtual override returns (uint256) {
        return _nonces[owner].current();
    }

    /**
     * @dev See {IERC20Permit-DOMAIN_SEPARATOR}.
     */
    // solhint-disable-next-line func-name-mixedcase
    function DOMAIN_SEPARATOR() external view override returns (bytes32) {
        return _domainSeparatorV4();
    }

    /**
     * @dev "Consume a nonce": return the current value and increment.
     *
     * _Available since v4.1._
     */
    function _useNonce(address owner) internal virtual returns (uint256 current) {
        Counters.Counter storage nonce = _nonces[owner];
        current = nonce.current();
        nonce.increment();
    }
}
IERC20Metadata.sol 28 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)

pragma solidity ^0.8.0;

import "../IERC20.sol";

/**
 * @dev Interface for the optional metadata functions from the ERC20 standard.
 *
 * _Available since v4.1._
 */
interface IERC20Metadata is IERC20 {
    /**
     * @dev Returns the name of the token.
     */
    function name() external view returns (string memory);

    /**
     * @dev Returns the symbol of the token.
     */
    function symbol() external view returns (string memory);

    /**
     * @dev Returns the decimals places of the token.
     */
    function decimals() external view returns (uint8);
}
IERC20Permit.sol 60 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/extensions/IERC20Permit.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
 * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
 *
 * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
 * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
 * need to send a transaction, and thus is not required to hold Ether at all.
 */
interface IERC20Permit {
    /**
     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
     * given ``owner``'s signed approval.
     *
     * IMPORTANT: The same issues {IERC20-approve} has related to transaction
     * ordering also apply here.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `deadline` must be a timestamp in the future.
     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
     * over the EIP712-formatted function arguments.
     * - the signature must use ``owner``'s current nonce (see {nonces}).
     *
     * For more information on the signature format, see the
     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
     * section].
     */
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

    /**
     * @dev Returns the current nonce for `owner`. This value must be
     * included whenever a signature is generated for {permit}.
     *
     * Every successful call to {permit} increases ``owner``'s nonce by one. This
     * prevents a signature from being used multiple times.
     */
    function nonces(address owner) external view returns (uint256);

    /**
     * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
     */
    // solhint-disable-next-line func-name-mixedcase
    function DOMAIN_SEPARATOR() external view returns (bytes32);
}
IERC20.sol 78 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the amount of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves `amount` tokens from the caller's account to `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, uint256 amount) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 amount) external returns (bool);

    /**
     * @dev Moves `amount` tokens from `from` to `to` using the
     * allowance mechanism. `amount` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 amount) external returns (bool);
}
SafeERC20.sol 143 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.3) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.0;

import "../IERC20.sol";
import "../extensions/IERC20Permit.sol";
import "../../../utils/Address.sol";

/**
 * @title SafeERC20
 * @dev Wrappers around ERC20 operations that throw on failure (when the token
 * contract returns false). Tokens that return no value (and instead revert or
 * throw on failure) are also supported, non-reverting calls are assumed to be
 * successful.
 * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeERC20 {
    using Address for address;

    /**
     * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeTransfer(IERC20 token, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
    }

    /**
     * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
     * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
     */
    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
    }

    /**
     * @dev Deprecated. This function has issues similar to the ones found in
     * {IERC20-approve}, and its usage is discouraged.
     *
     * Whenever possible, use {safeIncreaseAllowance} and
     * {safeDecreaseAllowance} instead.
     */
    function safeApprove(IERC20 token, address spender, uint256 value) internal {
        // safeApprove should only be called when setting an initial allowance,
        // or when resetting it to zero. To increase and decrease it, use
        // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
        require(
            (value == 0) || (token.allowance(address(this), spender) == 0),
            "SafeERC20: approve from non-zero to non-zero allowance"
        );
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
    }

    /**
     * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 oldAllowance = token.allowance(address(this), spender);
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));
    }

    /**
     * @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        unchecked {
            uint256 oldAllowance = token.allowance(address(this), spender);
            require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));
        }
    }

    /**
     * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
     * to be set to zero before setting it to a non-zero value, such as USDT.
     */
    function forceApprove(IERC20 token, address spender, uint256 value) internal {
        bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value);

        if (!_callOptionalReturnBool(token, approvalCall)) {
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));
            _callOptionalReturn(token, approvalCall);
        }
    }

    /**
     * @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.
     * Revert on invalid signature.
     */
    function safePermit(
        IERC20Permit token,
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal {
        uint256 nonceBefore = token.nonces(owner);
        token.permit(owner, spender, value, deadline, v, r, s);
        uint256 nonceAfter = token.nonces(owner);
        require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
        // the target address contains contract code and also asserts for success in the low-level call.

        bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
        require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     *
     * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
     */
    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
        // and not revert is the subcall reverts.

        (bool success, bytes memory returndata) = address(token).call(data);
        return
            success && (returndata.length == 0 || abi.decode(returndata, (bool))) && Address.isContract(address(token));
    }
}
Address.sol 244 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)

pragma solidity ^0.8.1;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     *
     * Furthermore, `isContract` will also return true if the target contract within
     * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
     * which only has an effect at the end of a transaction.
     * ====
     *
     * [IMPORTANT]
     * ====
     * You shouldn't rely on `isContract` to protect against flash loan attacks!
     *
     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
     * constructor.
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize/address.code.length, which returns 0
        // for contracts in construction, since the code is only stored at the end
        // of the constructor execution.

        return account.code.length > 0;
    }

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

        (bool success, ) = recipient.call{value: amount}("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain `call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason, it is bubbled up by this
     * function (like regular Solidity function calls).
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, "Address: low-level call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionDelegateCall(target, data, "Address: low-level delegate call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
     * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
     *
     * _Available since v4.8._
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        if (success) {
            if (returndata.length == 0) {
                // only check isContract if the call was successful and the return data is empty
                // otherwise we already know that it was a contract
                require(isContract(target), "Address: call to non-contract");
            }
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason or using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    function _revert(bytes memory returndata, string memory errorMessage) private pure {
        // Look for revert reason and bubble it up if present
        if (returndata.length > 0) {
            // The easiest way to bubble the revert reason is using memory via assembly
            /// @solidity memory-safe-assembly
            assembly {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            revert(errorMessage);
        }
    }
}
Context.sol 24 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)

pragma solidity ^0.8.0;

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }
}
Counters.sol 43 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Counters.sol)

pragma solidity ^0.8.0;

/**
 * @title Counters
 * @author Matt Condon (@shrugs)
 * @dev Provides counters that can only be incremented, decremented or reset. This can be used e.g. to track the number
 * of elements in a mapping, issuing ERC721 ids, or counting request ids.
 *
 * Include with `using Counters for Counters.Counter;`
 */
library Counters {
    struct Counter {
        // This variable should never be directly accessed by users of the library: interactions must be restricted to
        // the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add
        // this feature: see https://github.com/ethereum/solidity/issues/4637
        uint256 _value; // default: 0
    }

    function current(Counter storage counter) internal view returns (uint256) {
        return counter._value;
    }

    function increment(Counter storage counter) internal {
        unchecked {
            counter._value += 1;
        }
    }

    function decrement(Counter storage counter) internal {
        uint256 value = counter._value;
        require(value > 0, "Counter: decrement overflow");
        unchecked {
            counter._value = value - 1;
        }
    }

    function reset(Counter storage counter) internal {
        counter._value = 0;
    }
}
ECDSA.sol 217 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/cryptography/ECDSA.sol)

pragma solidity ^0.8.0;

import "../Strings.sol";

/**
 * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
 *
 * These functions can be used to verify that a message was signed by the holder
 * of the private keys of a given address.
 */
library ECDSA {
    enum RecoverError {
        NoError,
        InvalidSignature,
        InvalidSignatureLength,
        InvalidSignatureS,
        InvalidSignatureV // Deprecated in v4.8
    }

    function _throwError(RecoverError error) private pure {
        if (error == RecoverError.NoError) {
            return; // no error: do nothing
        } else if (error == RecoverError.InvalidSignature) {
            revert("ECDSA: invalid signature");
        } else if (error == RecoverError.InvalidSignatureLength) {
            revert("ECDSA: invalid signature length");
        } else if (error == RecoverError.InvalidSignatureS) {
            revert("ECDSA: invalid signature 's' value");
        }
    }

    /**
     * @dev Returns the address that signed a hashed message (`hash`) with
     * `signature` or error string. This address can then be used for verification purposes.
     *
     * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {toEthSignedMessageHash} on it.
     *
     * Documentation for signature generation:
     * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
     * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
     *
     * _Available since v4.3._
     */
    function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {
        if (signature.length == 65) {
            bytes32 r;
            bytes32 s;
            uint8 v;
            // ecrecover takes the signature parameters, and the only way to get them
            // currently is to use assembly.
            /// @solidity memory-safe-assembly
            assembly {
                r := mload(add(signature, 0x20))
                s := mload(add(signature, 0x40))
                v := byte(0, mload(add(signature, 0x60)))
            }
            return tryRecover(hash, v, r, s);
        } else {
            return (address(0), RecoverError.InvalidSignatureLength);
        }
    }

    /**
     * @dev Returns the address that signed a hashed message (`hash`) with
     * `signature`. This address can then be used for verification purposes.
     *
     * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {toEthSignedMessageHash} on it.
     */
    function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, signature);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
     *
     * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
     *
     * _Available since v4.3._
     */
    function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address, RecoverError) {
        bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
        uint8 v = uint8((uint256(vs) >> 255) + 27);
        return tryRecover(hash, v, r, s);
    }

    /**
     * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
     *
     * _Available since v4.2._
     */
    function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, r, vs);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Overload of {ECDSA-tryRecover} that receives the `v`,
     * `r` and `s` signature fields separately.
     *
     * _Available since v4.3._
     */
    function tryRecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address, RecoverError) {
        // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
        // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
        // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
        // signatures from current libraries generate a unique signature with an s-value in the lower half order.
        //
        // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
        // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
        // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
        // these malleable signatures as well.
        if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
            return (address(0), RecoverError.InvalidSignatureS);
        }

        // If the signature is valid (and not malleable), return the signer address
        address signer = ecrecover(hash, v, r, s);
        if (signer == address(0)) {
            return (address(0), RecoverError.InvalidSignature);
        }

        return (signer, RecoverError.NoError);
    }

    /**
     * @dev Overload of {ECDSA-recover} that receives the `v`,
     * `r` and `s` signature fields separately.
     */
    function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, v, r, s);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Returns an Ethereum Signed Message, created from a `hash`. This
     * produces hash corresponding to the one signed with the
     * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
     * JSON-RPC method as part of EIP-191.
     *
     * See {recover}.
     */
    function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32 message) {
        // 32 is the length in bytes of hash,
        // enforced by the type signature above
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, "\x19Ethereum Signed Message:\n32")
            mstore(0x1c, hash)
            message := keccak256(0x00, 0x3c)
        }
    }

    /**
     * @dev Returns an Ethereum Signed Message, created from `s`. This
     * produces hash corresponding to the one signed with the
     * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
     * JSON-RPC method as part of EIP-191.
     *
     * See {recover}.
     */
    function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n", Strings.toString(s.length), s));
    }

    /**
     * @dev Returns an Ethereum Signed Typed Data, created from a
     * `domainSeparator` and a `structHash`. This produces hash corresponding
     * to the one signed with the
     * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
     * JSON-RPC method as part of EIP-712.
     *
     * See {recover}.
     */
    function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32 data) {
        /// @solidity memory-safe-assembly
        assembly {
            let ptr := mload(0x40)
            mstore(ptr, "\x19\x01")
            mstore(add(ptr, 0x02), domainSeparator)
            mstore(add(ptr, 0x22), structHash)
            data := keccak256(ptr, 0x42)
        }
    }

    /**
     * @dev Returns an Ethereum Signed Data with intended validator, created from a
     * `validator` and `data` according to the version 0 of EIP-191.
     *
     * See {recover}.
     */
    function toDataWithIntendedValidatorHash(address validator, bytes memory data) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked("\x19\x00", validator, data));
    }
}
EIP712.sol 142 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/cryptography/EIP712.sol)

pragma solidity ^0.8.8;

import "./ECDSA.sol";
import "../ShortStrings.sol";
import "../../interfaces/IERC5267.sol";

/**
 * @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data.
 *
 * The encoding specified in the EIP is very generic, and such a generic implementation in Solidity is not feasible,
 * thus this contract does not implement the encoding itself. Protocols need to implement the type-specific encoding
 * they need in their contracts using a combination of `abi.encode` and `keccak256`.
 *
 * This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding
 * scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA
 * ({_hashTypedDataV4}).
 *
 * The implementation of the domain separator was designed to be as efficient as possible while still properly updating
 * the chain id to protect against replay attacks on an eventual fork of the chain.
 *
 * NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method
 * https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask].
 *
 * NOTE: In the upgradeable version of this contract, the cached values will correspond to the address, and the domain
 * separator of the implementation contract. This will cause the `_domainSeparatorV4` function to always rebuild the
 * separator from the immutable values, which is cheaper than accessing a cached version in cold storage.
 *
 * _Available since v3.4._
 *
 * @custom:oz-upgrades-unsafe-allow state-variable-immutable state-variable-assignment
 */
abstract contract EIP712 is IERC5267 {
    using ShortStrings for *;

    bytes32 private constant _TYPE_HASH =
        keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");

    // Cache the domain separator as an immutable value, but also store the chain id that it corresponds to, in order to
    // invalidate the cached domain separator if the chain id changes.
    bytes32 private immutable _cachedDomainSeparator;
    uint256 private immutable _cachedChainId;
    address private immutable _cachedThis;

    bytes32 private immutable _hashedName;
    bytes32 private immutable _hashedVersion;

    ShortString private immutable _name;
    ShortString private immutable _version;
    string private _nameFallback;
    string private _versionFallback;

    /**
     * @dev Initializes the domain separator and parameter caches.
     *
     * The meaning of `name` and `version` is specified in
     * https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]:
     *
     * - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol.
     * - `version`: the current major version of the signing domain.
     *
     * NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart
     * contract upgrade].
     */
    constructor(string memory name, string memory version) {
        _name = name.toShortStringWithFallback(_nameFallback);
        _version = version.toShortStringWithFallback(_versionFallback);
        _hashedName = keccak256(bytes(name));
        _hashedVersion = keccak256(bytes(version));

        _cachedChainId = block.chainid;
        _cachedDomainSeparator = _buildDomainSeparator();
        _cachedThis = address(this);
    }

    /**
     * @dev Returns the domain separator for the current chain.
     */
    function _domainSeparatorV4() internal view returns (bytes32) {
        if (address(this) == _cachedThis && block.chainid == _cachedChainId) {
            return _cachedDomainSeparator;
        } else {
            return _buildDomainSeparator();
        }
    }

    function _buildDomainSeparator() private view returns (bytes32) {
        return keccak256(abi.encode(_TYPE_HASH, _hashedName, _hashedVersion, block.chainid, address(this)));
    }

    /**
     * @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this
     * function returns the hash of the fully encoded EIP712 message for this domain.
     *
     * This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example:
     *
     * ```solidity
     * bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(
     *     keccak256("Mail(address to,string contents)"),
     *     mailTo,
     *     keccak256(bytes(mailContents))
     * )));
     * address signer = ECDSA.recover(digest, signature);
     * ```
     */
    function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) {
        return ECDSA.toTypedDataHash(_domainSeparatorV4(), structHash);
    }

    /**
     * @dev See {EIP-5267}.
     *
     * _Available since v4.9._
     */
    function eip712Domain()
        public
        view
        virtual
        override
        returns (
            bytes1 fields,
            string memory name,
            string memory version,
            uint256 chainId,
            address verifyingContract,
            bytes32 salt,
            uint256[] memory extensions
        )
    {
        return (
            hex"0f", // 01111
            _name.toStringWithFallback(_nameFallback),
            _version.toStringWithFallback(_versionFallback),
            block.chainid,
            address(this),
            bytes32(0),
            new uint256[](0)
        );
    }
}
IERC165.sol 25 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[EIP].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
Math.sol 339 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol)

pragma solidity ^0.8.0;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    enum Rounding {
        Down, // Toward negative infinity
        Up, // Toward infinity
        Zero // Toward zero
    }

    /**
     * @dev Returns the largest of two numbers.
     */
    function max(uint256 a, uint256 b) internal pure returns (uint256) {
        return a > b ? a : b;
    }

    /**
     * @dev Returns the smallest of two numbers.
     */
    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return a < b ? a : b;
    }

    /**
     * @dev Returns the average of two numbers. The result is rounded towards
     * zero.
     */
    function average(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b) / 2 can overflow.
        return (a & b) + (a ^ b) / 2;
    }

    /**
     * @dev Returns the ceiling of the division of two numbers.
     *
     * This differs from standard division with `/` in that it rounds up instead
     * of rounding down.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b - 1) / b can overflow on addition, so we distribute.
        return a == 0 ? 0 : (a - 1) / b + 1;
    }

    /**
     * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
     * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
     * with further edits by Uniswap Labs also under MIT license.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
        unchecked {
            // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
            // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
            // variables such that product = prod1 * 2^256 + prod0.
            uint256 prod0; // Least significant 256 bits of the product
            uint256 prod1; // Most significant 256 bits of the product
            assembly {
                let mm := mulmod(x, y, not(0))
                prod0 := mul(x, y)
                prod1 := sub(sub(mm, prod0), lt(mm, prod0))
            }

            // Handle non-overflow cases, 256 by 256 division.
            if (prod1 == 0) {
                // Solidity will revert if denominator == 0, unlike the div opcode on its own.
                // The surrounding unchecked block does not change this fact.
                // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
                return prod0 / denominator;
            }

            // Make sure the result is less than 2^256. Also prevents denominator == 0.
            require(denominator > prod1, "Math: mulDiv overflow");

            ///////////////////////////////////////////////
            // 512 by 256 division.
            ///////////////////////////////////////////////

            // Make division exact by subtracting the remainder from [prod1 prod0].
            uint256 remainder;
            assembly {
                // Compute remainder using mulmod.
                remainder := mulmod(x, y, denominator)

                // Subtract 256 bit number from 512 bit number.
                prod1 := sub(prod1, gt(remainder, prod0))
                prod0 := sub(prod0, remainder)
            }

            // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
            // See https://cs.stackexchange.com/q/138556/92363.

            // Does not overflow because the denominator cannot be zero at this stage in the function.
            uint256 twos = denominator & (~denominator + 1);
            assembly {
                // Divide denominator by twos.
                denominator := div(denominator, twos)

                // Divide [prod1 prod0] by twos.
                prod0 := div(prod0, twos)

                // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
                twos := add(div(sub(0, twos), twos), 1)
            }

            // Shift in bits from prod1 into prod0.
            prod0 |= prod1 * twos;

            // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
            // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
            // four bits. That is, denominator * inv = 1 mod 2^4.
            uint256 inverse = (3 * denominator) ^ 2;

            // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
            // in modular arithmetic, doubling the correct bits in each step.
            inverse *= 2 - denominator * inverse; // inverse mod 2^8
            inverse *= 2 - denominator * inverse; // inverse mod 2^16
            inverse *= 2 - denominator * inverse; // inverse mod 2^32
            inverse *= 2 - denominator * inverse; // inverse mod 2^64
            inverse *= 2 - denominator * inverse; // inverse mod 2^128
            inverse *= 2 - denominator * inverse; // inverse mod 2^256

            // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
            // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
            // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
            // is no longer required.
            result = prod0 * inverse;
            return result;
        }
    }

    /**
     * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
        uint256 result = mulDiv(x, y, denominator);
        if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
            result += 1;
        }
        return result;
    }

    /**
     * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
     *
     * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
     */
    function sqrt(uint256 a) internal pure returns (uint256) {
        if (a == 0) {
            return 0;
        }

        // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
        //
        // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
        // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
        //
        // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
        // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
        // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
        //
        // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
        uint256 result = 1 << (log2(a) >> 1);

        // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
        // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
        // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
        // into the expected uint128 result.
        unchecked {
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            return min(result, a / result);
        }
    }

    /**
     * @notice Calculates sqrt(a), following the selected rounding direction.
     */
    function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = sqrt(a);
            return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 2, rounded down, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 128;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 64;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 32;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 16;
            }
            if (value >> 8 > 0) {
                value >>= 8;
                result += 8;
            }
            if (value >> 4 > 0) {
                value >>= 4;
                result += 4;
            }
            if (value >> 2 > 0) {
                value >>= 2;
                result += 2;
            }
            if (value >> 1 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log2(value);
            return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 10, rounded down, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >= 10 ** 64) {
                value /= 10 ** 64;
                result += 64;
            }
            if (value >= 10 ** 32) {
                value /= 10 ** 32;
                result += 32;
            }
            if (value >= 10 ** 16) {
                value /= 10 ** 16;
                result += 16;
            }
            if (value >= 10 ** 8) {
                value /= 10 ** 8;
                result += 8;
            }
            if (value >= 10 ** 4) {
                value /= 10 ** 4;
                result += 4;
            }
            if (value >= 10 ** 2) {
                value /= 10 ** 2;
                result += 2;
            }
            if (value >= 10 ** 1) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log10(value);
            return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 256, rounded down, of a positive value.
     * Returns 0 if given 0.
     *
     * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
     */
    function log256(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 16;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 8;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 4;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 2;
            }
            if (value >> 8 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 256, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log256(value);
            return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);
        }
    }
}
SignedMath.sol 43 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SignedMath.sol)

pragma solidity ^0.8.0;

/**
 * @dev Standard signed math utilities missing in the Solidity language.
 */
library SignedMath {
    /**
     * @dev Returns the largest of two signed numbers.
     */
    function max(int256 a, int256 b) internal pure returns (int256) {
        return a > b ? a : b;
    }

    /**
     * @dev Returns the smallest of two signed numbers.
     */
    function min(int256 a, int256 b) internal pure returns (int256) {
        return a < b ? a : b;
    }

    /**
     * @dev Returns the average of two signed numbers without overflow.
     * The result is rounded towards zero.
     */
    function average(int256 a, int256 b) internal pure returns (int256) {
        // Formula from the book "Hacker's Delight"
        int256 x = (a & b) + ((a ^ b) >> 1);
        return x + (int256(uint256(x) >> 255) & (a ^ b));
    }

    /**
     * @dev Returns the absolute unsigned value of a signed value.
     */
    function abs(int256 n) internal pure returns (uint256) {
        unchecked {
            // must be unchecked in order to support `n = type(int256).min`
            return uint256(n >= 0 ? n : -n);
        }
    }
}
ShortStrings.sol 122 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/ShortStrings.sol)

pragma solidity ^0.8.8;

import "./StorageSlot.sol";

// | string  | 0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA   |
// | length  | 0x                                                              BB |
type ShortString is bytes32;

/**
 * @dev This library provides functions to convert short memory strings
 * into a `ShortString` type that can be used as an immutable variable.
 *
 * Strings of arbitrary length can be optimized using this library if
 * they are short enough (up to 31 bytes) by packing them with their
 * length (1 byte) in a single EVM word (32 bytes). Additionally, a
 * fallback mechanism can be used for every other case.
 *
 * Usage example:
 *
 * ```solidity
 * contract Named {
 *     using ShortStrings for *;
 *
 *     ShortString private immutable _name;
 *     string private _nameFallback;
 *
 *     constructor(string memory contractName) {
 *         _name = contractName.toShortStringWithFallback(_nameFallback);
 *     }
 *
 *     function name() external view returns (string memory) {
 *         return _name.toStringWithFallback(_nameFallback);
 *     }
 * }
 * ```
 */
library ShortStrings {
    // Used as an identifier for strings longer than 31 bytes.
    bytes32 private constant _FALLBACK_SENTINEL = 0x00000000000000000000000000000000000000000000000000000000000000FF;

    error StringTooLong(string str);
    error InvalidShortString();

    /**
     * @dev Encode a string of at most 31 chars into a `ShortString`.
     *
     * This will trigger a `StringTooLong` error is the input string is too long.
     */
    function toShortString(string memory str) internal pure returns (ShortString) {
        bytes memory bstr = bytes(str);
        if (bstr.length > 31) {
            revert StringTooLong(str);
        }
        return ShortString.wrap(bytes32(uint256(bytes32(bstr)) | bstr.length));
    }

    /**
     * @dev Decode a `ShortString` back to a "normal" string.
     */
    function toString(ShortString sstr) internal pure returns (string memory) {
        uint256 len = byteLength(sstr);
        // using `new string(len)` would work locally but is not memory safe.
        string memory str = new string(32);
        /// @solidity memory-safe-assembly
        assembly {
            mstore(str, len)
            mstore(add(str, 0x20), sstr)
        }
        return str;
    }

    /**
     * @dev Return the length of a `ShortString`.
     */
    function byteLength(ShortString sstr) internal pure returns (uint256) {
        uint256 result = uint256(ShortString.unwrap(sstr)) & 0xFF;
        if (result > 31) {
            revert InvalidShortString();
        }
        return result;
    }

    /**
     * @dev Encode a string into a `ShortString`, or write it to storage if it is too long.
     */
    function toShortStringWithFallback(string memory value, string storage store) internal returns (ShortString) {
        if (bytes(value).length < 32) {
            return toShortString(value);
        } else {
            StorageSlot.getStringSlot(store).value = value;
            return ShortString.wrap(_FALLBACK_SENTINEL);
        }
    }

    /**
     * @dev Decode a string that was encoded to `ShortString` or written to storage using {setWithFallback}.
     */
    function toStringWithFallback(ShortString value, string storage store) internal pure returns (string memory) {
        if (ShortString.unwrap(value) != _FALLBACK_SENTINEL) {
            return toString(value);
        } else {
            return store;
        }
    }

    /**
     * @dev Return the length of a string that was encoded to `ShortString` or written to storage using {setWithFallback}.
     *
     * WARNING: This will return the "byte length" of the string. This may not reflect the actual length in terms of
     * actual characters as the UTF-8 encoding of a single character can span over multiple bytes.
     */
    function byteLengthWithFallback(ShortString value, string storage store) internal view returns (uint256) {
        if (ShortString.unwrap(value) != _FALLBACK_SENTINEL) {
            return byteLength(value);
        } else {
            return bytes(store).length;
        }
    }
}
StorageSlot.sol 138 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/StorageSlot.sol)
// This file was procedurally generated from scripts/generate/templates/StorageSlot.js.

pragma solidity ^0.8.0;

/**
 * @dev Library for reading and writing primitive types to specific storage slots.
 *
 * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
 * This library helps with reading and writing to such slots without the need for inline assembly.
 *
 * The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
 *
 * Example usage to set ERC1967 implementation slot:
 * ```solidity
 * contract ERC1967 {
 *     bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
 *
 *     function _getImplementation() internal view returns (address) {
 *         return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
 *     }
 *
 *     function _setImplementation(address newImplementation) internal {
 *         require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
 *         StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
 *     }
 * }
 * ```
 *
 * _Available since v4.1 for `address`, `bool`, `bytes32`, `uint256`._
 * _Available since v4.9 for `string`, `bytes`._
 */
library StorageSlot {
    struct AddressSlot {
        address value;
    }

    struct BooleanSlot {
        bool value;
    }

    struct Bytes32Slot {
        bytes32 value;
    }

    struct Uint256Slot {
        uint256 value;
    }

    struct StringSlot {
        string value;
    }

    struct BytesSlot {
        bytes value;
    }

    /**
     * @dev Returns an `AddressSlot` with member `value` located at `slot`.
     */
    function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `BooleanSlot` with member `value` located at `slot`.
     */
    function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
     */
    function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `Uint256Slot` with member `value` located at `slot`.
     */
    function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `StringSlot` with member `value` located at `slot`.
     */
    function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `StringSlot` representation of the string storage pointer `store`.
     */
    function getStringSlot(string storage store) internal pure returns (StringSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := store.slot
        }
    }

    /**
     * @dev Returns an `BytesSlot` with member `value` located at `slot`.
     */
    function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`.
     */
    function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := store.slot
        }
    }
}
Strings.sol 85 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Strings.sol)

pragma solidity ^0.8.0;

import "./math/Math.sol";
import "./math/SignedMath.sol";

/**
 * @dev String operations.
 */
library Strings {
    bytes16 private constant _SYMBOLS = "0123456789abcdef";
    uint8 private constant _ADDRESS_LENGTH = 20;

    /**
     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
     */
    function toString(uint256 value) internal pure returns (string memory) {
        unchecked {
            uint256 length = Math.log10(value) + 1;
            string memory buffer = new string(length);
            uint256 ptr;
            /// @solidity memory-safe-assembly
            assembly {
                ptr := add(buffer, add(32, length))
            }
            while (true) {
                ptr--;
                /// @solidity memory-safe-assembly
                assembly {
                    mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
                }
                value /= 10;
                if (value == 0) break;
            }
            return buffer;
        }
    }

    /**
     * @dev Converts a `int256` to its ASCII `string` decimal representation.
     */
    function toString(int256 value) internal pure returns (string memory) {
        return string(abi.encodePacked(value < 0 ? "-" : "", toString(SignedMath.abs(value))));
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
     */
    function toHexString(uint256 value) internal pure returns (string memory) {
        unchecked {
            return toHexString(value, Math.log256(value) + 1);
        }
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
     */
    function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
        bytes memory buffer = new bytes(2 * length + 2);
        buffer[0] = "0";
        buffer[1] = "x";
        for (uint256 i = 2 * length + 1; i > 1; --i) {
            buffer[i] = _SYMBOLS[value & 0xf];
            value >>= 4;
        }
        require(value == 0, "Strings: hex length insufficient");
        return string(buffer);
    }

    /**
     * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
     */
    function toHexString(address addr) internal pure returns (string memory) {
        return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
    }

    /**
     * @dev Returns true if the two strings are equal.
     */
    function equal(string memory a, string memory b) internal pure returns (bool) {
        return keccak256(bytes(a)) == keccak256(bytes(b));
    }
}
Common.sol 672 lines
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;

// Common.sol
//
// Common mathematical functions needed by both SD59x18 and UD60x18. Note that these global functions do not
// always operate with SD59x18 and UD60x18 numbers.

/*//////////////////////////////////////////////////////////////////////////
                                CUSTOM ERRORS
//////////////////////////////////////////////////////////////////////////*/

/// @notice Thrown when the resultant value in {mulDiv} overflows uint256.
error PRBMath_MulDiv_Overflow(uint256 x, uint256 y, uint256 denominator);

/// @notice Thrown when the resultant value in {mulDiv18} overflows uint256.
error PRBMath_MulDiv18_Overflow(uint256 x, uint256 y);

/// @notice Thrown when one of the inputs passed to {mulDivSigned} is `type(int256).min`.
error PRBMath_MulDivSigned_InputTooSmall();

/// @notice Thrown when the resultant value in {mulDivSigned} overflows int256.
error PRBMath_MulDivSigned_Overflow(int256 x, int256 y);

/*//////////////////////////////////////////////////////////////////////////
                                    CONSTANTS
//////////////////////////////////////////////////////////////////////////*/

/// @dev The maximum value a uint128 number can have.
uint128 constant MAX_UINT128 = type(uint128).max;

/// @dev The maximum value a uint40 number can have.
uint40 constant MAX_UINT40 = type(uint40).max;

/// @dev The unit number, which the decimal precision of the fixed-point types.
uint256 constant UNIT = 1e18;

/// @dev The unit number inverted mod 2^256.
uint256 constant UNIT_INVERSE = 78156646155174841979727994598816262306175212592076161876661_508869554232690281;

/// @dev The the largest power of two that divides the decimal value of `UNIT`. The logarithm of this value is the least significant
/// bit in the binary representation of `UNIT`.
uint256 constant UNIT_LPOTD = 262144;

/*//////////////////////////////////////////////////////////////////////////
                                    FUNCTIONS
//////////////////////////////////////////////////////////////////////////*/

/// @notice Calculates the binary exponent of x using the binary fraction method.
/// @dev Has to use 192.64-bit fixed-point numbers. See https://ethereum.stackexchange.com/a/96594/24693.
/// @param x The exponent as an unsigned 192.64-bit fixed-point number.
/// @return result The result as an unsigned 60.18-decimal fixed-point number.
/// @custom:smtchecker abstract-function-nondet
function exp2(uint256 x) pure returns (uint256 result) {
    unchecked {
        // Start from 0.5 in the 192.64-bit fixed-point format.
        result = 0x800000000000000000000000000000000000000000000000;

        // The following logic multiplies the result by $\sqrt{2^{-i}}$ when the bit at position i is 1. Key points:
        //
        // 1. Intermediate results will not overflow, as the starting point is 2^191 and all magic factors are under 2^65.
        // 2. The rationale for organizing the if statements into groups of 8 is gas savings. If the result of performing
        // a bitwise AND operation between x and any value in the array [0x80; 0x40; 0x20; 0x10; 0x08; 0x04; 0x02; 0x01] is 1,
        // we know that `x & 0xFF` is also 1.
        if (x & 0xFF00000000000000 > 0) {
            if (x & 0x8000000000000000 > 0) {
                result = (result * 0x16A09E667F3BCC909) >> 64;
            }
            if (x & 0x4000000000000000 > 0) {
                result = (result * 0x1306FE0A31B7152DF) >> 64;
            }
            if (x & 0x2000000000000000 > 0) {
                result = (result * 0x1172B83C7D517ADCE) >> 64;
            }
            if (x & 0x1000000000000000 > 0) {
                result = (result * 0x10B5586CF9890F62A) >> 64;
            }
            if (x & 0x800000000000000 > 0) {
                result = (result * 0x1059B0D31585743AE) >> 64;
            }
            if (x & 0x400000000000000 > 0) {
                result = (result * 0x102C9A3E778060EE7) >> 64;
            }
            if (x & 0x200000000000000 > 0) {
                result = (result * 0x10163DA9FB33356D8) >> 64;
            }
            if (x & 0x100000000000000 > 0) {
                result = (result * 0x100B1AFA5ABCBED61) >> 64;
            }
        }

        if (x & 0xFF000000000000 > 0) {
            if (x & 0x80000000000000 > 0) {
                result = (result * 0x10058C86DA1C09EA2) >> 64;
            }
            if (x & 0x40000000000000 > 0) {
                result = (result * 0x1002C605E2E8CEC50) >> 64;
            }
            if (x & 0x20000000000000 > 0) {
                result = (result * 0x100162F3904051FA1) >> 64;
            }
            if (x & 0x10000000000000 > 0) {
                result = (result * 0x1000B175EFFDC76BA) >> 64;
            }
            if (x & 0x8000000000000 > 0) {
                result = (result * 0x100058BA01FB9F96D) >> 64;
            }
            if (x & 0x4000000000000 > 0) {
                result = (result * 0x10002C5CC37DA9492) >> 64;
            }
            if (x & 0x2000000000000 > 0) {
                result = (result * 0x1000162E525EE0547) >> 64;
            }
            if (x & 0x1000000000000 > 0) {
                result = (result * 0x10000B17255775C04) >> 64;
            }
        }

        if (x & 0xFF0000000000 > 0) {
            if (x & 0x800000000000 > 0) {
                result = (result * 0x1000058B91B5BC9AE) >> 64;
            }
            if (x & 0x400000000000 > 0) {
                result = (result * 0x100002C5C89D5EC6D) >> 64;
            }
            if (x & 0x200000000000 > 0) {
                result = (result * 0x10000162E43F4F831) >> 64;
            }
            if (x & 0x100000000000 > 0) {
                result = (result * 0x100000B1721BCFC9A) >> 64;
            }
            if (x & 0x80000000000 > 0) {
                result = (result * 0x10000058B90CF1E6E) >> 64;
            }
            if (x & 0x40000000000 > 0) {
                result = (result * 0x1000002C5C863B73F) >> 64;
            }
            if (x & 0x20000000000 > 0) {
                result = (result * 0x100000162E430E5A2) >> 64;
            }
            if (x & 0x10000000000 > 0) {
                result = (result * 0x1000000B172183551) >> 64;
            }
        }

        if (x & 0xFF00000000 > 0) {
            if (x & 0x8000000000 > 0) {
                result = (result * 0x100000058B90C0B49) >> 64;
            }
            if (x & 0x4000000000 > 0) {
                result = (result * 0x10000002C5C8601CC) >> 64;
            }
            if (x & 0x2000000000 > 0) {
                result = (result * 0x1000000162E42FFF0) >> 64;
            }
            if (x & 0x1000000000 > 0) {
                result = (result * 0x10000000B17217FBB) >> 64;
            }
            if (x & 0x800000000 > 0) {
                result = (result * 0x1000000058B90BFCE) >> 64;
            }
            if (x & 0x400000000 > 0) {
                result = (result * 0x100000002C5C85FE3) >> 64;
            }
            if (x & 0x200000000 > 0) {
                result = (result * 0x10000000162E42FF1) >> 64;
            }
            if (x & 0x100000000 > 0) {
                result = (result * 0x100000000B17217F8) >> 64;
            }
        }

        if (x & 0xFF000000 > 0) {
            if (x & 0x80000000 > 0) {
                result = (result * 0x10000000058B90BFC) >> 64;
            }
            if (x & 0x40000000 > 0) {
                result = (result * 0x1000000002C5C85FE) >> 64;
            }
            if (x & 0x20000000 > 0) {
                result = (result * 0x100000000162E42FF) >> 64;
            }
            if (x & 0x10000000 > 0) {
                result = (result * 0x1000000000B17217F) >> 64;
            }
            if (x & 0x8000000 > 0) {
                result = (result * 0x100000000058B90C0) >> 64;
            }
            if (x & 0x4000000 > 0) {
                result = (result * 0x10000000002C5C860) >> 64;
            }
            if (x & 0x2000000 > 0) {
                result = (result * 0x1000000000162E430) >> 64;
            }
            if (x & 0x1000000 > 0) {
                result = (result * 0x10000000000B17218) >> 64;
            }
        }

        if (x & 0xFF0000 > 0) {
            if (x & 0x800000 > 0) {
                result = (result * 0x1000000000058B90C) >> 64;
            }
            if (x & 0x400000 > 0) {
                result = (result * 0x100000000002C5C86) >> 64;
            }
            if (x & 0x200000 > 0) {
                result = (result * 0x10000000000162E43) >> 64;
            }
            if (x & 0x100000 > 0) {
                result = (result * 0x100000000000B1721) >> 64;
            }
            if (x & 0x80000 > 0) {
                result = (result * 0x10000000000058B91) >> 64;
            }
            if (x & 0x40000 > 0) {
                result = (result * 0x1000000000002C5C8) >> 64;
            }
            if (x & 0x20000 > 0) {
                result = (result * 0x100000000000162E4) >> 64;
            }
            if (x & 0x10000 > 0) {
                result = (result * 0x1000000000000B172) >> 64;
            }
        }

        if (x & 0xFF00 > 0) {
            if (x & 0x8000 > 0) {
                result = (result * 0x100000000000058B9) >> 64;
            }
            if (x & 0x4000 > 0) {
                result = (result * 0x10000000000002C5D) >> 64;
            }
            if (x & 0x2000 > 0) {
                result = (result * 0x1000000000000162E) >> 64;
            }
            if (x & 0x1000 > 0) {
                result = (result * 0x10000000000000B17) >> 64;
            }
            if (x & 0x800 > 0) {
                result = (result * 0x1000000000000058C) >> 64;
            }
            if (x & 0x400 > 0) {
                result = (result * 0x100000000000002C6) >> 64;
            }
            if (x & 0x200 > 0) {
                result = (result * 0x10000000000000163) >> 64;
            }
            if (x & 0x100 > 0) {
                result = (result * 0x100000000000000B1) >> 64;
            }
        }

        if (x & 0xFF > 0) {
            if (x & 0x80 > 0) {
                result = (result * 0x10000000000000059) >> 64;
            }
            if (x & 0x40 > 0) {
                result = (result * 0x1000000000000002C) >> 64;
            }
            if (x & 0x20 > 0) {
                result = (result * 0x10000000000000016) >> 64;
            }
            if (x & 0x10 > 0) {
                result = (result * 0x1000000000000000B) >> 64;
            }
            if (x & 0x8 > 0) {
                result = (result * 0x10000000000000006) >> 64;
            }
            if (x & 0x4 > 0) {
                result = (result * 0x10000000000000003) >> 64;
            }
            if (x & 0x2 > 0) {
                result = (result * 0x10000000000000001) >> 64;
            }
            if (x & 0x1 > 0) {
                result = (result * 0x10000000000000001) >> 64;
            }
        }

        // In the code snippet below, two operations are executed simultaneously:
        //
        // 1. The result is multiplied by $(2^n + 1)$, where $2^n$ represents the integer part, and the additional 1
        // accounts for the initial guess of 0.5. This is achieved by subtracting from 191 instead of 192.
        // 2. The result is then converted to an unsigned 60.18-decimal fixed-point format.
        //
        // The underlying logic is based on the relationship $2^{191-ip} = 2^{ip} / 2^{191}$, where $ip$ denotes the,
        // integer part, $2^n$.
        result *= UNIT;
        result >>= (191 - (x >> 64));
    }
}

/// @notice Finds the zero-based index of the first 1 in the binary representation of x.
///
/// @dev See the note on "msb" in this Wikipedia article: https://en.wikipedia.org/wiki/Find_first_set
///
/// Each step in this implementation is equivalent to this high-level code:
///
/// ```solidity
/// if (x >= 2 ** 128) {
///     x >>= 128;
///     result += 128;
/// }
/// ```
///
/// Where 128 is replaced with each respective power of two factor. See the full high-level implementation here:
/// https://gist.github.com/PaulRBerg/f932f8693f2733e30c4d479e8e980948
///
/// The Yul instructions used below are:
///
/// - "gt" is "greater than"
/// - "or" is the OR bitwise operator
/// - "shl" is "shift left"
/// - "shr" is "shift right"
///
/// @param x The uint256 number for which to find the index of the most significant bit.
/// @return result The index of the most significant bit as a uint256.
/// @custom:smtchecker abstract-function-nondet
function msb(uint256 x) pure returns (uint256 result) {
    // 2^128
    assembly ("memory-safe") {
        let factor := shl(7, gt(x, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF))
        x := shr(factor, x)
        result := or(result, factor)
    }
    // 2^64
    assembly ("memory-safe") {
        let factor := shl(6, gt(x, 0xFFFFFFFFFFFFFFFF))
        x := shr(factor, x)
        result := or(result, factor)
    }
    // 2^32
    assembly ("memory-safe") {
        let factor := shl(5, gt(x, 0xFFFFFFFF))
        x := shr(factor, x)
        result := or(result, factor)
    }
    // 2^16
    assembly ("memory-safe") {
        let factor := shl(4, gt(x, 0xFFFF))
        x := shr(factor, x)
        result := or(result, factor)
    }
    // 2^8
    assembly ("memory-safe") {
        let factor := shl(3, gt(x, 0xFF))
        x := shr(factor, x)
        result := or(result, factor)
    }
    // 2^4
    assembly ("memory-safe") {
        let factor := shl(2, gt(x, 0xF))
        x := shr(factor, x)
        result := or(result, factor)
    }
    // 2^2
    assembly ("memory-safe") {
        let factor := shl(1, gt(x, 0x3))
        x := shr(factor, x)
        result := or(result, factor)
    }
    // 2^1
    // No need to shift x any more.
    assembly ("memory-safe") {
        let factor := gt(x, 0x1)
        result := or(result, factor)
    }
}

/// @notice Calculates x*y÷denominator with 512-bit precision.
///
/// @dev Credits to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv.
///
/// Notes:
/// - The result is rounded toward zero.
///
/// Requirements:
/// - The denominator must not be zero.
/// - The result must fit in uint256.
///
/// @param x The multiplicand as a uint256.
/// @param y The multiplier as a uint256.
/// @param denominator The divisor as a uint256.
/// @return result The result as a uint256.
/// @custom:smtchecker abstract-function-nondet
function mulDiv(uint256 x, uint256 y, uint256 denominator) pure returns (uint256 result) {
    // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
    // use the Chinese Remainder Theorem to reconstruct the 512-bit result. The result is stored in two 256
    // variables such that product = prod1 * 2^256 + prod0.
    uint256 prod0; // Least significant 256 bits of the product
    uint256 prod1; // Most significant 256 bits of the product
    assembly ("memory-safe") {
        let mm := mulmod(x, y, not(0))
        prod0 := mul(x, y)
        prod1 := sub(sub(mm, prod0), lt(mm, prod0))
    }

    // Handle non-overflow cases, 256 by 256 division.
    if (prod1 == 0) {
        unchecked {
            return prod0 / denominator;
        }
    }

    // Make sure the result is less than 2^256. Also prevents denominator == 0.
    if (prod1 >= denominator) {
        revert PRBMath_MulDiv_Overflow(x, y, denominator);
    }

    ////////////////////////////////////////////////////////////////////////////
    // 512 by 256 division
    ////////////////////////////////////////////////////////////////////////////

    // Make division exact by subtracting the remainder from [prod1 prod0].
    uint256 remainder;
    assembly ("memory-safe") {
        // Compute remainder using the mulmod Yul instruction.
        remainder := mulmod(x, y, denominator)

        // Subtract 256 bit number from 512-bit number.
        prod1 := sub(prod1, gt(remainder, prod0))
        prod0 := sub(prod0, remainder)
    }

    unchecked {
        // Calculate the largest power of two divisor of the denominator using the unary operator ~. This operation cannot overflow
        // because the denominator cannot be zero at this point in the function execution. The result is always >= 1.
        // For more detail, see https://cs.stackexchange.com/q/138556/92363.
        uint256 lpotdod = denominator & (~denominator + 1);
        uint256 flippedLpotdod;

        assembly ("memory-safe") {
            // Factor powers of two out of denominator.
            denominator := div(denominator, lpotdod)

            // Divide [prod1 prod0] by lpotdod.
            prod0 := div(prod0, lpotdod)

            // Get the flipped value `2^256 / lpotdod`. If the `lpotdod` is zero, the flipped value is one.
            // `sub(0, lpotdod)` produces the two's complement version of `lpotdod`, which is equivalent to flipping all the bits.
            // However, `div` interprets this value as an unsigned value: https://ethereum.stackexchange.com/q/147168/24693
            flippedLpotdod := add(div(sub(0, lpotdod), lpotdod), 1)
        }

        // Shift in bits from prod1 into prod0.
        prod0 |= prod1 * flippedLpotdod;

        // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
        // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
        // four bits. That is, denominator * inv = 1 mod 2^4.
        uint256 inverse = (3 * denominator) ^ 2;

        // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
        // in modular arithmetic, doubling the correct bits in each step.
        inverse *= 2 - denominator * inverse; // inverse mod 2^8
        inverse *= 2 - denominator * inverse; // inverse mod 2^16
        inverse *= 2 - denominator * inverse; // inverse mod 2^32
        inverse *= 2 - denominator * inverse; // inverse mod 2^64
        inverse *= 2 - denominator * inverse; // inverse mod 2^128
        inverse *= 2 - denominator * inverse; // inverse mod 2^256

        // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
        // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
        // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
        // is no longer required.
        result = prod0 * inverse;
    }
}

/// @notice Calculates x*y÷1e18 with 512-bit precision.
///
/// @dev A variant of {mulDiv} with constant folding, i.e. in which the denominator is hard coded to 1e18.
///
/// Notes:
/// - The body is purposely left uncommented; to understand how this works, see the documentation in {mulDiv}.
/// - The result is rounded toward zero.
/// - We take as an axiom that the result cannot be `MAX_UINT256` when x and y solve the following system of equations:
///
/// $$
/// \begin{cases}
///     x * y = MAX\_UINT256 * UNIT \\
///     (x * y) \% UNIT \geq \frac{UNIT}{2}
/// \end{cases}
/// $$
///
/// Requirements:
/// - Refer to the requirements in {mulDiv}.
/// - The result must fit in uint256.
///
/// @param x The multiplicand as an unsigned 60.18-decimal fixed-point number.
/// @param y The multiplier as an unsigned 60.18-decimal fixed-point number.
/// @return result The result as an unsigned 60.18-decimal fixed-point number.
/// @custom:smtchecker abstract-function-nondet
function mulDiv18(uint256 x, uint256 y) pure returns (uint256 result) {
    uint256 prod0;
    uint256 prod1;
    assembly ("memory-safe") {
        let mm := mulmod(x, y, not(0))
        prod0 := mul(x, y)
        prod1 := sub(sub(mm, prod0), lt(mm, prod0))
    }

    if (prod1 == 0) {
        unchecked {
            return prod0 / UNIT;
        }
    }

    if (prod1 >= UNIT) {
        revert PRBMath_MulDiv18_Overflow(x, y);
    }

    uint256 remainder;
    assembly ("memory-safe") {
        remainder := mulmod(x, y, UNIT)
        result :=
            mul(
                or(
                    div(sub(prod0, remainder), UNIT_LPOTD),
                    mul(sub(prod1, gt(remainder, prod0)), add(div(sub(0, UNIT_LPOTD), UNIT_LPOTD), 1))
                ),
                UNIT_INVERSE
            )
    }
}

/// @notice Calculates x*y÷denominator with 512-bit precision.
///
/// @dev This is an extension of {mulDiv} for signed numbers, which works by computing the signs and the absolute values separately.
///
/// Notes:
/// - The result is rounded toward zero.
///
/// Requirements:
/// - Refer to the requirements in {mulDiv}.
/// - None of the inputs can be `type(int256).min`.
/// - The result must fit in int256.
///
/// @param x The multiplicand as an int256.
/// @param y The multiplier as an int256.
/// @param denominator The divisor as an int256.
/// @return result The result as an int256.
/// @custom:smtchecker abstract-function-nondet
function mulDivSigned(int256 x, int256 y, int256 denominator) pure returns (int256 result) {
    if (x == type(int256).min || y == type(int256).min || denominator == type(int256).min) {
        revert PRBMath_MulDivSigned_InputTooSmall();
    }

    // Get hold of the absolute values of x, y and the denominator.
    uint256 xAbs;
    uint256 yAbs;
    uint256 dAbs;
    unchecked {
        xAbs = x < 0 ? uint256(-x) : uint256(x);
        yAbs = y < 0 ? uint256(-y) : uint256(y);
        dAbs = denominator < 0 ? uint256(-denominator) : uint256(denominator);
    }

    // Compute the absolute value of x*y÷denominator. The result must fit in int256.
    uint256 resultAbs = mulDiv(xAbs, yAbs, dAbs);
    if (resultAbs > uint256(type(int256).max)) {
        revert PRBMath_MulDivSigned_Overflow(x, y);
    }

    // Get the signs of x, y and the denominator.
    uint256 sx;
    uint256 sy;
    uint256 sd;
    assembly ("memory-safe") {
        // "sgt" is the "signed greater than" assembly instruction and "sub(0,1)" is -1 in two's complement.
        sx := sgt(x, sub(0, 1))
        sy := sgt(y, sub(0, 1))
        sd := sgt(denominator, sub(0, 1))
    }

    // XOR over sx, sy and sd. What this does is to check whether there are 1 or 3 negative signs in the inputs.
    // If there are, the result should be negative. Otherwise, it should be positive.
    unchecked {
        result = sx ^ sy ^ sd == 0 ? -int256(resultAbs) : int256(resultAbs);
    }
}

/// @notice Calculates the square root of x using the Babylonian method.
///
/// @dev See https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method.
///
/// Notes:
/// - If x is not a perfect square, the result is rounded down.
/// - Credits to OpenZeppelin for the explanations in comments below.
///
/// @param x The uint256 number for which to calculate the square root.
/// @return result The result as a uint256.
/// @custom:smtchecker abstract-function-nondet
function sqrt(uint256 x) pure returns (uint256 result) {
    if (x == 0) {
        return 0;
    }

    // For our first guess, we calculate the biggest power of 2 which is smaller than the square root of x.
    //
    // We know that the "msb" (most significant bit) of x is a power of 2 such that we have:
    //
    // $$
    // msb(x) <= x <= 2*msb(x)$
    // $$
    //
    // We write $msb(x)$ as $2^k$, and we get:
    //
    // $$
    // k = log_2(x)
    // $$
    //
    // Thus, we can write the initial inequality as:
    //
    // $$
    // 2^{log_2(x)} <= x <= 2*2^{log_2(x)+1} \\
    // sqrt(2^k) <= sqrt(x) < sqrt(2^{k+1}) \\
    // 2^{k/2} <= sqrt(x) < 2^{(k+1)/2} <= 2^{(k/2)+1}
    // $$
    //
    // Consequently, $2^{log_2(x) /2} is a good first approximation of sqrt(x) with at least one correct bit.
    uint256 xAux = uint256(x);
    result = 1;
    if (xAux >= 2 ** 128) {
        xAux >>= 128;
        result <<= 64;
    }
    if (xAux >= 2 ** 64) {
        xAux >>= 64;
        result <<= 32;
    }
    if (xAux >= 2 ** 32) {
        xAux >>= 32;
        result <<= 16;
    }
    if (xAux >= 2 ** 16) {
        xAux >>= 16;
        result <<= 8;
    }
    if (xAux >= 2 ** 8) {
        xAux >>= 8;
        result <<= 4;
    }
    if (xAux >= 2 ** 4) {
        xAux >>= 4;
        result <<= 2;
    }
    if (xAux >= 2 ** 2) {
        result <<= 1;
    }

    // At this point, `result` is an estimation with at least one bit of precision. We know the true value has at
    // most 128 bits, since it is the square root of a uint256. Newton's method converges quadratically (precision
    // doubles at every iteration). We thus need at most 7 iteration to turn our partial result with one bit of
    // precision into the expected uint128 result.
    unchecked {
        result = (result + x / result) >> 1;
        result = (result + x / result) >> 1;
        result = (result + x / result) >> 1;
        result = (result + x / result) >> 1;
        result = (result + x / result) >> 1;
        result = (result + x / result) >> 1;
        result = (result + x / result) >> 1;

        // If x is not a perfect square, round the result toward zero.
        uint256 roundedResult = x / result;
        if (result >= roundedResult) {
            result = roundedResult;
        }
    }
}
OrigamiElevatedAccess.sol 14 lines
pragma solidity ^0.8.19;
// SPDX-License-Identifier: AGPL-3.0-or-later
// Origami (common/access/OrigamiElevatedAccessBase.sol)

import { OrigamiElevatedAccessBase } from "contracts/common/access/OrigamiElevatedAccessBase.sol";

/**
 * @notice Inherit to add Owner roles for DAO elevated access.
 */ 
abstract contract OrigamiElevatedAccess is OrigamiElevatedAccessBase {
    constructor(address initialOwner) {
        _init(initialOwner);
    }
}
OrigamiElevatedAccessBase.sol 100 lines
pragma solidity ^0.8.19;
// SPDX-License-Identifier: AGPL-3.0-or-later
// Origami (common/access/OrigamiElevatedAccessBase.sol)

import { IOrigamiElevatedAccess } from "contracts/interfaces/common/access/IOrigamiElevatedAccess.sol";
import { CommonEventsAndErrors } from "contracts/libraries/CommonEventsAndErrors.sol";

/**
 * @notice Inherit to add Owner roles for DAO elevated access.
 */ 
abstract contract OrigamiElevatedAccessBase is IOrigamiElevatedAccess {
    /**
     * @notice The address of the current owner.
     */ 
    address public override owner;

    /**
     * @notice Explicit approval for an address to execute a function.
     * allowedCaller => function selector => true/false
     */
    mapping(address => mapping(bytes4 => bool)) public override explicitFunctionAccess;

    /// @dev Track proposed owner
    address private _proposedNewOwner;

    /// @dev propose this as the new owner before revoking, for 2 step approval
    address private constant PROPOSED_DEAD_ADDRESS = 0x000000000000000000000000000000000000dEaD;

    function _init(address initialOwner) internal {
        if (owner != address(0)) revert CommonEventsAndErrors.InvalidAccess();
        if (initialOwner == address(0)) revert CommonEventsAndErrors.InvalidAddress(address(0));
        owner = initialOwner;
    }

    /**
     * @notice Revoke ownership. 
     * @dev To enforce a two-step revoke, it must first propose to 0x000...dEaD prior to calling.
     * This cannot be undone.
     */
    function revokeOwnership() external override onlyElevatedAccess {
        if (_proposedNewOwner != PROPOSED_DEAD_ADDRESS) revert CommonEventsAndErrors.InvalidAddress(_proposedNewOwner);

        emit NewOwnerAccepted(owner, address(0));
        owner = address(0);
    }

    /**
     * @notice Proposes a new Owner.
     * Can only be called by the current owner
     */
    function proposeNewOwner(address account) external override onlyElevatedAccess {
        if (account == address(0)) revert CommonEventsAndErrors.InvalidAddress(account);
        emit NewOwnerProposed(owner, _proposedNewOwner, account);
        _proposedNewOwner = account;
    }

    /**
     * @notice Caller accepts the role as new Owner.
     * Can only be called by the proposed owner
     */
    function acceptOwner() external override {
        if (msg.sender != _proposedNewOwner) revert CommonEventsAndErrors.InvalidAccess();

        emit NewOwnerAccepted(owner, msg.sender);
        owner = msg.sender;
        delete _proposedNewOwner;
    }

    /**
     * @notice Grant `allowedCaller` the rights to call the function selectors in the access list.
     * @dev fnSelector == bytes4(keccak256("fn(argType1,argType2,...)"))
     */
    function setExplicitAccess(address allowedCaller, ExplicitAccess[] calldata access) external override onlyElevatedAccess {
        if (allowedCaller == address(0)) revert CommonEventsAndErrors.InvalidAddress(allowedCaller);
        ExplicitAccess memory _access;
        for (uint256 i; i < access.length; ++i) {
            _access = access[i];
            emit ExplicitAccessSet(allowedCaller, _access.fnSelector, _access.allowed);
            explicitFunctionAccess[allowedCaller][_access.fnSelector] = _access.allowed;
        }
    }

    function isElevatedAccess(address caller, bytes4 fnSelector) internal view returns (bool) {
        return (
            caller == owner || 
            explicitFunctionAccess[caller][fnSelector]
        );
    }

    /**
     * @notice The owner is allowed to call, or if explicit access has been given to the caller.
     * @dev Important: Only for use when called from an *external* contract. 
     * If a function with this modifier is called internally then the `msg.sig` 
     * will still refer to the top level externally called function.
     */
    modifier onlyElevatedAccess() {
        if (!isElevatedAccess(msg.sender, msg.sig)) revert CommonEventsAndErrors.InvalidAccess();
        _;
    }
}
OrigamiTeleportableToken.sol 103 lines
pragma solidity ^0.8.19;
// SPDX-License-Identifier: AGPL-3.0-or-later
// Origami (common/omnichain/OrigamiTeleportableToken.sol)

import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { IERC165 } from "@openzeppelin/contracts/interfaces/IERC165.sol";
import { IERC20Permit } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol";
import { IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

import { ERC20Permit } from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol";
import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import { EIP712 } from "@openzeppelin/contracts/utils/cryptography/EIP712.sol";

import { OrigamiElevatedAccess } from "contracts/common/access/OrigamiElevatedAccess.sol";
import { CommonEventsAndErrors } from "contracts/libraries/CommonEventsAndErrors.sol";
import { IOrigamiTeleportableToken } from "contracts/interfaces/common/omnichain/IOrigamiTeleportableToken.sol";

import { MessagingFee, MessagingReceipt } from "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol";
import { SendParam, OFTReceipt } from "@layerzerolabs/lz-evm-oapp-v2/contracts/standards/oft-evm/interfaces/IOFT.sol";
import { IOFT } from "@layerzerolabs/lz-evm-oapp-v2/contracts/standards/oft-evm/interfaces/IOFT.sol";

/// @title Origami Teleportable Token
/// @notice An ERC20 token (supporting Permit) which does not require token approval to be spent
///     by the trusted teleporter. 
/// @dev There are intentionally no external mint/burn functions on this token, 
///     the teleporter is expected to be a 'locker', ie escrow the tokens.
contract OrigamiTeleportableToken is 
    ERC20Permit,
    OrigamiElevatedAccess,
    IOrigamiTeleportableToken
{
    using SafeERC20 for IERC20;
    /// @inheritdoc IOrigamiTeleportableToken
    IOFT public override teleporter;

    constructor(
        string memory name_,
        string memory symbol_,
        address initialOwner_
    )
        ERC20(name_, symbol_) 
        ERC20Permit(name_) 
        OrigamiElevatedAccess(initialOwner_)
    {}

    /// @inheritdoc IOrigamiTeleportableToken
    function setTeleporter(address newTeleporter) external override onlyElevatedAccess {
        if (newTeleporter == address(0)) revert CommonEventsAndErrors.InvalidAddress(newTeleporter);
        teleporter = IOFT(newTeleporter);
        emit TeleporterSet(newTeleporter);
    }

    /// @inheritdoc IERC20
    function allowance(address tokenOwner, address spender) public view virtual override(ERC20, IERC20) returns (uint256) {
        // If the spender is the trusted teleporter, then no approval is required.
        return spender == address(teleporter)
            ? type(uint256).max
            : super.allowance(tokenOwner, spender);
    }
    
    /// @inheritdoc IERC165
    function supportsInterface(bytes4 interfaceId) public virtual override pure returns (bool) {
        return interfaceId == type(IOrigamiTeleportableToken).interfaceId 
            || interfaceId == type(IERC20Metadata).interfaceId
            || interfaceId == type(IERC20).interfaceId
            || interfaceId == type(IERC20Permit).interfaceId
            || interfaceId == type(EIP712).interfaceId
            || interfaceId == type(IERC165).interfaceId;
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       LAYER ZERO SEND                      */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @inheritdoc IOrigamiTeleportableToken
    function send(
        SendParam calldata sendParam,
        MessagingFee calldata fee,
        address refundAddress
    ) external payable override returns (MessagingReceipt memory msgReceipt, OFTReceipt memory oftReceipt) {
        // pull tokens to vault first. no allowance needed
        uint256 amount = sendParam.amountLD;
        _transfer(msg.sender, address(this), amount);

        // no approval, teleporter is trusted as spender
        (msgReceipt, oftReceipt) = teleporter.send{value: msg.value}(sendParam, fee, refundAddress);

        // There may be a dust refund as LZ truncates to 6 decimals by default.
        uint256 refundAmount = amount - oftReceipt.amountSentLD;
        if (refundAmount > 0) {
            _transfer(address(this), msg.sender, refundAmount);
        }
    }

    /// @inheritdoc IOrigamiTeleportableToken
    function quoteSend(
        SendParam calldata sendParam,
        bool payInLzToken
    ) external view override returns (MessagingFee memory fee) {
        fee = teleporter.quoteSend(sendParam, payInLzToken);
    }
}
OrigamiTokenizedBalanceSheetVault.sol 880 lines
pragma solidity ^0.8.19;
// SPDX-License-Identifier: AGPL-3.0-or-later

import { ITokenizedBalanceSheetVault } from "contracts/interfaces/external/tokenizedBalanceSheetVault/ITokenizedBalanceSheetVault.sol";
import { IOrigamiTokenizedBalanceSheetVault } from "contracts/interfaces/common/IOrigamiTokenizedBalanceSheetVault.sol";

import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { IERC165 } from "@openzeppelin/contracts/interfaces/IERC165.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import { ReentrancyGuard } from "@openzeppelin/contracts/security/ReentrancyGuard.sol";

import { OrigamiMath } from "contracts/libraries/OrigamiMath.sol";
import { CommonEventsAndErrors } from "contracts/libraries/CommonEventsAndErrors.sol";
import { OrigamiTeleportableToken } from "contracts/common/omnichain/OrigamiTeleportableToken.sol";

/**
 * @title Tokenized 'Balance Sheet' Vault
 * @notice See `ITokenizedBalanceSheetVault` for more details on what a Tokenized Balance Sheet vault represents
 *
 * This Origami implementation is a more opinionated implementation:
 *  - Fees can be taken (from the shares minted/burned) on joins/exits
 *  - The vault needs to be initially seeded by elevated access prior to trading (and as such are protected from inflation style attacks)
 *  - The vault decimals are kept as 18 decimals regardless of the vault assets
 *  - Donations are allowed and will change the share price of the assets/liabilities
 *  - Assets and liabilities need to be in a tokenized form. If that's not possible then a wrapper must be used to represent the balances.
 *  - maxExitWithToken and maxExitWithShares for the sharesOwner=address(0) returns uint256.max. 
 *    This is to reflect that is no explicit exit limits other than the totalSupply. Useful for non-connected users as they browse the dapp
 */
abstract contract OrigamiTokenizedBalanceSheetVault is
    OrigamiTeleportableToken,
    ReentrancyGuard,
    IOrigamiTokenizedBalanceSheetVault
{
    using SafeERC20 for IERC20;
    using OrigamiMath for uint256;

    /// @dev The maximum total supply allowed for this vault.
    /// It is initially set to zero, and first set within seed()
    uint256 private _maxTotalSupply;

    /// @dev Internal cache to save recalculating token balances multiple times
    struct _Cache {
        address inputTokenAddress;
        bool inputTokenIsAsset;
        bool inputTokenIsLiability;
        uint256 inputTokenAmount;
        uint256 inputTokenBalance;
        uint256 totalSupply;
    }

    constructor(
        address initialOwner_,
        string memory name_,
        string memory symbol_
    )
        OrigamiTeleportableToken(name_, symbol_, initialOwner_)
    {}

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                           ADMIN                            */
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/

    /// @inheritdoc IOrigamiTokenizedBalanceSheetVault
    function setMaxTotalSupply(uint256 maxTotalSupply_) external override onlyElevatedAccess {
        // Cannot set if the totalSupply is zero - seed should be used first
        if (totalSupply() == 0) revert CommonEventsAndErrors.ExpectedNonZero();

        _maxTotalSupply = maxTotalSupply_;
        emit MaxTotalSupplySet(maxTotalSupply_);
    }

    /// @inheritdoc IOrigamiTokenizedBalanceSheetVault
    function seed(
        uint256[] calldata assetAmounts,
        uint256[] calldata liabilityAmounts,
        uint256 sharesToMint,
        address receiver,
        uint256 newMaxTotalSupply
    ) external override virtual onlyElevatedAccess {
        // Only to be used for the first deposit
        if (totalSupply() != 0) revert CommonEventsAndErrors.InvalidAccess();

        (address[] memory assetAddresses, address[] memory liabilityAddresses) = tokens();
        if (assetAmounts.length != assetAddresses.length) revert CommonEventsAndErrors.InvalidParam();
        if (liabilityAmounts.length != liabilityAddresses.length) revert CommonEventsAndErrors.InvalidParam();

        if (sharesToMint > newMaxTotalSupply) revert ExceededMaxJoinWithShares(receiver, sharesToMint, newMaxTotalSupply);
        _maxTotalSupply = newMaxTotalSupply;
        emit MaxTotalSupplySet(newMaxTotalSupply);

        _join(msg.sender, receiver, sharesToMint, assetAmounts, liabilityAmounts);
    }

    /// @dev Elevated access can recover any token.
    /// Note: If the asset/liability balances are kept directly in this contract
    /// (rather than delegated to a manager/3rd party upon a join), then the asset/liability tokens
    /// should not be recoverable. In that case the implementation should override this function.
    function recoverToken(address token, address to, uint256 amount) virtual external onlyElevatedAccess {
        emit CommonEventsAndErrors.TokenRecovered(to, token, amount);
        IERC20(token).safeTransfer(to, amount);
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                     ACCOUNTING LOGIC                       */
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/

    /// @inheritdoc ITokenizedBalanceSheetVault
    function tokens() public virtual override view returns (address[] memory assets, address[] memory liabilities) {
        assets = assetTokens();
        liabilities = liabilityTokens();
    }

    /// @inheritdoc ITokenizedBalanceSheetVault
    function balanceSheet() public virtual override view returns (
        uint256[] memory totalAssets, 
        uint256[] memory totalLiabilities
    ) {
        (address[] memory assetAddresses, address[] memory liabilityAddresses) = tokens();

        // First the assets
        uint256 index;
        uint256 length = assetAddresses.length;
        totalAssets = new uint256[](length);
        for (; index < assetAddresses.length; ++index) {
            totalAssets[index] = _tokenBalance(assetAddresses[index]);
        }

        // Now the liabilities
        length = liabilityAddresses.length;
        totalLiabilities = new uint256[](length);
        for (index = 0; index < liabilityAddresses.length; ++index) {
            totalLiabilities[index] = _tokenBalance(liabilityAddresses[index]);
        }
    }

    /// @inheritdoc ITokenizedBalanceSheetVault
    function convertFromToken(
        address tokenAddress,
        uint256 tokenAmount
    ) public view override returns (
        uint256 shares,
        uint256[] memory assets,
        uint256[] memory liabilities
    ) {
        // Following ERC4626 convention, round down for the two convert functions.
        (shares, assets, liabilities) = _convertOneTokenToSharesAndTokens({
            cache: _fillCache(tokenAddress, tokenAmount),
            sharesRounding: OrigamiMath.Rounding.ROUND_DOWN,
            assetsRounding: OrigamiMath.Rounding.ROUND_DOWN,
            liabilitiesRounding: OrigamiMath.Rounding.ROUND_DOWN
        });
    }

    /// @inheritdoc ITokenizedBalanceSheetVault
    function convertFromShares(
        uint256 shares
    ) public view override returns (
        uint256[] memory assets,
        uint256[] memory liabilities
    ) {
        // Following ERC4626 convention, round down for the two convert functions.
        return _proportionalTokenAmountFromShares({
            shares: shares,
            cache: _fillCache(),
            assetsRounding: OrigamiMath.Rounding.ROUND_DOWN,
            liabilitiesRounding: OrigamiMath.Rounding.ROUND_DOWN
        });
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*         IMPLEMENTATIONS TO OVERRIDE (EXTERNAL FNS)         */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @inheritdoc ITokenizedBalanceSheetVault
    function assetTokens() public virtual override view returns (address[] memory);

    /// @inheritdoc ITokenizedBalanceSheetVault
    function liabilityTokens() public virtual override view returns (address[] memory);

    /// @inheritdoc IOrigamiTokenizedBalanceSheetVault
    function joinFeeBps() public virtual override view returns (uint256) {
        return 0;
    }

    /// @inheritdoc IOrigamiTokenizedBalanceSheetVault
    function exitFeeBps() public virtual override view returns (uint256) {
        return 0;
    }

    /// @inheritdoc IOrigamiTokenizedBalanceSheetVault
    function maxTotalSupply() public override view returns (uint256) {
        return _maxTotalSupply;
    }

    /// @inheritdoc IOrigamiTokenizedBalanceSheetVault
    function areJoinsPaused() public virtual override view returns (bool) {
        return false;
    }

    /// @inheritdoc IOrigamiTokenizedBalanceSheetVault
    function areExitsPaused() public virtual override view returns (bool) {
        return false;
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                            JOINS                           */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @inheritdoc ITokenizedBalanceSheetVault
    function maxJoinWithToken(
        address tokenAddress, 
        address /*receiver*/
    ) public view override virtual returns (uint256 maxToken) {
        return _maxJoinWithToken(_fillCache(tokenAddress, 0), joinFeeBps());
    }

    /// @inheritdoc ITokenizedBalanceSheetVault
    function previewJoinWithToken(
        address tokenAddress,
        uint256 tokenAmount
    ) public view override returns (
        uint256 shares,
        uint256[] memory assets,
        uint256[] memory liabilities
    ) {
        (shares, /*shareFeesTaken*/, assets, liabilities) = _previewJoinWithToken(
            _fillCache(tokenAddress, tokenAmount),
            joinFeeBps()
        );
    }

    /// @inheritdoc ITokenizedBalanceSheetVault
    function joinWithToken(
        address tokenAddress,
        uint256 tokenAmount,
        address receiver
    ) public override nonReentrant returns (
        uint256 shares,
        uint256[] memory assets,
        uint256[] memory liabilities
    ) {
        _Cache memory cache = _fillCache(tokenAddress, tokenAmount);

        uint256 feeBps = joinFeeBps();
        uint256 maxTokens = _maxJoinWithToken(cache, feeBps);
        if (tokenAmount > maxTokens) {
            revert ExceededMaxJoinWithToken(receiver, tokenAddress, tokenAmount, maxTokens);
        }

        uint256 shareFeesTaken;
        (shares, shareFeesTaken, assets, liabilities) = _previewJoinWithToken(cache, feeBps);
        if (shareFeesTaken > 0) {
            emit InKindFees(FeeType.JOIN_FEE, feeBps, shareFeesTaken);
        }

        _join(msg.sender, receiver, shares, assets, liabilities);
    }

    /// @inheritdoc ITokenizedBalanceSheetVault
    function maxJoinWithShares(address /*receiver*/) public view override virtual returns (uint256 maxShares) {
        if (areJoinsPaused()) return 0;
        return availableSharesCapacity();
    }

    /// @inheritdoc ITokenizedBalanceSheetVault
    function previewJoinWithShares(
        uint256 shares
    ) public view override returns (
        uint256[] memory assets,
        uint256[] memory liabilities
    ) {
        (assets, liabilities, /*shareFeesTaken*/) = _previewJoinWithShares(shares, joinFeeBps());
    }

    /// @inheritdoc ITokenizedBalanceSheetVault
    function joinWithShares(
        uint256 shares,
        address receiver
    ) public override nonReentrant returns (
        uint256[] memory assets,
        uint256[] memory liabilities
    ) {
        uint256 maxShares = maxJoinWithShares(receiver);
        if (shares > maxShares) {
            revert ExceededMaxJoinWithShares(receiver, shares, maxShares);
        }

        uint256 feeBps = joinFeeBps();
        uint256 shareFeesTaken;
        (assets, liabilities, shareFeesTaken) = _previewJoinWithShares(shares, feeBps);
        if (shareFeesTaken > 0) {
            emit InKindFees(FeeType.JOIN_FEE, feeBps, shareFeesTaken);
        }

        _join(msg.sender, receiver, shares, assets, liabilities);
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                            EXITS                           */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @inheritdoc ITokenizedBalanceSheetVault
    function maxExitWithToken(
        address tokenAddress,
        address sharesOwner
    ) public view override virtual returns (uint256 maxToken) {
        return _maxExitWithToken(_fillCache(tokenAddress, 0), sharesOwner, exitFeeBps());
    }

    /// @inheritdoc ITokenizedBalanceSheetVault
    function previewExitWithToken(
        address tokenAddress,
        uint256 tokenAmount
    ) public view override returns (
        uint256 shares,
        uint256[] memory assets,
        uint256[] memory liabilities
    ) {
        (shares, /*shareFeesTaken*/, assets, liabilities) = _previewExitWithToken(
            _fillCache(tokenAddress, tokenAmount),
            exitFeeBps()
        );
    }

    /// @inheritdoc ITokenizedBalanceSheetVault
    function exitWithToken(
        address tokenAddress,
        uint256 tokenAmount,
        address receiver,
        address sharesOwner
    ) public override nonReentrant returns (
        uint256 shares,
        uint256[] memory assets,
        uint256[] memory liabilities
    ) {
        _Cache memory cache = _fillCache(tokenAddress, tokenAmount);

        uint256 feeBps = exitFeeBps();
        uint256 maxTokens = _maxExitWithToken(cache, sharesOwner, feeBps);
        if (tokenAmount > maxTokens) {
            revert ExceededMaxExitWithToken(sharesOwner, tokenAddress, tokenAmount, maxTokens);
        }

        uint256 shareFeesTaken;
        (shares, shareFeesTaken, assets, liabilities) = _previewExitWithToken(cache, feeBps);
        if (shareFeesTaken > 0) {
            emit InKindFees(FeeType.EXIT_FEE, feeBps, shareFeesTaken);
        }

        _exit(cache, msg.sender, receiver, sharesOwner, shares, assets, liabilities);
    }

    /// @inheritdoc ITokenizedBalanceSheetVault
    function maxExitWithShares(
        address sharesOwner
    ) public view override virtual returns (uint256 maxShares) {
        return _maxExitWithShares(sharesOwner);
    }

    /// @inheritdoc ITokenizedBalanceSheetVault
    function previewExitWithShares(
        uint256 shares
    ) public view override returns (
        uint256[] memory assets,
        uint256[] memory liabilities
    ) {
        (/*shareFeesTaken*/, assets, liabilities) = _previewExitWithShares(
            _fillCache(),
            shares, 
            exitFeeBps()
        );
    }

    /// @inheritdoc ITokenizedBalanceSheetVault
    function exitWithShares(
        uint256 shares,
        address receiver,
        address sharesOwner
    ) public override nonReentrant returns (
        uint256[] memory assets,
        uint256[] memory liabilities
    ) {
        uint256 maxShares = _maxExitWithShares(sharesOwner);
        if (shares > maxShares) {
            revert ExceededMaxExitWithShares(sharesOwner, shares, maxShares);
        }

        _Cache memory cache = _fillCache();
        uint256 feeBps = exitFeeBps();
        uint256 shareFeesTaken;
        (shareFeesTaken, assets, liabilities) = _previewExitWithShares(cache, shares, feeBps);
        if (shareFeesTaken > 0) {
            emit InKindFees(FeeType.EXIT_FEE, feeBps, shareFeesTaken);
        }

        _exit(cache, msg.sender, receiver, sharesOwner, shares, assets, liabilities);        
    }
    
    /// @inheritdoc IOrigamiTokenizedBalanceSheetVault
    function burn(uint256 amount) external virtual override {
        _burn(msg.sender, amount);
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       EXTERNAL VIEWS                       */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @inheritdoc IOrigamiTokenizedBalanceSheetVault
    function availableSharesCapacity() public override view returns (uint256 shares) {
        uint256 maxTotalSupply_ = maxTotalSupply();
        if (maxTotalSupply_ == type(uint256).max) return maxTotalSupply_;
        return _availableSharesCapacity(totalSupply(), maxTotalSupply_);
    }

    /// @inheritdoc ITokenizedBalanceSheetVault
    function isBalanceSheetToken(
        address tokenAddress
    ) public virtual override view returns (
        bool isAsset, 
        bool isLiability
    ) {
        // NOTE: The default implementation here iterates over the tokens to find a match
        // It is recommendation to implement this for the specific use case (if possible) for efficiency
        (address[] memory assetAddresses, address[] memory liabilityAddresses) = tokens();

        for (uint256 i; i < assetAddresses.length; ++i) {
            if (assetAddresses[i] == tokenAddress) {
                return (true, false);
            }
        }

        for (uint256 i; i < liabilityAddresses.length; ++i) {
            if (liabilityAddresses[i] == tokenAddress) {
                return (false, true);
            }
        }

        return (false, false);
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                      EXTERNAL ERC165                       */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @inheritdoc IERC165
    function supportsInterface(bytes4 interfaceId) public virtual override(IERC165, OrigamiTeleportableToken) pure returns (bool) {
        return OrigamiTeleportableToken.supportsInterface(interfaceId)
            || interfaceId == type(IOrigamiTokenizedBalanceSheetVault).interfaceId 
            || interfaceId == type(ITokenizedBalanceSheetVault).interfaceId;
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*         IMPLEMENTATIONS TO OVERRIDE (INTERNAL FNS)         */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Return the current token balance of one of the assets or liability tokens in the vault
    /// Should not revert - return zero if tokenAddress is not valid.
    function _tokenBalance(address tokenAddress) internal virtual view returns (uint256);

    /// @dev A hook for joins - it must pull assets from caller and send liabilities to receiver,
    /// along with any other interactions required.
    function _joinPreMintHook(
        address caller,
        address receiver,
        uint256 shares,
        uint256[] memory assets,
        uint256[] memory liabilities
    ) internal virtual;

    /// @dev A hook for exits - it must send assets to receiver and pull liabilities from caller,
    /// along with any other interactions required.
    function _exitPreBurnHook(
        address caller,
        address sharesOwner,
        address receiver,
        uint256 sharesPreBurn,
        uint256[] memory assets,
        uint256[] memory liabilities
    ) internal virtual;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*        INTERNAL TOKENIZED BALANCE SHEET VAULT - JOINS      */
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/

    /// @dev What's the max amount that can be joined into the vault for 
    /// a token address in the cache
    function _maxJoinWithToken(
        _Cache memory cache,
        uint256 feeBps
    ) internal virtual view returns (uint256 maxTokens) {
        if (areJoinsPaused()) return 0;

        // If the token balance of the requested token is zero then cannot join
        if (cache.inputTokenBalance == 0) return 0;

        uint256 maxTotalSupply_ = maxTotalSupply();
        if (maxTotalSupply_ == type(uint256).max) return maxTotalSupply_;

        uint256 availableShares = _availableSharesCapacity(cache.totalSupply, maxTotalSupply_);

        // The max number of tokens is always rounded down
        return _convertSharesToOneToken({
            // Number of shares can be rounded since this is the inverse
            shares: availableShares.inverseSubtractBps(feeBps, OrigamiMath.Rounding.ROUND_UP), 
            cache: cache, 
            rounding: OrigamiMath.Rounding.ROUND_DOWN
        });
    }

    /// @dev Preview a join for an amount of one of the asset/liability tokens specified in the cache
    function _previewJoinWithToken(
        _Cache memory cache,
        uint256 feeBps
    ) internal virtual view returns (
        uint256 shares,
        uint256 shareFeesTaken,
        uint256[] memory assets,
        uint256[] memory liabilities
    ) {
        // When calculating the number of shares/assets/liabilities when JOINING 
        // given an input token amount:
        //   - shares: 
        //       - if input token is an asset: ROUND_DOWN
        //       - if input token is a liability: ROUND_UP
        //   - assets: ROUND_UP (the assets which are pulled from the caller)
        //   - liabilities: ROUND_DOWN (liabilities sent to recipient)
        (shares, assets, liabilities) = _convertOneTokenToSharesAndTokens({
            cache: cache, 
            sharesRounding: cache.inputTokenIsAsset ? OrigamiMath.Rounding.ROUND_DOWN : OrigamiMath.Rounding.ROUND_UP,
            assetsRounding: OrigamiMath.Rounding.ROUND_UP,
            liabilitiesRounding: OrigamiMath.Rounding.ROUND_DOWN
        });

        // Deposit fees are taken from the shares in kind
        // The `shares` the recipient gets are rounded down (ie fees rounded up), in favour of the vault
        (shares, shareFeesTaken) = shares.splitSubtractBps(feeBps, OrigamiMath.Rounding.ROUND_DOWN);
    }

    /// @dev Preview a join for an amount of shares
    function _previewJoinWithShares(uint256 shares, uint256 feeBps) internal virtual view returns (
        uint256[] memory assets,
        uint256[] memory liabilities,
        uint256 shareFeesTaken
    ) {
        // Deposit fees are taken from the shares the user would otherwise receive
        // so calculate the amount of shares required before fees are taken.
        // Round up the fees taken, so it's in favour of the vault
        uint256 sharesBeforeFees = shares.inverseSubtractBps(feeBps, OrigamiMath.Rounding.ROUND_UP);
        unchecked {
            shareFeesTaken = sharesBeforeFees - shares;
        }

        // When calculating the number of assets/liabilities when JOINING
        // given a number of shares:
        //   - assets: ROUND_UP (the assets which are pulled from the caller)
        //   - liabilities: ROUND_DOWN (liabilities sent to recipient)
        (assets, liabilities) = _proportionalTokenAmountFromShares({
            shares: sharesBeforeFees,
            cache: _fillCache(),
            assetsRounding: OrigamiMath.Rounding.ROUND_UP,
            liabilitiesRounding: OrigamiMath.Rounding.ROUND_DOWN
        });
    }

    /// @dev join for specific assets and liabilities and mint exact shares to the receiver
    /// Implementations must implement the hook which pulls assets from caller and sends 
    /// liabilities to receiver
    function _join(
        address caller,
        address receiver,
        uint256 shares,
        uint256[] memory assets,
        uint256[] memory liabilities
    ) internal virtual {
        if (shares == 0) revert CommonEventsAndErrors.ExpectedNonZero();
        _joinPreMintHook(caller, receiver, shares, assets, liabilities);

        _mint(receiver, shares);

        emit Join(caller, receiver, assets, liabilities, shares);
    }
    
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*        INTERNAL TOKENIZED BALANCE SHEET VAULT - EXITS      */
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/

    /// @dev What's the max amount that can be exited from the vault for 
    /// a token address in the cache
    function _maxExitWithToken(
        _Cache memory cache,
        address sharesOwner,
        uint256 feeBps
    ) internal virtual view returns (uint256 maxTokens) {
        if (areExitsPaused()) return 0;

        // If the token balance of the requested token is zero then cannot join
        if (cache.inputTokenBalance == 0) return 0;

        // Special case for address(0), unlimited
        if (sharesOwner == address(0)) return type(uint256).max;

        uint256 shares = balanceOf(sharesOwner);

        // The max number of tokens is always rounded down
        (shares,) = shares.splitSubtractBps(feeBps, OrigamiMath.Rounding.ROUND_DOWN);
        uint256 maxFromShares = _convertSharesToOneToken({
            shares: shares,
            cache: cache,   
            rounding: OrigamiMath.Rounding.ROUND_DOWN
        });

        // Return the minimum of the available balance of the requested token in the balance sheet
        // and the derived amount from the available shares
        return maxFromShares < cache.inputTokenBalance ? maxFromShares : cache.inputTokenBalance;
    }

    /// @dev Preview an exit for an amount of one of the asset/liability tokens specified in the cache
    function _previewExitWithToken(
        _Cache memory cache,
        uint256 feeBps
    ) internal virtual view returns (
        uint256 shares,
        uint256 shareFeesTaken,
        uint256[] memory assets,
        uint256[] memory liabilities
    ) {
        // When calculating the number of shares/assets/liabilities when EXITING 
        // given an input token amount:
        //   - shares: 
        //       - if input token is an asset: ROUND_UP
        //       - if input token is a liability: ROUND_DOWN
        //   - shares: ROUND_UP (number of shares burned from the recipient)
        //   - assets: ROUND_DOWN (the assets which are sent to the recipient)
        //   - liabilities: ROUND_UP (liabilities pulled from the recipient)
        uint256 sharesAfterFees;
        (sharesAfterFees, assets, liabilities) = _convertOneTokenToSharesAndTokens({
            cache: cache,
            sharesRounding: cache.inputTokenIsAsset ? OrigamiMath.Rounding.ROUND_UP : OrigamiMath.Rounding.ROUND_DOWN,
            assetsRounding: OrigamiMath.Rounding.ROUND_DOWN,
            liabilitiesRounding: OrigamiMath.Rounding.ROUND_UP
        });

        // Exit fees are taken in 'shares' prior to redeeming to the underlying assets 
        // & liability tokens. So now calculate the total number of shares to burn from
        // the user.
        // Round up the shares needing to be burned, so it's in favour of the vault
        shares = sharesAfterFees.inverseSubtractBps(feeBps, OrigamiMath.Rounding.ROUND_UP);
        unchecked {
            shareFeesTaken = shares - sharesAfterFees;
        }
    }

    /// @dev What's the max amount of shares that can be exited from the vault for an owner
    function _maxExitWithShares(
        address sharesOwner
    ) internal virtual view returns (uint256 maxShares) {
        if (areExitsPaused()) return 0;
        return sharesOwner == address(0)
            ? type(uint256).max // Special case for address(0), unlimited exit
            : balanceOf(sharesOwner);
    }

    /// @dev Preview an exit for an amount of shares
    function _previewExitWithShares(
        _Cache memory cache,
        uint256 shares,
        uint256 feeBps
    ) internal virtual view returns (
        uint256 shareFeesTaken,
        uint256[] memory assets,
        uint256[] memory liabilities
    ) {
        // The `shares` the user receives are rounded down in favour of the vault
        (shares, shareFeesTaken) = shares.splitSubtractBps(feeBps, OrigamiMath.Rounding.ROUND_DOWN);

        // When calculating the number of assets/liabilities when EXITING
        // given a number of shares:
        //   - assets: ROUND_DOWN (assets are sent to the recipient)
        //   - liabilities: ROUND_UP (liabilities are pulled from the caller)
        (assets, liabilities) = _proportionalTokenAmountFromShares({
            shares: shares,
            cache: cache,
            assetsRounding: OrigamiMath.Rounding.ROUND_DOWN,
            liabilitiesRounding: OrigamiMath.Rounding.ROUND_UP
        });
    }

    /// @dev exit for specific assets and liabilities and burn exact shares to the receiver
    /// Can be called on behalf of a sharesOwner as long as allowance has been provided.
    /// Implementations must implement the hook which pulls liabilities from caller and sends 
    /// assets to receiver
    function _exit(
        _Cache memory cache,
        address caller,
        address receiver,
        address sharesOwner,
        uint256 shares,
        uint256[] memory assets,
        uint256[] memory liabilities
    ) internal virtual {
        if (shares == 0) revert CommonEventsAndErrors.ExpectedNonZero();
        if (sharesOwner == address(0)) revert CommonEventsAndErrors.InvalidAddress(sharesOwner);
        if (receiver == address(0)) revert CommonEventsAndErrors.InvalidAddress(receiver);
        if (caller != sharesOwner) {
            _spendAllowance(sharesOwner, caller, shares);
        }

        // Call the exit hook prior to burning shares, such that any
        // ERC20 _afterTokenTransfer() hooks the vault may implement (eg hOHM)
        // will use the latest balances
        _exitPreBurnHook(caller, sharesOwner, receiver, shares, assets, liabilities);

        _burn(sharesOwner, shares);
        cache.totalSupply -= shares;

        // If the vault has been fully exited, then reset the maxTotalSupply to zero, as if it were newly created.
        if (cache.totalSupply == 0) _maxTotalSupply = 0;
        
        emit Exit(caller, receiver, sharesOwner, assets, liabilities, shares);
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*    INTERNAL TOKENIZED BALANCE SHEET VAULT - CONVERSIONS    */
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/

    /// @dev Convert an amount of one of the asset/liability tokens (specified in the cache) into
    /// the full set of shares/assets/liabilities.
    /// Based off the balance of that token in the vault and total supply of shares
    function _convertOneTokenToSharesAndTokens(
        _Cache memory cache,
        OrigamiMath.Rounding sharesRounding,
        OrigamiMath.Rounding assetsRounding,
        OrigamiMath.Rounding liabilitiesRounding
    ) private view returns (
        uint256 shares,
        uint256[] memory assets,
        uint256[] memory liabilities
    ) {
        shares = _convertOneTokenToShares(cache, sharesRounding);

        (assets, liabilities) = _proportionalTokenAmountFromShares({
            shares: shares,
            cache: cache,
            assetsRounding: assetsRounding,
            liabilitiesRounding: liabilitiesRounding
        });
    }

    /// @dev For each token in tokenAddresses, calculate the proportional token
    /// amount given an amount of shares and the current total supply
    ///   otherTokenAmount = shares * otherTokenBalance / totalSupply
    function _proportionalTokenAmountFromShares(
        uint256 shares,
        _Cache memory cache,
        address[] memory tokenAddresses,
        OrigamiMath.Rounding rounding
    ) internal view returns (uint256[] memory amounts) {
        uint256 length = tokenAddresses.length;
        amounts = new uint256[](length);
        address iTokenAddress;
        for (uint256 i; i < length; ++i) {
            iTokenAddress = tokenAddresses[i];
            if (cache.totalSupply == 0) {
                // denominator is zero
                amounts[i] = 0;
            } else if (iTokenAddress != address(0) && iTokenAddress == cache.inputTokenAddress) {
                // This is from a calculation where the input token is used to calculate
                // the shares first, and then each of the token amounts is derived from the shares.
                // In this case, the exact input token amount needs to be used.
                amounts[i] = cache.inputTokenAmount;
            } else {
                amounts[i] = shares.mulDiv(
                    _getTokenBalance(tokenAddresses[i], cache),
                    cache.totalSupply,
                    rounding
                );
            }
        }
    }

    /// @dev For each of the asset and liability tokens, calculate the proportional token
    /// amount given an amount of shares and the current total supply
    function _proportionalTokenAmountFromShares(
        uint256 shares,
        _Cache memory cache,
        OrigamiMath.Rounding assetsRounding,
        OrigamiMath.Rounding liabilitiesRounding
    ) internal view returns (
        uint256[] memory assets,
        uint256[] memory liabilities
    ) {
        (address[] memory assetAddresses, address[] memory liabilityAddresses) = tokens();
        assets = _proportionalTokenAmountFromShares(shares, cache, assetAddresses, assetsRounding);
        liabilities = _proportionalTokenAmountFromShares(shares, cache, liabilityAddresses, liabilitiesRounding);
    }

    /// @dev Convert an amount of one of the asset/liability tokens into equivalent shares, based
    /// on the balance of that token and the total supply of the vault.
    function _convertOneTokenToShares(
        _Cache memory cache,
        OrigamiMath.Rounding rounding
    ) private pure returns (uint256) {
        // An initial seed deposit is required first. 
        // In the case of a new asset added to an existing vault, a donation of tokens 
        // must be added to the vault first.
        return cache.inputTokenBalance == 0
            ? 0
            : cache.inputTokenAmount.mulDiv(cache.totalSupply, cache.inputTokenBalance, rounding);
    }

    /// @dev Convert an amount of shares into one of the asset/liability tokens, based on the
    /// balance of that token and the total supply of the vault.
    function _convertSharesToOneToken(
        uint256 shares,
        _Cache memory cache,
        OrigamiMath.Rounding rounding
    ) private pure returns (uint256) {
        // An initial seed deposit is required first. 
        // In the case of a new asset added to an existing vault, a donation of tokens 
        // must be added to the vault first.
        return cache.inputTokenBalance == 0
            ? 0
            : shares.mulDiv(cache.inputTokenBalance, cache.totalSupply, rounding);
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*       INTERNAL TOKENIZED BALANCE SHEET VAULT - UTILS       */
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/

    /// @dev Fill the internal cache.
    /// For this purpose, only the totalSupply is required
    function _fillCache() private view returns (_Cache memory cache) {
        cache.totalSupply = totalSupply();
    }

    /// @dev Fill the internal cache.
    function _fillCache(address inputTokenAddress, uint256 inputTokenAmount) private view returns (_Cache memory cache) {       
        // Must exlusively be an asset or liability
        (bool inputTokenIsAsset, bool inputTokenIsLiability) = isBalanceSheetToken(inputTokenAddress);
        
        // Must be exlusively an asset or liability
        if ((inputTokenIsAsset && !inputTokenIsLiability) || (!inputTokenIsAsset && inputTokenIsLiability)) {
            cache = _Cache(
                inputTokenAddress, 
                inputTokenIsAsset, 
                inputTokenIsLiability, 
                inputTokenAmount, 
                _tokenBalance(inputTokenAddress), 
                totalSupply()
            );

            // If there's no balance of the token then ensure the inputTokenAmount is also zero
            if (cache.inputTokenBalance == 0) cache.inputTokenAmount = 0;
        }

        // leave uninitialized
    }

    /// @dev Get the cached balance of a token in the vault. 
    /// If the tokenAddress doesn't match the asset in the cache then it is calculated
    function _getTokenBalance(address tokenAddress, _Cache memory cache) private view returns (uint256) {
        return tokenAddress == cache.inputTokenAddress
            ? cache.inputTokenBalance
            : _tokenBalance(tokenAddress);
    }

    /// @dev What is the spare capacity of shares which can be minted under the maxTotalSupply
    function _availableSharesCapacity(
        uint256 totalSupply_, 
        uint256 maxTotalSupply_
    ) internal virtual pure returns (uint256 shares) {
        if (totalSupply_ > maxTotalSupply_) {
            shares = 0;
        } else {
            unchecked {
                shares = maxTotalSupply_ - totalSupply_;
            }
        }
    }
}
IOrigamiElevatedAccess.sol 53 lines
pragma solidity ^0.8.4;
// SPDX-License-Identifier: AGPL-3.0-or-later
// Origami (interfaces/common/access/IOrigamiElevatedAccess.sol)

/**
 * @notice Inherit to add Owner roles for DAO elevated access.
 */ 
interface IOrigamiElevatedAccess {
    event ExplicitAccessSet(address indexed account, bytes4 indexed fnSelector, bool indexed value);

    event NewOwnerProposed(address indexed oldOwner, address indexed oldProposedOwner, address indexed newProposedOwner);
    event NewOwnerAccepted(address indexed oldOwner, address indexed newOwner);

    struct ExplicitAccess {
        bytes4 fnSelector;
        bool allowed;
    }

    /**
     * @notice The address of the current owner.
     */ 
    function owner() external view returns (address);

    /**
     * @notice Explicit approval for an address to execute a function.
     * allowedCaller => function selector => true/false
     */
    function explicitFunctionAccess(address contractAddr, bytes4 functionSelector) external view returns (bool);

    /**
     * @notice Revoke ownership. Be very certain before calling this, as no
     * further elevated access can be called.
     */
    function revokeOwnership() external;

    /**
     * @notice Proposes a new Owner.
     * Can only be called by the current owner
     */
    function proposeNewOwner(address account) external;

    /**
     * @notice Caller accepts the role as new Owner.
     * Can only be called by the proposed owner
     */
    function acceptOwner() external;

    /**
     * @notice Grant `allowedCaller` the rights to call the function selectors in the access list.
     * @dev fnSelector == bytes4(keccak256("fn(argType1,argType2,...)"))
     */
    function setExplicitAccess(address allowedCaller, ExplicitAccess[] calldata access) external;
}
IOrigamiTokenizedBalanceSheetVault.sol 98 lines
pragma solidity ^0.8.4;
// SPDX-License-Identifier: AGPL-3.0-or-later

import { ITokenizedBalanceSheetVault } from "contracts/interfaces/external/tokenizedBalanceSheetVault/ITokenizedBalanceSheetVault.sol";
import { IOrigamiTeleportableToken } from "contracts/interfaces/common/omnichain/IOrigamiTeleportableToken.sol";

/**
 * @title Origami Tokenized Balance Sheet Vault
 * @notice A bit more of an opinionated version for our reference implementations
 */
interface IOrigamiTokenizedBalanceSheetVault is 
    ITokenizedBalanceSheetVault,
    IOrigamiTeleportableToken
{
    //============================================================================================//
    //                                           ERRORS                                           //
    //============================================================================================//
    
    /// @dev Attempted to deposit more assets than the max amount for `receiver`.
    error ExceededMaxJoinWithToken(address receiver, address tokenAddress, uint256 tokenAmount, uint256 max);

    /// @dev Attempted to mint more shares than the max amount for `receiver`.
    error ExceededMaxJoinWithShares(address receiver, uint256 shares, uint256 max);

    /// @dev Attempted to withdraw more assets than the max amount for `receiver`.
    error ExceededMaxExitWithToken(address owner, address tokenAddress, uint256 tokenAmount, uint256 max);

    /// @dev Attempted to redeem more shares than the max amount for `receiver`.
    error ExceededMaxExitWithShares(address owner, uint256 shares, uint256 max);
    
    //============================================================================================//
    //                                           EVENTS                                           //
    //============================================================================================//

    /// @dev What kind of fees - either Join or Exit
    enum FeeType {
        JOIN_FEE,
        EXIT_FEE
    }

    /// @dev Either join or exit fees have been applied
    event InKindFees(FeeType feeType, uint256 feeBps, uint256 feeAmount);

    /// @dev Emit when the max total supply has been changed
    event MaxTotalSupplySet(uint256 maxTotalSupply);

    /// @dev May be emitted if the vault sets a TokenPrices contract (used within the UI only)
    event TokenPricesSet(address indexed tokenPrices);

    /// @dev May be emitted if the vault sets a manager contract for delegated logic
    event ManagerSet(address indexed manager);

    //============================================================================================//
    //                                          ADMIN                                             //
    //============================================================================================//

    /// @notice Elevated access seeds the vault, specifying each of the asset amounts, the liability amounts,
    /// and the shares to joinWithShares. This determines the initial share price of each asset and liability.
    function seed(
        uint256[] calldata assetAmounts,
        uint256[] calldata liabilityAmounts,
        uint256 sharesToMint,
        address receiver,
        uint256 newMaxTotalSupply
    ) external;

    /// @notice Set the max total supply allowed for this vault
    /// @dev Will revert if the current totalSupply is zero as
    /// `seedDeposit()` needs to be called first
    function setMaxTotalSupply(uint256 maxTotalSupply) external;

    /// @notice Allows the caller to burn their own supply of vault shares.
    function burn(uint256 amount) external;

    //============================================================================================//
    //                                          VIEWS                                             //
    //============================================================================================//

    /// @notice The current joinWithShares or joinWithTokens fee in basis points.
    function joinFeeBps() external view returns (uint256);

    /// @notice The current exitToShares or exitToTokens fee in basis points.
    function exitFeeBps() external view returns (uint256);

    /// @notice The maxiumum total supply of shares for this vault.
    /// @dev may be set up to type(uint256).max
    function maxTotalSupply() external view returns (uint256 shares);

    /// @notice The available shares which can be minted as of now, under the maxTotalSupply
    /// @dev If maxTotalSupply() is type(uint256).max, then this will also be type(uint256).max
    function availableSharesCapacity() external view returns (uint256 shares);

    /// @notice Whether joinWithShares and joinWithAssets are currently paused
    function areJoinsPaused() external view returns (bool);

    /// @notice Whether exitToShares and exitToAssets are currently paused
    function areExitsPaused() external view returns (bool);
}
ITokenPrices.sol 21 lines
pragma solidity ^0.8.4;
// SPDX-License-Identifier: AGPL-3.0-or-later
// Origami (interfaces/common/ITokenPrices.sol)

/// @title Token Prices
/// @notice A utility contract to pull token prices from on-chain.
/// @dev composable functions (uisng encoded function calldata) to build up price formulas
interface ITokenPrices {
    /// @notice How many decimals places are the token prices reported in
    function decimals() external view returns (uint8);

    /// @notice Retrieve the price for a given token.
    /// @dev If not mapped, or an underlying error occurs, FailedPriceLookup will be thrown.
    /// @dev 0x000...0 is the native chain token (ETH/AVAX/etc)
    function tokenPrice(address token) external view returns (uint256 price);

    /// @notice Retrieve the price for a list of tokens.
    /// @dev If any aren't mapped, or an underlying error occurs, FailedPriceLookup will be thrown.
    /// @dev Not particularly gas efficient - wouldn't recommend to use on-chain
    function tokenPrices(address[] memory tokens) external view returns (uint256[] memory prices);
}
IOrigamiTeleportableToken.sol 73 lines
pragma solidity ^0.8.4;
// SPDX-License-Identifier: AGPL-3.0-or-later
// Origami (interfaces/common/omnichain/IOrigamiTeleportableToken.sol)

import { IERC20Permit } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol";
import { IERC165 } from "@openzeppelin/contracts/interfaces/IERC165.sol";
import { IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import { SendParam, OFTReceipt } from "@layerzerolabs/lz-evm-oapp-v2/contracts/standards/oft-evm/interfaces/IOFT.sol";
import { MessagingFee, MessagingReceipt } from "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol";
import { IOFT } from "@layerzerolabs/lz-evm-oapp-v2/contracts/standards/oft-evm/interfaces/IOFT.sol";

/// @title Origami Teleportable Token
/// @notice An ERC20 token (supporting Permit) which does not require token approval to be spent
///     by the trusted teleporter. 
/// @dev There are intentionally no external mint/burn functions on this token, 
///     the teleporter is expected to be a 'locker', ie escrow the tokens.
interface IOrigamiTeleportableToken is
    IERC20Metadata,
    IERC20Permit,
    IERC165
{
    event TeleporterSet(address indexed teleporter);

    /// @notice Set the trusted address permitted to to bridge this token to another chain.
    function setTeleporter(address newTeleporter) external;

    /// @notice The trusted address permitted to to bridge this token to another chain.
    function teleporter() external view returns (IOFT);

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       LAYER ZERO SEND                      */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /**
     * @notice This function is added for improved UX convenience and for users to interact directly 
     * with the vault. The vault relays the call to the trusted token teleporter contract. 
     * @dev Executes the send operation.
     * @param sendParam The parameters for the send operation.
     * @param fee The calculated fee for the send() operation.
     *      - nativeFee: The native fee.
     *      - lzTokenFee: The lzToken fee.
     * @param refundAddress The address to receive any excess funds.
     * @return msgReceipt The receipt for the send operation.
     * @return oftReceipt The OFT receipt information.
     *
     * @dev MessagingReceipt: LayerZero msg receipt
     *  - guid: The unique identifier for the sent message.
     *  - nonce: The nonce of the sent message.
     *  - fee: The LayerZero fee incurred for the message.
     */
    function send(
        SendParam memory sendParam,
        MessagingFee memory fee,
        address refundAddress
    ) external payable returns (MessagingReceipt memory msgReceipt, OFTReceipt memory oftReceipt);

    /**
     * @notice Provides a quote for the send() operation.
     *  This function is added for improved UX convenience and for users to interact directly with the vault
     *  The vault relays the call to the trusted token teleporter contract. 
     * @param sendParam The parameters for the send() operation.
     * @param payInLzToken Flag indicating whether the caller is paying in the LZ token.
     * @return msgFee The calculated LayerZero messaging fee from the send() operation.
     *
     * @dev MessagingFee: LayerZero msg fee
     *  - nativeFee: The native fee.
     *  - lzTokenFee: The lzToken fee.
     */
    function quoteSend(
        SendParam calldata sendParam,
        bool payInLzToken
    ) external view returns (MessagingFee memory msgFee);
}
IOrigamiSwapCallback.sol 17 lines
pragma solidity ^0.8.4;
// SPDX-License-Identifier: AGPL-3.0-or-later
// Origami (interfaces/common/swappers/IOrigamiSwapCallback.sol)

/**
 * @notice Interface to support callbacks when using the OrigamiSwapperWithCallback
 * contract
 */
interface IOrigamiSwapCallback {
    /// @notice Permisionless function which is called at the conclusion of a swap, 
    /// on the msg.sender of OrigamiSwapperWithCallback.execute()
    /// @dev The swap may be synchronous or asynchronous. The buyToken proceeds from 
    /// the swap are sent to the msg.sender prior to this callback being called.
    /// The buyToken amount may not be known as part of this callback (eg cowswap hooks)
    /// so it is not passed through. The implementation should rely on their current balance instead
    function swapCallback() external;
}
IOrigamiSwapper.sol 37 lines
pragma solidity ^0.8.4;
// SPDX-License-Identifier: AGPL-3.0-or-later
// Origami (interfaces/common/swappers/IOrigamiSwapper.sol)

import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

/**
 * @notice An on chain swapper contract to integrate with the 1Inch router | 0x proxy, 
 * possibly others which obtain quote calldata offchain and then execute via a low level call
 * to perform the swap onchain
 */
interface IOrigamiSwapper {
    error UnknownSwapError(bytes result);
    error InvalidSwap();
    error InvalidRouter(address router);

    event Swap(address indexed sellToken, uint256 sellTokenAmount, address indexed buyToken, uint256 buyTokenAmount);
    event RouterWhitelisted(address indexed router, bool allowed);

    struct RouteDataWithCallback {
        address router;
        uint256 minBuyAmount;
        address receiver;
        bytes data;
    }

    /**
     * @notice Execute the swap per the instructions in `swapData`
     * @dev Implementations MAY require `sellToken` to be transferred to the swapper contract prior to execution
     */
    function execute(
        IERC20 sellToken, 
        uint256 sellTokenAmount, 
        IERC20 buyToken,
        bytes memory swapData
    ) external returns (uint256 buyTokenAmount);
}
ICoolerLtvOracle.sol 120 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

/**
 * @title Cooler LTV Oracle
 * @notice It is a custom oracle (not dependant on external markets/AMMs/dependencies) to give the
 * serve both the Origination LTV and Liquidation LTV
 *  - They are both quoted in [debtToken / collateralToken] units
 *  - It is a fixed 18dp price
 *  - Origination LTV updates on a per second basis according to a policy set rate of change (and is up only or flat)
 *  - Liquidation LTV is a policy set percentage above the Origination LTV
 */
interface ICoolerLtvOracle {
    event OriginationLtvSetAt(
        uint96 oldOriginationLtv,
        uint96 newOriginationLtvTarget,
        uint256 targetTime
    );
    event MaxOriginationLtvDeltaSet(uint256 maxDelta);
    event MinOriginationLtvTargetTimeDeltaSet(uint32 maxTargetTimeDelta);
    event MaxOriginationLtvRateOfChangeSet(uint96 maxRateOfChange);
    event MaxLiquidationLtvPremiumBpsSet(uint96 maxPremiumBps);
    event LiquidationLtvPremiumBpsSet(uint96 premiumBps);

    error BreachedMaxOriginationLtvDelta(
        uint96 oldOriginationLtv,
        uint96 newOriginationLtv,
        uint256 maxDelta
    );
    error BreachedMinDateDelta(uint40 targetTime, uint40 currentDate, uint32 maxTargetTimeDelta);
    error BreachedMaxOriginationLtvRateOfChange(uint96 targetRateOfChange, uint96 maxRateOfChange);
    error CannotDecreaseLtv();
    error InvalidParam();

    /// @notice The collateral asset of the LTV [debtToken / collateralToken]
    function collateralToken() external view returns (IERC20);

    /// @notice The debt asset of the LTV [debtToken / collateralToken]
    function debtToken() external view returns (IERC20);

    /// @notice The current Origination LTV and Liquidation LTV
    function currentLtvs() external view returns (uint96 originationLtv, uint96 liquidationLtv);

    /// @notice The current Origination LTV
    function currentOriginationLtv() external view returns (uint96);

    /// @notice The current Liquidation LTV
    function currentLiquidationLtv() external view returns (uint96);

    /// @notice The maximum allowed Origination LTV change on any single `setOriginationLtvAt()`, in absolute terms
    /// between the Origination LTV as of now and the targetOriginationLtv
    /// @dev 18 decimal places, 0.20e18 == $0.20.
    /// Used as a bound to avoid unintended/fat fingering when updating Origination LTV
    function maxOriginationLtvDelta() external view returns (uint96);

    /// @notice The minimum time delta required for Origination LTV to reach it's target value when
    /// `setOriginationLtvAt()` is called.
    /// @dev In seconds.
    /// Used as a bound to avoid unintended/fat fingering when updating Origination LTV
    function minOriginationLtvTargetTimeDelta() external view returns (uint32);

    /// @notice The maximum (positive) rate of change of Origination LTV allowed, when
    /// `setOriginationLtvAt()` is called.
    /// @dev Units: [Origination LTV / second]
    function maxOriginationLtvRateOfChange() external view returns (uint96);

    /// @notice The current Origination LTV state data
    function originationLtvData()
        external
        view
        returns (
            uint96 startingValue,
            uint40 startTime,
            uint96 targetValue,
            uint40 targetTime,
            uint96 slope
        );

    /// @notice The maximum Liquidation LTV premium (in basis points) which is allowed to be set when calling
    /// `setLiquidationLtvPremiumBps()`
    function maxLiquidationLtvPremiumBps() external view returns (uint16);

    /// @notice The premium (in basis points) of the Liquidation LTV above the Origination LTV
    function liquidationLtvPremiumBps() external view returns (uint16);

    /// @notice Set maximum Liquidation LTV premium (in basis points) which is allowed to be set when calling
    /// `setLiquidationLtvPremiumBps()`.
    function setLiquidationLtvPremiumBps(uint16 premiumBps) external;

    /// @notice Set Liquidation LTV premium (in basis points) of the Liquidation LTV above the Origination LTV
    function setMaxLiquidationLtvPremiumBps(uint16 premiumBps) external;

    /// @notice Set the maximum allowed Origination LTV change on any single `setOriginationLtvAt()`, in absolute terms
    /// between the Origination LTV as of now and the targetOriginationLtv
    /// @dev 18 decimal places, 0.20e18 == $0.20
    function setMaxOriginationLtvDelta(uint96 maxDelta) external;

    /// @notice Set the minimum time delta required for Origination LTV to reach it's target value when
    /// `setOriginationLtvAt()` is called.
    /// @dev In seconds.
    function setMinOriginationLtvTargetTimeDelta(uint32 minTargetTimeDelta) external;

    /// @notice Set the maximum (positive) rate of change of Origination LTV allowed, when
    /// `setOriginationLtvAt()` is called.
    /// @dev Units: [Origination LTV / second]
    function setMaxOriginationLtvRateOfChange(
        uint96 originationLtvDelta,
        uint32 timeDelta
    ) external;

    /// @notice Set the target Origination LTV which will incrementally increase from it's current value to `targetOriginationLtv`
    /// between now and `targetTime`.
    /// @dev targetTime is unixtime, targetOriginationLtv is 18 decimal places, 1.05e18 == $1.05
    function setOriginationLtvAt(uint96 targetOriginationLtv, uint40 targetTime) external;

    /// @notice The decimal precision of both the Origination LTV and Liquidation LTV
    function DECIMALS() external view returns (uint8);
}
IDLGTE.v1.sol 161 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

interface IDLGTEv1 {
    // =========  ERRORS ========= //

    error DLGTE_InvalidAddress();
    error DLGTE_InvalidDelegationRequests();
    error DLGTE_TooManyDelegates();
    error DLGTE_InvalidDelegateEscrow();
    error DLGTE_InvalidAmount();

    error DLGTE_ExceededUndelegatedBalance(uint256 balance, uint256 requested);
    error DLGTE_ExceededDelegatedBalance(address delegate, uint256 balance, uint256 requested);
    error DLGTE_ExceededPolicyAccountBalance(uint256 balance, uint256 requested);

    // ========= EVENTS ========= //

    event DelegationApplied(address indexed account, address indexed delegate, int256 amount);

    event MaxDelegateAddressesSet(address indexed account, uint256 maxDelegateAddresses);

    // ========= STRUCTS ======= //

    struct DelegationRequest {
        /// @dev The address of the delegate
        address delegate;
        /// @dev The amount to (un)delegate.
        /// positive means delegate, negative undelegate.
        int256 amount;
    }

    struct AccountDelegation {
        /// @dev The delegate address - the receiver account of the gOHM voting power.
        address delegate;
        /// @dev The amount of gOHM delegated to `delegate`
        uint256 amount;
        /// @dev The DelegateEscrow contract address for this `delegate`
        address escrow;
    }

    // ========= FUNCTIONS ========= //

    /**
     * @notice Set an account to have more or less than the DEFAULT_MAX_DELEGATE_ADDRESSES
     * number of delegates.
     */
    function setMaxDelegateAddresses(address account, uint32 maxDelegateAddresses) external;

    /**
     * @notice gOHM is pulled from the calling policy and added to the undelegated balance.
     * @dev
     *   - This gOHM cannot be used for governance voting until it is delegated.
     *   - Deposted gOHM balances are tracked per policy. policyA cannot withdraw gOHM that policyB deposited
     */
    function depositUndelegatedGohm(address onBehalfOf, uint256 amount) external;

    /**
     * @notice Undelegated gOHM is transferred to the calling policy.
     * @dev
     *   - If `autoRescindMaxNumDelegates` is greater than zero, the delegations will be automatically rescinded if required
     *     from up to `autoRescindMaxNumDelegates` number of delegate escrows. See `rescindDelegations()` for details
     *   - Will revert if there is still not enough undelegated gOHM for `onBehalfOf` OR
     *     if policy is attempting to withdraw more gOHM than it deposited
     *     Deposted gOHM balances are tracked per policy. policyA cannot withdraw gOHM that policyB deposited
     */
    function withdrawUndelegatedGohm(
        address onBehalfOf,
        uint256 amount,
        uint256 autoRescindMaxNumDelegates
    ) external;

    /**
     * @notice Apply a set of delegation requests on behalf of a given account.
     *  - Each delegation request either delegates or undelegates to an address
     *  - It applies across total gOHM balances for a given account across all calling policies
     *    So policyA may (un)delegate the account's gOHM set by policyA, B and C
     */
    function applyDelegations(
        address onBehalfOf,
        DelegationRequest[] calldata delegationRequests
    )
        external
        returns (uint256 totalDelegated, uint256 totalUndelegated, uint256 undelegatedBalance);

    /**
     * @notice Rescind delegations until the amount undelegated for the `onBehalfOf` account
     * is greater or equal to `requestedUndelegatedBalance`. No more than `maxNumDelegates`
     * will be rescinded as part of this
     * @dev
     *    - Delegations are rescinded by iterating through the delegate addresses for the
     *      `onBehalfOf` address.
     *    - No guarantees on the order of who is rescinded -- it may change as delegations are
     *      removed
     *    - A calling policy may be able to rescind more than it added via `depositUndelegatedGohm()`
     *      however the policy cannot then withdraw an amount higher than what it deposited.
     *    - If the full `requestedUndelegatedBalance` cannot be fulfilled the `actualUndelegatedBalance`
     *      return parameter may be less than `requestedUndelegatedBalance`. The caller must decide
     *      on how to handle that.
     */
    function rescindDelegations(
        address onBehalfOf,
        uint256 requestedUndelegatedBalance,
        uint256 maxNumDelegates
    ) external returns (uint256 totalRescinded, uint256 newUndelegatedBalance);

    /**
     * @notice Report the total delegated and undelegated gOHM balance for an account
     * in a given policy
     */
    function policyAccountBalances(
        address policy,
        address account
    ) external view returns (uint256 gOhmBalance);

    /**
     * @notice Paginated view of an account's delegations
     * @dev This can be called sequentially, increasing the `startIndex` each time by the number of items
     * returned in the previous call, until number of items returned is less than `maxItems`
     * The `totalAmount` delegated within the return struct is across all policies for that account delegate
     */
    function accountDelegationsList(
        address account,
        uint256 startIndex,
        uint256 maxItems
    ) external view returns (AccountDelegation[] memory delegations);

    /**
     * @notice A summary of an account's delegations
     */
    function accountDelegationSummary(
        address account
    )
        external
        view
        returns (
            uint256 totalGOhm,
            uint256 delegatedGOhm,
            uint256 numDelegateAddresses,
            uint256 maxAllowedDelegateAddresses
        );

    /**
     * @notice The total amount delegated to a particular delegate across all policies,
     * and externally made delegations (including any permanent donations)
     */
    function totalDelegatedTo(address delegate) external view returns (uint256);

    /**
     * @notice The maximum number of delegates an account can have accross all policies
     */
    function maxDelegateAddresses(address account) external view returns (uint32 result);

    /// @notice The gOhm token supplied by accounts
    function gOHM() external view returns (IERC20);

    /// @notice The default maximum number of addresses an account can delegate to
    function DEFAULT_MAX_DELEGATE_ADDRESSES() external view returns (uint32);
}
IMonoCooler.sol 483 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { IDLGTEv1 } from "contracts/interfaces/external/olympus/IDLGTE.v1.sol";
import { ICoolerLtvOracle } from "contracts/interfaces/external/olympus/ICoolerLtvOracle.sol";

/**
 * @title Mono Cooler
 * @notice A borrow/lend market where users can deposit their gOHM as collateral and then
 * borrow a stablecoin debt token up to a certain LTV
 *  - The debt token may change over time - eg DAI to USDS (or USDC), determined by the
 *    `CoolerTreasuryBorrower`
 *  - The collateral and debt amounts tracked on this contract are always reported in wad,
 *    ie 18 decimal places
 *  - gOHM collateral can be delegated to accounts for voting, via the DLGTE module
 *  - Positions can be liquidated if the LTV breaches the 'liquidation LTV' as determined by the
 *    `LTV Oracle`
 *  - Users may set an authorization for one other address to act on its behalf.
 */
interface IMonoCooler {
    error ExceededMaxOriginationLtv(uint256 newLtv, uint256 maxOriginationLtv);
    error ExceededCollateralBalance();
    error MinDebtNotMet(uint256 minRequired, uint256 current);
    error InvalidAddress();
    error InvalidParam();
    error ExpectedNonZero();
    error Paused();
    error CannotLiquidate();
    error InvalidDelegationRequests();
    error ExceededPreviousLtv(uint256 oldLtv, uint256 newLtv);
    error InvalidCollateralDelta();
    error ExpiredSignature(uint256 deadline);
    error InvalidNonce(uint256 deadline);
    error InvalidSigner(address signer, address owner);
    error UnauthorizedOnBehalfOf();

    event BorrowPausedSet(bool isPaused);
    event LiquidationsPausedSet(bool isPaused);
    event InterestRateSet(uint96 interestRateWad);
    event LtvOracleSet(address indexed oracle);
    event TreasuryBorrowerSet(address indexed treasuryBorrower);
    event CollateralAdded(
        address indexed caller,
        address indexed onBehalfOf,
        uint128 collateralAmount
    );
    event CollateralWithdrawn(
        address indexed caller,
        address indexed onBehalfOf,
        address indexed recipient,
        uint128 collateralAmount
    );
    event Borrow(
        address indexed caller,
        address indexed onBehalfOf,
        address indexed recipient,
        uint128 amount
    );
    event Repay(address indexed caller, address indexed onBehalfOf, uint128 repayAmount);
    event Liquidated(
        address indexed caller,
        address indexed account,
        uint128 collateralSeized,
        uint128 debtWiped,
        uint128 incentives
    );
    event AuthorizationSet(
        address indexed caller,
        address indexed account,
        address indexed authorized,
        uint96 authorizationDeadline
    );

    /// @notice The record of an individual account's collateral and debt data
    struct AccountState {
        /// @notice The amount of gOHM collateral the account has posted
        uint128 collateral;
        /**
         * @notice A checkpoint of user debt, updated after a borrow/repay/liquidation
         * @dev Debt as of now =  (
         *    `account.debtCheckpoint` *
         *    `debtTokenData.interestAccumulator` /
         *    `account.interestAccumulator`
         * )
         */
        uint128 debtCheckpoint;
        /// @notice The account's last interest accumulator checkpoint
        uint256 interestAccumulatorRay;
    }

    struct Authorization {
        /// @notice The address of the account granting authorization
        address account;
        /// @notice The address of who is authorized to act on the the accounts behalf
        address authorized;
        /// @notice The unix timestamp that the access is automatically revoked
        uint96 authorizationDeadline;
        /// @notice For replay protection
        uint256 nonce;
        /// @notice A unix timestamp for when the signature is valid until
        uint256 signatureDeadline;
    }

    struct Signature {
        uint8 v;
        bytes32 r;
        bytes32 s;
    }

    /// @notice The status for whether an account can be liquidated or not
    struct LiquidationStatus {
        /// @notice The amount [in gOHM collateral terms] of collateral which has been provided by the user
        uint128 collateral;
        /// @notice The up to date amount of debt [in debtToken terms]
        uint128 currentDebt;
        /// @notice The current LTV of this account [in debtTokens per gOHM collateral terms]
        uint128 currentLtv;
        /// @notice Has this account exceeded the liquidation LTV
        bool exceededLiquidationLtv;
        /// @notice Has this account exceeded the max origination LTV
        bool exceededMaxOriginationLtv;
        /// @notice A liquidator will receive this amount [in gOHM collateral terms] if
        /// this account is liquidated as of this block
        uint128 currentIncentive;
    }

    /// @notice An account's collateral and debt position details
    /// Provided for UX
    struct AccountPosition {
        /// @notice The amount [in gOHM collateral terms] of collateral which has been provided by the user
        /// @dev To 18 decimal places
        uint256 collateral;
        /// @notice The up to date amount of debt
        /// @dev To 18 decimal places
        uint256 currentDebt;
        /// @notice The maximum amount of debtToken's this account can borrow given the
        /// collateral posted, up to `maxOriginationLtv`
        /// @dev To 18 decimal places
        uint256 maxOriginationDebtAmount;
        /// @notice The maximum amount of debtToken's this account can accrue before being
        /// eligable to be liquidated, up to `liquidationLtv`
        /// @dev To 18 decimal places
        uint256 liquidationDebtAmount;
        /// @notice The health factor of this accounts position.
        /// Anything less than 1 can be liquidated, relative to `liquidationLtv`
        /// @dev To 18 decimal places
        uint256 healthFactor;
        /// @notice The current LTV of this account [in debtTokens per gOHM collateral terms]
        /// @dev To 18 decimal places
        uint256 currentLtv;
        /// @notice The total collateral delegated for this user across all delegates
        /// @dev To 18 decimal places
        uint256 totalDelegated;
        /// @notice The current number of addresses this account has delegated to
        uint256 numDelegateAddresses;
        /// @notice The max number of delegates this account is allowed to delegate to
        uint256 maxDelegateAddresses;
    }

    /// @notice The collateral token supplied by users/accounts, eg gOHM
    function collateralToken() external view returns (IERC20);

    /// @notice The debt token which can be borrowed, eg DAI or USDS
    function debtToken() external view returns (IERC20);

    /// @notice Unwrapped gOHM
    function ohm() external view returns (IERC20);

    /// @notice staking contract to unstake (and burn) OHM from liquidations
    function staking() external view returns (address);

    /// @notice The minimum debt a user needs to maintain
    /// @dev It costs gas to liquidate users, so we don't want dust amounts.
    /// To 18 decimal places
    function minDebtRequired() external view returns (uint256);

    /// @notice The total amount of collateral posted across all accounts.
    /// @dev To 18 decimal places
    function totalCollateral() external view returns (uint128);

    /// @notice The total amount of debt which has been borrowed across all users
    /// as of the latest checkpoint
    /// @dev To 18 decimal places
    function totalDebt() external view returns (uint128);

    /// @notice Liquidations may be paused in order for users to recover/repay debt after
    /// emergency actions or interest rate changes
    function liquidationsPaused() external view returns (bool);

    /// @notice Borrows may be paused for emergency actions or deprecating the facility
    function borrowsPaused() external view returns (bool);

    /// @notice The flat interest rate (APR).
    /// @dev Interest (approximately) continuously compounds at this rate.
    /// @dev To 18 decimal places
    function interestRateWad() external view returns (uint96);

    /// @notice The oracle serving both the Max Origination LTV and the Liquidation LTV
    function ltvOracle() external view returns (ICoolerLtvOracle);

    /// @notice The policy which borrows/repays from Treasury on behalf of Cooler
    function treasuryBorrower() external view returns (address);

    /// @notice The current Max Origination LTV and Liquidation LTV from the `ltvOracle()`
    /// @dev Both to 18 decimal places
    function loanToValues() external view returns (uint96 maxOriginationLtv, uint96 liquidationLtv);

    /// @notice The last time the global debt accumulator was updated
    function interestAccumulatorUpdatedAt() external view returns (uint40);

    /// @notice The accumulator index used to track the compounding of debt, starting at 1e27 at genesis
    /// @dev To RAY (1e27) precision
    function interestAccumulatorRay() external view returns (uint256);

    /// @notice Whether `authorized` is authorized to act on `authorizer`'s behalf for all user actions
    /// up until the `authorizationDeadline` unix timestamp.
    /// @dev Anyone is authorized to modify their own positions, regardless of this variable.
    function authorizations(
        address authorizer,
        address authorized
    ) external view returns (uint96 authorizationDeadline);

    /// @notice The `authorizer`'s current nonce. Used to prevent replay attacks with EIP-712 signatures.
    function authorizationNonces(address authorizer) external view returns (uint256);

    /// @dev Returns the domain separator used in the encoding of the signature for `setAuthorizationWithSig()`, as defined by {EIP712}.
    function DOMAIN_SEPARATOR() external view returns (bytes32);

    /// @notice Sets the authorization for `authorized` to manage `msg.sender`'s positions until `authorizationDeadline`
    /// @param authorized The authorized address.
    /// @param authorizationDeadline The unix timestamp that they the authorization is valid until.
    /// @dev Authorization can be revoked by setting the `authorizationDeadline` into the past
    function setAuthorization(address authorized, uint96 authorizationDeadline) external;

    /// @notice Sets the authorization for `authorization.authorized` to manage `authorization.authorizer`'s positions
    /// until `authorization.authorizationDeadline`.
    /// @dev Warning: Reverts if the signature has already been submitted.
    /// @dev The signature is malleable, but it has no impact on the security here.
    /// @dev The nonce is passed as argument to be able to revert with a different error message.
    /// @param authorization The `Authorization` struct.
    /// @param signature The signature.
    /// @dev Authorization can be revoked by calling `setAuthorization()` and setting the `authorizationDeadline` into the past
    function setAuthorizationWithSig(
        Authorization calldata authorization,
        Signature calldata signature
    ) external;

    /// @dev Returns whether the `sender` is authorized to manage `onBehalf`'s positions.
    function isSenderAuthorized(address sender, address onBehalf) external view returns (bool);

    //============================================================================================//
    //                                        COLLATERAL                                          //
    //============================================================================================//

    /**
     * @notice Deposit gOHM as collateral
     * @param collateralAmount The amount to deposit to 18 decimal places
     *    - MUST be greater than zero
     * @param onBehalfOf A caller can add collateral on behalf of themselves or another address.
     *    - MUST NOT be address(0)
     * @param delegationRequests The set of delegations to apply after adding collateral.
     *    - MAY be empty, meaning no delegations are applied.
     *    - MUST ONLY be requests to add delegations, and that total MUST BE less than the `collateralAmount` argument
     *    - If `onBehalfOf` does not equal the caller, the caller must be authorized via
     *      `setAuthorization()` or `setAuthorizationWithSig()`
     */
    function addCollateral(
        uint128 collateralAmount,
        address onBehalfOf,
        IDLGTEv1.DelegationRequest[] calldata delegationRequests
    ) external;

    /**
     * @notice Withdraw gOHM collateral.
     *    - Account LTV MUST be less than or equal to `maxOriginationLtv` after the withdraw is applied
     *    - At least `collateralAmount` collateral MUST be undelegated for this account.
     *      Use the `delegationRequests` to rescind enough as part of this request.
     * @param collateralAmount The amount of collateral to remove to 18 decimal places
     *    - MUST be greater than zero
     *    - If set to type(uint128).max then withdraw the max amount up to maxOriginationLtv
     * @param onBehalfOf A caller can withdraw collateral on behalf of themselves or another address if
     *      authorized via `setAuthorization()` or `setAuthorizationWithSig()`
     * @param recipient Send the gOHM collateral to a specified recipient address.
     *    - MUST NOT be address(0)
     * @param delegationRequests The set of delegations to apply before removing collateral.
     *    - MAY be empty, meaning no delegations are applied.
     *    - MUST ONLY be requests to undelegate, and that total undelegated MUST BE less than the `collateralAmount` argument
     */
    function withdrawCollateral(
        uint128 collateralAmount,
        address onBehalfOf,
        address recipient,
        IDLGTEv1.DelegationRequest[] calldata delegationRequests
    ) external returns (uint128 collateralWithdrawn);

    /**
     * @notice Apply a set of delegation requests on behalf of a given user.
     * @param delegationRequests The set of delegations to apply.
     *    - MAY be empty, meaning no delegations are applied.
     *    - Total collateral delegated as part of these requests MUST BE less than the account collateral.
     *    - MUST NOT apply delegations that results in more collateral being undelegated than
     *      the account has collateral for.
     *    - It applies across total gOHM balances for a given account across all calling policies
     *      So this may (un)delegate the account's gOHM set by another policy
     * @param onBehalfOf A caller can apply delegations on behalf of themselves or another address if
     *      authorized via `setAuthorization()` or `setAuthorizationWithSig()`
     */
    function applyDelegations(
        IDLGTEv1.DelegationRequest[] calldata delegationRequests,
        address onBehalfOf
    )
        external
        returns (uint256 totalDelegated, uint256 totalUndelegated, uint256 undelegatedBalance);

    //============================================================================================//
    //                                       BORROW/REPAY                                         //
    //============================================================================================//

    /**
     * @notice Borrow `debtToken`
     *    - Account LTV MUST be less than or equal to `maxOriginationLtv` after the borrow is applied
     *    - Total debt for this account MUST be greater than or equal to the `minDebtRequired`
     *      after the borrow is applied
     * @param borrowAmountInWad The amount of `debtToken` to borrow, to 18 decimals regardless of the debt token
     *    - MUST be greater than zero
     *    - If set to type(uint128).max then borrow the max amount up to maxOriginationLtv
     * @param onBehalfOf A caller can borrow on behalf of themselves or another address if
     *      authorized via `setAuthorization()` or `setAuthorizationWithSig()`
     * @param recipient Send the borrowed token to a specified recipient address.
     *    - MUST NOT be address(0)
     * @return amountBorrowedInWad The amount actually borrowed.
     */
    function borrow(
        uint128 borrowAmountInWad,
        address onBehalfOf,
        address recipient
    ) external returns (uint128 amountBorrowedInWad);

    /**
     * @notice Repay a portion, or all of the debt
     *    - MUST NOT be called for an account which has no debt
     *    - If the entire debt isn't paid off, then the total debt for this account
     *      MUST be greater than or equal to the `minDebtRequired` after the borrow is applied
     * @param repayAmountInWad The amount to repay, to 18 decimals regardless of the debt token
     *    - MUST be greater than zero
     *    - MAY be greater than the latest debt as of this block. In which case it will be capped
     *      to that latest debt
     * @param onBehalfOf A caller can repay the debt on behalf of themselves or another address
     * @return amountRepaidInWad The amount actually repaid.
     */
    function repay(
        uint128 repayAmountInWad,
        address onBehalfOf
    ) external returns (uint128 amountRepaidInWad);

    //============================================================================================//
    //                                       LIQUIDATIONS                                         //
    //============================================================================================//

    /**
     * @notice Liquidate one or more accounts which have exceeded the `liquidationLtv`
     * The gOHM collateral is seized (unstaked to OHM and burned), and the accounts debt is wiped.
     * @dev
     *    - If one of the provided accounts in the batch hasn't exceeded the max LTV then it is skipped.
     *    - Delegations are auto-rescinded if required. Ordering of this is not guaranteed.
     */
    function batchLiquidate(
        address[] calldata accounts
    )
        external
        returns (
            uint128 totalCollateralClaimed,
            uint128 totalDebtWiped,
            uint128 totalLiquidationIncentive
        );

    /**
     * @notice If an account becomes unhealthy and has many delegations such that liquidation can't be
     * performed in one transaction, then delegations can be rescinded over multiple transactions
     * in order to get this account into a state where it can then be liquidated.
     */
    function applyUnhealthyDelegations(
        address account,
        uint256 autoRescindMaxNumDelegates
    ) external returns (uint256 totalUndelegated, uint256 undelegatedBalance);

    //============================================================================================//
    //                                           ADMIN                                            //
    //============================================================================================//

    /// @notice Set the oracle which serves the max Origination LTV and the Liquidation LTV
    function setLtvOracle(address newOracle) external;

    /// @notice Set the policy which borrows/repays from Treasury on behalf of Cooler
    function setTreasuryBorrower(address newTreasuryBorrower) external;

    /// @notice Liquidation may be paused in order for users to recover/repay debt after emergency actions
    /// @dev Can only be called by emergency or admin roles
    function setLiquidationsPaused(bool isPaused) external;

    /// @notice Pause any new borrows of `debtToken`
    /// @dev Can only be called by emergency or admin roles
    function setBorrowPaused(bool isPaused) external;

    /// @notice Update the interest rate (APR), specified in Wad (18 decimals)
    /// @dev
    ///     - Cannot be set higher than 10% APR
    ///     - Interest (approximately) continuously compounds at this rate.
    function setInterestRateWad(uint96 newInterestRateWad) external;

    /// @notice Allow an account to have more or less than the DEFAULT_MAX_DELEGATE_ADDRESSES
    /// number of delegates.
    function setMaxDelegateAddresses(address account, uint32 maxDelegateAddresses) external;

    /// @notice Update and checkpoint the total debt up until now
    /// @dev May be useful in case there are no new user actions for some time.
    function checkpointDebt()
        external
        returns (uint128 totalDebtInWad, uint256 interestAccumulatorRay);

    //============================================================================================//
    //                                      AUX FUNCTIONS                                         //
    //============================================================================================//

    /**
     * @notice Calculate the difference in debt required in order to be at or just under
     * the maxOriginationLTV if `collateralDelta` was added/removed
     * from the current position.
     * A positive `debtDeltaInWad` means the account can borrow that amount after adding that `collateralDelta` collateral
     * A negative `debtDeltaInWad` means it needs to repay that amount in order to withdraw that `collateralDelta` collateral
     * @dev debtDeltaInWad is always to 18 decimal places
     */
    function debtDeltaForMaxOriginationLtv(
        address account,
        int128 collateralDelta
    ) external view returns (int128 debtDeltaInWad);

    /**
     * @notice An view of an accounts current and up to date position as of this block
     * @param account The account to get a position for
     */
    function accountPosition(
        address account
    ) external view returns (AccountPosition memory position);

    /**
     * @notice Compute the liquidity status for a set of accounts.
     * @dev This can be used to verify if accounts can be liquidated or not.
     * @param accounts The accounts to get the status for.
     */
    function computeLiquidity(
        address[] calldata accounts
    ) external view returns (LiquidationStatus[] memory status);

    /**
     * @notice Paginated view of an account's delegations
     * @dev Can call sequentially increasing the `startIndex` each time by the number of items returned in the previous call,
     * until number of items returned is less than `maxItems`
     */
    function accountDelegationsList(
        address account,
        uint256 startIndex,
        uint256 maxItems
    ) external view returns (IDLGTEv1.AccountDelegation[] memory delegations);

    /// @notice A view of the last checkpoint of account data (not as of this block)
    function accountState(address account) external view returns (AccountState memory);

    /// @notice An account's current collateral
    /// @dev to 18 decimal places
    function accountCollateral(address account) external view returns (uint128 collateralInWad);

    /// @notice An account's current debt as of this block
    //  @notice to 18 decimal places regardless of the debt token
    function accountDebt(address account) external view returns (uint128 debtInWad);

    /// @notice A view of the derived/internal cache data.
    function globalState()
        external
        view
        returns (uint128 totalDebt, uint256 interestAccumulatorRay);
}
ITokenizedBalanceSheetVault.sol 406 lines
pragma solidity ^0.8.4;
// SPDX-License-Identifier: AGPL-3.0-or-later

import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";

/**
 * @title Tokenized 'Balance Sheet' Vault
 * @notice
 *  Shares in this vault represent a proportional slice of the balance sheet of this vault. The balance sheet adheres to the
 *  fundamental accounting equation: equity = assets - liabilities
 *    - ASSETS represent positive value of the vault, and may be composed of zero or more ERC20 tokens.
 *      The balance of each asset token in the balance sheet may be of different amounts.
 *    - LIABILITIES represent debt that this vault owes, and may be composed of zero or more ERC20 tokens.
 *      The balance of each liability token in the balance sheet may be of different amounts.
 *
 *  When a user mints new shares (aka equity):
 *    - They PROVIDE a proportional amount of each ASSET in the balance sheet as of that moment, for that number of shares.
 *    - They RECEIVE a proportional amount of each LIABILITY in the balance sheet as of that moment, for that number of shares.
 *    - The shares representing that proportional slice of the balance sheet equity are minted.
 *
 *  When a user redeems shares:
 *    - They PROVIDE a proportional amount of each LIABILITY in the balance sheet as of that moment, for that number of shares.
 *    - They RECEIVE a proportional amount of each ASSET in the balance sheet as of that moment, for that number of shares.
 *    - Those shares are burned.
 *
 *  The ASSET or LIABILITY amounts on the balance sheet can change over time:
 *    - The balances of the ASSETS may grow or shrink over time (eg yield, rebalancing the weights of the assets)
 *    - The balances of the LIABILITIES may grow or shrink over time (eg borrow cost increasing the debt over time)
 *
 *  The ASSET or LIABILITY token addresses can change over time:
 *    - A new asset may be added to the balance sheet (rolling Pendle PT expiries)
 *    - An asset can be removed from the balance sheet (effectively zero balance - removed for efficiency)
 *
 * The interface is inspired by ERC-4626. The intended high level UX is:
 *    - joinWithShares(shares):
 *        Caller specifies the number of shares to mint and the amount of each ASSET (pulled from user) and LIABILITY (sent to user)
 *        token is derived from the existing balance weights of the vault's balance sheet.
 *    - joinWithToken(tokenAddress, amount):
 *        Caller specifies either one of the ASSET addresses (pulled from user) or one of the LIABILITY addresses (sent to user),
 *        and the amount of that token. The number of shares to mint, and the required number of the remaining ASSET (pulled from user) and
 *        LIABILITY (sent to user) tokens are derived from the existing balance weights of the vault's balance sheet.
 *    - exitWithShares(shares):
 *        Caller specifies the number of shares to burn and the amount of each ASSET (sent to user) and LIABILITY (pulled from user)
 *        token is derived from the existing balance weights of the vault's balance sheet.
 *    - exitWithToken(tokenAddress, amount):
 *        Caller specifies either one of the ASSET addresses (sent to user) or one of the LIABILITY addresses (pulled from user),
 *        and the amount of that token. The number of shares to burn, and the required number of the remaining ASSET (sent to user) and
 *        LIABILITY (pulled from user) tokens are derived from the existing balance weights of the vault's balance sheet.
 *
 * The benefits of representing the Balance Sheet tokens 'in kind' include
 *    - No oracles required to convert into a single vault asset (like ERC-4626 would require)
 *    - There is no realisation of exposure (from a vault perspective) from one asset/liability into a single vault asset at a certain point in time.
 *    - The vault is not affected if there is a lack of liquidity to convert the other assets/liabilities into a single vault asset.
 *
 * A drawback is that the UX and integration is obviously tricker.
 *    - In most cases, it's likely that there will be AMM liquidity in order for users to easily buy/sell the vault token, rather than minting redeeming
 *    - More sophisticated can mint/redeem directly providing and receiving all required balance sheet tokens.
 *      This could include arbitrage bots which can ensure the AMM liquidity is pegged to the 'real' vault price.
 *    - 'zaps' can be added where possible for dapps to allocate in to the right assets - eg to provide the best price to users (AMM buy vs direct mint)
 *    - The asset and liability tokens can change over time depending on the specific implementation. Integrators need to be aware of this.
 *
 * As with ERC-4626, there is no slippage/deadline guarantees within this interface enforcing bounds on the sent/received. If required for a particular integration,
 * it can be handled via an intermediate 'router' contract enforcing bounds on the assets/liabilities/shares which are transferred.
 */
interface ITokenizedBalanceSheetVault is IERC20, IERC20Metadata {
    
    /// @dev Emitted during a joinWithToken or joinWithShares
    event Join(
        address indexed sender,
        address indexed owner,
        uint256[] assets,
        uint256[] liabilities,
        uint256 shares
    );

    /// @dev Emitted during a exitWithToken or exitWithShares
    event Exit(
        address indexed sender,
        address indexed receiver,
        address indexed owner,
        uint256[] assets,
        uint256[] liabilities,
        uint256 shares
    );

    /**
     * @notice Returns the addresses of the underlying tokens which represent the ASSETS and LIABILITIES in the Vault's balance sheet
     * @dev
     * - MUST be ERC-20 token contracts.
     * - MUST NOT revert.
     *
     * NOTE: tokens MAY be added or removed over time.
     */
    function tokens() external view returns (address[] memory assetTokens, address[] memory liabilityTokens);

    /**
     * @notice Returns the addresses of the underlying tokens which represent the ASSETS in the Vault's balance sheet
     * @dev
     * - MUST be ERC-20 token contracts.
     * - MUST NOT revert.
     *
     * NOTE: tokens MAY be added or removed over time.
     */
    function assetTokens() external view returns (address[] memory tokens);

    /**
     * @notice Returns the addresses of the underlying tokens which represent the LIABILITIES in the Vault's balance sheet
     * @dev
     * - MUST be ERC-20 token contracts.
     * - MUST NOT revert.
     *
     * NOTE: tokens MAY be added or removed over time.
     */
    function liabilityTokens() external view returns (address[] memory tokens);

    /**
     * @notice Validates if a given token is either an asset or a liability
     */
    function isBalanceSheetToken(address tokenAddress) external view returns (bool isAsset, bool isLiability);

    /**
     * @notice Returns the total amount of the ASSETS and LIABILITIES managed by this vault.
     * @dev
     * - `totalAssets` MUST return a list which is the same size and order as `assetTokens()`
     * - `totalLiabilities` MUST return a list which is the same size and order as `liabilityTokens()`
     * - SHOULD include any compounding that occurs from yield.
     * - MUST be inclusive of any fees that are charged against assets in the Vault.
     * - MUST NOT revert.
     */
    function balanceSheet() external view returns (uint256[] memory totalAssets, uint256[] memory totalLiabilities);

    /**
     * @notice Returns the amount of shares that the Vault would exchange for the amount of `tokenAddress` provided, in an ideal
     * scenario where all the conditions are met.
     * The address and exact number of tokens of one of the `balanceSheetTokens()` is specified.
     * The remaining assets and liabilities are derived from the current balance sheet, along with the number of shares that would represent.
     *  @dev
     * - MUST NOT be inclusive of any fees that are charged against assets in the Vault.
     * - MUST NOT show any variations depending on the caller.
     * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.
     * - MUST NOT revert.
     * - If `tokenAddress` is not one of the `balanceSheetTokens()` then shares, assets and libilities MUST have zero amounts.
     *
     * NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the
     * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and
     * from.
     */
    function convertFromToken(
        address tokenAddress,
        uint256 tokenAmount
    ) external view returns (
        uint256 shares,
        uint256[] memory assets,
        uint256[] memory liabilities
    );

    /**
     * @notice Returns the amount of assets and liabilities that the Vault would exchange for the amount of shares provided, in an ideal
     * scenario where all the conditions are met.
     * The assets and liabilities are derived from the current balance sheet.
     * @dev
     * - MUST NOT be inclusive of any fees that are charged against assets in the Vault.
     * - MUST NOT show any variations depending on the caller.
     * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.
     * - MUST NOT revert.
     *
     * NOTE: this calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the
     * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and
     * from.
     */
    function convertFromShares(
        uint256 shares
    ) external view returns (
        uint256[] memory assets,
        uint256[] memory liabilities
    );

    /**
     * @notice Returns the maximum amount of `tokenAddress` that can be joined into the vault for the `receiver`,
     * through a joinWithToken call
     * `tokenAddress` must represent one of the assetTokens or liabilityTokens within `balanceSheetTokens()`
     * @dev
     * - MUST return a limited value if receiver is subject to some limit.
     * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of `tokenAddress` that may be joined.
     * - MUST NOT revert.
     * - If `tokenAddress` is not one of the `balanceSheetTokens()` then this MUST return 0.
     */
    function maxJoinWithToken(
        address tokenAddress,
        address receiver
    ) external view returns (uint256 maxTokens);

    /**
     * @notice Allows an on-chain or off-chain user to simulate the effects of their joined at the current block, given
     * current on-chain conditions.
     * The address and exact number of tokens of one of the `balanceSheetTokens()` is specified.
     * The remaining assets and liabilities are derived from the current balance sheet, along with the number of shares that would represent.
     * @dev
     * - MUST return the `shares` as close to and NO MORE than the exact amount of Vault shares
     *    that would be minted in a joinWithToken call in the same transaction (ie round down).
     * - MUST return the `assets` as close to and NO LESS than the exact amount of tokens
     *    transferred FROM the sender for a joinWithToken call in the same transaction (ie round up).
     * - MUST return the `liabilities` as close to and NO MORE than the exact amount of tokens
     *    transferred TO the receiver for a joinWithToken call in the same transaction (ie round down).
     * - MUST NOT account for join limits like those returned from maxJoinWithToken and should always act as though the
     *    join would be accepted, regardless if the user has enough tokens approved, etc.
     * - MUST be inclusive of join fees. Integrators should be aware of the existence of join fees.
     * - MUST NOT revert.
     * - If `tokenAddress` is not one of the `balanceSheetTokens()` then shares, assets and libilities MUST have zero amounts.
     */
    function previewJoinWithToken(
        address tokenAddress,
        uint256 tokenAmount
    ) external view returns (
        uint256 shares,
        uint256[] memory assets,
        uint256[] memory liabilities
    );

    /**
      * @notice Mints Vault shares to receiver by transferring an exact amount of underlying tokens proportional to the current balance sheet.
      * The address and exact number of tokens of one of the `balanceSheetTokens()` is specified.
      * The remaining assets and liabilities are derived from the current balance sheet, along with the number of shares that would represent.
      * @dev
      * - MUST emit the JoinWithToken event
      * - MUST revert if all of assets cannot be joined (due to join limit being reached, slippage, the user not
      *   approving enough underlying tokens to the Vault contract, etc).
      *
      * NOTE: most implementations will require pre-approval of the Vault with all of the asset tokens
      */
    function joinWithToken(
        address tokenAddress,
        uint256 tokenAmount,
        address receiver
    ) external returns (
        uint256 shares,
        uint256[] memory assets,
        uint256[] memory liabilities
    );

    /**
     * @notice Returns the maximum amount of the Vault shares that can be minted for the `receiver`, through a joinWithShares call.
     * @dev
     * - MUST return a limited value if receiver is subject to some mint limit.
     * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of shares that may be minted.
     * - MUST NOT revert.
     */
    function maxJoinWithShares(
        address receiver
    ) external view returns (uint256 maxShares);

    /**
     * @notice Allows an on-chain or off-chain user to simulate the effects of their joinWithShares at the current block, given
     * current on-chain conditions.
     * The assets and liabilities are derived from the current balance sheet for that number of shares.
     * @dev
     * - MUST return the `assets` as close to and NO LESS than the exact amount of tokens
     *    transferred FROM the sender for a joinWithShares call in the same transaction (ie round up).
     * - MUST return the `liabilities` as close to and NO MORE than the exact amount of tokens
     *    transferred TO the receiver for a joinWithShares call in the same transaction (ie round down).
     * - MUST NOT account for mint limits like those returned from maxJoinWithShares and should always act as though the
     *    joinWithShares would be accepted, regardless if the user has enough tokens approved, etc.
     * - MUST be inclusive of join fees. Integrators should be aware of the existence of join fees.
     * - MUST NOT revert.
     */
    function previewJoinWithShares(
        uint256 shares
    ) external view returns (
        uint256[] memory assets,
        uint256[] memory liabilities
    );

    /**
      * @notice Mints exactly `shares` Vault shares to receiver by joining amount of underlying tokens.
      * The assets and liabilities are derived from the current balance sheet for that number of shares.
      * @dev
      * - MUST emit the Join event
      * - MUST revert if all of shares cannot be minted (due to join limit being reached, slippage, the user not
      *   approving enough underlying tokens to the Vault contract, etc).
      *
      * NOTE: most implementations will require pre-approval of the Vault with all of the asset tokens
      */
    function joinWithShares(
        uint256 shares,
        address receiver
    ) external returns (
        uint256[] memory assets,
        uint256[] memory liabilities
    );

    /**
     * @notice Returns the maximum amount of `tokenAddress` that can be withdrawn from the vault given the owner balance in the vault,
     * through a exitWithToken call
     * `tokenAddress` must represent one of the assetTokens or liabilityTokens within `balanceSheetTokens()`
     * @dev
     * - MUST return a limited value if owner is subject to some exit limit or timelock.
     * - MUST NOT revert.
     * - If `tokenAddress` is not one of the `balanceSheetTokens()` then this MUST return 0.
     */
    function maxExitWithToken(
        address tokenAddress,
        address owner
    ) external view returns (uint256 maxTokens);

    /**
     * @notice Allows an on-chain or off-chain user to simulate the effects of their exit at the current block,
     * given current on-chain conditions.
     * The address and exact number of tokens of one of the `balanceSheetTokens()` is specified.
     * The remaining assets and liabilities are derived from the current balance sheet, along with the number of shares that would represent.
     * @dev
     * - MUST return the `shares` as close to and NO LESS than the exact amount of Vault shares
     *    that would be burned in a exitWithToken call in the same transaction. (ie round up)
     * - MUST return the `assets` as close to and NO MORE than the exact amount of tokens
     *    transferred TO the sender for a exitWithToken call in the same transaction (ie round down).
     * - MUST return the `liabilities` as close to and NO LESS than the exact amount of tokens
     *    transferred FROM the receiver for a exitWithToken call in the same transaction (ie round up).
     * - MUST NOT account for exit limits like those returned from maxExitWithToken and should always act as though the
     *    exit would be accepted, regardless if the user has enough tokens approved, etc.
     * - MUST be inclusive of exit fees. Integrators should be aware of the existence of exit fees.
     * - MUST NOT revert.
     * - If `tokenAddress` is not one of the `balanceSheetTokens()` then shares, assets and libilities MUST have zero amounts.
     */
    function previewExitWithToken(
        address tokenAddress,
        uint256 tokenAmount
    ) external view returns (
        uint256 shares,
        uint256[] memory assets,
        uint256[] memory liabilities
    );

    /**
      * @notice Burns shares from owner, sends exactly `tokenAddress` of underlying tokens to receiver.
      * The address and exact number of tokens of one of the `balanceSheetTokens()` is specified.
      * The remaining assets and liabilities are derived from the current balance sheet, along with the number of shares that would represent.
      * @dev
      * - MUST emit the Exit event.
      * - MUST revert if all of assets cannot be withdrawn (due to exit limit being reached, slippage, the owner
      *    not having enough shares, etc).
      *
      * NOTE: most implementations will require pre-approval of the Vault with all of the liability tokens.
      */
    function exitWithToken(
        address tokenAddress,
        uint256 tokenAmount,
        address receiver,
        address owner
    ) external returns (
        uint256 shares,
        uint256[] memory assets,
        uint256[] memory liabilities
    );

    /**
     * @notice Returns the maximum amount of Vault shares that can be redeemed from the vault given the owner balance in the vault,
     * through a exitWithShares call.
     * @dev
     * - MUST return a limited value if owner is subject to some exit limit or timelock.
     * - MUST return balanceOf(owner) if owner is not subject to any exit limit or timelock.
     * - MUST NOT revert.
     */
    function maxExitWithShares(
        address owner
    ) external view returns (uint256 maxShares);

    /**
     * @notice Allows an on-chain or off-chain user to simulate the effects of their redemption at the current block,
     * given current on-chain conditions.
     * The assets and liabilities are derived from the current balance sheet for that number of shares.
     * @dev
     * - MUST return the `assets` as close to and NO MORE than the exact amount of tokens
     *    transferred TO the sender for a exitWithShares call in the same transaction (ie round down).
     * - MUST return the `liabilities` as close to and NO LESS than the exact amount of tokens
     *    transferred FROM the receiver for a exitWithShares call in the same transaction (ie round up).
     * - MUST NOT account for redemption limits like those returned from maxExitWithShares and should always act as though the
     *    redemption would be accepted, regardless if the user has enough tokens approved, etc.
     * - MUST be inclusive of exit fees. Integrators should be aware of the existence of exit fees.
     * - MUST NOT revert.
     */
    function previewExitWithShares(
        uint256 shares
    ) external view returns (
        uint256[] memory assets,
        uint256[] memory liabilities
    );

    /**
      * @notice Burns shares from owner.
      * The assets and liabilities are derived from the current balance sheet for that number of shares.
      * @dev
      * - MUST emit the Exit event.
      * - MUST revert if all of assets cannot be redeemed (due to exit limit being reached, slippage, the owner
      *    not having enough shares, etc).
      *
      * NOTE: most implementations will require pre-approval of the Vault with all of the liability tokens.
      */
    function exitWithShares(
        uint256 shares,
        address receiver,
        address owner
    ) external returns (
        uint256[] memory assets,
        uint256[] memory liabilities
    );
}
IOrigamiHOhmManager.sol 427 lines
pragma solidity ^0.8.4;
// SPDX-License-Identifier: AGPL-3.0-or-later
// Origami (interfaces/olympus/IOrigamiHOhmManager.sol)

import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { IERC4626 } from "@openzeppelin/contracts/interfaces/IERC4626.sol";
import { IERC165 } from "@openzeppelin/contracts/interfaces/IERC165.sol";

import { IMonoCooler } from "contracts/interfaces/external/olympus/IMonoCooler.sol";
import { IOrigamiSwapper } from "contracts/interfaces/common/swappers/IOrigamiSwapper.sol";
import { IOrigamiSwapCallback } from "contracts/interfaces/common/swappers/IOrigamiSwapCallback.sol";

/**
 * @title Origami lovOHM Manager
 * @notice Handles adding and removing collateral and max borrowing in Olympus' MonoCooler
 *
 * @dev
 *   - There will be a surplus `debtToken` amount held by this contract which can expand and contract
 *     on each join and exit. 
 *   - This surplus is excluded from the balance sheet totals used to calculate the `debtToken per hOHM` 
 *     share price.
 *   - Under normal circumstances it will grow on aggregate:
 *     a/ The origination LTV within cooler increases every second to some set gradient. 
 *        This increases the capacity to borrow
 *     b/ The cooler interest rate is flat (0.5% APY as of writing). 
 *        This decreases the capacity to borrow
 *     c/ Any surplus is added into a savings vault (eg sUSDS) which has a higher interest rate than (b). 
 *        The surplus in debtToken terms (eg USDS) increases faster than the cooler debt.
 *     It is expected that (a)+(c) will outpace (b)
 *   - sweep() can be called in order to use the surplus `debtToken` to buy hOHM from the open market
 *     and then burn the hOHM. 
 *     When this happens the totalSupply decreases, which increases the share price of both the collateral and 
 *     debt tokens per hOHM
 */
interface IOrigamiHOhmManager is IOrigamiSwapCallback, IERC165 {
    event CoolerBorrowsDisabledSet(bool value);
    event SwapperSet(address indexed newSwapper);
    event SweepParamsSet(uint40 newSweepCooldownSecs, uint96 newMaxSweepSellAmount);
    event DebtTokenSet(address indexed debtToken, address indexed savingsVault);
    event PerformanceFeeSet(uint256 fee);
    event FeeCollectorSet(address indexed feeCollector);
    event ExitFeeBpsSet(uint256 feeBps);

    /// @notice The swap was initiated to sell `debtTokenAmount` of surplus debtTokens into hOHM
    event SweepStarted(address indexed debtToken, uint256 debtTokenAmount);

    /// @notice The swap was finalized. An amount was burned, and an amount was sent to the `feeCollector`
    /// @dev The SweepStarted and SweepFinished events may be executed across multiple transactions.
    event SweepFinished(uint256 hohmBurned, uint256 feeAmount);

    /// @notice A join has been performed by adding the `collateralAmount` as collateral into cooler
    /// and paying `receiver` the `debtAmount` of debtToken's. 
    /// `collateralAmount` and `debtAmount` are always in the tokens native decimals.
    /// `coolerDebtDeltaInWad` is how much debt was borrowed (positive value) or repaid (negative value) in
    ///  cooler in order to get to the max origination LTV (always to 18 decimals regardless of the debt token)
    event Join(uint256 collateralAmount, uint256 debtAmount, address receiver, int256 coolerDebtDeltaInWad);

    /// @notice An exit has been performed by repaying debt and withdrawing the `collateralAmount` from cooler
    /// and sending to the `receiver`
    /// `collateralAmount` and `debtAmount` are always in the tokens native decimals.
    /// `coolerDebtDeltaInWad` is how much debt was borrowed (positive value) or repaid (negative value) in
    ///  cooler in order to get to the max origination LTV (always to 18 decimals regardless of the debt token)
    event Exit(uint256 collateralAmount, uint256 debtAmount, address receiver, int256 coolerDebtDeltaInWad);

    event DelegationApplied(address indexed account, address indexed delegate, int256 amount);

    error IsNotPaused();
    error BeforeCooldownEnd();
    error SweepTooLarge();

    /**
     * @notice Set the exit fees which are taken in kind. This benefits existing
     * vault owners, they do not go to Origami Treasury.
     * @dev Fees cannot increase
     * Represented in basis points
     */
    function setExitFees(uint16 newFeeBps) external;

    /**
     * @notice Set whether cooler borrows are currently disabled
     */
    function setCoolerBorrowsDisabled(bool value) external;

    /**
     * @notice Set the sweep cooldown seconds and max debt token sell amount
     * for each call
     */
    function setSweepParams(
        uint40 newSweepCooldownSecs,
        uint96 newMaxSweepSellAmount
    ) external;

    /**
     * @notice Set the swapper contract responsible for swapping 
     * `debtToken` to lovOHM
     */
    function setSweepSwapper(address newSwapper) external;

    /**
     * @notice Set the performance fees for Origami
     * Represented in basis points
     */
    function setPerformanceFeesBps(uint16 newFeeBps) external;

    /**
     * @notice Set the Origami performance fee collector address
     */
    function setFeeCollector(address newFeeCollector) external;

    /**
     * @notice If the Olympus Cooler debtToken has changed (eg USDS => USDC), then this will
     * need to be called. 
     * @dev Full fork testing is expected to be done first given it is a somewhat manual process.
     *  - The vault is required to be paused first
     *  - Any surplus 'old' debt token will need to be sold into the 'new' debt token
     *    The old debt token & savings vault token can be recovered in order to do this, 
     *    once this function has been called.
     *  - `setSweepParams()` will need to be called especially if the new debt token has 
     *    different decimal places
     *  - Once set, the hOHM vault will need to have `setManager()` called to refresh it's
     *    cached debtToken value
     *  - Only once all updated should the vault be unpaused.
     * @param savingsVault The debtToken can optionally be deposited IOrigamiHOhmManagerinto an ERC4626 vault (eg sUSDS)
     *                     in order to have passive yield. If not required, can be set to address(0)
     */
    function setDebtTokenFromCooler(address savingsVault) external;
    
    /**
     * @notice Deposit/Withdraw from the savings vault such that the debt token amount in this contract
     * is a certain balance.
     * @dev The amount actually withdrawn from savings will be capped to the max amount possible.
     * `requiredDebtTokenBalance` is expected to be in the decimal places of `debtToken`
     */
    function syncDebtTokenSavings(uint256 requiredDebtTokenBalance) external;

    /**
     * @notice Sweeping will sell a number of surplus debtToken's for the hOHM vault token
     *   and then burns the resulting hOHM vault token.
     * @dev 
     *   - When the hOHM tokens are burned, the share price of both the collateral and debt tokens
     *     will increase.
     *   - sweep's are limited in the amount per call and also how frequently it can be called
     *   - swapData is the encoded bytes of the swapper (if required for that implementation). It's
     *     up to the swapper to perform slippage checks and also to burn the purchased hOHM
     * `amount` is expected to be in the decimal places of `debtToken`
     */
    function sweep(
        uint256 amount,
        bytes memory swapData
    ) external;

    /**
     * @notice Add equity by depositing `collateralAmount` collateral and borrowing `debtAmount` from Cooler.
     * Receiver receives the `debtAmount`
     * @param collateralAmount The amount of collateral to deposit, in its native decimals places
     * @param debtAmount the amount of debt to send to the receiver, in its native decimals places
     * @param receiver The address to receive the `debtAmount` of `debtToken`
     * @param receiverSharesPostMint The number of shares `receiver` will have including after the effect of
              this vault join
     * @param totalSupplyPostMint The vault total supply including after this vault join
     */
    function join(   
        uint256 collateralAmount,
        uint256 debtAmount,
        address receiver,
        uint256 receiverSharesPostMint,
        uint256 totalSupplyPostMint
    ) external;

    /**
     * @notice Remove equity by repaying `debtAmount` and removing `collateralAmount` collateral from Cooler.
     * Receiver receives the gOHM collateral
     * @param collateralAmount The amount of collateral to send to the receiver, in its native decimals places
     * @param debtAmount the amount of debt to repay, in its native decimals places
     * @param sharesOwner the owner of the shares who is exiting
     * @param receiver The address to receive the `collateralAmount` of `collateralToken`
     * @param ownerSharesPostBurn The number of shares `sharesOwner` will have including after the effect of
              this vault exit
     * @param totalSupplyPostBurn The vault total supply including after this vault exit
     */
    function exit(
        uint256 collateralAmount,
        uint256 debtAmount,
        address sharesOwner,
        address receiver,
        uint256 ownerSharesPostBurn,
        uint256 totalSupplyPostBurn
    ) external;

    /**
     * @notice Update the gOHM delegate address and amount for a particular account.
     * The new gOHM amount is based on the latest gOHM collateral this contract has in cooler 
     * and the accounts share proportion of the totalSupply.
     * @dev 
     *  - `account` cannot be address(0) - this will revert
     *  - `newDelegateAddress` may be address(0), meaning that gOHM collateral will become
     *    undelegated.
     *  - `newDelegateAddress` may remain the same as the existing one, meaning just the amount
     *    is updated
     *  - `accountShares` may be zero, meaning that any existing gOHM collateral is undelegated.
     *    Future calls to `updateAmounts()` for this account will still delegate to `newDelegateAddress`
     */
    function updateDelegateAndAmount(
        address account, 
        uint256 accountShares, 
        uint256 totalSupply, 
        address newDelegateAddress
    ) external;

    /**
     * @notice Update the gOHM delegation amount for one account, using the 
     * existing delegate address (if set).
     * The new gOHM amount is based on the latest gOHM collateral this contract has in cooler 
     * and the accounts share proportion of the totalSupply.
     * @dev 
     *  - `account` cannot be address(0) - this will revert
     *  - The existing delegate address for the account may be address(0) in which case
     *    no change is made - the gOHM remains undelegated
     *  - `accountShares` may be zero, meaning that any existing gOHM collateral is undelegated.
     *    Future calls to `setDelegationAmount1()` or `setDelegationAmount2()` for this account 
     *    will still delegate to their existing delegate
     */
    function setDelegationAmount1(
        address account,
        uint256 accountShares,
        uint256 totalSupply
    ) external;

    /**
     * @notice Update the gOHM delegation amounts for two accounts, using the 
     * existing delegate address for that account (if set).
     * The new gOHM amount for that account is based on the latest gOHM collateral this contract has in cooler 
     * and the accounts share proportion of the totalSupply.
     * @dev 
     *  - `account1` cannot be the same as `account2` - this will revert.
     *  - `account1` or `account2` cannot be address(0) - this will revert.
     *  - The existing delegate address for the account may be address(0) in which case
     *    no change is made - the gOHM remains undelegated
     *  - `accountShares1` or `accountShares2` may be zero, meaning that any existing gOHM collateral is undelegated.
     *    Future calls to `setDelegationAmount1()` or `setDelegationAmount2()` for that account 
     *    will still delegate to their existing delegate
     */
    function setDelegationAmount2(
        address account1,
        uint256 account1Shares,
        address account2,
        uint256 account2Shares,
        uint256 totalSupply
    ) external;

    /**
     * @notice Synchronise the the LTV such that it equals (or is just under) the max origination LTV in cooler
     * @dev Provided in case there have been no new joins/exits for some time (which auto max-borrow)
     *   - If the current LTV is greater than the cooler origination LTV, this will repay (using surplus debtToken)
     *   - If the current LTV is less than the cooler origination LTV, this will borrow (increasing the surplus)
     * Will revert if `coolerBorrowsDisabled` is true and a cooler borrow is attempted.
     * A positive `coolerDebtDeltaInWad` means that amount has been borrowed and added to surplus
     * A negative `coolerDebtDeltaInWad` means that amount has been repaid from the surplus
     */
    function maxBorrowFromCooler() external returns (int128 coolerDebtDeltaInWad);

    /**
     * @notice The Origami vault this is managing
     */
    function vault() external view returns (address);

    /**
     * @notice Whether joinWithShares and joinWithAssets are currently paused
     */
    function areJoinsPaused() external view returns (bool);

    /**
     * @notice Whether exitToShares and exitToAssets are currently paused
     */
    function areExitsPaused() external view returns (bool);

    /**
     * @notice The Olympus Cooler contract.
     */
    function cooler() external view returns (IMonoCooler);

    /**
     * @notice The Olympus Governance token (gOHM)
     */
    function collateralToken() external view returns (IERC20);

    /**
     * @notice The current debt token (eg USDS)
     * @dev must have decimal places <= 18
     */
    function debtToken() external view returns (IERC20);

    /**
     * @notice The multiplier to convert `debtToken` into WAD
     */
    function debtTokenDecimalsToWadScalar() external view returns (uint96);

    /**
     * @notice A ERC4626 vault for `debtToken` savings.
     */
    function debtTokenSavingsVault() external view returns (IERC4626);

    /**
     * @notice The amount of shares which are burned prior to redeeming for underlying assets.
     * This benefits existing vault owners, they do not go to Origami Treasury.
     * @dev Fees cannot increase
     * Represented in basis points
     */
    function exitFeeBps() external view returns (uint16 feeBps);

    /**
     * @notice Cooler borrows may need to be disabled in order to allow
     * exits (and cooler repayments)
     */
    function coolerBorrowsDisabled() external view returns (bool);

    /**
     * @notice Set the swapper contract responsible for swapping 
     * `debtToken` to lovOHM
     */
    function sweepSwapper() external view returns (IOrigamiSwapper);

    /**
     * @notice Sweeping can only be performed once every `sweepCooldownSecs`
     */
    function sweepCooldownSecs() external view returns (uint40);

    /**
     * @notice The last time sweep was successfully called
     */
    function lastSweepTime() external view returns (uint40);

    /**
     * @notice The maximum amount of debtToken which can be sold in each sweep
     * In the decimal places of `debtToken`
     */
    function maxSweepSellAmount() external view returns (uint96);

    /**
     * @notice The address used to collect the Origami performance fees
     */
    function feeCollector() external view returns (address);

    /**
     * @notice The performance fee to Origami treasury
     * Represented in basis points
     */
    function performanceFeeBps() external view returns (uint16);

    /**
     * @notice The maximum exit fee in basis points: 3.3%
     */
    function MAX_EXIT_FEE_BPS() external view returns (uint16 feeBps);

    /**
     * @notice The maximum performance fee that Origami can take when calling sweep()
     */
    function MAX_PERFORMANCE_FEE_BPS() external view returns (uint16);

    /**
     * @notice The minimum amount of gOHM collateral required in order to delegate
     * @dev 
     *    - If the account's proportional gOHM falls below this on a transfer/exit then 
     *      the delegation will be rescinded in Cooler
     *    - If the account sets a delegation and the gOHM collateral is less than this threshold
     *      the delegation won't apply
     *    - If the account has set a delegation and then the gOHm collateral balance increases over
     *      the threshold, the delegation will be applied on the next sync
     */
    function MIN_DELEGATION_AMOUNT() external view returns (uint256);

    /**
     * @notice The current delegate address and gOHM collateral 
     * amount delegated for an account
     */
    function delegations(address account) external view returns (address delegateAddress, uint256 amount);

    /**
     * @notice The net balance of `debtToken` used for the `debtToken per hOHM` share price
     * @dev Defined as the current cooler debt minus any surplus debtToken held for future buybacks
     * In the decimal places of `debtToken`
     */
    function debtTokenBalance() external view returns (uint256);

    /**
     * @notice The net balance of `collateralToken` used for the `collateralToken per hOHM` share price
     * In the decimal places of `collateralToken`
     */
    function collateralTokenBalance() external view returns (uint256);

    /**
     * @notice Convert a number of shares to the proportional amount of gOHM collateral tokens
     * @dev Will revert if `shares` is greater than `totalSupply`
     * shares, totalSupply and collateral are in 18 decimal places
     */
    function convertSharesToCollateral(uint256 shares, uint256 totalSupply) external view returns (uint256);

    /**
     * @notice Given an account and their shares, calculate the proportional amount of gOHM collateral
     * that account is eligable to delegate, and their current delegate and delegated amount
     */
    function accountDelegationBalances(
        address account,
        uint256 shares,
        uint256 totalSupply
    ) external view returns (
        uint256 totalCollateral,
        address delegateAddress,
        uint256 delegatedCollateral
    );

    /**
     * @notice The surplus amount of surplusDebtTokenAmount (in `debtToken` liability terms)
     * @dev This includes:
     *   - debtToken held in this contract
     *   - debtTokenSavingsVault (in debtToken terms) held in this contract
     *   - debtToken held in this the `sweepSwapper` (waiting to be swapped)
     * In the decimal places of `debtToken`
     */
    function surplusDebtTokenAmount() external view returns (uint256);

    /**
     * @notice The current balance of debt (including interest) in OHM Cooler (in `debtToken` liability terms)
     * @dev To 18 decimal places (WAD) regardless of the debt token
     */
    function coolerDebtInWad() external view returns (uint128);
}
IOrigamiHOhmVault.sol 81 lines
pragma solidity ^0.8.4;
// SPDX-License-Identifier: AGPL-3.0-or-later
// Origami (interfaces/olympus/IOrigamiHOhmVault.sol)

import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { IOrigamiTokenizedBalanceSheetVault } from "contracts/interfaces/common/IOrigamiTokenizedBalanceSheetVault.sol";
import { ITokenPrices } from "contracts/interfaces/common/ITokenPrices.sol";

interface IOrigamiHOhmVault is IOrigamiTokenizedBalanceSheetVault {
    event DebtTokenSet(address indexed debtToken);

    /**
     * @notice Set the helper to calculate current off-chain/subgraph integration
     */
    function setTokenPrices(address tokenPrices) external;

    /**
     * @notice Set the Origami delegated manager 
     */
    function setManager(address manager) external;

    /**
     * @notice Change gOHM voting power delegation for the msg.sender
     *  - An account can delegate their (proportionally owned) gOHM balance to one address.
     *  - If `to` is address(0), then this undelegates the entire balance.
     *  - An account's 'proportionally owned' gOHM balance is the total vault gOHM collateral scaled
     *    by the number of shares they own proportional to the total supply.
     *  - If account exits or transfers their vault shares to another address (and they have an 
     *    existing delegation), that proportional amount of gOHM is automatically removed 
     *    from their delegated balance.
     *  - If account joins or vault shares are transferred into their address (and they have an 
     *    existing delegation), then that new proportional amount of gOHM is automatically 
     *    delegated to the same address.
     */
    function delegateVotingPower(address to) external;

    /**
     * @notice Sync the delegation amount for any account based on
     * their proportional gOHM balance, and to that account's existing delegate
     * nomination.
     * @dev Provided in case gOHM balances of the vault increase.
     */
    function syncDelegation(address account) external;

    /**
     * @dev Receives and executes a batch of function calls on this contract.
     */
    function multicall(bytes[] calldata data) external returns (bytes[] memory results);

    /**
     * @notice The Olympus Governance token (gOHM)
     */
    function collateralToken() external view returns (IERC20);

    /**
     * @notice The cooler debt token
     */
    function debtToken() external view returns (IERC20);

    /**
     * @notice The helper contract to retrieve Origami USD prices
     * @dev Required for off-chain/subgraph integration
     */
    function tokenPrices() external view returns (ITokenPrices);

    /**
     * @notice The Origami contract managing the application of
     * the deposit tokens into the underlying protocol
     */
    function manager() external view returns (address);
    
    /**
     * @notice Given an account, calculate the proportional amount of gOHM collateral
     * that account is eligable to delegate, and their current delegate and delegated amount
     */
    function accountDelegationBalances(address account) external view returns (
        uint256 totalCollateral,
        address delegateAddress,
        uint256 delegatedCollateral
    );
}
IOrigamiManagerPausable.sol 30 lines
pragma solidity ^0.8.4;
// SPDX-License-Identifier: AGPL-3.0-or-later
// Origami (interfaces/investments/util/IOrigamiManagerPausable.sol)

/**
 * @title A mixin to add pause/unpause for Origami manager contracts
 */
interface IOrigamiManagerPausable {
    struct Paused {
        bool investmentsPaused;
        bool exitsPaused;
    }

    event PauserSet(address indexed account, bool canPause);
    event PausedSet(Paused paused);

    /// @notice A set of accounts which are allowed to pause deposits/withdrawals immediately
    /// under emergency
    function pausers(address) external view returns (bool);

    /// @notice Pause/unpause deposits or withdrawals
    /// @dev Can only be called by allowed pausers or governance.
    function setPaused(Paused memory updatedPaused) external;

    /// @notice Allow/Deny an account to pause/unpause deposits or withdrawals
    function setPauser(address account, bool canPause) external;

    /// @notice Check if given account can pause investments/exits
    function isPauser(address account) external view returns (bool canPause);
}
OrigamiHOhmManager.sol 671 lines
pragma solidity ^0.8.19;
// SPDX-License-Identifier: AGPL-3.0-or-later
// Origami (investments/olympus/OrigamiHOhmManager.sol)

import { IERC165 } from "@openzeppelin/contracts/interfaces/IERC165.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import { IERC4626 } from "@openzeppelin/contracts/interfaces/IERC4626.sol";

import { IOrigamiSwapCallback } from "contracts/interfaces/common/swappers/IOrigamiSwapCallback.sol";
import { IOrigamiSwapper } from "contracts/interfaces/common/swappers/IOrigamiSwapper.sol";
import { IOrigamiHOhmManager } from "contracts/interfaces/investments/olympus/IOrigamiHOhmManager.sol";
import { IMonoCooler } from "contracts/interfaces/external/olympus/IMonoCooler.sol";
import { IDLGTEv1 } from "contracts/interfaces/external/olympus/IDLGTE.v1.sol";

import { OrigamiHOhmVault } from "contracts/investments/olympus/OrigamiHOhmVault.sol";
import { CommonEventsAndErrors } from "contracts/libraries/CommonEventsAndErrors.sol";
import { OrigamiElevatedAccess } from "contracts/common/access/OrigamiElevatedAccess.sol";
import { OrigamiMath } from "contracts/libraries/OrigamiMath.sol";
import { OrigamiManagerPausable } from "contracts/investments/util/OrigamiManagerPausable.sol";
import { SafeCast } from "contracts/libraries/SafeCast.sol";
import { OlympusCoolerDelegation } from "contracts/libraries/OlympusCoolerDelegation.sol";

/**
 * @title Origami lovOHM Manager
 * @notice Handles adding and removing collateral and max borrowing in Olympus' MonoCooler
 *
 * @dev
 *   - There will be a surplus `debtToken` amount held by this contract which can expand and contract
 *     on each join and exit. 
 *   - This surplus is excluded from the balance sheet totals used to calculate the `debtToken per hOHM` 
 *     share price.
 *   - Under normal circumstances it will grow on aggregate:
 *     a/ The origination LTV within cooler increases every second to some set gradient. 
 *        This increases the capacity to borrow
 *     b/ The cooler interest rate is flat (0.5% APY as of writing). 
 *        This decreases the capacity to borrow
 *     c/ Any surplus is added into a savings vault (eg sUSDS) which has a higher interest rate than (b). 
 *        The surplus in debtToken terms (eg USDS) increases faster than the cooler debt.
 *     It is expected that (a)+(c) will outpace (b)
 *   - sweep() can be called in order to use the surplus `debtToken` to buy hOHM from the open market
 *     and then burn the hOHM. 
 *     When this happens the totalSupply decreases, which increases the share price of both the collateral and 
 *     debt tokens per hOHM
 */
contract OrigamiHOhmManager is 
    IOrigamiHOhmManager,
    OrigamiElevatedAccess,
    OrigamiManagerPausable
{
    using SafeERC20 for IERC20;
    using SafeERC20 for OrigamiHOhmVault;
    using SafeCast for uint256;
    using OrigamiMath for uint256;
    using OlympusCoolerDelegation for OlympusCoolerDelegation.Data;

    /// @dev The lovOhm vault
    OrigamiHOhmVault private immutable _vault;

    /// @inheritdoc IOrigamiHOhmManager
    IMonoCooler public immutable override cooler;

    /// @inheritdoc IOrigamiHOhmManager
    IERC20 public immutable override collateralToken;

    /// @inheritdoc IOrigamiHOhmManager
    IERC20 public override debtToken;

    /// @inheritdoc IOrigamiHOhmManager
    uint96 public override debtTokenDecimalsToWadScalar;

    /// @inheritdoc IOrigamiHOhmManager
    IERC4626 public override debtTokenSavingsVault;

    /// @inheritdoc IOrigamiHOhmManager
    uint16 public override exitFeeBps;

    /// @inheritdoc IOrigamiHOhmManager
    bool public override coolerBorrowsDisabled;

    /// @inheritdoc IOrigamiHOhmManager
    IOrigamiSwapper public override sweepSwapper;

    /// @inheritdoc IOrigamiHOhmManager
    uint96 public override maxSweepSellAmount;

    /// @inheritdoc IOrigamiHOhmManager
    uint40 public override sweepCooldownSecs;

    /// @inheritdoc IOrigamiHOhmManager
    uint40 public override lastSweepTime;

    /// @inheritdoc IOrigamiHOhmManager
    uint16 public override performanceFeeBps;

    /// @inheritdoc IOrigamiHOhmManager
    address public override feeCollector;

    /// @inheritdoc IOrigamiHOhmManager
    mapping(address account => OlympusCoolerDelegation.Data delegation) public override delegations;

    /// @inheritdoc IOrigamiHOhmManager
    uint16 public override constant MAX_EXIT_FEE_BPS = 330; // 3.3%

    /// @inheritdoc IOrigamiHOhmManager
    uint16 public constant override MAX_PERFORMANCE_FEE_BPS = 330; // 3.3%

    /// @inheritdoc IOrigamiHOhmManager
    uint256 public constant override MIN_DELEGATION_AMOUNT = 0.1e18; // gOHM collateral per account

    constructor(
        address initialOwner_,
        address vault_,
        address cooler_,
        address debtTokenSavingsVault_,
        uint16 performanceFeeBps_,
        address feeCollector_
    ) 
        OrigamiElevatedAccess(initialOwner_)
    {
        _vault = OrigamiHOhmVault(vault_);
        collateralToken = IERC20(_vault.collateralToken());
        cooler = IMonoCooler(cooler_);

        // Max approve the collateral token to cooler (to add collateral)
        collateralToken.safeApprove(address(cooler), type(uint256).max);

        // Set the state and max approvals for the cooler debtToken and savings (if set)
        _setDebtTokenAndSavings(cooler.debtToken(), IERC4626(debtTokenSavingsVault_));

        if (performanceFeeBps_ > MAX_PERFORMANCE_FEE_BPS) revert CommonEventsAndErrors.InvalidParam();
        performanceFeeBps = performanceFeeBps_;
        feeCollector = feeCollector_;
    }

    /// @inheritdoc IOrigamiHOhmManager
    function setExitFees(uint16 newFeeBps) external override onlyElevatedAccess {
        if (newFeeBps > MAX_EXIT_FEE_BPS) revert CommonEventsAndErrors.InvalidParam();

        emit ExitFeeBpsSet(newFeeBps);
        exitFeeBps = newFeeBps;
    }

    /// @inheritdoc IOrigamiHOhmManager
    function setCoolerBorrowsDisabled(bool value) external override onlyElevatedAccess {
        coolerBorrowsDisabled = value;
        emit CoolerBorrowsDisabledSet(value);
    }

    /// @inheritdoc IOrigamiHOhmManager
    function setSweepParams(
        uint40 newSweepCooldownSecs,
        uint96 newMaxSweepSellAmount
    ) external override onlyElevatedAccess {
        sweepCooldownSecs = newSweepCooldownSecs;
        maxSweepSellAmount = newMaxSweepSellAmount;
        emit SweepParamsSet(newSweepCooldownSecs, newMaxSweepSellAmount);
    }
    
    /// @inheritdoc IOrigamiHOhmManager
    function setSweepSwapper(address newSwapper) external override onlyElevatedAccess {
        if (newSwapper == address(0)) revert CommonEventsAndErrors.InvalidAddress(newSwapper);

        emit SwapperSet(newSwapper);
        sweepSwapper = IOrigamiSwapper(newSwapper);
    }

    /// @inheritdoc IOrigamiHOhmManager
    function setPerformanceFeesBps(uint16 newFeeBps) external override onlyElevatedAccess {
        /// @dev Cannot be raised higher than MAX_PERFORMANCE_FEE_BPS
        if (newFeeBps > MAX_PERFORMANCE_FEE_BPS) revert CommonEventsAndErrors.InvalidParam();

        emit PerformanceFeeSet(newFeeBps);
        performanceFeeBps = newFeeBps;
    }

    /// @inheritdoc IOrigamiHOhmManager
    function setFeeCollector(address newFeeCollector) external override onlyElevatedAccess {
        if (newFeeCollector == address(0)) revert CommonEventsAndErrors.InvalidAddress(address(0));
        emit FeeCollectorSet(newFeeCollector);
        feeCollector = newFeeCollector;
    }

    /// @inheritdoc IOrigamiHOhmManager
    function setDebtTokenFromCooler(address newDebtTokenSavingsVault) external override onlyElevatedAccess {
        // Ensure the vault is paused first -- see the natspec for considerations.
        if (!_paused.investmentsPaused || !_paused.exitsPaused) revert IsNotPaused();

        // Remove approvals for the old debtToken and savings vault
        IERC20 _oldDebtToken = debtToken;
        address _oldDebtTokenSavingsVault = address(debtTokenSavingsVault);
        _oldDebtToken.safeApprove(address(cooler), 0);
        if (address(_oldDebtTokenSavingsVault) != address(0)) {
            _oldDebtToken.safeApprove(_oldDebtTokenSavingsVault, 0);
        }
    
        // Set the state and approvals for the new debt token and savings
        IERC20 _newDebtToken = cooler.debtToken();
        IERC4626 _newSavingsVault = IERC4626(newDebtTokenSavingsVault);
        _setDebtTokenAndSavings(_newDebtToken, _newSavingsVault);

        emit DebtTokenSet(address(_newDebtToken), address(_newSavingsVault));
    }

    /**
     * @notice Recover tokens accidentally sent here
     * @param token Token to recover
     * @param to Recipient address
     * @param amount Amount to recover
     */
    function recoverToken(address token, address to, uint256 amount) external onlyElevatedAccess {
        // Collateral is added/removed from Cooler just in time. Any other collateral sent here by mistake can be recovered.
        // The current debt token surplus cannot be recovered, it can only be transferred by using `sweep()`
        if (token == address(debtToken) || token == address(debtTokenSavingsVault)) revert CommonEventsAndErrors.InvalidToken(token);

        emit CommonEventsAndErrors.TokenRecovered(to, token, amount);
        IERC20(token).safeTransfer(to, amount);
    }

    /// @inheritdoc IOrigamiHOhmManager
    function syncDebtTokenSavings(uint256 requiredDebtTokenBalance) external override onlyElevatedAccess {
        _syncSavings(requiredDebtTokenBalance);
    }

    /// @inheritdoc IOrigamiHOhmManager
    function sweep(
        uint256 amount,
        bytes memory swapData
    ) external override onlyElevatedAccess {
        if (amount > maxSweepSellAmount) revert SweepTooLarge();
        if (block.timestamp < lastSweepTime + sweepCooldownSecs) revert BeforeCooldownEnd();
        lastSweepTime = uint40(block.timestamp);

        // Send the debtTokens to the swapper
        IOrigamiSwapper _swapper = sweepSwapper;
        IERC4626 _savingsVault = debtTokenSavingsVault;
        IERC20 _debtToken = debtToken;
        IERC20 _sweepDebtToken = address(_savingsVault) == address(0)
            ? _debtToken
            : _savingsVault;

        _sweepDebtToken.safeTransfer(address(_swapper), amount);

        emit SweepStarted(address(_sweepDebtToken), amount);

        // This swap may be synchronous (eg via 1Inch/Kyberswap/0x), or asynchronous (eg CoW swap programmatic orders)
        // The swapper is responsible for checking slippage (minBuyAmount) and calling the permisionless `sweepCallback()`
        // after the swap has concluded. For CoW swap that may be via their hooks framework.
        _swapper.execute(_sweepDebtToken, amount, _vault, swapData);
    }

    /// @inheritdoc IOrigamiSwapCallback
    function swapCallback() external override {
        uint256 vaultBalance = _vault.balanceOf(address(this));
        (uint256 amountToBurn, uint256 feeAmount) = vaultBalance.splitSubtractBps(performanceFeeBps, OrigamiMath.Rounding.ROUND_DOWN);

        // No correlation id between the SweepStarted and SweepFinished events since this can be
        // permissionlessly called anyway
        emit SweepFinished(amountToBurn, feeAmount);
        if (feeAmount > 0) {
            _vault.safeTransfer(feeCollector, feeAmount);
        }

        if (amountToBurn > 0) {
            _vault.burn(amountToBurn);
        }
    }

    /// @inheritdoc IOrigamiHOhmManager
    function join(
        uint256 collateralAmount,
        uint256 debtAmount,
        address receiver,
        uint256 receiverSharesPostMint,
        uint256 totalSupplyPostMint
    ) external override onlyVault {
        // Sync delegations to the latest proportional gOHM the will have after the join.
        // If it doesn't have a delegate set, there will be no delegation requests.
        IDLGTEv1.DelegationRequest[] memory delegationRequests = _delegationRequest(
            receiver,
            int256(collateralAmount),
            receiverSharesPostMint,
            totalSupplyPostMint
        );

        // Add the gOHM balance as collateral. The vault is trusted to have sent the exact amount first
        IDLGTEv1.DelegationRequest[] memory emptyDR;
        cooler.addCollateral(collateralAmount.encodeUInt128(), address(this), emptyDR);

        // Now apply the delegation requests
        // NB: This needs to be done as a separate step after addCollateral() because of the `MIN_DELEGATION_AMOUNT`
        // Otherwise MonoCooler could revert as it may try and delegate more than it's trying to add, if the 
        // delegation was previously floored at zero
        if (delegationRequests.length != 0) cooler.applyDelegations(delegationRequests, address(this));

        // borrow or repay such that this contract has a cooler LTV equal to the origination LTV
        int128 coolerDebtDeltaInWad = _coolerMaxBorrow(0, debtAmount);

        // Transfer the `debtToken` to the receiver
        debtToken.safeTransfer(receiver, debtAmount);

        emit Join(collateralAmount, debtAmount, receiver, coolerDebtDeltaInWad);
    }

    /// @inheritdoc IOrigamiHOhmManager
    function exit(
        uint256 collateralAmount,
        uint256 debtAmount,
        address sharesOwner,
        address receiver,
        uint256 ownerSharesPostBurn,
        uint256 totalSupplyPostBurn
    ) external override onlyVault {
        // The vault is trusted to have sent the exact amount of debt first
        // borrow or repay such that this contract has a cooler LTV equal to the origination LTV
        uint128 collateralAmount128 = collateralAmount.encodeUInt128();
        int128 negCollateralAmount128 = -int128(collateralAmount128);
        int128 coolerDebtDeltaInWad = _coolerMaxBorrow(negCollateralAmount128, 0);

        // Sync delegations to the latest proportional gOHM the will have after the exit.
        {
            // If `sharesOwner` doesn't have a delegate set, there will be no delegation requests.
            IDLGTEv1.DelegationRequest[] memory delegationRequests = _delegationRequest(
                sharesOwner,
                negCollateralAmount128,
                ownerSharesPostBurn,
                totalSupplyPostBurn
            );

            // NB: This needs to be done as a separate step prior to withdrawCollateral() because of the `MIN_DELEGATION_AMOUNT`
            // Otherwise MonoCooler could revert as it may try and undelegate more than it's trying to withdraw in order to bring
            // the delegation to zero
            if (delegationRequests.length != 0) cooler.applyDelegations(delegationRequests, address(this));
        }

        IDLGTEv1.DelegationRequest[] memory emptyDR;
        cooler.withdrawCollateral(collateralAmount128, address(this), receiver, emptyDR);

        emit Exit(collateralAmount, debtAmount, receiver, coolerDebtDeltaInWad);
    }

    /// @inheritdoc IOrigamiHOhmManager
    function updateDelegateAndAmount(
        address account, 
        uint256 accountShares, 
        uint256 totalSupply, 
        address newDelegateAddress
    ) external override onlyVault {
        _applyDelegations(
            delegations[account].updateDelegateAndAmount(
                account,
                newDelegateAddress,
                _convertSharesToCollateral(accountShares, collateralTokenBalance(), totalSupply, true)
            )
        );
    }

    /// @inheritdoc IOrigamiHOhmManager
    function setDelegationAmount1(
        address account,
        uint256 accountShares,
        uint256 totalSupply
    ) external override onlyVault {
        _applyDelegations(
            delegations[account].syncAccountAmount(
                account,
                _convertSharesToCollateral(accountShares, collateralTokenBalance(), totalSupply, true)
            )
        );
    }

    /// @inheritdoc IOrigamiHOhmManager
    function setDelegationAmount2(
        address account1,
        uint256 account1Shares,
        address account2,
        uint256 account2Shares,
        uint256 totalSupply
    ) external override onlyVault {
        uint256 totalGOhm = collateralTokenBalance();

        _applyDelegations(
            OlympusCoolerDelegation.syncAccountAmount(
                delegations[account1],
                account1,
                _convertSharesToCollateral(account1Shares, totalGOhm, totalSupply, true),
                delegations[account2],
                account2,
                _convertSharesToCollateral(account2Shares, totalGOhm, totalSupply, true)
            )
        );
    }

    /// @inheritdoc IOrigamiHOhmManager
    function maxBorrowFromCooler() external override returns (int128 coolerDebtDeltaInWad) {
        return _coolerMaxBorrow(0, 0);
    }

    /// @inheritdoc IOrigamiHOhmManager
    function vault() external override view returns (address) {
        return address(_vault);
    }

    /// @inheritdoc IOrigamiHOhmManager
    function areJoinsPaused() external virtual override view returns (bool) {
        return _paused.investmentsPaused;
    }

    /// @inheritdoc IOrigamiHOhmManager
    function areExitsPaused() external virtual override view returns (bool) {
        return _paused.exitsPaused;
    }

    /// @inheritdoc IOrigamiHOhmManager
    function debtTokenBalance() external override view returns (uint256) {
        // Convert the debt into the debt token units, rounding up
        uint256 _coolerDebtInDebtTokens = OrigamiMath.scaleDown(
            coolerDebtInWad(),
            debtTokenDecimalsToWadScalar,
            OrigamiMath.Rounding.ROUND_UP
        );
        uint256 _surplus = surplusDebtTokenAmount();

        // Since:
        //  - The cooler debt could be repaid by someone else
        //  - The surplus just trackes current token balances
        // Either is a donation which will change the [debtToken per hOHM] share price
        return _coolerDebtInDebtTokens > _surplus
            ? _coolerDebtInDebtTokens - _surplus
            : 0;
    }

    /// @inheritdoc IOrigamiHOhmManager
    function coolerDebtInWad() public override view returns (uint128) {
        return cooler.accountDebt(address(this));
    }

    /// @inheritdoc IOrigamiHOhmManager
    function surplusDebtTokenAmount() public override view returns (uint256 surplus) {
        IERC20 _debtToken = debtToken;
        IERC4626 _savingsVault = debtTokenSavingsVault;
        bool hasSavingsVault = address(_savingsVault) != address(0);
        address _sweepSwapper = address(sweepSwapper);
        bool hasSweepSwapper = address(_sweepSwapper) != address(0);

        // This contract's balance - may have either the savings vault or the debt token
        surplus = _debtToken.balanceOf(address(this));
        if (hasSavingsVault) {
            surplus += _uncappedSavingsVaultBalance(_savingsVault, address(this));
        }

        // The sweep swapper's balance
        // It may be asynchronous, and may also have a balance of either the savingsVault token or the debtToken
        if (hasSweepSwapper) {
            surplus += _debtToken.balanceOf(_sweepSwapper);
            if (hasSavingsVault) {
                surplus += _uncappedSavingsVaultBalance(_savingsVault, _sweepSwapper);
            }
        }
    }

    /// @inheritdoc IOrigamiHOhmManager
    function collateralTokenBalance() public override view returns (uint256) {
        // Donations are allowed - if someone adds collateral into cooler on this contracts behalf.
        // A donation will change the [collateral token per hOHM] share price
        return cooler.accountCollateral(address(this));
    }

    /// @inheritdoc IOrigamiHOhmManager
    function convertSharesToCollateral(
        uint256 shares,
        uint256 totalSupply
    ) public override view returns (uint256) {
        return _convertSharesToCollateral(
            shares,
            collateralTokenBalance(),
            totalSupply,
            false
        );
    }

    /// @inheritdoc IOrigamiHOhmManager
    function accountDelegationBalances(
        address account,
        uint256 shares,
        uint256 totalSupply
    ) external override view returns (
        uint256 totalCollateral,
        address delegateAddress,
        uint256 delegatedCollateral
    ) {
        totalCollateral = convertSharesToCollateral(shares, totalSupply);
        OlympusCoolerDelegation.Data memory delegation = delegations[account];
        (delegateAddress, delegatedCollateral) = (delegation.delegateAddress, delegation.amount);
    }

    /// @inheritdoc IERC165
    function supportsInterface(bytes4 interfaceId) public override pure returns (bool) {
        return interfaceId == type(IOrigamiHOhmManager).interfaceId
            || interfaceId == type(IERC165).interfaceId;
    }

    /// @dev Perform either a borrow or repayment in the cooler, in order to get the LTV
    /// as close to the cooler max Origination LTV as possible, given an amount of added/removed collateral
    /// and a required of raw debtTokens (not in the savings vault) to be available in this contract.
    /// 
    /// This will withdraw the required debtToken from the `debtTokenSavingsVault` if required.
    /// @param changeInCollateral The change in `collateralToken` to use when solving for the required change 
    ///        in debt to hit max origination LTV in cooler, in the native decimals of the collateralToken
    /// @param requiredDebtTokenAmount The amount of `debtToken` required to be available in the contract
    ///        after the cooler borrow or repay
    function _coolerMaxBorrow(
        int128 changeInCollateral, 
        uint256 requiredDebtTokenAmount
    ) private returns (int128 debtDeltaInWad) {
        // Calculate the change in debt (always 18dp regardless of the token) in order to get the LTV
        // to the max cooler origination LTV.
        debtDeltaInWad = cooler.debtDeltaForMaxOriginationLtv(address(this), changeInCollateral);
        if (debtDeltaInWad > 0) {
            if (coolerBorrowsDisabled) return 0;

            // A positive `debtDeltaInWad` means we have surplus which can be borrowed to the Origination LTV
            cooler.borrow(uint128(debtDeltaInWad), address(this), address(this));

            // Sync any surplus debtToken into savings, leaving the requiredDebtTokenAmount as raw debtTokens to send to the receiver
            _syncSavings(requiredDebtTokenAmount);
        } else if (debtDeltaInWad < 0) {
            // A negative `debtDeltaInWad` means we need to repay that amount to get to the Origination LTV

            // Sync any surplus debtToken into savings, ensuring there's raw debtToken amounts of
            // the requiredDebtTokenAmount to send the reciever + the amount we want to repay.
            // Convert `debtDeltaInWad` into the debt token decimals, rounding up to ensure there's enough
            uint128 repayAmountWad = uint128(-debtDeltaInWad);
            uint256 repayAmountNative = OrigamiMath.scaleDown(
                repayAmountWad,
                debtTokenDecimalsToWadScalar,
                OrigamiMath.Rounding.ROUND_UP
            );

            _syncSavings(requiredDebtTokenAmount + repayAmountNative);

            // And then repay
            // In the case where there is not enough debtToken to repay, this will (intentionally) revert.
            // The surplus is expected to always be growing/replenishing, if not it means Cooler interest rate is increasing
            // faster than the Olympus treasury, then Cooler no longer has product market fit.
            // Debt repayment will need to be funded from external sources for remaining users to exit.
            cooler.repay(repayAmountWad, address(this));
        }
    }

    function _syncSavings(uint256 requiredDebtTokenBalance) private {
        IERC4626 _savingsVault = debtTokenSavingsVault;
        if (address(_savingsVault) == address(0)) return;

        uint256 _debtTokenBalance = debtToken.balanceOf(address(this));
        uint256 delta;
        if (_debtTokenBalance > requiredDebtTokenBalance) {
            // deposit any surplus into savings
            unchecked {
                delta = _debtTokenBalance - requiredDebtTokenBalance;
            }
            uint256 maxDeposit = _savingsVault.maxDeposit(address(this));
            if (delta > maxDeposit) delta = maxDeposit;
            if (delta > 0) {
                _savingsVault.deposit(delta, address(this));
            }
        } else {
            // withdraw deficit from savings. Cap to the max amount which can be withdrawn.
            unchecked {
                delta = requiredDebtTokenBalance - _debtTokenBalance;
            }
            uint256 maxWithdraw = _savingsVault.maxWithdraw(address(this));
            if (delta > maxWithdraw) delta = maxWithdraw;
            if (delta > 0) {
                _savingsVault.withdraw(delta, address(this), address(this));
            }
        }
    }

    /// @dev The total delegated amount for this account should equal the proportional amount of gOHM collateral
    /// held on behalf of this account based on their share balance.
    function _convertSharesToCollateral(
        uint256 shares,
        uint256 totalCollateral,
        uint256 totalSupply,
        bool applyMinDelegationAmount
    ) private pure returns (uint256 collateral) {
        if (totalSupply == 0) return 0;
        if (shares > totalSupply) revert CommonEventsAndErrors.InvalidParam();
        collateral = totalCollateral.mulDiv(shares, totalSupply, OrigamiMath.Rounding.ROUND_DOWN);
        if (applyMinDelegationAmount && collateral < MIN_DELEGATION_AMOUNT) collateral = 0;
    }

    // @dev Set the debtToken and debtTokenSavingsVault, along with setting
    // max approvals for those.
    function _setDebtTokenAndSavings(
        IERC20 newDebtToken,
        IERC4626 newDebtTokenSavingsVault
    ) private {
        uint8 _decimals = IERC20Metadata(address(newDebtToken)).decimals();
        if (_decimals > OrigamiMath.WAD_DECIMALS) revert CommonEventsAndErrors.InvalidToken(address(newDebtToken));
        debtToken = newDebtToken;
        debtTokenDecimalsToWadScalar = uint96(10 ** (OrigamiMath.WAD_DECIMALS - _decimals));
        debtTokenSavingsVault = newDebtTokenSavingsVault;

        // Max approve the debt token to cooler (to repay)
        // And the debt token to the savings vault if set
        newDebtToken.safeApprove(address(cooler), type(uint256).max);
        if (address(newDebtTokenSavingsVault) != address(0)) {
            if (address(newDebtTokenSavingsVault.asset()) != address(newDebtToken)) {
                revert CommonEventsAndErrors.InvalidToken(address(newDebtTokenSavingsVault));
            }
            newDebtToken.safeApprove(address(newDebtTokenSavingsVault), type(uint256).max);
        }
    }

    /// @dev Use the entire balance rather maxRedeem, so this amount isn't capped by redemption limits
    function _uncappedSavingsVaultBalance(
        IERC4626 sVault, 
        address account
    ) private view returns (uint256 balance) {
        uint256 savingsSurplus = sVault.balanceOf(account);
        if (savingsSurplus > 0) {
            balance = sVault.previewRedeem(savingsSurplus);
        }
    }

    /// @dev Create a Cooler DelegationRequest if an account already has a delegate
    /// set. Uses the existing gOHM collateral balance plus the `_collateralDelta`,
    /// then calculates the user prorportional gOHM given the account shares and totalSupply
    function _delegationRequest(
        address account,
        int256 collateralDelta,
        uint256 newAccountShares,
        uint256 newTotalSupply
    ) private returns (IDLGTEv1.DelegationRequest[] memory delegationRequests) {
        OlympusCoolerDelegation.Data storage $delegation = delegations[account];
        if ($delegation.delegateAddress != address(0)) {
            int256 newTotalCollateral = int256(collateralTokenBalance()) + collateralDelta;

            // An underflow shouldn't be possible, as it would fail later when withdrawing collateral
            // (can't withdraw more than the supplied collateral)
            // But added here as insurance, for safer int256=>uint256 casting
            if (newTotalCollateral < 0) revert CommonEventsAndErrors.InvalidParam();

            delegationRequests = $delegation.syncAccountAmount(
                account,
                _convertSharesToCollateral(
                    newAccountShares,
                    uint256(newTotalCollateral),
                    newTotalSupply,
                    true
                )
            );
        }

        // else left uninitialized
    }

    function _applyDelegations(IDLGTEv1.DelegationRequest[] memory requests) private {
        if (requests.length > 0) {
            cooler.applyDelegations(requests, address(this));
        }
    }
    
    modifier onlyVault() {
        if (msg.sender != address(_vault)) revert CommonEventsAndErrors.InvalidAccess();
        _;
    }
}
OrigamiHOhmVault.sol 265 lines
pragma solidity ^0.8.19;
// SPDX-License-Identifier: AGPL-3.0-or-later
// Origami (investments/olympus/OrigamiHOhmVault.sol)

import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import { Address } from "@openzeppelin/contracts/utils/Address.sol";
import { IERC165 } from "@openzeppelin/contracts/interfaces/IERC165.sol";

import { ITokenizedBalanceSheetVault } from "contracts/interfaces/external/tokenizedBalanceSheetVault/ITokenizedBalanceSheetVault.sol";
import { IOrigamiTokenizedBalanceSheetVault } from "contracts/interfaces/common/IOrigamiTokenizedBalanceSheetVault.sol";
import { IOrigamiHOhmManager } from "contracts/interfaces/investments/olympus/IOrigamiHOhmManager.sol";
import { IOrigamiHOhmVault } from "contracts/interfaces/investments/olympus/IOrigamiHOhmVault.sol";
import { OrigamiTokenizedBalanceSheetVault } from "contracts/common/OrigamiTokenizedBalanceSheetVault.sol";
import { ITokenPrices } from "contracts/interfaces/common/ITokenPrices.sol";
import { CommonEventsAndErrors } from "contracts/libraries/CommonEventsAndErrors.sol";

/**
 * @title Origami lovOHM Tokenized Balance Sheet Vault
 * @notice The logic to add/remove collateral and max borrow/repay from Cooler is delegated to a manager.
 */
contract OrigamiHOhmVault is
    OrigamiTokenizedBalanceSheetVault,
    IOrigamiHOhmVault
{
    using SafeERC20 for IERC20;
    
    /// @inheritdoc IOrigamiHOhmVault
    IERC20 public immutable override collateralToken;

    /// @inheritdoc IOrigamiHOhmVault
    ITokenPrices public override tokenPrices;

    // @inheritdoc IOrigamiHOhmVault
    IERC20 public override debtToken;

    /// @dev The internal manager
    IOrigamiHOhmManager private _manager;

    constructor(
        address initialOwner_,
        string memory name_,
        string memory symbol_,
        address collateralToken_,
        address tokenPrices_
    )
        OrigamiTokenizedBalanceSheetVault(initialOwner_, name_, symbol_)
    {
        collateralToken = IERC20(collateralToken_);
        tokenPrices = ITokenPrices(tokenPrices_);
    }

    /// @inheritdoc IOrigamiHOhmVault
    function setManager(address newManager) external override onlyElevatedAccess {
        if (newManager == address(0)) revert CommonEventsAndErrors.InvalidAddress(address(0));

        if (newManager != address(_manager)) {
            _manager = IOrigamiHOhmManager(newManager);
            emit ManagerSet(newManager);
        }

        // And update the debtToken in case it's changed.
        // This can also be used to refresh the cached debtToken storage value in case it has changed in
        // the upstream cooler and manager, even if the manager itself has not changed.
        IERC20 newDebtToken = _manager.debtToken();
        if (address(newDebtToken) != address(debtToken)) {
            debtToken = newDebtToken;
            emit DebtTokenSet(address(newDebtToken));
        }
    }

    /// @inheritdoc IOrigamiHOhmVault
    function setTokenPrices(address _tokenPrices) external override onlyElevatedAccess {
        if (_tokenPrices == address(0)) revert CommonEventsAndErrors.InvalidAddress(address(0));
        emit TokenPricesSet(_tokenPrices);
        tokenPrices = ITokenPrices(_tokenPrices);
    }

    /// @inheritdoc IOrigamiHOhmVault
    function delegateVotingPower(address delegate) external override {
        // test for 0 supply
        _manager.updateDelegateAndAmount(
            msg.sender,
            balanceOf(msg.sender),
            totalSupply(),
            delegate
        );
    }

    /// @inheritdoc IOrigamiHOhmVault
    function syncDelegation(address account) public override {
        _manager.setDelegationAmount1(
            account,
            balanceOf(account),
            totalSupply()
        );
    }

    /// @inheritdoc IOrigamiHOhmVault
    function multicall(bytes[] calldata data) external override returns (bytes[] memory results) {
        results = new bytes[](data.length);
        for (uint256 i = 0; i < data.length; i++) {
            results[i] = Address.functionDelegateCall(address(this), data[i]);
        }
        return results;
    }

    /// @inheritdoc IOrigamiTokenizedBalanceSheetVault
    function burn(uint256 amount) external override(IOrigamiTokenizedBalanceSheetVault, OrigamiTokenizedBalanceSheetVault) {
        _burn(msg.sender, amount);

        // Ensure the delegation is synchronized for this caller with the latest gOHM balance and the
        // updated share balance & totalSupply
        _manager.setDelegationAmount1(
            msg.sender,
            balanceOf(msg.sender),
            totalSupply()
        );
    }
    
    /// @inheritdoc IOrigamiTokenizedBalanceSheetVault
    function areJoinsPaused() public virtual override(IOrigamiTokenizedBalanceSheetVault, OrigamiTokenizedBalanceSheetVault) view returns (
        bool
    ) {
        return _manager.areJoinsPaused();
    }

    /// @inheritdoc IOrigamiTokenizedBalanceSheetVault
    function areExitsPaused() public virtual override(IOrigamiTokenizedBalanceSheetVault, OrigamiTokenizedBalanceSheetVault) view returns (
        bool
    ) {
        return _manager.areExitsPaused();
    }

    /// @inheritdoc IOrigamiHOhmVault
    function manager() external override view returns (address) {
        return address(_manager);
    }

    /// @inheritdoc IOrigamiTokenizedBalanceSheetVault
    function exitFeeBps() public virtual override(IOrigamiTokenizedBalanceSheetVault, OrigamiTokenizedBalanceSheetVault) view returns (
        uint256
    ) {
        return _manager.exitFeeBps();
    }

    /// @inheritdoc ITokenizedBalanceSheetVault
    function assetTokens() public virtual override(ITokenizedBalanceSheetVault, OrigamiTokenizedBalanceSheetVault) view returns (
        address[] memory assets
    ) {
        assets = new address[](1);
        assets[0] = address(collateralToken);
    }

    /// @inheritdoc ITokenizedBalanceSheetVault
    function liabilityTokens() public virtual override(ITokenizedBalanceSheetVault, OrigamiTokenizedBalanceSheetVault) view returns (
        address[] memory liabilities
    ) {
        liabilities = new address[](1);
        liabilities[0] = address(debtToken);
    }

    /// @inheritdoc ITokenizedBalanceSheetVault
    function isBalanceSheetToken(address tokenAddress) public virtual override(ITokenizedBalanceSheetVault, OrigamiTokenizedBalanceSheetVault) view returns (
        bool isAsset,
        bool isLiability
    ) {
        if (tokenAddress == address(collateralToken)) return (true, false);
        if (tokenAddress == address(debtToken)) return (false, true);
        return (false, false);
    }

    /// @inheritdoc IOrigamiHOhmVault
    function accountDelegationBalances(address account) external override view returns (
        uint256 totalCollateral,
        address delegateAddress,
        uint256 delegatedCollateral
    ) {
        return _manager.accountDelegationBalances(account, balanceOf(account), totalSupply());
    }

    /// @inheritdoc IERC165
    function supportsInterface(
        bytes4 interfaceId
    ) public virtual override(IERC165, OrigamiTokenizedBalanceSheetVault) pure returns (
        bool
    ) {
        return OrigamiTokenizedBalanceSheetVault.supportsInterface(interfaceId)
            || interfaceId == type(IOrigamiHOhmVault).interfaceId;
    }

    /// @dev A hook for joins - it must pull assets from caller and send liabilities to receiver,
    /// along with any other interactions required.
    function _joinPreMintHook(
        address caller,
        address receiver,
        uint256 shares,
        uint256[] memory assets,
        uint256[] memory liabilities
    ) internal override virtual {
        uint256 collateralAmount = assets[0];
        uint256 debtAmount = liabilities[0];

        // Transfer gOHM collateral to the manager then join
        collateralToken.safeTransferFrom(caller, address(_manager), collateralAmount);

        uint256 receiverSharesPostMint = balanceOf(receiver) + shares;
        uint256 totalSupplyPostMint = totalSupply() + shares;
        _manager.join(collateralAmount, debtAmount, receiver, receiverSharesPostMint, totalSupplyPostMint);
    }

    /// @dev A hook for exits - it must send assets to receiver and pull liabilities from caller,
    /// along with any other interactions required.
    function _exitPreBurnHook(
        address caller,
        address sharesOwner,
        address receiver,
        uint256 shares,
        uint256[] memory assets,
        uint256[] memory liabilities
    ) internal override virtual {
        uint256 collateralAmount = assets[0];
        uint256 debtAmount = liabilities[0];

        // Transfer liabilities to the manager then exit
        debtToken.safeTransferFrom(caller, address(_manager), debtAmount);

        uint256 ownerSharesPostBurn = balanceOf(sharesOwner) - shares;
        uint256 totalSupplyPostBurn = totalSupply() - shares;
        _manager.exit(collateralAmount, debtAmount, sharesOwner, receiver, ownerSharesPostBurn, totalSupplyPostBurn);
    }

    /// @dev Return the current token balance of one of the assets or liability tokens in the vault
    /// Should not revert - return zero if tokenAddress is not valid.
    function _tokenBalance(address tokenAddress) internal override view returns (uint256) {
        if (tokenAddress == address(debtToken)) {
            return _manager.debtTokenBalance();
        } else if (tokenAddress == address(collateralToken)) {
            return _manager.collateralTokenBalance();
        }

        return 0;
    }

    /// @dev Use the Openzeppelin ERC20 post hook to update delegations on a transfer.
    ///  - The gOHM delegation for the `from` and `to` account is synchronized using the latest
    ///    gOHM balances and the account's share proportion.
    ///  - If either (or both) of the accounts has not set a delegate then this will be a no-op 
    ///    for that account
    ///  - Delegations are already synchronized for join/exit/burn, so only need to handle transfers
    ///  - Transfer to self is ignored (the account can use `syncDelegation()` for that)
    function _afterTokenTransfer(address from, address to, uint256 /* shares */) internal override {
        if (from != to && from != address(0) && to != address(0)) {
            // This sync's the latest proportional gOHM balance for each account, even if 
            // the share balance remains the same, as the gOHM per share will increase over time.
            _manager.setDelegationAmount2(
                from,
                balanceOf(from),
                to,
                balanceOf(to),
                totalSupply()
            );
        }
    }
}
OrigamiManagerPausable.sol 48 lines
pragma solidity ^0.8.19;
// SPDX-License-Identifier: AGPL-3.0-or-later
// Origami (investments/util/OrigamiManagerPausable.sol)

import { CommonEventsAndErrors } from "contracts/libraries/CommonEventsAndErrors.sol";
import { OrigamiElevatedAccess } from "contracts/common/access/OrigamiElevatedAccess.sol";
import { IOrigamiManagerPausable } from "contracts/interfaces/investments/util/IOrigamiManagerPausable.sol";

/**
 * @title A mixin to add pause/unpause for Origami manager contracts
 */
abstract contract OrigamiManagerPausable is IOrigamiManagerPausable, OrigamiElevatedAccess {
    /**
     * @notice A set of accounts which are allowed to pause deposits/withdrawals immediately
     * under emergency
     */
    mapping(address account => bool canPause) public pausers;

    /**
     * @notice The current paused/unpaused state of deposits/exits.
     */
    Paused internal _paused;

    /**
     * @notice Pause/unpause deposits or exits
     * @dev Can only be called by allowed pausers.
     */
    function setPaused(Paused calldata updatedPaused) external {
        if (!pausers[msg.sender]) revert CommonEventsAndErrors.InvalidAccess();
        emit PausedSet(updatedPaused);
        _paused = updatedPaused;
    }

    /**
     * @notice Allow/Deny an account to pause/unpause deposits or exits
     */
    function setPauser(address account, bool canPause) external onlyElevatedAccess {
        pausers[account] = canPause;
        emit PauserSet(account, canPause);
    }

    /**
     * @notice Check if given account can pause deposits/exits
     */
    function isPauser(address account) external view override returns (bool canPause) {
        canPause = pausers[account];
    }
}
CommonEventsAndErrors.sol 20 lines
pragma solidity ^0.8.19;
// SPDX-License-Identifier: AGPL-3.0-or-later
// Origami (libraries/CommonEventsAndErrors.sol)

/// @notice A collection of common events and errors thrown within the Origami contracts
library CommonEventsAndErrors {
    error InsufficientBalance(address token, uint256 required, uint256 balance);
    error InvalidToken(address token);
    error InvalidParam();
    error InvalidAddress(address addr);
    error InvalidAmount(address token, uint256 amount);
    error ExpectedNonZero();
    error Slippage(uint256 minAmountExpected, uint256 actualAmount);
    error IsPaused();
    error UnknownExecuteError(bytes returndata);
    error InvalidAccess();
    error BreachedMaxTotalSupply(uint256 totalSupply, uint256 maxTotalSupply);

    event TokenRecovered(address indexed to, address indexed token, uint256 amount);
}
OlympusCoolerDelegation.sol 223 lines
pragma solidity ^0.8.19;
// SPDX-License-Identifier: AGPL-3.0-or-later
// Origami (libraries/OlympusCoolerDelegation.sol)

import { IDLGTEv1 } from "contracts/interfaces/external/olympus/IDLGTE.v1.sol";
import { CommonEventsAndErrors } from "contracts/libraries/CommonEventsAndErrors.sol";
import { IOrigamiHOhmManager } from "contracts/interfaces/investments/olympus/IOrigamiHOhmManager.sol";
import { SafeCast } from "contracts/libraries/SafeCast.sol";

/**
 * @notice Generate Olympus Cooler delegation requests on behalf of Origami users
 */
library OlympusCoolerDelegation {
    using SafeCast for uint256;

    struct Data {
        address delegateAddress;
        uint256 amount;
    }

    struct _Request {
        /// @dev The account to sync the delegation request for.
        /// If this request is not required to be applied (eg the delegated balance
        /// won't change), this will be address(0)
        address account;

        /// @dev The cooler delegation request
        IDLGTEv1.DelegationRequest request;
    }

    /**
     * @notice Update the gOHM delegation address and amount for a particular account, 
     * and update the `$delegation` state for that account.
     * @dev 
     *  - `account` cannot be address(0) - this will revert
     *  - `newDelegateAddress` may be address(0), meaning that gOHM collateral will become
     *    undelegated.
     *  - `newDelegateAddress` may remain the same as the existing one, meaning just the amount
     *    is updated
     *  - `newAmount` may be zero, meaning that any existing gOHM collateral is undelegated.
     *    however the `$delegation.delegateAddress` will remain as is unless `newDelegateAddress` 
     *    has also changed
     */
    function updateDelegateAndAmount(
        Data storage $delegation,
        address account,
        address newDelegateAddress,
        uint256 newAmount
    ) internal returns (IDLGTEv1.DelegationRequest[] memory) {
        _Request memory cdr1;
        _Request memory cdr2;
        address existingDelegateAddress = $delegation.delegateAddress;
        uint256 existingAmount = $delegation.amount;

        if (newDelegateAddress == existingDelegateAddress) {
            // The same delegate address - only need to sync the amount
            cdr1 = _syncAmount($delegation, account, existingDelegateAddress, existingAmount, newAmount);
        } else {
            // Update to a new delegate address along with a new amount
            // Set the old delegation amount to zero if it previously had a delegation address
            if (existingDelegateAddress != address(0)) {
                cdr1 = _syncDelegationRequest(account, existingDelegateAddress, existingAmount, 0);
            }

            // Set the delegation to the new address.
            // If the new delegation address is zero (request is to undelegate) then the amount just needs
            // to be sync'd to storage - no second cooler delegation request is needed.
            if (newDelegateAddress == address(0)) {
                newAmount = 0;
            } else {
                cdr2 = _syncDelegationRequest(account, newDelegateAddress, 0, newAmount);
            }

            // Persist state
            $delegation.delegateAddress = newDelegateAddress;
            if (existingAmount != newAmount) {
                $delegation.amount = newAmount;
            }
        }

        return _generateRequests(cdr1, cdr2);
    }

    /**
     * @dev Create a request (and sync state) to update the delegated amount for `account`
     */
    function syncAccountAmount(
        Data storage $delegation,
        address account,
        uint256 accountNewAmount
    ) internal returns (IDLGTEv1.DelegationRequest[] memory) {
        return _generateRequests(
            _syncAmount(
                $delegation,
                account,
                $delegation.delegateAddress,
                $delegation.amount,
                accountNewAmount
            )
        );
    }

    /**
     * @dev Create a request (and sync state) to update the delegated amount for `account1`
     * and `account2`
     */
    function syncAccountAmount(
        Data storage $delegation1,
        address account1,
        uint256 account1NewAmount,
        Data storage $delegation2,
        address account2,
        uint256 account2NewAmount
    ) internal returns (IDLGTEv1.DelegationRequest[] memory) {
        if (account1 == account2) revert CommonEventsAndErrors.InvalidAddress(account2);

        return _generateRequests(
            _syncAmount(
                $delegation1,
                account1,
                $delegation1.delegateAddress,
                $delegation1.amount,
                account1NewAmount
            ),
            _syncAmount(
                $delegation2,
                account2,
                $delegation2.delegateAddress,
                $delegation2.amount,
                account2NewAmount
            )
        );
    }

    /**
     * @dev Create a request (and sync state) to update the delegate address and
     * amount for `account`
     */
    function _syncAmount(
        Data storage $delegation,
        address account,
        address delegateAddress,
        uint256 existingAmount,
        uint256 newAmount
    ) private returns (
        _Request memory request
    ) {
        // If the delegate address is 0, then ensure the amount is also zero
        if (delegateAddress == address(0)) {
            newAmount = 0;
        }

        // Sync the amount if the new amount is different to the existing one.
        if (existingAmount != newAmount) {
            request = _syncDelegationRequest(account, delegateAddress, existingAmount, newAmount);

            // Persist state
            $delegation.amount = newAmount;
        }
    }

    /**
     * @dev Create the cooler delegation request for an account & delegate address for a new delegation amount.
     * The request will be for the amount delta (vs the existing delegated amount).
     * If no delegate address or no difference in the amount, then `request` will be left uninitialised
     */
    function _syncDelegationRequest(
        address account,
        address delegateAddress,
        uint256 existingDelegationAmount,
        uint256 newDelegationAmount
    ) private pure returns (_Request memory request) {
        // No delegate for this account - no request required.
        if (delegateAddress == address(0)) return request;

        // Only set the sync request item if the existing delegated amount is 
        // different to the target amount
        int256 delta = newDelegationAmount.encodeInt256() - existingDelegationAmount.encodeInt256();

        if (delta != 0) {
            request = _Request(
                account,
                IDLGTEv1.DelegationRequest({
                    delegate: delegateAddress,
                    amount: delta
                })
            );
        }
    }

    /// @dev Generate the Cooler DelegationRequest list for one (potentially uninitialized) request
    function _generateRequests(
        _Request memory cdr
    ) private returns (IDLGTEv1.DelegationRequest[] memory requests) {
        if (cdr.account != address(0)) {
            requests = new IDLGTEv1.DelegationRequest[](1);
            requests[0] = cdr.request;
            emit IOrigamiHOhmManager.DelegationApplied(cdr.account, cdr.request.delegate, cdr.request.amount);
        }

        // else requests is left uninitialized
    }

    /// @dev Generate the Cooler DelegationRequest list for two (potentially uninitialized) requests
    function _generateRequests(
        _Request memory cdr1,
        _Request memory cdr2
    ) private returns (IDLGTEv1.DelegationRequest[] memory requests) {
        if (cdr1.account != address(0) && cdr2.account != address(0)) {
            requests = new IDLGTEv1.DelegationRequest[](2);
            (requests[0], requests[1]) = (cdr1.request, cdr2.request);

            emit IOrigamiHOhmManager.DelegationApplied(cdr1.account, cdr1.request.delegate, cdr1.request.amount);
            emit IOrigamiHOhmManager.DelegationApplied(cdr2.account, cdr2.request.delegate, cdr2.request.amount);
        } else if (cdr1.account != address(0)) {
            return _generateRequests(cdr1);
        } else if (cdr2.account != address(0)) {
            return _generateRequests(cdr2);
        }

        // else requests is left uninitialized
    }
}
OrigamiMath.sol 174 lines
pragma solidity ^0.8.19;
// SPDX-License-Identifier: AGPL-3.0-or-later
// Origami (libraries/OrigamiMath.sol)

import { mulDiv as prbMulDiv, PRBMath_MulDiv_Overflow } from "@prb/math/src/Common.sol";
import { CommonEventsAndErrors } from "contracts/libraries/CommonEventsAndErrors.sol";

/**
 * @notice Utilities to operate on fixed point math multipliation and division
 * taking rounding into consideration
 */
library OrigamiMath {
    enum Rounding {
        ROUND_DOWN,
        ROUND_UP
    }

    uint256 public constant BASIS_POINTS_DIVISOR = 10_000;
    uint256 public constant WAD_DECIMALS = 18;
    uint256 public constant WAD = 10 ** WAD_DECIMALS;

    function scaleUp(uint256 amount, uint256 scalar) internal pure returns (uint256) {
        // Special case for scalar == 1, as it's common for token amounts to not need
        // scaling if decimal places are the same
        return scalar == 1 ? amount : amount * scalar;
    }

    function scaleDown(
        uint256 amount, 
        uint256 scalar, 
        Rounding roundingMode
    ) internal pure returns (uint256 result) {
        // Special case for scalar == 1, as it's common for token amounts to not need
        // scaling if decimal places are the same
        unchecked {
            if (scalar == 1) {
                result = amount;
            } else if (roundingMode == Rounding.ROUND_DOWN) {
                result = amount / scalar;
            } else {
                // ROUND_UP uses the same logic as OZ Math.ceilDiv()
                result = amount == 0 ? 0 : (amount - 1) / scalar + 1;
            }
        }
    }

    /**
     * @notice Calculates x * y / denominator with full precision,
     * rounding up
     */
    function mulDiv(
        uint256 x, 
        uint256 y, 
        uint256 denominator,
        Rounding roundingMode
    ) internal pure returns (uint256 result) {
        result = prbMulDiv(x, y, denominator);
        if (roundingMode == Rounding.ROUND_UP) {
            if (mulmod(x, y, denominator) != 0) {
                if (result < type(uint256).max) {
                    unchecked {
                        result = result + 1;
                    }
                } else {
                    revert PRBMath_MulDiv_Overflow(x, y, denominator);
                }
            }
        }
    }

    function subtractBps(
        uint256 inputAmount, 
        uint256 basisPoints,
        Rounding roundingMode
    ) internal pure returns (uint256 result) {
        uint256 numeratorBps;
        unchecked {
            numeratorBps = BASIS_POINTS_DIVISOR - basisPoints;
        }

        result = basisPoints < BASIS_POINTS_DIVISOR
            ? mulDiv(
                inputAmount,
                numeratorBps, 
                BASIS_POINTS_DIVISOR, 
                roundingMode
            ) : 0;
    }

    function addBps(
        uint256 inputAmount,
        uint256 basisPoints,
        Rounding roundingMode
    ) internal pure returns (uint256 result) {
        uint256 numeratorBps = BASIS_POINTS_DIVISOR + basisPoints;

        // Round up for max amounts out expected
        result = mulDiv(
            inputAmount,
            numeratorBps, 
            BASIS_POINTS_DIVISOR, 
            roundingMode
        );
    }

    /**
     * @notice Split the `inputAmount` into two parts based on the `basisPoints` fraction.
     * eg: 3333 BPS (33.3%) can be used to split an input amount of 600 into: (result=400, removed=200).
     * @dev The rounding mode is applied to the `result`
     */
    function splitSubtractBps(
        uint256 inputAmount, 
        uint256 basisPoints,
        Rounding roundingMode
    ) internal pure returns (uint256 result, uint256 removed) {
        if (basisPoints == 0) return (inputAmount, 0); // gas shortcut for 0

        result = subtractBps(inputAmount, basisPoints, roundingMode);
        unchecked {
            removed = inputAmount - result;
        }
    }

    /**
     * @notice Reverse the fractional amount of an input.
     * eg: For 3333 BPS (33.3%) and the remainder=400, the result is 600
     */
    function inverseSubtractBps(
        uint256 remainderAmount, 
        uint256 basisPoints,
        Rounding roundingMode
    ) internal pure returns (uint256 result) {
        if (basisPoints == 0) return remainderAmount; // gas shortcut for 0
        if (basisPoints >= BASIS_POINTS_DIVISOR) revert CommonEventsAndErrors.InvalidParam();

        uint256 denominatorBps;
        unchecked {
            denominatorBps = BASIS_POINTS_DIVISOR - basisPoints;
        }
        result = mulDiv(
            remainderAmount,
            BASIS_POINTS_DIVISOR, 
            denominatorBps, 
            roundingMode
        );
    }

    /**
     * @notice Calculate the relative difference of a value to a reference
     * @dev `value` and `referenceValue` must have the same precision
     * The denominator is always the referenceValue
     */
    function relativeDifferenceBps(
        uint256 value,
        uint256 referenceValue,
        Rounding roundingMode
    ) internal pure returns (uint256) {
        if (referenceValue == 0) revert CommonEventsAndErrors.InvalidParam();

        uint256 absDelta;
        unchecked {
            absDelta = value < referenceValue
                ? referenceValue - value
                : value - referenceValue;
        }

        return mulDiv(
            absDelta,
            BASIS_POINTS_DIVISOR,
            referenceValue,
            roundingMode
        );
    }
}
SafeCast.sol 31 lines
pragma solidity ^0.8.19;
// SPDX-License-Identifier: AGPL-3.0-or-later
// Origami (libraries/SafeCast.sol)

/**
 * @notice A helper library for safe uint downcasting
 */
library SafeCast {
    error Overflow(uint256 amount);

    function encodeUInt128(uint256 amount) internal pure returns (uint128) {
        if (amount > type(uint128).max) {
            revert Overflow(amount);
        }
        return uint128(amount);
    }
    
    function encodeUInt112(uint256 amount) internal pure returns (uint112) {
        if (amount > type(uint112).max) {
            revert Overflow(amount);
        }
        return uint112(amount);
    }

    function encodeInt256(uint256 amount) internal pure returns (int256) {
        if (amount > uint256(type(int256).max)) {
            revert Overflow(amount);
        }
        return int256(amount);
    }
}

Read Contract

MAX_EXIT_FEE_BPS 0x9e628977 → uint16
MAX_PERFORMANCE_FEE_BPS 0x70889165 → uint16
MIN_DELEGATION_AMOUNT 0x0a5517ca → uint256
accountDelegationBalances 0x757bb813 → uint256, address, uint256
areExitsPaused 0x9a6b27cf → bool
areJoinsPaused 0xaf1454d3 → bool
collateralToken 0xb2016bd4 → address
collateralTokenBalance 0xdd50e2d3 → uint256
convertSharesToCollateral 0x724593c0 → uint256
cooler 0xcf6a9a94 → address
coolerBorrowsDisabled 0x90421bf6 → bool
coolerDebtInWad 0xd49dbdeb → uint128
debtToken 0xf8d89898 → address
debtTokenBalance 0x59b97ddb → uint256
debtTokenDecimalsToWadScalar 0xd015740b → uint96
debtTokenSavingsVault 0x51e163c0 → address
delegations 0xbffe3486 → address, uint256
exitFeeBps 0x57b17a52 → uint16
explicitFunctionAccess 0xdaeccc79 → bool
feeCollector 0xc415b95c → address
isPauser 0x46fbf68e → bool
lastSweepTime 0x12481ad6 → uint40
maxSweepSellAmount 0x2f6a2967 → uint96
owner 0x8da5cb5b → address
pausers 0x80f51c12 → bool
performanceFeeBps 0xb9d4e879 → uint16
supportsInterface 0x01ffc9a7 → bool
surplusDebtTokenAmount 0x06bb50bf → uint256
sweepCooldownSecs 0xa0810570 → uint40
sweepSwapper 0x0ef616cf → address
vault 0xfbfa77cf → address

Write Contract 23 functions

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

acceptOwner 0xebbc4965
No parameters
exit 0x167860a7
uint256 collateralAmount
uint256 debtAmount
address sharesOwner
address receiver
uint256 ownerSharesPostBurn
uint256 totalSupplyPostBurn
join 0x86dd0518
uint256 collateralAmount
uint256 debtAmount
address receiver
uint256 receiverSharesPostMint
uint256 totalSupplyPostMint
maxBorrowFromCooler 0x9672093e
No parameters
returns: int128
proposeNewOwner 0xb1f8100d
address account
recoverToken 0xa7229fd9
address token
address to
uint256 amount
revokeOwnership 0x2b968958
No parameters
setCoolerBorrowsDisabled 0xf1bffe27
bool value
setDebtTokenFromCooler 0xbfcc8c41
address newDebtTokenSavingsVault
setDelegationAmount1 0x05a8b830
address account
uint256 accountShares
uint256 totalSupply
setDelegationAmount2 0x85a3a589
address account1
uint256 account1Shares
address account2
uint256 account2Shares
uint256 totalSupply
setExitFees 0xb54e0e1f
uint16 newFeeBps
setExplicitAccess 0xf48a954d
address allowedCaller
tuple[] access
setFeeCollector 0xa42dce80
address newFeeCollector
setPaused 0xf528b52b
tuple updatedPaused
setPauser 0x7180c8ca
address account
bool canPause
setPerformanceFeesBps 0xcbc9af88
uint16 newFeeBps
setSweepParams 0x72637c18
uint40 newSweepCooldownSecs
uint96 newMaxSweepSellAmount
setSweepSwapper 0x2d97c13d
address newSwapper
swapCallback 0x6a68c718
No parameters
sweep 0xd2ae9a0d
uint256 amount
bytes swapData
syncDebtTokenSavings 0x633854ce
uint256 requiredDebtTokenBalance
updateDelegateAndAmount 0xd805b074
address account
uint256 accountShares
uint256 totalSupply
address newDelegateAddress

Recent Transactions

No transactions found for this address