Address Contract Verified
Address
0x70f6DCFAfD058bAE381D3390f6e8b11cD91c3c81
Balance
0.008000 ETH
Nonce
1
Code Size
24523 bytes
Creator
0xba5Ed099...a5Ed at tx 0xdbad3b84...f963db
Indexed Transactions
0
Contract Bytecode
24523 bytes
0x608060405260043610610674575f3560e01c806370b921dc11610349578063b875ad99116101b8578063e7642c5c116100f5578063fbe0545111610099578063ff11343a11610076578063ff11343a14611406578063ff77074a14611425578063ff7da91e14611444578063ffcad5af1461146357005b8063fbe05451146113b3578063fcc088ac146113c8578063fd762d92146113e757005b8063e985e9c5116100d2578063e985e9c514611320578063ec33bca914611354578063f29f335714611369578063fa13cee61461139457005b8063e7642c5c146112c2578063e8a3d485146112e1578063e96b6c74146112f557005b8063d007af5c1161015c578063d976043411610139578063d97604341461121a578063daea31c01461124d578063df4f5a7214611284578063e66121ed146112a357005b8063d007af5c146111d4578063d4263a53146111e8578063d94911a6146111fb57005b8063be537f4311610195578063be537f431461112f578063c87b56dd14611150578063cb2876561461116f578063cdf1bcc61461119b57005b8063b875ad99146110de578063b88d4fde146110fd578063bb3df8b81461111057005b80639a2fb2db11610286578063a48301141161022a578063b035866b11610207578063b035866b14611079578063b17535b81461108c578063b3b3390c146110ab578063b5545a3c146110ca57005b8063a48301141461100d578063a9fc664e1461102c578063ad3e63d81461104b57005b8063a22cb46511610263578063a22cb46514610f6f578063a2309ff814610f8e578063a278d7c014610fb2578063a2d6613714610fee57005b80639a2fb2db14610f125780639b82fb9c14610f315780639d645a4414610f5057005b80638750a4b4116102ed5780638cfdfd59116102ca5780638cfdfd5914610eb65780638da5cb5b14610ed55780638de65ce414610ee957806395d89b4114610efe57005b80638750a4b414610e4a57806387d1476314610e785780638c91279614610e9757005b80637a9a44cf116103265780637a9a44cf14610de25780637aa4244d14610df75780637c71035e14610e165780638405d4a114610e2b57005b806370b921dc14610d925780637355077614610da7578063765d51f414610dcf57005b806341e0ae23116104e557806358bc29f01161042257806362a5af3b116103c65780636a9412bd116103a35780636a9412bd14610d2c5780636c3b869914610d4b5780636ff6021014610d5f57806370a0823114610d7357005b806362a5af3b14610cda5780636352211e14610cee57806363d425c814610d0d57005b80635d4c1d46116103ff5780635d4c1d4614610c515780635d95af2d14610c7d5780635fc5156014610c9c5780636134716214610cbb57005b806358bc29f014610c005780635a1f3c2814610c135780635a94007714610c3257005b8063495c8bf9116104895780634f1c25ca116104665780634f1c25ca14610b6c5780634f558e7914610b9a57806354bf3f1514610bb9578063577d411314610be157005b8063495c8bf914610b0d57806349c1764214610b2e5780634dc12f0714610b4d57005b806344004cc1116104c257806344004cc114610a915780634499e42c14610ab05780634782f77914610acf578063491bb66a14610aee57005b806341e0ae2314610a3157806342842e0e14610a5f57806342966c6814610a7257005b80631c33b328116105b35780632c6de652116105575780632f45dcfe116105345780632f45dcfe146109b157806332a8f30f146109df57806334cfb56c146109fe5780633d759a9214610a1d57005b80632c6de652146109535780632e8da829146109735780632eb4287a1461099257005b8063222b498911610590578063222b4989146108b557806323250cae146108e357806323b872dd146109025780632a55205a1461091557005b80631c33b328146108615780631dd640571461088257806321fdde67146108a157005b8063081812fc1161061a5780630fb54386116105f75780630fb54386146107ed578063167ef4951461080657806318160ddd146108255780631b25b0771461084257005b8063081812fc1461079f578063095ea7b3146107be578063098144d4146107d157005b806301ffc9a71161065357806301ffc9a714610710578063054f7d9c1461073f57806306fdde031461075f57806307f3aa691461078057005b806238170314610676578063012b23f51461069557806301463546146106d3575b005b348015610681575f5ffd5b50610674610690366004614919565b611476565b3480156106a0575f5ffd5b506106c06106af366004614919565b601d6020525f908152604090205481565b6040519081526020015b60405180910390f35b3480156106de575f5ffd5b506106f871721c310194ccfc01e523fc93c9cccfa2a0ac81565b6040516001600160a01b0390911681526020016106ca565b34801561071b575f5ffd5b5061072f61072a366004614934565b61150b565b60405190151581526020016106ca565b34801561074a575f5ffd5b5060175461072f90600160a01b900460ff1681565b34801561076a575f5ffd5b50610773611553565b6040516106ca9190614989565b34801561078b575f5ffd5b506013546106f8906001600160a01b031681565b3480156107aa575f5ffd5b506106f86107b936600461499b565b6115e3565b6106746107cc3660046149b2565b61161e565b3480156107dc575f5ffd5b505f546001600160a01b03166106f8565b3480156107f8575f5ffd5b50601b5461072f9060ff1681565b348015610811575f5ffd5b506106f861082036600461499b565b61162d565b348015610830575f5ffd5b506004546001600160801b03166106c0565b34801561084d575f5ffd5b5061072f61085c3660046149dc565b611655565b34801561086c575f5ffd5b50610875600181565b6040516106ca9190614a48565b34801561088d575f5ffd5b506011546106f8906001600160a01b031681565b3480156108ac575f5ffd5b50600b546106c0565b3480156108c0575f5ffd5b5061072f6108cf366004614919565b60216020525f908152604090205460ff1681565b3480156108ee575f5ffd5b5061072f6108fd36600461499b565b6116e7565b610674610910366004614a5b565b611707565b348015610920575f5ffd5b5061093461092f366004614a99565b611816565b604080516001600160a01b0390931683526020830191909152016106ca565b610966610961366004614af9565b6118c2565b6040516106ca9190614b71565b34801561097e575f5ffd5b5061072f61098d366004614919565b6118e2565b34801561099d575f5ffd5b506106746109ac366004614b90565b6119e5565b3480156109bc575f5ffd5b5061072f6109cb366004614919565b600d546001600160a01b0391821691161490565b3480156109ea575f5ffd5b506017546106f8906001600160a01b031681565b348015610a09575f5ffd5b506016546106f8906001600160a01b031681565b348015610a28575f5ffd5b50601f546106c0565b348015610a3c575f5ffd5b5061072f610a4b366004614919565b60266020525f908152604090205460ff1681565b610674610a6d366004614a5b565b611a56565b348015610a7d575f5ffd5b50610674610a8c36600461499b565b611a82565b348015610a9c575f5ffd5b50610674610aab366004614a5b565b611b12565b348015610abb575f5ffd5b50600a546106f8906001600160a01b031681565b348015610ada575f5ffd5b50610674610ae93660046149b2565b611b90565b348015610af9575f5ffd5b506010546106f8906001600160a01b031681565b348015610b18575f5ffd5b50610b21611c08565b6040516106ca9190614bbe565b348015610b39575f5ffd5b50610674610b48366004614b90565b611d10565b348015610b58575f5ffd5b50600f546106f8906001600160a01b031681565b348015610b77575f5ffd5b5061072f610b8636600461499b565b60226020525f908152604090205460ff1681565b348015610ba5575f5ffd5b5061072f610bb436600461499b565b611d69565b610bcc610bc7366004614c1a565b611d73565b604080519283526020830191909152016106ca565b348015610bec575f5ffd5b50610674610bfb366004614c65565b611d94565b6106c0610c0e366004614d44565b611fab565b348015610c1e575f5ffd5b506106f8610c2d36600461499b565b611fb7565b348015610c3d575f5ffd5b506106c0610c4c36600461499b565b611ff4565b348015610c5c575f5ffd5b50610c65600181565b6040516001600160781b0390911681526020016106ca565b348015610c88575f5ffd5b50610674610c9736600461499b565b612079565b348015610ca7575f5ffd5b50610674610cb6366004614d7b565b612212565b348015610cc6575f5ffd5b50610674610cd5366004614db6565b61222d565b348015610ce5575f5ffd5b50610674612375565b348015610cf9575f5ffd5b506106f8610d0836600461499b565b612392565b348015610d18575f5ffd5b50610674610d27366004614919565b61239c565b348015610d37575f5ffd5b506106c0610d46366004614919565b6123c7565b348015610d56575f5ffd5b50610674612433565b348015610d6a575f5ffd5b5061067461251c565b348015610d7e575f5ffd5b506106c0610d8d366004614919565b612572565b348015610d9d575f5ffd5b506106c061271081565b348015610db2575f5ffd5b506106c0610dc1366004614df3565b5f9182526020526040902090565b6106c0610ddd366004614e16565b6125aa565b348015610ded575f5ffd5b506106c060085481565b348015610e02575f5ffd5b50610674610e1136600461499b565b6127c7565b348015610e21575f5ffd5b506106c060075481565b348015610e36575f5ffd5b506106c0610e45366004614a99565b6127d4565b348015610e55575f5ffd5b5061072f610e6436600461499b565b60286020525f908152604090205460ff1681565b348015610e83575f5ffd5b506014546106f8906001600160a01b031681565b348015610ea2575f5ffd5b50610674610eb1366004614e4f565b61287a565b348015610ec1575f5ffd5b50610674610ed036600461499b565b61292d565b348015610ee0575f5ffd5b506106f8612a05565b348015610ef4575f5ffd5b506106c060185481565b348015610f09575f5ffd5b50610773612a21565b348015610f1d575f5ffd5b50610674610f2c36600461499b565b612a30565b348015610f3c575f5ffd5b506106f8610f4b36600461499b565b612a6d565b348015610f5b575f5ffd5b5061072f610f6a366004614919565b612a7c565b348015610f7a575f5ffd5b50610674610f89366004614e6b565b612b3e565b348015610f99575f5ffd5b50600454600160801b90046001600160801b03166106c0565b348015610fbd575f5ffd5b50610fd1610fcc36600461499b565b612b91565b604080519283526001600160a01b039091166020830152016106ca565b348015610ff9575f5ffd5b50610674611008366004614a99565b612bdc565b348015611018575f5ffd5b50610674611027366004614a99565b612c9f565b348015611037575f5ffd5b50610674611046366004614919565b612cd5565b348015611056575f5ffd5b5061072f61106536600461499b565b60236020525f908152604090205460ff1681565b61067461108736600461499b565b612df2565b348015611097575f5ffd5b50600d546106f8906001600160a01b031681565b3480156110b6575f5ffd5b506106746110c5366004614d7b565b61315a565b3480156110d5575f5ffd5b506106746131ac565b3480156110e9575f5ffd5b506106746110f8366004614ead565b6131dc565b61067461110b366004614ec6565b613263565b34801561111b575f5ffd5b506015546106f8906001600160a01b031681565b34801561113a575f5ffd5b506111436132bd565b6040516106ca9190614f5d565b34801561115b575f5ffd5b5061077361116a36600461499b565b613371565b34801561117a575f5ffd5b5061118e611189366004614af9565b6133af565b6040516106ca9190614f9d565b3480156111a6575f5ffd5b5061072f6111b5366004614df3565b602760209081525f928352604080842090915290825290205460ff1681565b3480156111df575f5ffd5b50610b21613449565b3480156111f3575f5ffd5b506106c05f81565b348015611206575f5ffd5b506012546106f8906001600160a01b031681565b348015611225575f5ffd5b506106f861123436600461499b565b602080525f90815260409020546001600160a01b031681565b348015611258575f5ffd5b5060195461126c906001600160601b031681565b6040516001600160601b0390911681526020016106ca565b34801561128f575f5ffd5b506003546106f8906001600160a01b031681565b3480156112ae575f5ffd5b506106746112bd366004614df3565b6134fe565b3480156112cd575f5ffd5b506106746112dc366004614a99565b613567565b3480156112ec575f5ffd5b5061077361357a565b348015611300575f5ffd5b506106c061130f36600461499b565b60256020525f908152604090205481565b34801561132b575f5ffd5b5061072f61133a366004615000565b601c52670a5a2e7a000000006008525f526030600c205490565b34801561135f575f5ffd5b506106c060095481565b348015611374575f5ffd5b506106c061138336600461502c565b601c6020525f908152604090205481565b34801561139f575f5ffd5b506106746113ae366004615267565b6135e8565b3480156113be575f5ffd5b506106c0601a5481565b3480156113d3575f5ffd5b506106746113e2366004615000565b613768565b3480156113f2575f5ffd5b506106746114013660046152aa565b6137e6565b348015611411575f5ffd5b50600e546106f8906001600160a01b031681565b348015611430575f5ffd5b5061067461143f366004615303565b6138db565b34801561144f575f5ffd5b50600c546106f8906001600160a01b031681565b6106c0611471366004614d44565b613a1b565b61147e613af0565b600b80546001810182555f919091527f0175b7a638427703f0dbe7bb9bbf987a2551717b34e79f33b5b1008d1fa01db9810180546001600160a01b0319166001600160a01b0384169081179091556040805183815260208101929092527f478d1205bef06ee49aec8f7e1b3341ddc0d656fbf54bd1d4ca8ce8691e33926a91015b60405180910390a15050565b5f6001600160e01b0319821663e8a3d48560e01b148061152f575061152f82613b2c565b8061153e575061153e82613b6d565b8061154d575061154d82613b6d565b92915050565b60606005805461156290615342565b80601f016020809104026020016040519081016040528092919081815260200182805461158e90615342565b80156115d95780601f106115b0576101008083540402835291602001916115d9565b820191905f5260205f20905b8154815290600101906020018083116115bc57829003601f168201915b5050505050905090565b5f815f52673ec412a9852d173d60c11b601c5260205f2082018201805460601b6116145763ceea21b65f526004601cfd5b6001015492915050565b611629338383613ba1565b5050565b601f818154811061163c575f80fd5b5f918252602090912001546001600160a01b0316905081565b5f80546001600160a01b0316156116dc575f5460405163050bf71960e31b81526001600160a01b038681166004830152858116602483015284811660448301529091169063285fb8c8906064015f6040518083038186803b1580156116b8575f5ffd5b505afa9250505080156116c9575060015b6116d457505f6116e0565b5060016116e0565b5060015b9392505050565b600881901c5f9081526029602052604081205460ff83161c60011661154d565b611712838383613c3b565b5f818152673ec412a9852d173d60c11b3317601c52602090208101810180546001600160a01b0394851694938416938116919082861483026117635767ceea21b6a1148100831560021b526004601cfd5b855f52816001015492508233148633141761178f576030600c205461178f57634b6e7f185f526004601cfd5b821561179c575f82600101555b85851818905550601c600c81812080545f190190555f84905220805460010163ffffffff811684026117dd5767ea553b3401336cea841560021b526004601cfd5b90558082847fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef5f38a4611811838383613c46565b505050565b5f8281526002602090815260408083208151808301909252546001600160a01b038116808352600160a01b9091046001600160601b031692820192909252829161188a5750604080518082019091526001546001600160a01b0381168252600160a01b90046001600160601b031660208201525b60208101515f90612710906118a8906001600160601b031687615388565b6118b2919061539f565b91519350909150505b9250929050565b60606118cc613c51565b6118d860018484613cd2565b905061154d613e23565b5f80546001600160a01b0316156119de575f54604051635caaa2a960e11b81523060048201526001600160a01b039091169063d72dde5e90829063b955455290602401606060405180830381865afa158015611940573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061196491906153be565b602001516040516001600160e01b031960e084901b1681526001600160781b0390911660048201526001600160a01b03851660248201526044015b602060405180830381865afa1580156119ba573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061154d9190615412565b505f919050565b6119ee82612392565b6001600160a01b0316336001600160a01b031614611a1f576040516323065c7760e21b815260040160405180910390fd5b5f82815260226020908152604091829020805460ff191684151517905590518381525f516020615f765f395f51905f5291016114ff565b611a61838383611707565b813b156118115761181183838360405180602001604052805f815250613e4a565b611a8b81612392565b6001600160a01b0316336001600160a01b031614611abc576040516323065c7760e21b815260040160405180910390fd5b600480546fffffffffffffffffffffffffffffffff1981166001600160801b039182165f1901909116179055600881901c5f9081526029602052604090208054600160ff84161b179055611b0f81613ecb565b50565b611b1a613af0565b60405163a9059cbb60e01b81526001600160a01b0383811660048301526024820183905284169063a9059cbb906044016020604051808303815f875af1158015611b66573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611b8a9190615412565b50505050565b611b98613af0565b5f826001600160a01b0316826040515f6040518083038185875af1925050503d805f8114611be1576040519150601f19603f3d011682016040523d82523d5f602084013e611be6565b606091505b505090508061181157604051637d376dff60e01b815260040160405180910390fd5b5f546060906001600160a01b031615611cfe575f54604051635caaa2a960e11b81523060048201526001600160a01b0390911690633fe5df9990829063b955455290602401606060405180830381865afa158015611c68573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611c8c91906153be565b602001516040516001600160e01b031960e084901b1681526001600160781b0390911660048201526024015b5f60405180830381865afa158015611cd2573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052611cf9919081019061542d565b905090565b50604080515f81526020810190915290565b611d1982612392565b6001600160a01b0316336001600160a01b031614611d4a576040516323065c7760e21b815260040160405180910390fd5b5f91825260286020526040909120805460ff1916911515919091179055565b5f61154d82613ed5565b5f5f611d7f85856127d4565b9150611d8a836125aa565b9050935093915050565b611d9c613af0565b601754600160a01b900460ff1615611dc75760405163698f24c760e11b815260040160405180910390fd5b600c80546001600160a01b03199081166001600160a01b038f811691909117909255600a805482168e8416179055600d805482168d8416179055600e805482168c8416179055600f805482168b84161790556010805482168a841617905560118054821689841617905560038054909116918716918217905560405163620c0c4b60e01b815230600482015263620c0c4b906024015f604051808303815f87803b158015611e73575f5ffd5b505af1158015611e85573d5f5f3e3d5ffd5b505060405163620c0c4b60e01b81526001600160a01b038f811660048301528816925063620c0c4b91506024015f604051808303815f87803b158015611ec9575f5ffd5b505af1158015611edb573d5f5f3e3d5ffd5b5050601280546001600160a01b038089166001600160a01b0319928316179092556013805488841690831617905560148054878416908316179055601580548684169216821790558b82165f90815260266020526040808220805460ff1990811660019081179092558e861684528284208054821683179055948d16835281832080548616821790559282528082208054909416909217909255517fa5d4097edda6d87cb9329af83fb3712ef77eeb13738ffe43cc35a4ce305ad9629350909150a1505050505050505050505050565b5f61154d600183613ef8565b5f818152601e6020526040812054601f80549091908110611fda57611fda6154c6565b5f918252602090912001546001600160a01b031692915050565b600d5460405163038fc50560e01b8152600481018390525f9160259183916001600160a01b03169063038fc50590602401602060405180830381865afa158015612040573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061206491906154da565b81526020019081526020015f20549050919050565b6040518181525f516020615f765f395f51905f529060200160405180910390a1600d546040516344c810b960e11b8152600481018390525f916001600160a01b031690638990217290602401602060405180830381865afa1580156120e0573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061210491906154f1565b9050600481600681111561211a5761211a614a24565b11156121e15761212982611fb7565b6001600160a01b0316637f3770366040518163ffffffff1660e01b81526004015f604051808303815f87803b158015612160575f5ffd5b505af1158015612172573d5f5f3e3d5ffd5b5050505061217f82611fb7565b604051632920c04560e21b81525f60048201525f1960248201526001600160a01b03919091169063a4830114906044015b5f604051808303815f87803b1580156121c7575f5ffd5b505af11580156121d9573d5f5f3e3d5ffd5b505050505050565b600c546040516318c85cf560e11b8152600481018490526001600160a01b0390911690633190b9ea906024016121b0565b61221a613af0565b601b805460ff1916911515919091179055565b612235613af0565b5f546001600160a01b03168061225e57604051631cffe3dd60e11b815260040160405180910390fd5b604051630368065360e61b81526001600160a01b0382169063da0194c09061228c903090889060040161550c565b5f604051808303815f87803b1580156122a3575f5ffd5b505af11580156122b5573d5f5f3e3d5ffd5b5050604051631182550160e11b81526001600160a01b0384169250632304aa0291506122e79030908790600401615532565b5f604051808303815f87803b1580156122fe575f5ffd5b505af1158015612310573d5f5f3e3d5ffd5b505060405163235d10c560e21b81526001600160a01b0384169250638d74431491506123429030908690600401615532565b5f604051808303815f87803b158015612359575f5ffd5b505af115801561236b573d5f5f3e3d5ffd5b5050505050505050565b61237d613af0565b6017805460ff60a01b1916600160a01b179055565b5f61154d82613fe6565b6123a4613af0565b6001600160a01b03165f908152602660205260409020805460ff19166001179055565b600354604051636a9412bd60e01b81526001600160a01b0383811660048301525f921690636a9412bd90602401602060405180830381865afa15801561240f573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061154d91906154da565b61243b613af0565b61245671721c310194ccfc01e523fc93c9cccfa2a0ac612cd5565b604051630368065360e61b815271721c310194ccfc01e523fc93c9cccfa2a0ac9063da0194c09061248e90309060019060040161550c565b5f604051808303815f87803b1580156124a5575f5ffd5b505af11580156124b7573d5f5f3e3d5ffd5b5050604051631182550160e11b815271721c310194ccfc01e523fc93c9cccfa2a0ac9250632304aa0291506124f3903090600190600401615532565b5f604051808303815f87803b15801561250a575f5ffd5b505af1158015611b8a573d5f5f3e3d5ffd5b6125283060075461401d565b6040515f81525f516020615f765f395f51905f529060200160405180910390a16040517fa5d4097edda6d87cb9329af83fb3712ef77eeb13738ffe43cc35a4ce305ad962905f90a1565b5f8161258557638f4eb6045f526004601cfd5b673ec412a9852d173d60c11b601c52815f5263ffffffff601c600c2054169050919050565b5f6125b3613c51565b5f6125c060056001614072565b90506125cc600161409d565b915061261882826125e06040870187615554565b808060200260200160405190810160405280939291908181526020018383602002808284375f920191909152503392506140df915050565b600d5461262e906001600160a01b031683614131565b600d546040516316beb6ed60e31b81525f916001600160a01b03169063b5f5b768906126609087908790600401615971565b6020604051808303815f875af115801561267c573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906126a09190615a9c565b60035460405163620c0c4b60e01b81526001600160a01b03808416600483015292935091169063620c0c4b906024015f604051808303815f87803b1580156126e6575f5ffd5b505af11580156126f8573d5f5f3e3d5ffd5b505050506001600160a01b038181165f818152601d60209081526040808320889055601f8054898552601e9093528184208390556001830181559092527fa03837a25210ee280c2113ff4b77ca23440b19d4866cca721c801278fd08d8070180546001600160a01b0319168317905560145490516368f4fa1560e11b815260048101929092529091169063d1e9f42a906024015f604051808303815f87803b1580156127a2575f5ffd5b505af11580156127b4573d5f5f3e3d5ffd5b5050505050506127c2613e23565b919050565b6127cf613af0565b600955565b5f8281523360205260409020806127fe5760405163544c7d3160e01b815260040160405180910390fd5b5f818152602560205260409020541561282a576040516395147ebd60e01b815260040160405180910390fd5b5f8181526025602090815260409182902084905581518381529081018490527fbc8089ccbf636c3a13c8b652d29b1964432b64ff07c7934a26642540ada031e5910160405180910390a192915050565b612882613af0565b61288b826141df565b915080601c5f8460068111156128a3576128a3614a24565b60068111156128b4576128b4614a24565b81526020019081526020015f208190555080601c5f8460068111156128db576128db614a24565b6128e6906001615ab7565b60068111156128f7576128f7614a24565b600681111561290857612908614a24565b600681111561291957612919614a24565b815260208101919091526040015f20555050565b612935613af0565b600a5f9054906101000a90046001600160a01b03166001600160a01b031663528cfa986040518163ffffffff1660e01b8152600401602060405180830381865afa158015612985573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906129a991906154da565b8111156129c95760405163715622cd60e01b815260040160405180910390fd5b60188190556040518181527f2958a951faa12b8e2b4155e8088b407323ac9bc01f6cc3bebba296d52f098ef6906020015b60405180910390a150565b5f612a0f5f613ed5565b612a1857505f90565b611cf95f612392565b60606006805461156290615342565b612a38613af0565b601a8190556040518181527f68d8df032b39b8d8b77703e359661caae062480d79137fe74cf12e960ff044b0906020016129fa565b600b818154811061163c575f80fd5b5f80546001600160a01b0316156119de575f54604051635caaa2a960e11b81523060048201526001600160a01b0390911690639445f53090829063b955455290602401606060405180830381865afa158015612ada573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612afe91906153be565b60409081015190516001600160e01b031960e084901b1681526001600160781b0390911660048201526001600160a01b038516602482015260440161199f565b801515905081601c52670a5a2e7a00000000600852335f52806030600c2055805f528160601b60601c337f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c3160205fa35050565b5f8181526024602052604081205490612ba983613ed5565b8015612bc257505f8381526023602052604090205460ff165b612bcc5730612bd5565b612bd583612392565b9050915091565b5f8281526020812090612bf882335f9182526020526040902090565b5f8181526025602052604081205491925003612c2757604051635cfeb6a760e11b815260040160405180910390fd5b600d54604051633f0cd3a360e21b815260048101839052602481018690526001600160a01b039091169063fc334e8c906044015f604051808303815f87803b158015612c71575f5ffd5b505af1158015612c83573d5f5f3e3d5ffd5b505050506001831015612c965750505050565b611b8a83612079565b60408051838152602081018390527f6bd5c950a8d8df17f772f5af37cb3655737899cbf903264b9795592da439661c91016114ff565b612cdd613af0565b5f6001600160a01b0382163b15612d56576040516301ffc9a760e01b81525f60048201526001600160a01b038316906301ffc9a790602401602060405180830381865afa925050508015612d4e575060408051601f3d908101601f19168201909252612d4b91810190615412565b60015b15612d565790505b6001600160a01b03821615801590612d6c575080155b15612d8a576040516332483afb60e01b815260040160405180910390fd5b5f54604080516001600160a01b03928316815291841660208301527fcc5dc080ff977b3c3a211fa63ab74f90f658f5ba9d3236e92c8f59570f442aac910160405180910390a1505f80546001600160a01b0319166001600160a01b0392909216919091179055565b612dfa613c51565b5f8181526023602052604090205460ff1615612e295760405163d7b5d6a960e01b815260040160405180910390fd5b600d546040516344c810b960e11b8152600481018390525f916001600160a01b031690638990217290602401602060405180830381865afa158015612e70573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612e9491906154f1565b90505f816006811115612ea957612ea9614a24565b03612ec7576040516303edc22f60e01b815260040160405180910390fd5b5f5f612ed483600161421c565b91509150612ee36001836142a0565b600a54604051631c97e03560e31b8152600481018690525f916001600160a01b03169063e4bf01a890602401602060405180830381865afa158015612f2a573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612f4e91906154da565b90508015612fb657600a546040516340927f5360e11b8152600481018790523060248201526001600160a01b0390911690638124fea6906044015f604051808303815f87803b158015612f9f575f5ffd5b505af1158015612fb1573d5f5f3e3d5ffd5b505050505b6014545f86815260208052604080822054905163bf2bf05b60e01b81526001600160a01b039182166004820152921691829063bf2bf05b90602401602060405180830381865afa15801561300c573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906130309190615a9c565b604051631c65159f60e31b81526001600160a01b0380831660048301529192509083169063e328acf890602401602060405180830381865afa158015613078573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061309c9190615412565b1561312f575f60026130ae8587615ab7565b6130b8919061539f565b60405163049b228560e51b81526001600160a01b03848116600483015291925061312d9185169063936450a090602401602060405180830381865afa158015613103573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906131279190615a9c565b826142c9565b505b5050505f848152602360205260409020805460ff19169215159290921790915550611b0f9050613e23565b335f81815260216020908152604091829020805460ff19168515159081179091558251938452908301527febb5ae49e62e9a9fb3bf3efd00ccc2800074fb474c4f98bbbf77f358b82b8ac991016129fa565b60035460405163bffa55d560e01b81523360048201526001600160a01b039091169063bffa55d5906024016124f3565b6131e4613af0565b612710816001600160601b031611156132105760405163715622cd60e01b815260040160405180910390fd5b601980546bffffffffffffffffffffffff19166001600160601b0383169081179091556040519081527f2cdb3d69ef93931e633b8fc2d0573daddc8e96ba97ed640d25dcb4527e3001b2906020016129fa565b61326e858585611707565b833b156132b6576132b685858585858080601f0160208091040260200160405190810160405280939291908181526020018383808284375f92019190915250613e4a92505050565b5050505050565b604080516060810182525f8082526020820181905291810182905290546001600160a01b031615613351575f54604051635caaa2a960e11b81523060048201526001600160a01b039091169063b955455290602401606060405180830381865afa15801561332d573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611cf991906153be565b50604080516060810182525f808252602082018190529181019190915290565b60605f61337d83612392565b506133888333614308565b92509050806133a95760405162393acd60e91b815260040160405180910390fd5b50919050565b60408051601f1960208402603f011681019091528181525f5f5f5b848110156134405760608686838181106133e6576133e66154c6565b9050602002013592506133f883613ed5565b6134025750613438565b61340c8333614308565b90945090508315613436578085838151811061342a5761342a6154c6565b60200260200101819052505b505b6001016133ca565b50505092915050565b5f546060906001600160a01b031615611cfe575f54604051635caaa2a960e11b81523060048201526001600160a01b03909116906317e94a6c90829063b955455290602401606060405180830381865afa1580156134a9573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906134cd91906153be565b60409081015190516001600160e01b031960e084901b1681526001600160781b039091166004820152602401611cb8565b61350782612392565b6001600160a01b0316336001600160a01b031614613538576040516323065c7760e21b815260040160405180910390fd5b5f9182526027602090815260408084206001600160a01b0390931684529190529020805460ff19166001179055565b61356f613af0565b600891909155600755565b60115460408051636a608fcd60e01b815290516060926001600160a01b031691636a608fcd916004808301925f9291908290030181865afa1580156135c1573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052611cf99190810190615aca565b6135f182612392565b6001600160a01b0316336001600160a01b031614613622576040516323065c7760e21b815260040160405180910390fd5b600d546040516344c810b960e11b8152600481018490525f916001600160a01b031690638990217290602401602060405180830381865afa158015613669573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061368d91906154f1565b905060018160068111156136a3576136a3614a24565b10806136c0575060048160068111156136be576136be614a24565b115b156136de576040516319b06a5d60e31b815260040160405180910390fd5b600d54604051637d09e77360e11b81526001600160a01b039091169063fa13cee6906137109086908690600401615bc7565b5f604051808303815f87803b158015613727575f5ffd5b505af1158015613739573d5f5f3e3d5ffd5b505050505f516020615f765f395f51905f528360405161375b91815260200190565b60405180910390a1505050565b613770613af0565b601680546001600160a01b038085166001600160a01b0319928316811790935560178054918516919092161790557f26ffcfca571f5ed5511d2503c2136e72442828f896bfcc36031f46c0068dc7a190158015906137d657506001600160a01b03821615155b60405190151581526020016114ff565b6137ee613af0565b6137f784612cd5565b604051630368065360e61b81526001600160a01b0385169063da0194c090613825903090879060040161550c565b5f604051808303815f87803b15801561383c575f5ffd5b505af115801561384e573d5f5f3e3d5ffd5b5050604051631182550160e11b81526001600160a01b0387169250632304aa0291506138809030908690600401615532565b5f604051808303815f87803b158015613897575f5ffd5b505af11580156138a9573d5f5f3e3d5ffd5b505060405163235d10c560e21b81526001600160a01b0387169250638d74431491506123429030908590600401615532565b6138e483612392565b6001600160a01b0316336001600160a01b031614613915576040516323065c7760e21b815260040160405180910390fd5b600d54604051600162447c5b60e11b031981526001600160a01b039091169063ff77074a9061394c90869086908690600401615bdf565b5f604051808303815f87803b158015613963575f5ffd5b505af1158015613975573d5f5f3e3d5ffd5b5050505060018310156139af576040517fa5d4097edda6d87cb9329af83fb3712ef77eeb13738ffe43cc35a4ce305ad962905f90a1505050565b5f838152601e6020526040812054601f805490919081106139d2576139d26154c6565b5f91825260208220015460408051633f9bb81b60e11b815290516001600160a01b0390921693508392637f3770369260048084019382900301818387803b158015612359575f5ffd5b5f5f613a2960036001614072565b9050613a35600161409d565b9150613a4682826125e08680615554565b600d546001600160a01b031663d7ed289a613a618580615554565b6020870135613a736040890189615c0f565b88606001886040518763ffffffff1660e01b8152600401613a9996959493929190615cbc565b5f604051808303815f87803b158015613ab0575f5ffd5b505af1158015613ac2573d5f5f3e3d5ffd5b5050505f838152602460205260409020839055506133a9613aea610100850160e08601614919565b83614405565b613af95f612392565b6001600160a01b0316336001600160a01b031614613b2a576040516323065c7760e21b815260040160405180910390fd5b565b5f6001600160e01b031982166310c8aba560e31b148061154d575061154d826301ffc9a760e09190911c9081146380ac58cd821417635b5e139f9091141790565b5f6001600160e01b0319821663152a902d60e11b148061154d57506301ffc9a760e01b6001600160e01b031983161461154d565b5f1960601c82811692508381169350815f5283673ec412a9852d173d60c11b17601c5260205f208201820180548216915081613be45763ceea21b65f526004601cfd5b818514851517613c0857815f526030600c2054613c0857634b6e7f185f526004601cfd5b6001018390558183827f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9255f38a450505050565b61181183838361441e565b611811838383614474565b7fc2d81f23b9a7981cf4e6820cb2e22f71e8713048cbc161918592a76ba052d35e805c905f198201613cca5760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064015b60405180910390fd5b6001815d5050565b6060815f613ce08683614072565b90505f613cec8361409d565b60408051603f6020870201601f1916810190915284815294509050613d0f61488b565b5f5b84811015613e1757878782818110613d2b57613d2b6154c6565b9050602002810190613d3d9190615d0a565b613d4690615d9e565b9150613d578385845f0151336140df565b600d5f9054906101000a90046001600160a01b03166001600160a01b03166387767da78a845f0151856020015186604001518760600151896040518763ffffffff1660e01b8152600401613db096959493929190615e8f565b5f604051808303815f87803b158015613dc7575f5ffd5b505af1158015613dd9573d5f5f3e3d5ffd5b50505050613deb826080015184614405565b82806001019350868281518110613e0457613e046154c6565b6020908102919091010152600101613d11565b50505050509392505050565b7fc2d81f23b9a7981cf4e6820cb2e22f71e8713048cbc161918592a76ba052d35e5f815d50565b60405163150b7a028082523360208301528560601b60601c604083015283606083015260808083015282518060a08401528015613e91578060c08401826020870160045afa505b60208360a48301601c86015f8a5af1613eb2573d15613eb2573d5f843e3d83fd5b508060e01b8251146121d95763d1a57ed65f526004601cfd5b611b0f5f826144bb565b5f818152673ec412a9852d173d60c11b601c52602090208101015460601b151590565b5f5f613f05846001614072565b9050613f11600161409d565b9150613f2282826125e08680615554565b600d546001600160a01b03166387767da785613f3e8680615554565b6020880135613f5060408a018a615c0f565b89606001896040518863ffffffff1660e01b8152600401613f779796959493929190615f16565b5f604051808303815f87803b158015613f8e575f5ffd5b505af1158015613fa0573d5f5f3e3d5ffd5b505f9250613fb2915085905080615554565b90501115613fcb575f8281526024602052604090208290555b613fdf613aea610100850160e08601614919565b5092915050565b5f818152673ec412a9852d173d60c11b601c526020902081018101546001600160a01b0316806127c25763ceea21b65f526004601cfd5b6140278282614592565b6040516001600160601b03821681526001600160a01b038316907f8a8bae378cb731c5c40b632330c6836c2f916f48edb967699c86736f9a6a76ef9060200160405180910390a25050565b5f5f5f3411614081575f614084565b60015b9050614090848461421c565b509150613fdf81836142a0565b60408051808201909152600480546001600160801b038082168501808552600160801b9283900482169586016020909501859052811693160291909117905590565b5f848152602360205260409020805460ff1916841515179055614102828261468f565b5f93845260208052604090932080546001600160a01b0319166001600160a01b03909416939093179092555050565b61413c5f8383613c3b565b8160601b60601c9150805f52673ec412a9852d173d60c11b601c5260205f208101810180548060601b156141775763c991cbb15f526004601cfd5b831790555f829052601c600c20805460010163ffffffff811684026141ab5767ea553b3401336cea841560021b526004601cfd5b905580825f7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8138a46116295f8383613c46565b5f808260068111156141f3576141f3614a24565b03614211576040516303edc22f60e01b815260040160405180910390fd5b506001811615900390565b5f5f82601c5f86600681111561423457614234614a24565b600681111561424557614245614a24565b81526020019081526020015f205461425d9190615388565b9050600181101561427157600191506118bb565b80341015614281575f91506118bb565b8034111561429557614295338234036146c9565b600191509250929050565b8180156142ab575080155b156116295760405163b6a82ee760e01b815260040160405180910390fd5b804710156142de5763b12d13eb5f526004601cfd5b5f385f388486620186a0f161162957815f526073600b5360ff6020536016600b82f0611629573838fd5b5f8281526028602052604081205460609060ff16801561437f57506001600160a01b03831632148061435157506001600160a01b0383165f9081526026602052604090205460ff165b8061437d57505f8481526027602090815260408083206001600160a01b038716845290915290205460ff165b155b1561438c575f91506118bb565b60115460405163097580f960e01b8152600481018690526001916001600160a01b03169063097580f9906024015f60405180830381865afa1580156143d3573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f191682016040526143fa9190810190615aca565b915091509250929050565b611629828260405180602001604052805f81525061472a565b6001600160a01b0383811615908316158180156144385750805b1561445657604051635cbd944160e01b815260040160405180910390fd5b8115614462575b6132b6565b8061445d576132b63386868634614747565b6001600160a01b03838116159083161581801561448e5750805b156144ac57604051635cbd944160e01b815260040160405180910390fd5b8161445d578061445d576132b6565b5f6144c582612392565b90506144d2815f84613c3b565b505f8181526001600160a01b03928316673ec412a9852d173d60c11b8117601c5260209091208201820180549193821691826145155763ceea21b65f526004601cfd5b825f52816001015480861484871417861517614542576030600c205461454257634b6e7f185f526004601cfd5b801561454f575f83600101555b5082189055601c600c2080545f19019055815f827fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8238a4611811815f84613c46565b6127106001600160601b03821611156146005760405162461bcd60e51b815260206004820152602a60248201527f455243323938313a20726f79616c7479206665652077696c6c206578636565646044820152692073616c65507269636560b01b6064820152608401613cc1565b6001600160a01b0382166146565760405162461bcd60e51b815260206004820152601960248201527f455243323938313a20696e76616c6964207265636569766572000000000000006044820152606401613cc1565b604080518082019091526001600160a01b039092168083526001600160601b039091166020909201829052600160a01b90910217600155565b5f5f5b8351811015611b8a578381815181106146ad576146ad6154c6565b602002602001015191506146c182846147c6565b600101614692565b600354604051632d806cdd60e01b81526001600160a01b03848116600483015290911690632d806cdd9083906024015f604051808303818588803b15801561470f575f5ffd5b505af1158015614721573d5f5f3e3d5ffd5b50505050505050565b6147348383614131565b823b15611811576118115f848484613e4a565b5f546001600160a01b0316156132b6575f5460405163050bf71960e31b81526001600160a01b038781166004830152868116602483015285811660448301529091169063285fb8c8906064015f6040518083038186803b1580156147a9575f5ffd5b505afa1580156147bb573d5f5f3e3d5ffd5b505050505050505050565b6147cf82613ed5565b15806147f557506147df82612392565b6001600160a01b0316816001600160a01b031614155b1561162957600c5460405163ae43a60160e01b81526001600160a01b038381166004830152602482018590529091169063ae43a601906044016020604051808303815f875af115801561484a573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061486e9190615412565b611629576040516338b7caf560e11b815260040160405180910390fd5b6040518060a00160405280606081526020015f81526020016148c860405180606001604052805f1515815260200160608152602001606081525090565b8152604080516080810182525f8082526020828101829052928201819052606082015291019081525f60209091015290565b6001600160a01b0381168114611b0f575f5ffd5b80356127c2816148fa565b5f60208284031215614929575f5ffd5b81356116e0816148fa565b5f60208284031215614944575f5ffd5b81356001600160e01b0319811681146116e0575f5ffd5b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b602081525f6116e0602083018461495b565b5f602082840312156149ab575f5ffd5b5035919050565b5f5f604083850312156149c3575f5ffd5b82356149ce816148fa565b946020939093013593505050565b5f5f5f606084860312156149ee575f5ffd5b83356149f9816148fa565b92506020840135614a09816148fa565b91506040840135614a19816148fa565b809150509250925092565b634e487b7160e01b5f52602160045260245ffd5b60078110611b0f57611b0f614a24565b60208101614a5583614a38565b91905290565b5f5f5f60608486031215614a6d575f5ffd5b8335614a78816148fa565b92506020840135614a88816148fa565b929592945050506040919091013590565b5f5f60408385031215614aaa575f5ffd5b50508035926020909101359150565b5f5f83601f840112614ac9575f5ffd5b5081356001600160401b03811115614adf575f5ffd5b6020830191508360208260051b85010111156118bb575f5ffd5b5f5f60208385031215614b0a575f5ffd5b82356001600160401b03811115614b1f575f5ffd5b614b2b85828601614ab9565b90969095509350505050565b5f8151808452602084019350602083015f5b82811015614b67578151865260209586019590910190600101614b49565b5093949350505050565b602081525f6116e06020830184614b37565b8015158114611b0f575f5ffd5b5f5f60408385031215614ba1575f5ffd5b823591506020830135614bb381614b83565b809150509250929050565b602080825282518282018190525f918401906040840190835b81811015614bfe5783516001600160a01b0316835260209384019390920191600101614bd7565b509095945050505050565b5f61012082840312156133a9575f5ffd5b5f5f5f60608486031215614c2c575f5ffd5b833592506020840135915060408401356001600160401b03811115614c4f575f5ffd5b614c5b86828701614c09565b9150509250925092565b5f5f5f5f5f5f5f5f5f5f5f5f6101808d8f031215614c81575f5ffd5b8c35614c8c816148fa565b9b5060208d0135614c9c816148fa565b9a5060408d0135614cac816148fa565b995060608d0135614cbc816148fa565b985060808d0135614ccc816148fa565b9750614cda60a08e0161490e565b9650614ce860c08e0161490e565b9550614cf660e08e0161490e565b9450614d056101008e0161490e565b9350614d146101208e0161490e565b9250614d236101408e0161490e565b9150614d326101608e0161490e565b90509295989b509295989b509295989b565b5f60208284031215614d54575f5ffd5b81356001600160401b03811115614d69575f5ffd5b820161010081850312156116e0575f5ffd5b5f60208284031215614d8b575f5ffd5b81356116e081614b83565b60078110611b0f575f5ffd5b6001600160781b0381168114611b0f575f5ffd5b5f5f5f60608486031215614dc8575f5ffd5b8335614dd381614d96565b92506020840135614de381614da2565b91506040840135614a1981614da2565b5f5f60408385031215614e04575f5ffd5b823591506020830135614bb3816148fa565b5f60208284031215614e26575f5ffd5b81356001600160401b03811115614e3b575f5ffd5b614e4784828501614c09565b949350505050565b5f5f60408385031215614e60575f5ffd5b82356149ce81614d96565b5f5f60408385031215614e7c575f5ffd5b8235614e87816148fa565b91506020830135614bb381614b83565b80356001600160601b03811681146127c2575f5ffd5b5f60208284031215614ebd575f5ffd5b6116e082614e97565b5f5f5f5f5f60808688031215614eda575f5ffd5b8535614ee5816148fa565b94506020860135614ef5816148fa565b93506040860135925060608601356001600160401b03811115614f16575f5ffd5b8601601f81018813614f26575f5ffd5b80356001600160401b03811115614f3b575f5ffd5b886020828401011115614f4c575f5ffd5b959894975092955050506020019190565b81516060820190614f6d81614a38565b808352506001600160781b0360208401511660208301526001600160781b03604084015116604083015292915050565b5f602082016020835280845180835260408501915060408160051b8601019250602086015f5b82811015614ff457603f19878603018452614fdf85835161495b565b94506020938401939190910190600101614fc3565b50929695505050505050565b5f5f60408385031215615011575f5ffd5b823561501c816148fa565b91506020830135614bb3816148fa565b5f6020828403121561503c575f5ffd5b81356116e081614d96565b634e487b7160e01b5f52604160045260245ffd5b604051606081016001600160401b038111828210171561507d5761507d615047565b60405290565b60405160a081016001600160401b038111828210171561507d5761507d615047565b604051601f8201601f191681016001600160401b03811182821017156150cd576150cd615047565b604052919050565b5f6001600160401b038211156150ed576150ed615047565b50601f01601f191660200190565b5f82601f83011261510a575f5ffd5b813561511d615118826150d5565b6150a5565b818152846020838601011115615131575f5ffd5b816020850160208301375f918101602001919091529392505050565b5f6001600160401b0382111561516557615165615047565b5060051b60200190565b5f6060828403121561517f575f5ffd5b61518761505b565b9050813561519481614b83565b815260208201356001600160401b038111156151ae575f5ffd5b6151ba848285016150fb565b60208301525060408201356001600160401b038111156151d8575f5ffd5b8201601f810184136151e8575f5ffd5b80356151f66151188261514d565b8082825260208201915060208360051b850101925086831115615217575f5ffd5b602084015b838110156152575780356001600160401b03811115615239575f5ffd5b615248896020838901016150fb565b8452506020928301920161521c565b5060408501525091949350505050565b5f5f60408385031215615278575f5ffd5b8235915060208301356001600160401b03811115615294575f5ffd5b6152a08582860161516f565b9150509250929050565b5f5f5f5f608085870312156152bd575f5ffd5b84356152c8816148fa565b935060208501356152d881614d96565b925060408501356152e881614da2565b915060608501356152f881614da2565b939692955090935050565b5f5f5f60608486031215615315575f5ffd5b8335925060208401356001600160401b03811115615331575f5ffd5b840160608187031215614a09575f5ffd5b600181811c9082168061535657607f821691505b6020821081036133a957634e487b7160e01b5f52602260045260245ffd5b634e487b7160e01b5f52601160045260245ffd5b808202811582820484141761154d5761154d615374565b5f826153b957634e487b7160e01b5f52601260045260245ffd5b500490565b5f60608284031280156153cf575f5ffd5b506153d861505b565b82516153e381614d96565b815260208301516153f381614da2565b6020820152604083015161540681614da2565b60408201529392505050565b5f60208284031215615422575f5ffd5b81516116e081614b83565b5f6020828403121561543d575f5ffd5b81516001600160401b03811115615452575f5ffd5b8201601f81018413615462575f5ffd5b80516154706151188261514d565b8082825260208201915060208360051b850101925086831115615491575f5ffd5b6020840193505b828410156154bc5783516154ab816148fa565b825260209384019390910190615498565b9695505050505050565b634e487b7160e01b5f52603260045260245ffd5b5f602082840312156154ea575f5ffd5b5051919050565b5f60208284031215615501575f5ffd5b81516116e081614d96565b6001600160a01b03831681526040810161552583614a38565b8260208301529392505050565b6001600160a01b039290921682526001600160781b0316602082015260400190565b5f5f8335601e19843603018112615569575f5ffd5b8301803591506001600160401b03821115615582575f5ffd5b6020019150600581901b36038213156118bb575f5ffd5b5f8235607e198336030181126155ad575f5ffd5b90910192915050565b5f5f8335601e198436030181126155cb575f5ffd5b83016020810192503590506001600160401b038111156155e9575f5ffd5b8036038213156118bb575f5ffd5b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b5f61562a82836155b6565b6080855261563c6080860182846155f7565b91505061564c60208401846155b6565b858303602087015261565f8382846155f7565b9250505061567060408401846155b6565b85830360408701526156838382846155f7565b92505050606083013561569581614b83565b8015156060860152508091505092915050565b5f5f8335601e198436030181126156bd575f5ffd5b83016020810192503590506001600160401b038111156156db575f5ffd5b8060051b36038213156118bb575f5ffd5b8183525f6001600160fb1b03831115615703575f5ffd5b8260051b80836020870137939093016020019392505050565b5f8235605e198336030181126155ad575f5ffd5b5f813561573c81614b83565b1515835261574d60208301836155b6565b606060208601526157626060860182846155f7565b91505061577260408401846156a8565b8583036040870152808352602080840190600583901b850101835f5b848110156157cc57868303601f190184526157a982876155b6565b6157b48582846155f7565b6020968701969095509390930192505060010161578e565b509098975050505050505050565b5f823561017e198336030181126155ad575f5ffd5b8035600281106127c2575f5ffd5b6002811061580d5761580d614a24565b9052565b803582526020808201359083015260408082013590830152606080820135908301526080808201359083015260a0808201359083015260c080820135908301525f61585e60e083016157ef565b61586b60e08501826157fd565b5061010082810135908401526158846101208301614e97565b6001600160601b03166101208401526158a16101408301836156a8565b6101806101408601526158b9610180860182846156ec565b9150506158ca6101608401846156a8565b8583036101608701526154bc8382846156ec565b5f8235603e198336030181126155ad575f5ffd5b5f6040830161590183846156a8565b6040865291829052905f90606086015b81831015615942578335615924816148fa565b6001600160a01b031681526020938401936001939093019201615911565b61594f60208701876156a8565b9450925086810360208801526159668185856156ec565b979650505050505050565b604081525f6159808485615599565b610120604084015261599661016084018261561f565b6020860135606085015290506159af60408601866156a8565b848303603f190160808601526159c68382846156ec565b925050506159d7606086018661571c565b838203603f190160a08501526159ed8282615730565b9150506159fd60808601866157da565b838203603f190160c0850152615a138282615811565b915050615a2360a08601866158de565b838203603f190160e0850152615a3982826158f2565b915050615a4860c0860161490e565b6001600160a01b0381166101008501525060e0850135610120840152615a726101008601866155b6565b848303603f1901610140860152615a8a8382846155f7565b93505050508260208301529392505050565b5f60208284031215615aac575f5ffd5b81516116e0816148fa565b8082018082111561154d5761154d615374565b5f60208284031215615ada575f5ffd5b81516001600160401b03811115615aef575f5ffd5b8201601f81018413615aff575f5ffd5b8051615b0d615118826150d5565b818152856020838501011115615b21575f5ffd5b8160208401602083015e5f91810160200191909152949350505050565b8051151582525f602082015160606020850152615b5e606085018261495b565b90506040830151848203604086015281815180845260208401915060208160051b8501016020840193505f5b82811015615bbb57601f19868303018452615ba682865161495b565b60209586019594909401939150600101615b8a565b50979650505050505050565b828152604060208201525f614e476040830184615b3e565b838152606060208201525f615bf76060830185615730565b905060018060a01b0383166040830152949350505050565b5f8235605e19833603018112615c23575f5ffd5b9190910192915050565b80356001600160401b03811681146127c2575f5ffd5b80356001600160701b03811681146127c2575f5ffd5b6001600160401b03615c6a82615c2d565b1682526001600160701b03615c8160208301615c43565b1660208301526001600160701b03615c9b60408301615c43565b1660408301526060810135615caf81614b83565b8015156060840152505050565b61010081525f615cd16101008301888a6156ec565b8660208401528281036040840152615ce98187615730565b915050615cf96060830185615c59565b8260e0830152979650505050505050565b5f823560fe19833603018112615c23575f5ffd5b5f60808284031215615d2e575f5ffd5b604051608081016001600160401b0381118282101715615d5057615d50615047565b604052905080615d5f83615c2d565b8152615d6d60208401615c43565b6020820152615d7e60408401615c43565b60408201526060830135615d9181614b83565b6060919091015292915050565b5f6101008236031215615daf575f5ffd5b615db7615083565b82356001600160401b03811115615dcc575f5ffd5b830136601f820112615ddc575f5ffd5b8035615dea6151188261514d565b8082825260208201915060208360051b850101925036831115615e0b575f5ffd5b6020840193505b82841015615e2d578335825260209384019390910190615e12565b84525050506020838101359082015260408301356001600160401b03811115615e54575f5ffd5b615e603682860161516f565b604083015250615e733660608501615d1e565b6060820152615e8460e0840161490e565b608082015292915050565b615e9887614a38565b86815261012060208201525f615eb2610120830188614b37565b8660408401528281036060840152615eca8187615b3e565b85516001600160401b0316608085015260208601516001600160701b0390811660a086015260408701511660c0850152606090950151151560e084015250506101000152949350505050565b615f1f88614a38565b87815261012060208201525f615f3a6101208301888a6156ec565b8660408401528281036060840152615f528187615730565b915050615f626080830185615c59565b826101008301529897505050505050505056fef8e1a15aba9398e019f0b49df1a4fde98ee17ae345cb5f6b5e2c27f5033e8ce7a2646970667358221220179eea022ae2dd0d6966063ed7546aaf6511416817812b777ec8a5df58e219a564736f6c634300081c0033
Verified Source Code Full Match
Compiler: v0.8.28+commit.7893614a
EVM: cancun
Optimization: Yes (200 runs)
Hub.sol 676 lines
// SPDX-License-Identifier: HVPL - HEXED VIRAL PUBLIC LICENSE
pragma solidity 0.8.28;
/*
@@@@@@@ @@@@@@@ @@@@@@@@ @@@@@@ @@@@@@@@@@
@@@@@@@@ @@@@@@@@ @@@@@@@@ @@@@@@@@ @@@@@@@@@@@
@@! @@@ @@! @@@ @@! @@! @@@ @@! @@! @@!
!@! @!@ !@! @!@ !@! !@! @!@ !@! !@! !@!
@!@ !@! @!@!!@! @!!!:! @!@!@!@! @!! !!@ @!@
!@! !!! !!@!@! !!!!!: !!!@!!!! !@! ! !@!
!!: !!! !!: :!! !!: !!: !!! !!: !!: by no_side666
:!: !:! :!: !:! :!: :!: !:! :!: :!:
:::: :: :: ::: :: :::: :: ::: ::: :: dreamstack.xyz
:: : : : : : : :: :: : : : : :
@@@@@@ @@@@@@@ @@@@@@ @@@@@@@ @@@ @@@ @@@ @@@ @@@@@@
@@@@@@@ @@@@@@@ @@@@@@@@ @@@@@@@@ @@@ @@@ @@@ @@@ @@@@@@@@
!@@ @@! @@! @@@ !@@ @@! !@@ @@! @@@ @@@
!@! !@! !@! @!@ !@! !@! @!! !@! @!@ @!@
!!@@!! @!! @!@!@!@! !@! @!@@!@! @!@ !@! !!@
!!@!!! !!! !!!@!!!! !!! !!@!!! !@! !!! !!:
!:! !!: !!: !!! :!! !!: :!! :!: !!: !:!
!:! :!: :!: !:! :!: :!: !:! ::!!:! :!:
:::: :: :: :: ::: ::: ::: :: ::: :::: :: :::::
:: : : : : : : :: :: : : ::: : :: : :::
**/
import "lib/solady/src/utils/LibBitmap.sol";
import "lib/solady/src/utils/SafeTransferLib.sol";
import "./modded/creator-token-standards/ERC721C.sol";
import "./modded/creator-token-standards/BasicRoyalties.sol";
import "./modded/openzeppelin/ReentrancyGuard.sol"; // this version uses tstore
import "./Refunds.sol";
import "./Interfaces.sol";
import "./Structs.sol";
import "./Withdrawable.sol"; // for stray tokens
import "./LibPack.sol";
import "./Errors.sol";
import "./Common.sol";
import "./LICENSE.sol";
// The main Hub for DreamStack V2
contract Hub is IHub, ERC721C, BasicRoyalties, Withdrawable, Refunds, ReentrancyGuard {
Supply private _supply;
string private _name;
string private _symbol;
uint256 public constant HUB_OWNER_TOKENID = 0;
uint256 public constant HUB_DIVISOR = 100_00;
uint256 public hubRoyalty = 2_50;
uint256 public hubPercentage = 2_50;
uint256 public blockHashBeaconRewardPercentage = 10_00;
IPaymentFilterer public paymentFilterer;
address[] public nftTemplates; // allow for versioning of nftTemplates since we may find from users that they desire nft templates with added/reduced functionality or improved implementations
IPremierAccessERC1155 public premierAccess;
IImmortalizer public immortalizer;
IImmortalStorage public immortalStorage;
IRobustRenderer public robustRenderer;
IValidityLens public validityLens;
IURI public uriRenderer;
IBlockHashBeacon public blockHashBeacon;
IDiscountDemon public discountDemon;
ICrews public crews;
IDreams public dreams;
IBridging public bridging;
IProver public prover;
bool public frozen = false;
uint256 public freelancerPercentage;
uint96 public minFeeNumerator; // to ensure downstream elements get recognized
uint256 public maxBurnWindow;
bool public framesAnimated;
mapping(Type => uint256) public immortalizeFee; // = type(uint256).max; // setImmortalizeFee will unlock!
mapping(INFT => uint256) public collectionCurationTokenId;
mapping(uint256 => uint256) private _collectionIdxs;
INFT[] public allCollections;
mapping(uint256 => address) public originator;
mapping(address => bool) public accountIncentivizedReleaseOptedOut;
mapping(uint256 => bool) public curationTokenFramedOptedIn;
mapping(uint256 => bool) public paidImmortalizeFee;
mapping(uint256 => uint256) private _beneficiaries;
mapping(bytes32 => uint256) public pledgedRevealTimestamps;
mapping(address => bool) public platformApprovedWrapper;
mapping(uint256 => mapping(address => bool)) public ownerApprovedTokenWrapper;
mapping(uint256 => bool) public restrictedViewing;
// didn't do bitmap in above maps since they are all staticcall render related so would
// not benefit greatly from gas vs bytecode size
LibBitmap.Bitmap private _burned;
event FreelancerPercentageSet(uint256 newFreelancerPercentage);
event MinFeeNumeratorSet(uint96 newFeeNumerator);
event MaxBurnWindowSet(uint256 newMax);
event NewNFTTemplate(uint256 idx, address newNFTTemplate);
event AccountOptedOut(address account, bool tf);
event NewEncryptedReference(bytes32 encrypted, uint256 pledgedRevealTimestamp);
event BridgingSet(bool active);
event ContractURIUpdated();
event MetadataUpdate(uint256 curationTokenId);
event BatchMetadataUpdate(uint256 fromTokenId, uint256 toTokenId);
event TreasuryRejectedTribute(uint256 amount);
/// forge-lint: disable-next-line(unsafe-typecast)
constructor(address initialOwner) payable BasicRoyalties(initialOwner, uint96(hubRoyalty)) {
_name = "DreamStackV2";
_symbol = "DSV2";
_incrementSupply(1);
originator[HUB_OWNER_TOKENID] = initialOwner;
_mint(initialOwner, HUB_OWNER_TOKENID);
// means tabs cannot have tokenId < 1 lol
allCollections.push(INFT(address(0))); // so that getCollection will consider 0 idx as pathological
// forge-lint: disable-next-line(unsafe-typecast)
_setDefaultRoyalty(address(this), uint96(hubRoyalty));
}
receive() external payable {}
fallback() external payable {}
function supportsInterface(bytes4 interfaceId) public view virtual override(ERC721C, ERC2981) returns (bool) {
return interfaceId == type(IERC7572).interfaceId || ERC721C.supportsInterface(interfaceId)
|| ERC2981.supportsInterface(interfaceId) || super.supportsInterface(interfaceId);
}
function name() public view override returns (string memory) {
return _name;
}
function symbol() public view override returns (string memory) {
return _symbol;
}
function freeze() external {
_onlyOwner();
frozen = true;
}
function setComponents(
IPremierAccessERC1155 premierAccess_,
IPaymentFilterer unifiedPaymentFilterer_,
IImmortalizer immortalizer_,
IImmortalStorage immortalStorage_,
IRobustRenderer robustRenderer_,
IValidityLens validityLens_,
IURI uri_,
IRefunder refunder_,
IBlockHashBeacon blockHashBeacon_,
IDiscountDemon discountDemon_,
ICrews crews_,
IDreams dreams_
) external {
_onlyOwner();
// before frozen, owner can set updated components in the case of bugs etc
if (frozen) revert IsFrozen_error();
premierAccess = premierAccess_;
paymentFilterer = unifiedPaymentFilterer_;
immortalizer = immortalizer_;
immortalStorage = immortalStorage_;
robustRenderer = robustRenderer_;
validityLens = validityLens_;
uriRenderer = uri_;
refunder = refunder_;
refunder_.setCustomer(address(this));
refunder_.setCustomer(address(premierAccess_));
blockHashBeacon = blockHashBeacon_;
discountDemon = discountDemon_;
crews = crews_;
dreams = dreams_;
platformApprovedWrapper[address(robustRenderer_)] = true;
platformApprovedWrapper[address(validityLens_)] = true;
platformApprovedWrapper[address(uri_)] = true;
platformApprovedWrapper[address(dreams_)] = true;
emit ContractURIUpdated();
}
function isPaymentFilterIniter(address account) external view override returns (bool) {
return account == address(immortalizer);
}
function totalSupply() external view returns (uint256) {
return uint256(_supply.totalSupply);
}
function totalMinted() external view returns (uint256) {
return uint256(_supply.totalMinted);
}
function owner() public view override(IHub) returns (address) {
if (!_exists(HUB_OWNER_TOKENID)) return address(0);
return ownerOf(HUB_OWNER_TOKENID);
}
function contractURI() external view returns (string memory) {
// wrapping is ok
return uriRenderer.hubContractURI();
}
function tokenURI(uint256 curationTokenId) public view override(ERC721, IHub) returns (string memory ret) {
bool ok;
ownerOf(curationTokenId); // will throw if dne!
(ok, ret) = _tokenURI(curationTokenId, msg.sender);
if (!ok) revert NoWrapping_error();
}
function _tokenURI(uint256 curationTokenId, address caller) private view returns (bool ok, string memory ret) {
if (
restrictedViewing[curationTokenId]
&& !(caller == tx.origin
|| platformApprovedWrapper[caller]
|| ownerApprovedTokenWrapper[curationTokenId][caller])
) return (false, ret);
return (true, uriRenderer.hubTokenURI(curationTokenId));
}
function multiTokenURI(uint256[] calldata curationTokenIds) public view returns (string[] memory ret) {
ret = _allocateStringArr(curationTokenIds.length);
bool ok;
uint256 id;
for (uint256 i; i < curationTokenIds.length; ++i) {
string memory uri;
id = curationTokenIds[i];
if (!_exists(id)) continue; // gracefully ignores
(ok, uri) = _tokenURI(id, msg.sender);
if (ok) ret[i] = uri; // otherwise gracefully ignores
}
}
// can be called by anyone
function setDefaultRoyalty() external {
// forge-lint: disable-next-line(unsafe-typecast)
_setDefaultRoyalty(address(this), uint96(hubRoyalty));
emit MetadataUpdate(HUB_OWNER_TOKENID);
emit ContractURIUpdated();
}
function setBlockHashBeaconRewardPercentage(uint256 blockHashBeaconRewardPercentage_) external {
_onlyOwner();
blockHashBeaconRewardPercentage = blockHashBeaconRewardPercentage_;
}
function setHubValues(uint256 hubPercentage_, uint256 hubRoyalty_) external {
_onlyOwner();
hubPercentage = hubPercentage_;
hubRoyalty = hubRoyalty_;
}
function addNewNFTTemplate(address newNFTTemplate) external {
_onlyOwner();
uint256 idx = nftTemplates.length;
nftTemplates.push(newNFTTemplate);
emit NewNFTTemplate(idx, newNFTTemplate);
}
function nftTemplatesLength() external view returns (uint256) {
return nftTemplates.length;
}
// VERY convenient in the case of V2.2 etc
function setApprovedWrapper(address wrapper) external {
_onlyOwner();
platformApprovedWrapper[wrapper] = true;
}
function setRestrictedViewing(uint256 curationTokenId, bool tf) external {
if (msg.sender != ownerOf(curationTokenId)) revert NotOwner_error();
restrictedViewing[curationTokenId] = tf;
}
function setApprovedTokenWrapper(uint256 curationTokenId, address wrapper) external {
if (msg.sender != ownerOf(curationTokenId)) revert NotOwner_error();
ownerApprovedTokenWrapper[curationTokenId][wrapper] = true;
}
function setBridging(IBridging bridging_, IProver prover_) external {
_onlyOwner();
bridging = bridging_;
prover = prover_;
emit BridgingSet(address(bridging_) != address(0) && address(prover_) != address(0));
// we have bridging for the INFT's but not for these DreamStack curated components,
// since payment streams of curated components would not map properly when bridged.
// specifically if we had the PaymentFilterer check some "isBridged", then a bridged
// frame could not receive a release which would stall transfers to featuredIds.
// perhaps this feature will be in V2!!
}
function ownerOf(uint256 tokenId) public view override(IHub, ERC721) returns (address) {
return super.ownerOf(tokenId);
}
function exists(uint256 tokenId) public view returns (bool) {
return _exists(tokenId);
}
function beneficiariesOf(uint256 tokenId) public view returns (uint256 beneficiary, address holder) {
beneficiary = _beneficiaries[tokenId]; // if non-null, then is payment filterer associated with frame
holder = (_exists(tokenId) && paidImmortalizeFee[tokenId]) ? ownerOf(tokenId) : address(this);
}
function retroactivelyPayImmortalizeFee(uint256 curationTokenId) external payable nonReentrant {
if (paidImmortalizeFee[curationTokenId]) revert AlreadyPaid_error();
Type t = immortalizer.immortalizedType(curationTokenId);
if (t == Type.NONE) revert InvalidType_error();
(bool paid, uint256 cost) = _processImmortalizeFee({t: t, qty: 1});
_processPaying({paying: true, paid: paid});
uint256 releasable = paymentFilterer.releasable(curationTokenId);
if (releasable > 0) {
paymentFilterer.release(curationTokenId, address(this));
}
ICrews _crews = crews;
IERC721 crew = _crews.claimsCrew(originator[curationTokenId]);
if (_crews.isApprovedCrew(crew)) {
uint256 crewSplit = (cost + releasable) / 2;
SafeTransferLib.forceSafeTransferETH(_crews.crewTreasury(crew), crewSplit);
}
paidImmortalizeFee[curationTokenId] = paid; // now beneficiaryOf points to curationTokenId owner as target
}
function optOutIncentivizedRelease(bool optOut) external {
accountIncentivizedReleaseOptedOut[msg.sender] = optOut;
emit AccountOptedOut(msg.sender, optOut);
}
function optInCurationFramed(uint256 curationTokenId, bool optIn) external {
if (msg.sender != ownerOf(curationTokenId)) revert NotOwner_error();
curationTokenFramedOptedIn[curationTokenId] = optIn;
emit MetadataUpdate(curationTokenId);
}
function setFramesAnimated(bool animateTF) external {
_onlyOwner();
framesAnimated = animateTF;
}
function emitBatchMetadataUpdate(uint256 fromTokenId, uint256 toTokenId) external {
emit BatchMetadataUpdate(fromTokenId, toTokenId);
}
function getCollection(uint256 curationTokenId) public view returns (INFT) {
return allCollections[_collectionIdxs[curationTokenId]];
}
function allCollectionsLength() public view returns (uint256) {
return allCollections.length;
}
// note: feeDenominator is 100_00
function setMinFeeNumerator(uint96 newFeeNumerator) external {
_onlyOwner();
if (newFeeNumerator > 100_00) revert InvalidInput_error();
minFeeNumerator = newFeeNumerator;
emit MinFeeNumeratorSet(newFeeNumerator);
}
function setMaxBurnWindow(uint256 newMax) external {
_onlyOwner();
maxBurnWindow = newMax;
emit MaxBurnWindowSet(newMax);
}
function setFreelancerPercentage(uint256 newPercentage) external {
_onlyOwner();
if (newPercentage > IPaymentFilterer(paymentFilterer).BASIS()) revert InvalidInput_error();
freelancerPercentage = newPercentage;
emit FreelancerPercentageSet(newPercentage);
}
function setImmortalizeFee(Type t, uint256 newFee) external {
_onlyOwner();
t = _normalizeType(t);
immortalizeFee[t] = newFee;
immortalizeFee[Type(uint256(t) + 1)] = newFee;
}
function immortalizeTab(ImmortalizeComponentData calldata icd) external payable returns (uint256 tokenId) {
return _immortalize(Type.TAB, icd);
}
function immortalizeTabBulk(ImmortalizeComponentData[] calldata icds)
external
payable
nonReentrant
returns (uint256[] memory tokenIds)
{
// nonReentrant since is _safeMint in a loop
return _immortalizeBulk(Type.TAB, icds);
}
function immortalizeFrame(ImmortalizeComponentData calldata icd) external payable returns (uint256 tokenId) {
bool paid = _checkIfPaying({t: Type.FRAME, qty: 1});
tokenId = _incrementSupply(1);
_processToken(tokenId, paid, icd.featuredIds, msg.sender);
immortalizer.immortalizeFrame(icd.featuredIds, icd.encrypted, icd.compressed, icd.exclusivityData, tokenId);
_beneficiaries[tokenId] = tokenId; // mechanism of beneficiaryOf
_safeMint(icd.to, tokenId);
}
// FileBundle can be empty if intent is to delete current override
function setColorClassOverrides(uint256 curationTokenId, FileBundle memory compressedColorClassOverrides) external {
if (msg.sender != ownerOf(curationTokenId)) revert NotOwner_error(); // throws if DNE
Type t = immortalizer.immortalizedType(curationTokenId);
if (t < Type.TAB || t > Type.FRAME_ENCRYPTED) revert InvalidComponent_error();
immortalizer.setColorClassOverrides(curationTokenId, compressedColorClassOverrides);
emit MetadataUpdate(curationTokenId);
}
function immortalizeCollection(ImmortalizeCollectionData calldata icd)
public
payable
nonReentrant
returns (uint256 tokenId)
{
bool paid = _checkIfPaying({t: Type.COLLECTION, qty: 1});
// nonreentrant since future nftTemplate versions are in control of future hub owners
tokenId = _incrementSupply(1);
_processToken(tokenId, paid, icd.featuredIds, msg.sender);
// safeMint not needed since we know recipient
_mint(address(immortalizer), tokenId);
INFT nftClone = immortalizer.immortalizeCollection(icd, tokenId);
refunder.setCustomer(address(nftClone));
collectionCurationTokenId[nftClone] = tokenId;
_collectionIdxs[tokenId] = allCollections.length;
allCollections.push(nftClone);
crews.tryDefineCollectionCrew(nftClone);
}
function immortalizeCollectionCombined(
bytes32 encryptionPre,
uint256 pledgedRevealTimestamp_,
ImmortalizeCollectionData calldata icd
) external payable returns (bytes32 encryptionReference, uint256 tokenId) {
encryptionReference = setEncryptedRevealTime(encryptionPre, pledgedRevealTimestamp_);
tokenId = immortalizeCollection(icd);
}
function computeEncryptionReference(bytes32 encryptionPre, address account)
public
pure
returns (bytes32 encryptionReference)
{
assembly {
// efficient hashing lol
mstore(0x00, encryptionPre)
mstore(0x20, account)
encryptionReference := keccak256(0x00, 0x40)
}
}
function setEncryptedRevealTime(bytes32 encryptionPre, uint256 pledgedRevealTimestamp_)
public
returns (bytes32 encryptionReference)
{
// bound to msg.sender to prevent frontrunning griefoors
encryptionReference = computeEncryptionReference(encryptionPre, msg.sender);
if (encryptionReference == bytes32(0)) revert ZeroInput_error(); // overzealous assert
if (pledgedRevealTimestamps[encryptionReference] != 0) revert RepeatedEncryptionReference_error();
pledgedRevealTimestamps[encryptionReference] = pledgedRevealTimestamp_;
emit NewEncryptedReference(encryptionReference, pledgedRevealTimestamp_);
}
// optionalCurationTokenId is for the sake of emitting logs to trigger updates on exchanges
function reveal(bytes32 key, uint256 optionalCurationTokenId) external {
bytes32 encryptedPre;
assembly {
// efficient hashing lol, equivalent to // = keccak256(abi.encodePacked(key));
mstore(0x0, key)
encryptedPre := keccak256(0x0, 0x20)
}
// bound to msg.sender to prevent frontrunning griefoors
bytes32 encryptionReference = computeEncryptionReference(encryptedPre, msg.sender);
if (pledgedRevealTimestamps[encryptionReference] == 0) revert InvalidKey_error();
immortalizer.reveal(encryptionReference, key); // only allows ONE reveal per encryptedReference
if (optionalCurationTokenId < 1) return;
emitUpdated(optionalCurationTokenId);
}
function emitUpdated(uint256 curationTokenId) public {
emit MetadataUpdate(curationTokenId);
Type t = immortalizer.immortalizedType(curationTokenId);
if (t > Type.FRAME_ENCRYPTED) {
getCollection(curationTokenId).emitContractURIUpdated();
getCollection(curationTokenId).emitBatchMetadataUpdate(0, type(uint256).max);
return;
}
premierAccess.emitMetadataUpdate(curationTokenId);
}
function pledgedRevealTimestamp(uint256 id) external view returns (uint256) {
return pledgedRevealTimestamps[immortalizer.encryptionReference(id)];
}
function updateContractURIImage(uint256 curationTokenId, FileBundle calldata imageData, address customRenderer)
external
{
if (msg.sender != ownerOf(curationTokenId)) revert NotOwner_error(); // throws if dne!
immortalizer.updateContractURIImage(curationTokenId, imageData, customRenderer);
if (curationTokenId < 1) {
emit ContractURIUpdated();
} else {
INFT nft = allCollections[_collectionIdxs[curationTokenId]];
nft.emitContractURIUpdated();
}
}
function burn(uint256 tokenId) external {
if (msg.sender != ownerOf(tokenId)) revert NotOwner_error();
unchecked {
--_supply.totalSupply;
}
LibBitmap.set(_burned, tokenId);
_burn(tokenId);
}
function burned(uint256 tokenId) external view returns (bool) {
return LibBitmap.get(_burned, tokenId);
}
function _immortalize(Type t, ImmortalizeComponentData calldata icd) private returns (uint256 tokenId) {
bool paid = _checkIfPaying({t: t, qty: 1});
tokenId = _incrementSupply({qty: 1});
_processToken(tokenId, paid, icd.featuredIds, msg.sender);
immortalizer.immortalize(t, icd.featuredIds, icd.encrypted, icd.compressed, icd.exclusivityData, tokenId);
if (icd.featuredIds.length > 0) _beneficiaries[tokenId] = tokenId; // mechanism of beneficiaryOf
_safeMint(icd.to, tokenId);
}
function _immortalizeBulk(Type t, ImmortalizeComponentData[] calldata icds)
private
returns (uint256[] memory tokenIds)
{
// nonReentrant since is _safeMint in a loop
uint256 qty = icds.length;
bool paid = _checkIfPaying({t: t, qty: qty});
uint256 tokenId = _incrementSupply(qty);
tokenIds = _allocateUintArr(qty);
ImmortalizeComponentData memory icd;
unchecked {
for (uint256 i; i < qty; ++i) {
icd = icds[i];
_processToken(tokenId, paid, icd.featuredIds, msg.sender);
immortalizer.immortalize(
t, icd.featuredIds, icd.encrypted, icd.compressed, icd.exclusivityData, tokenId
);
_safeMint(icd.to, tokenId);
tokenIds[i] = tokenId++;
}
} // uc
}
function _onlyOwner() internal view override {
if (msg.sender != ownerOf(HUB_OWNER_TOKENID)) revert NotOwner_error();
}
function _processImmortalizeFee(Type t, uint256 qty) private returns (bool paid, uint256 cost) {
// type is assumed to be entered as a nomalized form by calling fn
cost = immortalizeFee[t] * qty;
if (cost < 1) return (true, cost);
if (msg.value < cost) return (false, cost);
if (msg.value > cost) {
unchecked {
_setRefund(msg.sender, msg.value - cost);
} // uc
}
return (true, cost);
}
function _checkIfPaying(Type t, uint256 qty) private returns (bool paid) {
bool paying = (msg.value > 0) ? true : false;
(paid,) = _processImmortalizeFee({t: t, qty: qty});
_processPaying(paying, paid);
}
function _processPaying(bool paying, bool paid) private pure {
if (paying && !paid) revert InsufficientImmortalizeFee_error();
}
function _normalizeType(Type t) private pure returns (Type) {
if (t == Type.NONE) revert InvalidType_error();
// normalize
assembly {
// if t%2==0, then --t
t := sub(t, eq(mod(t, 2), 0))
}
return t;
}
function _processToken(uint256 tokenId, bool paid, uint256[] memory featuredIds, address account) private {
paidImmortalizeFee[tokenId] = paid;
_processPremierAccess(featuredIds, account);
originator[tokenId] = account;
}
function _processPremierAccess(uint256[] memory featuredIds, address account) private {
uint256 id;
for (uint256 i; i < featuredIds.length; ++i) {
id = featuredIds[i];
_processPremierAccess(id, account);
}
}
function _processPremierAccess(uint256 id, address account) private {
if (!_exists(id) || account != ownerOf(id)) {
// check exists since can be burned
if (!premierAccess.processAccess(account, id)) revert MustRespectExclusivity_error();
}
}
function _incrementSupply(uint256 qty) private returns (uint256 tokenId) {
Supply memory s = _supply;
assembly {
tokenId := mload(add(s, 0x20)) // = s.totalMinted
mstore(s, add(mload(s), qty)) //s.totalSupply += uint128(qty);
mstore(add(s, 0x20), add(tokenId, qty)) //s.totalMinted += uint128(qty);
}
_supply = s;
}
function _requireCallerIsContractOwner() internal view override {
if (msg.sender != ownerOf(HUB_OWNER_TOKENID)) revert NotOwner_error();
}
}
LibBitmap.sol 236 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
import {LibBit} from "./LibBit.sol";
/// @notice Library for storage of packed unsigned booleans.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibBitmap.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/LibBitmap.sol)
/// @author Modified from Solidity-Bits (https://github.com/estarriolvetch/solidity-bits/blob/main/contracts/BitMaps.sol)
library LibBitmap {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CONSTANTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The constant returned when a bitmap scan does not find a result.
uint256 internal constant NOT_FOUND = type(uint256).max;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* STRUCTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev A bitmap in storage.
struct Bitmap {
mapping(uint256 => uint256) map;
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the boolean value of the bit at `index` in `bitmap`.
function get(Bitmap storage bitmap, uint256 index) internal view returns (bool isSet) {
// It is better to set `isSet` to either 0 or 1, than zero vs non-zero.
// Both cost the same amount of gas, but the former allows the returned value
// to be reused without cleaning the upper bits.
uint256 b = (bitmap.map[index >> 8] >> (index & 0xff)) & 1;
/// @solidity memory-safe-assembly
assembly {
isSet := b
}
}
/// @dev Updates the bit at `index` in `bitmap` to true.
function set(Bitmap storage bitmap, uint256 index) internal {
bitmap.map[index >> 8] |= (1 << (index & 0xff));
}
/// @dev Updates the bit at `index` in `bitmap` to false.
function unset(Bitmap storage bitmap, uint256 index) internal {
bitmap.map[index >> 8] &= ~(1 << (index & 0xff));
}
/// @dev Flips the bit at `index` in `bitmap`.
/// Returns the boolean result of the flipped bit.
function toggle(Bitmap storage bitmap, uint256 index) internal returns (bool newIsSet) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x20, bitmap.slot)
mstore(0x00, shr(8, index))
let storageSlot := keccak256(0x00, 0x40)
let shift := and(index, 0xff)
let storageValue := xor(sload(storageSlot), shl(shift, 1))
// It makes sense to return the `newIsSet`,
// as it allow us to skip an additional warm `sload`,
// and it costs minimal gas (about 15),
// which may be optimized away if the returned value is unused.
newIsSet := and(1, shr(shift, storageValue))
sstore(storageSlot, storageValue)
}
}
/// @dev Updates the bit at `index` in `bitmap` to `shouldSet`.
function setTo(Bitmap storage bitmap, uint256 index, bool shouldSet) internal {
/// @solidity memory-safe-assembly
assembly {
mstore(0x20, bitmap.slot)
mstore(0x00, shr(8, index))
let storageSlot := keccak256(0x00, 0x40)
let storageValue := sload(storageSlot)
let shift := and(index, 0xff)
sstore(
storageSlot,
// Unsets the bit at `shift` via `and`, then sets its new value via `or`.
or(and(storageValue, not(shl(shift, 1))), shl(shift, iszero(iszero(shouldSet))))
)
}
}
/// @dev Consecutively sets `amount` of bits starting from the bit at `start`.
function setBatch(Bitmap storage bitmap, uint256 start, uint256 amount) internal {
/// @solidity memory-safe-assembly
assembly {
let max := not(0)
let shift := and(start, 0xff)
mstore(0x20, bitmap.slot)
mstore(0x00, shr(8, start))
if iszero(lt(add(shift, amount), 257)) {
let storageSlot := keccak256(0x00, 0x40)
sstore(storageSlot, or(sload(storageSlot), shl(shift, max)))
let bucket := add(mload(0x00), 1)
let bucketEnd := add(mload(0x00), shr(8, add(amount, shift)))
amount := and(add(amount, shift), 0xff)
shift := 0
for {} iszero(eq(bucket, bucketEnd)) { bucket := add(bucket, 1) } {
mstore(0x00, bucket)
sstore(keccak256(0x00, 0x40), max)
}
mstore(0x00, bucket)
}
let storageSlot := keccak256(0x00, 0x40)
sstore(storageSlot, or(sload(storageSlot), shl(shift, shr(sub(256, amount), max))))
}
}
/// @dev Consecutively unsets `amount` of bits starting from the bit at `start`.
function unsetBatch(Bitmap storage bitmap, uint256 start, uint256 amount) internal {
/// @solidity memory-safe-assembly
assembly {
let shift := and(start, 0xff)
mstore(0x20, bitmap.slot)
mstore(0x00, shr(8, start))
if iszero(lt(add(shift, amount), 257)) {
let storageSlot := keccak256(0x00, 0x40)
sstore(storageSlot, and(sload(storageSlot), not(shl(shift, not(0)))))
let bucket := add(mload(0x00), 1)
let bucketEnd := add(mload(0x00), shr(8, add(amount, shift)))
amount := and(add(amount, shift), 0xff)
shift := 0
for {} iszero(eq(bucket, bucketEnd)) { bucket := add(bucket, 1) } {
mstore(0x00, bucket)
sstore(keccak256(0x00, 0x40), 0)
}
mstore(0x00, bucket)
}
let storageSlot := keccak256(0x00, 0x40)
sstore(
storageSlot, and(sload(storageSlot), not(shl(shift, shr(sub(256, amount), not(0)))))
)
}
}
/// @dev Returns number of set bits within a range by
/// scanning `amount` of bits starting from the bit at `start`.
function popCount(Bitmap storage bitmap, uint256 start, uint256 amount)
internal
view
returns (uint256 count)
{
unchecked {
uint256 bucket = start >> 8;
uint256 shift = start & 0xff;
if (!(amount + shift < 257)) {
count = LibBit.popCount(bitmap.map[bucket] >> shift);
uint256 bucketEnd = bucket + ((amount + shift) >> 8);
amount = (amount + shift) & 0xff;
shift = 0;
for (++bucket; bucket != bucketEnd; ++bucket) {
count += LibBit.popCount(bitmap.map[bucket]);
}
}
count += LibBit.popCount((bitmap.map[bucket] >> shift) << (256 - amount));
}
}
/// @dev Returns the index of the most significant set bit in `[0..upTo]`.
/// If no set bit is found, returns `NOT_FOUND`.
function findLastSet(Bitmap storage bitmap, uint256 upTo)
internal
view
returns (uint256 setBitIndex)
{
setBitIndex = NOT_FOUND;
uint256 bucket = upTo >> 8;
uint256 bits;
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, bucket)
mstore(0x20, bitmap.slot)
let offset := and(0xff, not(upTo)) // `256 - (255 & upTo) - 1`.
bits := shr(offset, shl(offset, sload(keccak256(0x00, 0x40))))
if iszero(or(bits, iszero(bucket))) {
for {} 1 {} {
bucket := add(bucket, setBitIndex) // `sub(bucket, 1)`.
mstore(0x00, bucket)
bits := sload(keccak256(0x00, 0x40))
if or(bits, iszero(bucket)) { break }
}
}
}
if (bits != 0) {
setBitIndex = (bucket << 8) | LibBit.fls(bits);
/// @solidity memory-safe-assembly
assembly {
setBitIndex := or(setBitIndex, sub(0, gt(setBitIndex, upTo)))
}
}
}
/// @dev Returns the index of the least significant unset bit in `[begin..upTo]`.
/// If no unset bit is found, returns `NOT_FOUND`.
function findFirstUnset(Bitmap storage bitmap, uint256 begin, uint256 upTo)
internal
view
returns (uint256 unsetBitIndex)
{
unsetBitIndex = NOT_FOUND;
uint256 bucket = begin >> 8;
uint256 negBits;
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, bucket)
mstore(0x20, bitmap.slot)
let offset := and(0xff, begin)
negBits := shl(offset, shr(offset, not(sload(keccak256(0x00, 0x40)))))
if iszero(negBits) {
let lastBucket := shr(8, upTo)
for {} 1 {} {
bucket := add(bucket, 1)
mstore(0x00, bucket)
negBits := not(sload(keccak256(0x00, 0x40)))
if or(negBits, gt(bucket, lastBucket)) { break }
}
if gt(bucket, lastBucket) {
negBits := shl(and(0xff, not(upTo)), shr(and(0xff, not(upTo)), negBits))
}
}
}
if (negBits != 0) {
uint256 r = (bucket << 8) | LibBit.ffs(negBits);
/// @solidity memory-safe-assembly
assembly {
unsetBitIndex := or(r, sub(0, or(gt(r, upTo), lt(r, begin))))
}
}
}
}
SafeTransferLib.sol 566 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/SafeTransferLib.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)
/// @author Permit2 operations from (https://github.com/Uniswap/permit2/blob/main/src/libraries/Permit2Lib.sol)
///
/// @dev Note:
/// - For ETH transfers, please use `forceSafeTransferETH` for DoS protection.
library SafeTransferLib {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The ETH transfer has failed.
error ETHTransferFailed();
/// @dev The ERC20 `transferFrom` has failed.
error TransferFromFailed();
/// @dev The ERC20 `transfer` has failed.
error TransferFailed();
/// @dev The ERC20 `approve` has failed.
error ApproveFailed();
/// @dev The ERC20 `totalSupply` query has failed.
error TotalSupplyQueryFailed();
/// @dev The Permit2 operation has failed.
error Permit2Failed();
/// @dev The Permit2 amount must be less than `2**160 - 1`.
error Permit2AmountOverflow();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CONSTANTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Suggested gas stipend for contract receiving ETH that disallows any storage writes.
uint256 internal constant GAS_STIPEND_NO_STORAGE_WRITES = 2300;
/// @dev Suggested gas stipend for contract receiving ETH to perform a few
/// storage reads and writes, but low enough to prevent griefing.
uint256 internal constant GAS_STIPEND_NO_GRIEF = 100000;
/// @dev The unique EIP-712 domain domain separator for the DAI token contract.
bytes32 internal constant DAI_DOMAIN_SEPARATOR =
0xdbb8cf42e1ecb028be3f3dbc922e1d878b963f411dc388ced501601c60f7c6f7;
/// @dev The address for the WETH9 contract on Ethereum mainnet.
address internal constant WETH9 = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
/// @dev The canonical Permit2 address.
/// [Github](https://github.com/Uniswap/permit2)
/// [Etherscan](https://etherscan.io/address/0x000000000022D473030F116dDEE9F6B43aC78BA3)
address internal constant PERMIT2 = 0x000000000022D473030F116dDEE9F6B43aC78BA3;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* ETH OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
// If the ETH transfer MUST succeed with a reasonable gas budget, use the force variants.
//
// The regular variants:
// - Forwards all remaining gas to the target.
// - Reverts if the target reverts.
// - Reverts if the current contract has insufficient balance.
//
// The force variants:
// - Forwards with an optional gas stipend
// (defaults to `GAS_STIPEND_NO_GRIEF`, which is sufficient for most cases).
// - If the target reverts, or if the gas stipend is exhausted,
// creates a temporary contract to force send the ETH via `SELFDESTRUCT`.
// Future compatible with `SENDALL`: https://eips.ethereum.org/EIPS/eip-4758.
// - Reverts if the current contract has insufficient balance.
//
// The try variants:
// - Forwards with a mandatory gas stipend.
// - Instead of reverting, returns whether the transfer succeeded.
/// @dev Sends `amount` (in wei) ETH to `to`.
function safeTransferETH(address to, uint256 amount) internal {
/// @solidity memory-safe-assembly
assembly {
if iszero(call(gas(), to, amount, codesize(), 0x00, codesize(), 0x00)) {
mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
revert(0x1c, 0x04)
}
}
}
/// @dev Sends all the ETH in the current contract to `to`.
function safeTransferAllETH(address to) internal {
/// @solidity memory-safe-assembly
assembly {
// Transfer all the ETH and check if it succeeded or not.
if iszero(call(gas(), to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) {
mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
revert(0x1c, 0x04)
}
}
}
/// @dev Force sends `amount` (in wei) ETH to `to`, with a `gasStipend`.
function forceSafeTransferETH(address to, uint256 amount, uint256 gasStipend) internal {
/// @solidity memory-safe-assembly
assembly {
if lt(selfbalance(), amount) {
mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
revert(0x1c, 0x04)
}
if iszero(call(gasStipend, to, amount, codesize(), 0x00, codesize(), 0x00)) {
mstore(0x00, to) // Store the address in scratch space.
mstore8(0x0b, 0x73) // Opcode `PUSH20`.
mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
if iszero(create(amount, 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
}
}
}
/// @dev Force sends all the ETH in the current contract to `to`, with a `gasStipend`.
function forceSafeTransferAllETH(address to, uint256 gasStipend) internal {
/// @solidity memory-safe-assembly
assembly {
if iszero(call(gasStipend, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) {
mstore(0x00, to) // Store the address in scratch space.
mstore8(0x0b, 0x73) // Opcode `PUSH20`.
mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
if iszero(create(selfbalance(), 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
}
}
}
/// @dev Force sends `amount` (in wei) ETH to `to`, with `GAS_STIPEND_NO_GRIEF`.
function forceSafeTransferETH(address to, uint256 amount) internal {
/// @solidity memory-safe-assembly
assembly {
if lt(selfbalance(), amount) {
mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
revert(0x1c, 0x04)
}
if iszero(call(GAS_STIPEND_NO_GRIEF, to, amount, codesize(), 0x00, codesize(), 0x00)) {
mstore(0x00, to) // Store the address in scratch space.
mstore8(0x0b, 0x73) // Opcode `PUSH20`.
mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
if iszero(create(amount, 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
}
}
}
/// @dev Force sends all the ETH in the current contract to `to`, with `GAS_STIPEND_NO_GRIEF`.
function forceSafeTransferAllETH(address to) internal {
/// @solidity memory-safe-assembly
assembly {
// forgefmt: disable-next-item
if iszero(call(GAS_STIPEND_NO_GRIEF, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) {
mstore(0x00, to) // Store the address in scratch space.
mstore8(0x0b, 0x73) // Opcode `PUSH20`.
mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
if iszero(create(selfbalance(), 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
}
}
}
/// @dev Sends `amount` (in wei) ETH to `to`, with a `gasStipend`.
function trySafeTransferETH(address to, uint256 amount, uint256 gasStipend)
internal
returns (bool success)
{
/// @solidity memory-safe-assembly
assembly {
success := call(gasStipend, to, amount, codesize(), 0x00, codesize(), 0x00)
}
}
/// @dev Sends all the ETH in the current contract to `to`, with a `gasStipend`.
function trySafeTransferAllETH(address to, uint256 gasStipend)
internal
returns (bool success)
{
/// @solidity memory-safe-assembly
assembly {
success := call(gasStipend, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* ERC20 OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Sends `amount` of ERC20 `token` from `from` to `to`.
/// Reverts upon failure.
///
/// The `from` account must have at least `amount` approved for
/// the current contract to manage.
function safeTransferFrom(address token, address from, address to, uint256 amount) internal {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Cache the free memory pointer.
mstore(0x60, amount) // Store the `amount` argument.
mstore(0x40, to) // Store the `to` argument.
mstore(0x2c, shl(96, from)) // Store the `from` argument.
mstore(0x0c, 0x23b872dd000000000000000000000000) // `transferFrom(address,address,uint256)`.
let success := call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
if iszero(and(eq(mload(0x00), 1), success)) {
if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) {
mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
revert(0x1c, 0x04)
}
}
mstore(0x60, 0) // Restore the zero slot to zero.
mstore(0x40, m) // Restore the free memory pointer.
}
}
/// @dev Sends `amount` of ERC20 `token` from `from` to `to`.
///
/// The `from` account must have at least `amount` approved for the current contract to manage.
function trySafeTransferFrom(address token, address from, address to, uint256 amount)
internal
returns (bool success)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Cache the free memory pointer.
mstore(0x60, amount) // Store the `amount` argument.
mstore(0x40, to) // Store the `to` argument.
mstore(0x2c, shl(96, from)) // Store the `from` argument.
mstore(0x0c, 0x23b872dd000000000000000000000000) // `transferFrom(address,address,uint256)`.
success := call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
if iszero(and(eq(mload(0x00), 1), success)) {
success := lt(or(iszero(extcodesize(token)), returndatasize()), success)
}
mstore(0x60, 0) // Restore the zero slot to zero.
mstore(0x40, m) // Restore the free memory pointer.
}
}
/// @dev Sends all of ERC20 `token` from `from` to `to`.
/// Reverts upon failure.
///
/// The `from` account must have their entire balance approved for the current contract to manage.
function safeTransferAllFrom(address token, address from, address to)
internal
returns (uint256 amount)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Cache the free memory pointer.
mstore(0x40, to) // Store the `to` argument.
mstore(0x2c, shl(96, from)) // Store the `from` argument.
mstore(0x0c, 0x70a08231000000000000000000000000) // `balanceOf(address)`.
// Read the balance, reverting upon failure.
if iszero(
and( // The arguments of `and` are evaluated from right to left.
gt(returndatasize(), 0x1f), // At least 32 bytes returned.
staticcall(gas(), token, 0x1c, 0x24, 0x60, 0x20)
)
) {
mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
revert(0x1c, 0x04)
}
mstore(0x00, 0x23b872dd) // `transferFrom(address,address,uint256)`.
amount := mload(0x60) // The `amount` is already at 0x60. We'll need to return it.
// Perform the transfer, reverting upon failure.
let success := call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
if iszero(and(eq(mload(0x00), 1), success)) {
if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) {
mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
revert(0x1c, 0x04)
}
}
mstore(0x60, 0) // Restore the zero slot to zero.
mstore(0x40, m) // Restore the free memory pointer.
}
}
/// @dev Sends `amount` of ERC20 `token` from the current contract to `to`.
/// Reverts upon failure.
function safeTransfer(address token, address to, uint256 amount) internal {
/// @solidity memory-safe-assembly
assembly {
mstore(0x14, to) // Store the `to` argument.
mstore(0x34, amount) // Store the `amount` argument.
mstore(0x00, 0xa9059cbb000000000000000000000000) // `transfer(address,uint256)`.
// Perform the transfer, reverting upon failure.
let success := call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
if iszero(and(eq(mload(0x00), 1), success)) {
if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) {
mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
revert(0x1c, 0x04)
}
}
mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
}
}
/// @dev Sends all of ERC20 `token` from the current contract to `to`.
/// Reverts upon failure.
function safeTransferAll(address token, address to) internal returns (uint256 amount) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, 0x70a08231) // Store the function selector of `balanceOf(address)`.
mstore(0x20, address()) // Store the address of the current contract.
// Read the balance, reverting upon failure.
if iszero(
and( // The arguments of `and` are evaluated from right to left.
gt(returndatasize(), 0x1f), // At least 32 bytes returned.
staticcall(gas(), token, 0x1c, 0x24, 0x34, 0x20)
)
) {
mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
revert(0x1c, 0x04)
}
mstore(0x14, to) // Store the `to` argument.
amount := mload(0x34) // The `amount` is already at 0x34. We'll need to return it.
mstore(0x00, 0xa9059cbb000000000000000000000000) // `transfer(address,uint256)`.
// Perform the transfer, reverting upon failure.
let success := call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
if iszero(and(eq(mload(0x00), 1), success)) {
if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) {
mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
revert(0x1c, 0x04)
}
}
mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
}
}
/// @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract.
/// Reverts upon failure.
function safeApprove(address token, address to, uint256 amount) internal {
/// @solidity memory-safe-assembly
assembly {
mstore(0x14, to) // Store the `to` argument.
mstore(0x34, amount) // Store the `amount` argument.
mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
let success := call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
if iszero(and(eq(mload(0x00), 1), success)) {
if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) {
mstore(0x00, 0x3e3f8f73) // `ApproveFailed()`.
revert(0x1c, 0x04)
}
}
mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
}
}
/// @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract.
/// If the initial attempt to approve fails, attempts to reset the approved amount to zero,
/// then retries the approval again (some tokens, e.g. USDT, requires this).
/// Reverts upon failure.
function safeApproveWithRetry(address token, address to, uint256 amount) internal {
/// @solidity memory-safe-assembly
assembly {
mstore(0x14, to) // Store the `to` argument.
mstore(0x34, amount) // Store the `amount` argument.
mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
// Perform the approval, retrying upon failure.
let success := call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
if iszero(and(eq(mload(0x00), 1), success)) {
if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) {
mstore(0x34, 0) // Store 0 for the `amount`.
mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
pop(call(gas(), token, 0, 0x10, 0x44, codesize(), 0x00)) // Reset the approval.
mstore(0x34, amount) // Store back the original `amount`.
// Retry the approval, reverting upon failure.
success := call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
if iszero(and(eq(mload(0x00), 1), success)) {
// Check the `extcodesize` again just in case the token selfdestructs lol.
if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) {
mstore(0x00, 0x3e3f8f73) // `ApproveFailed()`.
revert(0x1c, 0x04)
}
}
}
}
mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
}
}
/// @dev Returns the amount of ERC20 `token` owned by `account`.
/// Returns zero if the `token` does not exist.
function balanceOf(address token, address account) internal view returns (uint256 amount) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x14, account) // Store the `account` argument.
mstore(0x00, 0x70a08231000000000000000000000000) // `balanceOf(address)`.
amount :=
mul( // The arguments of `mul` are evaluated from right to left.
mload(0x20),
and( // The arguments of `and` are evaluated from right to left.
gt(returndatasize(), 0x1f), // At least 32 bytes returned.
staticcall(gas(), token, 0x10, 0x24, 0x20, 0x20)
)
)
}
}
/// @dev Returns the total supply of the `token`.
/// Reverts if the token does not exist or does not implement `totalSupply()`.
function totalSupply(address token) internal view returns (uint256 result) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, 0x18160ddd) // `totalSupply()`.
if iszero(
and(gt(returndatasize(), 0x1f), staticcall(gas(), token, 0x1c, 0x04, 0x00, 0x20))
) {
mstore(0x00, 0x54cd9435) // `TotalSupplyQueryFailed()`.
revert(0x1c, 0x04)
}
result := mload(0x00)
}
}
/// @dev Sends `amount` of ERC20 `token` from `from` to `to`.
/// If the initial attempt fails, try to use Permit2 to transfer the token.
/// Reverts upon failure.
///
/// The `from` account must have at least `amount` approved for the current contract to manage.
function safeTransferFrom2(address token, address from, address to, uint256 amount) internal {
if (!trySafeTransferFrom(token, from, to, amount)) {
permit2TransferFrom(token, from, to, amount);
}
}
/// @dev Sends `amount` of ERC20 `token` from `from` to `to` via Permit2.
/// Reverts upon failure.
function permit2TransferFrom(address token, address from, address to, uint256 amount)
internal
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
mstore(add(m, 0x74), shr(96, shl(96, token)))
mstore(add(m, 0x54), amount)
mstore(add(m, 0x34), to)
mstore(add(m, 0x20), shl(96, from))
// `transferFrom(address,address,uint160,address)`.
mstore(m, 0x36c78516000000000000000000000000)
let p := PERMIT2
let exists := eq(chainid(), 1)
if iszero(exists) { exists := iszero(iszero(extcodesize(p))) }
if iszero(
and(
call(gas(), p, 0, add(m, 0x10), 0x84, codesize(), 0x00),
lt(iszero(extcodesize(token)), exists) // Token has code and Permit2 exists.
)
) {
mstore(0x00, 0x7939f4248757f0fd) // `TransferFromFailed()` or `Permit2AmountOverflow()`.
revert(add(0x18, shl(2, iszero(iszero(shr(160, amount))))), 0x04)
}
}
}
/// @dev Permit a user to spend a given amount of
/// another user's tokens via native EIP-2612 permit if possible, falling
/// back to Permit2 if native permit fails or is not implemented on the token.
function permit2(
address token,
address owner,
address spender,
uint256 amount,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) internal {
bool success;
/// @solidity memory-safe-assembly
assembly {
for {} shl(96, xor(token, WETH9)) {} {
mstore(0x00, 0x3644e515) // `DOMAIN_SEPARATOR()`.
if iszero(
and( // The arguments of `and` are evaluated from right to left.
lt(iszero(mload(0x00)), eq(returndatasize(), 0x20)), // Returns 1 non-zero word.
// Gas stipend to limit gas burn for tokens that don't refund gas when
// an non-existing function is called. 5K should be enough for a SLOAD.
staticcall(5000, token, 0x1c, 0x04, 0x00, 0x20)
)
) { break }
// After here, we can be sure that token is a contract.
let m := mload(0x40)
mstore(add(m, 0x34), spender)
mstore(add(m, 0x20), shl(96, owner))
mstore(add(m, 0x74), deadline)
if eq(mload(0x00), DAI_DOMAIN_SEPARATOR) {
mstore(0x14, owner)
mstore(0x00, 0x7ecebe00000000000000000000000000) // `nonces(address)`.
mstore(add(m, 0x94), staticcall(gas(), token, 0x10, 0x24, add(m, 0x54), 0x20))
mstore(m, 0x8fcbaf0c000000000000000000000000) // `IDAIPermit.permit`.
// `nonces` is already at `add(m, 0x54)`.
// `1` is already stored at `add(m, 0x94)`.
mstore(add(m, 0xb4), and(0xff, v))
mstore(add(m, 0xd4), r)
mstore(add(m, 0xf4), s)
success := call(gas(), token, 0, add(m, 0x10), 0x104, codesize(), 0x00)
break
}
mstore(m, 0xd505accf000000000000000000000000) // `IERC20Permit.permit`.
mstore(add(m, 0x54), amount)
mstore(add(m, 0x94), and(0xff, v))
mstore(add(m, 0xb4), r)
mstore(add(m, 0xd4), s)
success := call(gas(), token, 0, add(m, 0x10), 0xe4, codesize(), 0x00)
break
}
}
if (!success) simplePermit2(token, owner, spender, amount, deadline, v, r, s);
}
/// @dev Simple permit on the Permit2 contract.
function simplePermit2(
address token,
address owner,
address spender,
uint256 amount,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) internal {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
mstore(m, 0x927da105) // `allowance(address,address,address)`.
{
let addressMask := shr(96, not(0))
mstore(add(m, 0x20), and(addressMask, owner))
mstore(add(m, 0x40), and(addressMask, token))
mstore(add(m, 0x60), and(addressMask, spender))
mstore(add(m, 0xc0), and(addressMask, spender))
}
let p := mul(PERMIT2, iszero(shr(160, amount)))
if iszero(
and( // The arguments of `and` are evaluated from right to left.
gt(returndatasize(), 0x5f), // Returns 3 words: `amount`, `expiration`, `nonce`.
staticcall(gas(), p, add(m, 0x1c), 0x64, add(m, 0x60), 0x60)
)
) {
mstore(0x00, 0x6b836e6b8757f0fd) // `Permit2Failed()` or `Permit2AmountOverflow()`.
revert(add(0x18, shl(2, iszero(p))), 0x04)
}
mstore(m, 0x2b67b570) // `Permit2.permit` (PermitSingle variant).
// `owner` is already `add(m, 0x20)`.
// `token` is already at `add(m, 0x40)`.
mstore(add(m, 0x60), amount)
mstore(add(m, 0x80), 0xffffffffffff) // `expiration = type(uint48).max`.
// `nonce` is already at `add(m, 0xa0)`.
// `spender` is already at `add(m, 0xc0)`.
mstore(add(m, 0xe0), deadline)
mstore(add(m, 0x100), 0x100) // `signature` offset.
mstore(add(m, 0x120), 0x41) // `signature` length.
mstore(add(m, 0x140), r)
mstore(add(m, 0x160), s)
mstore(add(m, 0x180), shl(248, v))
if iszero( // Revert if token does not have code, or if the call fails.
mul(extcodesize(token), call(gas(), p, 0, add(m, 0x1c), 0x184, codesize(), 0x00))) {
mstore(0x00, 0x6b836e6b) // `Permit2Failed()`.
revert(0x1c, 0x04)
}
}
}
}
ERC721C.sol 31 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
import "lib/solady/src/tokens/ERC721.sol";
import "./CreatorTokenBase.sol";
// modified by no_side666 to favor the solady library
/**
* @title ERC721C
* @author Limit Break, Inc.
* @notice Extends OpenZeppelin's ERC721 implementation with Creator Token functionality, which
* allows the contract owner to update the transfer validation logic by managing a security policy in
* an external transfer validation security policy registry. See {CreatorTokenTransferValidator}.
*/
abstract contract ERC721C is ERC721, CreatorTokenBase {
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(ICreatorToken).interfaceId || super.supportsInterface(interfaceId);
}
/// @dev Ties the solady _beforeTokenTransfer hook to more granular transfer validation logic
function _beforeTokenTransfer(address from, address to, uint256 id) internal virtual override {
_validateBeforeTransfer(from, to, id);
}
/// @dev Ties the solady _afterTokenTransfer hook to more granular transfer validation logic
function _afterTokenTransfer(address from, address to, uint256 id) internal virtual override {
_validateAfterTransfer(from, to, id);
}
}
BasicRoyalties.sol 35 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
import "lib/openzeppelin-contracts/contracts/token/common/ERC2981.sol";
/**
* @title BasicRoyaltiesBase
* @author Limit Break, Inc.
* @dev Base functionality of an NFT mix-in contract implementing the most basic form of programmable royalties.
*/
abstract contract BasicRoyaltiesBase is ERC2981 {
event DefaultRoyaltySet(address indexed receiver, uint96 feeNumerator);
event TokenRoyaltySet(uint256 indexed tokenId, address indexed receiver, uint96 feeNumerator);
function _setDefaultRoyalty(address receiver, uint96 feeNumerator) internal virtual override {
super._setDefaultRoyalty(receiver, feeNumerator);
emit DefaultRoyaltySet(receiver, feeNumerator);
}
function _setTokenRoyalty(uint256 tokenId, address receiver, uint96 feeNumerator) internal virtual override {
super._setTokenRoyalty(tokenId, receiver, feeNumerator);
emit TokenRoyaltySet(tokenId, receiver, feeNumerator);
}
}
/**
* @title BasicRoyalties
* @author Limit Break, Inc.
* @notice Constructable BasicRoyalties Contract implementation.
*/
abstract contract BasicRoyalties is BasicRoyaltiesBase {
constructor(address receiver, uint96 feeNumerator) {
_setDefaultRoyalty(receiver, feeNumerator);
}
}
ReentrancyGuard.sol 84 lines
// SPDX-License-Identifier: MIT
// MODIFIED by no_side666, the change being using transient storage for gas saving$$$.
// adapted from: OpenZeppelin Contracts (last updated v4.9.0) (security/ReentrancyGuard.sol)
pragma solidity ^0.8.25;
/**
* @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.
uint256 private constant _NOT_ENTERED = 0; // using tstore so 0 is no problem and ideal
uint256 private constant _ENTERED = 1;
uint256 private immutable _tstoreKey = uint256(keccak256(abi.encode("ReentrancyGuard", address(this))));
constructor() {}
/**
* @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
uint256 status;
uint256 tstoreKey = _tstoreKey;
assembly {
status := tload(tstoreKey)
}
require(status != _ENTERED, "ReentrancyGuard: reentrant call");
// Any calls to nonReentrant after this point will fail
assembly {
tstore(tstoreKey, _ENTERED)
}
}
function _nonReentrantAfter() private {
uint256 tstoreKey = _tstoreKey;
assembly {
tstore(tstoreKey, _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) {
uint256 status;
uint256 tstoreKey = _tstoreKey;
assembly {
status := tload(tstoreKey)
}
return status == _ENTERED;
}
}
Refunds.sol 29 lines
// SPDX-License-Identifier: HVPL - HEXED VIRAL PUBLIC LICENSE
pragma solidity ^0.8.25;
import "./Interfaces.sol";
import "./Errors.sol";
import "./LICENSE.sol";
// by no_side666
abstract contract Refunds is IRefunds {
IRefunder public refunder;
function _setRefunder(IRefunder refunder_) internal {
if (address(refunder) != address(0)) revert AlreadySet_error();
refunder = refunder_;
}
function refundAvailable(address account) external view returns (uint256) {
return refunder.refundAvailable(account);
}
function claimRefund() external {
return refunder.claimRefund(msg.sender);
}
function _setRefund(address account, uint256 amount) internal {
return refunder.setRefund{value: amount}(account);
}
}
Interfaces.sol 524 lines
// SPDX-License-Identifier: HVPL - HEXED VIRAL PUBLIC LICENSE
pragma solidity ^0.8.25;
import "lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";
import "./modded/creator-token-standards/TransferPolicy.sol";
import "./Structs.sol";
import "./FixedPoint.sol";
import "./IBlockHashBeacon.sol";
import "./IDiscountDemon.sol";
import "./LICENSE.sol";
/*
@@@@@@@ @@@@@@@ @@@@@@@@ @@@@@@ @@@@@@@@@@
@@@@@@@@ @@@@@@@@ @@@@@@@@ @@@@@@@@ @@@@@@@@@@@
@@! @@@ @@! @@@ @@! @@! @@@ @@! @@! @@!
!@! @!@ !@! @!@ !@! !@! @!@ !@! !@! !@!
@!@ !@! @!@!!@! @!!!:! @!@!@!@! @!! !!@ @!@
!@! !!! !!@!@! !!!!!: !!!@!!!! !@! ! !@!
!!: !!! !!: :!! !!: !!: !!! !!: !!: by no_side666
:!: !:! :!: !:! :!: :!: !:! :!: :!:
:::: :: :: ::: :: :::: :: ::: ::: :: dreamstack.xyz
:: : : : : : : :: :: : : : : :
@@@@@@ @@@@@@@ @@@@@@ @@@@@@@ @@@ @@@ @@@ @@@ @@@@@@
@@@@@@@ @@@@@@@ @@@@@@@@ @@@@@@@@ @@@ @@@ @@@ @@@ @@@@@@@@
!@@ @@! @@! @@@ !@@ @@! !@@ @@! @@@ @@@
!@! !@! !@! @!@ !@! !@! @!! !@! @!@ @!@
!!@@!! @!! @!@!@!@! !@! @!@@!@! @!@ !@! !!@
!!@!!! !!! !!!@!!!! !!! !!@!!! !@! !!! !!:
!:! !!: !!: !!! :!! !!: :!! :!: !!: !:!
!:! :!: :!: !:! :!: :!: !:! ::!!:! :!:
:::: :: :: :: ::: ::: ::: :: ::: :::: :: :::::
:: : : : : : : :: :: : : ::: : :: : :::
**/
interface IFS {
function flzCompressContents(bytes calldata contents) external pure returns (bytes memory);
function fileBundleFromContents(bytes calldata contents) external view returns (FileBundle memory);
function saveFileBundle(FileBundle calldata fb) external returns (FSPtr memory ptr);
function readFile(FSPtr memory ptr) external view returns (bytes memory);
}
interface IFiltererMaster {
function isPaymentFilterIniter(address account) external view returns (bool);
function beneficiariesOf(uint256 payeeId) external view returns (uint256 beneficiaryId, address target);
function freelancerPercentage() external view returns (uint256);
function accountIncentivizedReleaseOptedOut(address account) external view returns (bool);
}
interface IERC7572 {
function contractURI() external view returns (string memory);
}
interface IHub is IFiltererMaster, IERC7572 {
function owner() external view returns (address);
function HUB_OWNER_TOKENID() external view returns (uint256);
function hubRoyalty() external view returns (uint256);
function hubPercentage() external view returns (uint256);
function HUB_DIVISOR() external view returns (uint256);
function paymentFilterer() external view returns (IPaymentFilterer);
function discountDemon() external view returns (IDiscountDemon);
function blockHashBeacon() external view returns (IBlockHashBeacon);
function blockHashBeaconRewardPercentage() external view returns (uint256);
function immortalizer() external view returns (IImmortalizer);
function immortalStorage() external view returns (IImmortalStorage);
function robustRenderer() external view returns (IRobustRenderer);
function uriRenderer() external view returns (IURI);
function crews() external view returns (ICrews);
function dreams() external view returns (IDreams);
function framesAnimated() external view returns (bool);
function originator(uint256 curationTokenId) external view returns (address);
function nftTemplates(uint256 nftTemplateId) external view returns (address);
function nftTemplatesLength() external view returns (uint256);
function validityLens() external view returns (IValidityLens);
function totalSupply() external view returns (uint256);
function totalMinted() external view returns (uint256);
function ownerOf(uint256 tokenId) external view returns (address);
function exists(uint256 tokenId) external view returns (bool);
function burned(uint256 tokenId) external view returns (bool);
function emitUpdated(uint256 curationTokenId) external;
function beneficiariesOf(uint256 tokenId) external view returns (uint256 beneficiary, address holder);
function curationTokenFramedOptedIn(uint256 curationTokenId) external view returns (bool);
function minFeeNumerator() external view returns (uint96);
function maxBurnWindow() external view returns (uint256);
function setMaxBurnWindow(uint256) external;
function addNewNFTTemplate(address) external;
function freelancerPercentage() external view returns (uint256);
function premierAccess() external view returns (IPremierAccessERC1155);
function platformApprovedWrapper(address account) external view returns (bool);
function ownerApprovedTokenWrapper(uint256 curationTokenId, address account) external view returns (bool);
function restrictedViewing(uint256 curationTokenId) external view returns (bool);
function bridging() external view returns (IBridging);
function prover() external view returns (IProver);
function updateContractURIImage(uint256 curationTokenId, FileBundle memory imageData, address customRenderer)
external;
function immortalizeCollection(ImmortalizeCollectionData calldata icd)
external
payable
returns (uint256 curationTokenId);
function getCollection(uint256 curationTokenId) external view returns (INFT);
function collectionCurationTokenId(INFT) external view returns (uint256);
function pledgedRevealTimestamps(bytes32 encryptionReference) external view returns (uint256);
function pledgedRevealTimestamp(uint256 curationTokenId) external view returns (uint256);
function tokenURI(uint256 tokenId) external view returns (string memory);
}
interface IImmortalizer {
function immortalize(
Type t,
uint256[] calldata featuredIds, // unique array by client, validated in contract
bytes32 encrypted,
FileBundle calldata compressed,
ExclusivityData calldata exclusivityData,
uint256 tokenId
) external;
function immortalizeFrame(
uint256[] calldata featuredIds, // unique array by client, validated in contract
bytes32 encrypted,
FileBundle calldata compressedFrame,
ExclusivityData calldata exclusivityData,
uint256 tokenId
) external;
function setColorClassOverrides(uint256 curationTokenId, FileBundle memory compressedColorClassOverrides) external;
function immortalizeCollection(ImmortalizeCollectionData calldata icd, uint256 tokenId)
external
returns (INFT nftClone);
function immortalizeDream(uint256 tokenId, FileBundle memory compressedDream) external;
function getDream(uint256 tokenId) external view returns (DreamData memory);
function updateContractURIImage(uint256 id, FileBundle calldata fb, address customRenderer) external;
function getContractURIData(uint256 collectionId) external view returns (bytes memory data, address renderer);
function fs() external view returns (IFS);
function immortalized(bytes32 fingerprint) external view returns (uint256 id);
function decrypted(uint256 id) external view returns (bool tf);
function encryptionReference(uint256 id) external view returns (bytes32);
function getTypedPtr(uint256 id) external view returns (TypedPtr memory tp);
function immortalizedType(uint256 id) external view returns (Type);
function isWalker(uint256 id) external view returns (bool);
function reveal(bytes32 encryptionReference, bytes32 key) external;
function decompressCollectionData(uint256 collectionCurationId) external view returns (CollectionData memory);
function decompressFrame(uint256 frameId) external view returns (Frame memory);
function decompressTab(uint256 tabId)
external
view
returns (
bytes[] memory attributes,
bytes[] memory paints,
bytes[][2] memory paintRoots,
uint256 resolution,
bytes[] memory paths
);
function decompressKVOverrides(uint256 curationTokenId) external view returns (KVOverride[] memory kvos);
function getNumberOfFrameStates(uint256 frameId) external view returns (uint256);
function key(uint256 id) external view returns (bytes32);
function keySafe(uint256 id) external view returns (bytes32);
function setGenericData(bytes32 namespace, FileBundle memory compressed) external;
function getGenericData(bytes32 namespace) external view returns (bytes memory);
}
interface IComponentValidator {
function validateTabPayees(IHub hub, uint256 tabId, bytes[][2] calldata colorClasses)
external
view
returns (bool ok);
function validateFramePayeeConfiguration(IHub hub, Frame calldata frame, uint256 frameId)
external
view
returns (bool ok);
function validateCollectionPayees(
IHub,
uint256 collectionId,
uint256[] calldata frameIds,
KVOverride[] memory overrides
) external view returns (bool ok);
function validateDimensions(FixedPoint.FP memory a, FixedPoint.FP memory b) external pure;
}
interface INFT is IERC7572 {
function curationTokenId() external view returns (uint256);
function owner() external view returns (address);
function ownerOf(uint256 tokenId) external view returns (address);
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function contractURI() external view returns (string memory);
function emitContractURIUpdated() external;
function emitBatchMetadataUpdate(uint256 fromTokenId, uint256 toTokenId) external;
function tokenURI(uint256 tokenId) external view returns (string memory);
function mintedAtBlock(uint256 tokenId) external view returns (uint256);
function description() external view returns (string memory);
function website() external view returns (string memory);
function maxSupply() external view returns (uint256);
function totalSupply() external view returns (uint256);
function totalMinted() external view returns (uint256);
function qtyAvailableToMint() external view returns (uint256);
function mintStarts() external view returns (uint256);
function mintEnds() external view returns (uint256);
function mintStarted() external view returns (bool);
function mintRevealBlockOffset() external view returns (uint256);
function mintPriceCurrent() external view returns (uint256);
function mintPricesInRange(uint256 nth, uint256 qty) external view returns (uint256[] memory);
function paymentPushGranularity() external view returns (uint256);
function mintPrices() external view returns (uint256[] memory mintQtyCheckpoints, uint256[] memory mintPrices);
function ONE() external view returns (uint256);
function mintEconomics() external view returns (MintEconomics memory);
function willBeFinalized() external view returns (uint256);
function initialize(
uint256 curationTokenId,
IRefunder refunder_,
CollectionNames calldata names,
MintEconomics calldata mintEconomics,
DiscountData calldata dd,
bytes calldata auxData
) external;
function paymentFilterer() external view returns (IPaymentFilterer);
function pushETHToPaymentFilterer() external;
function setMintEnds(uint256 mintEndsTime) external;
function updateSupply() external;
function mint(address to, uint256 qty) external payable;
function mintDiscounted(address to, IERC721[] calldata collections, uint256[] calldata tokenIds) external payable;
function burn(uint256[] memory ids) external;
}
interface IERC1155 is IERC165 {
function balanceOf(address account, uint256 id) external view returns (uint256);
function setApprovalForAll(address operator, bool approved) external;
function isApprovedForAll(address account, address operator) external view returns (bool);
function safeTransferFrom(address from, address to, uint256 id, uint256 amount, bytes calldata data) external;
function safeBatchTransferFrom(
address from,
address to,
uint256[] calldata ids,
uint256[] calldata amounts,
bytes calldata data
) external;
}
interface IERC1155MetadataURI {
function uri(uint256 id) external view returns (string memory);
}
interface IPremierAccessERC1155 is IERC1155, IERC1155MetadataURI {
function ONE() external view returns (uint256);
function feePercentage() external view returns (uint256);
function setExclusivityData(uint256 curationTokenId, ExclusivityData calldata xclusivityData) external;
function exclusivityData(uint256 curationTokenId) external view returns (ExclusivityData memory);
function supply(uint256 curationTokenId) external view returns (Supply memory);
function processAccess(address account, uint256 curationTokenId) external returns (bool ok);
function mint(uint256 curationTokenId, uint256 qty, address to) external payable;
function emitMetadataUpdate(uint256 curationTokenId) external;
}
interface IImmortalizerDecompressUtil {
function decompressCollectionData(bytes32 key, bytes memory compressed)
external
view
returns (CollectionData memory ret);
function decompressFrame(bytes32 key, bytes memory packed) external view returns (Frame memory frame);
function decompressTab(bytes32 key, bytes memory compressedTab)
external
view
returns (
bytes[] memory attributes,
bytes[] memory paints,
bytes[][2] memory paintRoots,
uint256 dimension,
bytes[] memory paths
);
function decompressKVOverrides(bytes memory compressedOverrides) external view returns (KVOverride[] memory kvos);
function decompressDreamData(bytes memory compressedDreamData) external view returns (DreamData memory dreamData);
}
interface IImmortalStorage {
function setCompressedPtr(uint256 id, TypedPtr memory tp) external;
function getCompressedPtr(uint256 id) external view returns (TypedPtr memory tp);
function setImmortalized(bytes32 fingerprint, uint256 id) external;
function getImmortalized(bytes32 fingerprint) external view returns (uint256 id);
function setEncryptionReference(uint256 id, bytes32 encryptionReference) external;
function getEncryptionReference(uint256 id) external view returns (bytes32 encryptionRference);
function setKey(bytes32 encryptionReference, bytes32 key) external;
function getKey(bytes32 encryptionReference) external view returns (bytes32 key);
function setContractURIImagePtr(uint256 id, FSPtr memory ptr) external;
function getContractURIImagePtr(uint256 id) external view returns (FSPtr memory ptr);
function setContractURIImageRenderer(uint256 id, address renderer) external;
function getContractURIImageRenderer(uint256 id) external view returns (address renderer);
function getGenericData(bytes32 hash) external view returns (FSPtr memory ptr);
function setGenericData(bytes32 hash, FSPtr memory ptr) external;
}
interface IRobustRenderer {
function renderMinimal(uint256 id, PointAndBounded calldata pb) external view returns (Element memory);
function renderFrame(uint256 frameId, uint256 entropy, uint256 seed, PointAndBounded calldata pb)
external
view
returns (Element memory);
function renderCollection(uint256 collectionId, uint256 entropy, uint256 seed, PointAndBounded calldata pb)
external
view
returns (RenderedCollectionData memory renderedCollectionData);
function renderContractURIImage(uint256 id) external view returns (string memory);
function formSVG(FixedPoint.FP memory resolution, PointAndBounded memory pb, bytes memory nestedData)
external
pure
returns (string memory);
}
interface IColorUtils {
function shadingToRoot(bytes memory shadingKey, bytes memory shadingValue)
external
pure
returns (bytes memory rootColorClassValue);
function getSLDetails(bytes memory key) external pure returns (int256 sPerc, int256 lPerc, bytes memory details);
function getSLPercs(bytes memory key, uint256 baseLength) external pure returns (int256 sPerc, int256 lPerc);
}
interface IValidityLens {
function scanValidity(uint256 id, uint256 seed, uint256 runs)
external
view
returns (Validity validity, bytes memory error);
function checkEncrypted(uint256 pledgedRevealTimestamp, bytes32 key) external view returns (Validity);
}
interface IURI {
function svgFramed() external view returns (ISVGFramed);
function uriFinisher() external view returns (IURIFinisher);
function maxMsgLength() external view returns (uint256);
function hubContractURI() external view returns (string memory);
function hubTokenURI(uint256 curationTokenId) external view returns (string memory);
function hubURI(uint256 curationTokenId) external view returns (string memory);
function collectionContractURI() external view returns (string memory);
function collectionTokenURI(uint256 tokenId, RevealedStatus revealedStatus, uint256 entropy)
external
view
returns (string memory);
}
interface ISVGFramed {
function MAT_OFFSET() external view returns (uint256);
function MAT_INNER() external view returns (uint256);
function framed(string memory color, bytes memory _msg, string memory sSvg, bool truncate, bool animateTF)
external
view
returns (string memory ret);
}
interface IURIFinisher {
function finishContractURI(
CollectionNames calldata names,
string calldata sSvg,
uint256 feeBasisPoints,
address feeRecipient
) external view returns (string memory ret);
function finishPending(Type t, Validity v, uint256 pledgedRevealTimestamp, uint256 curationTokenId)
external
view
returns (string memory ret);
function finishCollectionTokenURI(
CollectionNames calldata names,
string[][2] calldata attributes,
JSON[][2] calldata additionalAttributes,
string[][2] calldata colorClasses,
string[][2] calldata textures,
string calldata sSvg
) external view returns (string memory ret);
function finishTokenURI(
CollectionNames calldata names,
string[][2] calldata attributes,
JSON[][2] calldata additionalAttributes,
bytes memory formattedClasses,
string calldata sSvg
) external view returns (string memory ret);
function finishOutOfGas() external view returns (string memory ret);
}
interface IPaymentFilterer {
function BASIS() external view returns (uint256);
function initFilter(uint256 id, uint256[] memory payeeTokenIds, uint256[] memory shares_) external;
function receivePayment(uint256 id) external payable;
function payeesLength(uint256 id) external view returns (uint256);
function payee(uint256 id, uint256 idx) external view returns (uint256);
function shares(uint256 id, uint256 payeeTokenId) external view returns (uint256);
function balances(uint256 id) external view returns (uint256);
function totalShares(uint256 id) external view returns (uint256);
function isPayee(uint256 id, uint256 payeeTokenId) external view returns (bool);
function releasable(uint256 reflectiveId) external view returns (uint256);
function releasable(uint256 id, uint256 payeeTokenId) external view returns (uint256);
//function releasable(IERC20 token, uint256 payeeTokenId) external view returns (uint256);
function release(uint256 reflectiveId, address to) external;
function release(uint256 id, uint256 payeeTokenId, address to) external;
//function release(IERC20 token, uint256 payeeTokenId, address to) external;
function incentivizedRelease(uint256 reflectiveId) external;
function incentivizedRelease(uint256 id, uint256 payeeTokenId) external;
//function incentivizedRelease(IERC20 token, uint256 payeeTokenId) external;
}
interface IRefunds {
function refunder() external view returns (IRefunder);
function refundAvailable(address account) external view returns (uint256);
function claimRefund() external;
}
interface IRefunder {
function setCustomer(address customer) external;
function refundAvailable(address account) external view returns (uint256);
function claimRefund(address account) external;
function setRefund(address account) external payable;
}
interface IProver {
function validateProof(address to, INFT nft, uint256[] calldata tokenIds, uint256 l2Id, bytes calldata proof)
external
returns (bool);
}
interface IBridging {
function bridgeTo(address from, INFT nft, uint256[] calldata tokenIds, uint256 l2Id) external;
function bridgeFrom(address to, INFT nft, uint256[] calldata tokenIds, uint256 l2Id) external;
function isBridged(INFT nft, uint256 tokenId) external view returns (bool);
}
interface ISudoPoolValidator {
function validateAndAddSudoPoolsToWhitelist(address[] calldata sudoPools) external;
}
interface IOwnable {
function owner() external view returns (address);
}
interface ICrews {
function getCrewData(address account) external view returns (CrewData memory);
function isApprovedCrew(IERC721 crew) external view returns (bool);
function crewAdmin(IERC721 crew) external view returns (IOwnable);
function crewTreasury(IERC721 crew) external view returns (address);
function claimsCrew(address account) external view returns (IERC721);
function getCrewName(address account) external view returns (string memory);
function viewCrewImage(IERC721 crew) external view returns (bytes memory);
function setCrewImage(IERC721 crew, FileBundle calldata imageData) external;
function defineCrew(IERC721 crew, IOwnable newAdmin, address payable newTreasury) external;
function tryDefineCollectionCrew(INFT collection) external;
function retroDefineCollectionCrew(uint256 collectionCurationTokenId) external;
function claimCrew(IERC721 crew) external;
function leftCrew(IERC721 crew, address account) external;
function setNewCrewAdmin(IERC721 crew, IOwnable newAdmin) external;
function setCrewTreasury(IERC721 crew, address payable newTreasury) external;
}
interface IDreams {
function numAvailableBackgrounds() external view returns (uint256);
function viewBackground(uint256 backgroundId) external view returns (string memory);
function immortalizeDream(
string calldata name,
uint256 crewPercentage_,
FileBundle calldata compressedDream,
address to
) external;
function exists(uint256 tokenId) external view returns (bool);
function extendedDreamData(uint256 tokenId) external view returns (ExtendedDreamData memory);
function balanceETH(uint256 tokenId) external view returns (uint256);
function withdrawnETH(uint256 tokenId) external view returns (uint256);
function ownsERC721(uint256 dreamId, IERC721 token) external view returns (uint256[] memory tokenIdsOwned);
function balancesERC1155(uint256 dreamId, IERC1155 token)
external
view
returns (uint256[] memory tokens, uint256[] memory balances);
}
interface IDreamsRenderer {
function render(uint256 tokenId, string memory name, DreamData memory dd, bytes memory compressedRenderData)
external
view
returns (string memory);
}
interface IERC721Supplied is IERC721 {
function totalSupply() external view returns (uint256);
function totalMinted() external view returns (uint256);
function exists(uint256 tokenId) external view returns (bool);
}
Structs.sol 315 lines
// SPDX-License-Identifier: HVPL - HEXED VIRAL PUBLIC LICENSE
pragma solidity ^0.8.25;
/*
@@@@@@@ @@@@@@@ @@@@@@@@ @@@@@@ @@@@@@@@@@
@@@@@@@@ @@@@@@@@ @@@@@@@@ @@@@@@@@ @@@@@@@@@@@
@@! @@@ @@! @@@ @@! @@! @@@ @@! @@! @@!
!@! @!@ !@! @!@ !@! !@! @!@ !@! !@! !@!
@!@ !@! @!@!!@! @!!!:! @!@!@!@! @!! !!@ @!@
!@! !!! !!@!@! !!!!!: !!!@!!!! !@! ! !@!
!!: !!! !!: :!! !!: !!: !!! !!: !!: by no_side666
:!: !:! :!: !:! :!: :!: !:! :!: :!:
:::: :: :: ::: :: :::: :: ::: ::: :: dreamstack.xyz
:: : : : : : : :: :: : : : : :
@@@@@@ @@@@@@@ @@@@@@ @@@@@@@ @@@ @@@ @@@ @@@ @@@@@@
@@@@@@@ @@@@@@@ @@@@@@@@ @@@@@@@@ @@@ @@@ @@@ @@@ @@@@@@@@
!@@ @@! @@! @@@ !@@ @@! !@@ @@! @@@ @@@
!@! !@! !@! @!@ !@! !@! @!! !@! @!@ @!@
!!@@!! @!! @!@!@!@! !@! @!@@!@! @!@ !@! !!@
!!@!!! !!! !!!@!!!! !!! !!@!!! !@! !!! !!:
!:! !!: !!: !!! :!! !!: :!! :!: !!: !:!
!:! :!: :!: !:! :!: :!: !:! ::!!:! :!:
:::: :: :: :: ::: ::: ::: :: ::: :::: :: :::::
:: : : : : : : :: :: : : ::: : :: : :::
**/
import "lib/openzeppelin-contracts/contracts/token/ERC721/IERC721.sol";
import "./Interfaces.sol";
import "./FixedPoint.sol";
import "./LICENSE.sol";
struct Supply {
uint128 totalSupply;
uint128 totalMinted;
}
struct ImmortalizeCompressed {
uint256[] featuredIds;
FileBundle fileBundle;
}
struct ImmortalizeComponentData {
uint256[] featuredIds;
bytes32 encrypted;
FileBundle compressed;
ExclusivityData exclusivityData;
address to;
}
struct ImmortalizeCollectionData {
CollectionNames names;
bytes32 encrypted; // if ANY frame has encrypted reference then this must be set
uint256[] featuredIds;
FileBundle compressedCollectionData;
MintEconomics mintEconomics;
DiscountData dd;
address to;
uint256 nftVersionId;
bytes auxData;
}
struct Element {
string[][2] attributes;
string[][2] colorClasses;
string[][2] textures;
string svg;
FixedPoint.FP resolution;
bytes background;
}
struct CollectionNames {
string name;
string symbol;
string description;
bool walker;
}
enum MintPricingType {
TIMED,
BATCHED
}
struct MintEconomics {
uint256 curatorShare;
uint256 mintStarts;
uint256 mintEnds;
uint256 maxFreeMintsPerTx;
uint256 burnWindow;
uint256 mintRevealBlockOffset;
uint256 paymentPushGranularity;
MintPricingType mintPricingType;
uint256 maxSupply;
uint96 feeNumerator;
uint256[] mintCheckpoints;
uint256[] mintPrices;
}
struct ExclusivityData {
uint64 exclusivityWindow; // uint32 is too small
uint112 premiumAccessMax;
uint112 premiumAccessPrice;
bool sharesWithCrew;
}
struct DiscountData {
IERC721[] discountedCollections;
uint256[] discountFactors;
}
struct DreamData {
uint256 suggestedDonationETH;
uint256 targetETH;
uint256 fontSize;
uint256 backgroundId;
bytes description;
bytes data;
bytes[] keywords;
}
struct ExtendedDreamData {
address originator;
string name;
uint256 crewPercentage;
DreamData dreamData;
}
/////////////////////////
/*
// reference
struct Tab {
bytes[] attributes;
bytes[] paints;
bytes[] paintRoots;
uint256 dimension;
bytes[] paths;
}
*/
struct RenderedCollectionData {
string[][2] attributes;
string[][2] colorClasses;
string[][2] textures;
string sSvg;
FixedPoint.FP resolution;
bytes background;
}
struct Frame {
uint256 dimension; // validated so that all children tabs match dimension
bytes backgroundColor;
bytes[] attributes; // {face: pretty, .. etc}
uint256[] tabProbabilities; // can be len=0 if a walker
uint256[] tabIds; // checked against payment splitter on render
bytes forkAttributeKey;
uint256[] forkProbabilities; // can be len=0 if a walker
Fork[] forks;
bytes[][2] overrides; // smart ui ensures all textures have backfills
}
struct Fork {
bytes forkAttributeValue;
uint256[] frameIds; // checked against payment splitter on render
uint256[] footprints; // footprint & dimension gives scale
uint256[] positions; // array w len%2=0 for obvs reasons.. x,y..
}
struct CollectionData {
bytes32 idxBlinder;
uint256[] frameProbabilities; // can be len=0 if a walker
uint256[] frameIds; // checked against payment splitter on render, see _validatePayeeConficguration
KVOverride[] overrides;
bytes[][2] textureBackfills; // smart ui ensures all textures have backfills
AttributeOverride[] attributeOverrides;
}
struct KVOverride {
bytes k;
uint256[] probabilityKeys; // can be len=0 if a walker
bytes[] vs;
bytes[] aliases;
}
struct TextureInstanceStringData {
string id;
string backgroundColorClass;
}
struct AttributeOverride {
uint256 curationTokenId;
bytes[] attributes;
}
enum Type {
NONE,
TAB,
TAB_ENCRYPTED,
FRAME,
FRAME_ENCRYPTED,
COLLECTION,
COLLECTION_ENCRYPTED
}
struct TypedPtr {
bool encrypted;
bool walker;
Type t;
FSPtr ptr;
}
enum FSType {
MINIMAL,
MULTI
}
struct FSPtr {
address ptr;
FSType fsType;
}
enum PaintType {
STANDARD,
COLORCLASS,
TEXTURE,
BACKGROUND
}
enum ShadingType {
ROOT,
SHADING
}
enum Validity {
UNKNOWN,
PENDING_DECRYPTION,
VALID,
INVALID
}
enum CallStatus {
OK,
OUT_OF_GAS,
REVERTED
}
struct Point {
uint256 x;
uint256 y;
}
struct PointAndBounded {
Point p;
Point b;
}
struct UIBasicData {
Type t;
uint256 tokenId;
bool burned;
bool encrypted;
uint256 pledgedRevealTimestamp;
CollectionNames names;
MintEconomics mintEconomics;
ExclusivityData exclusivityData;
Supply exclusivitySupply;
address owner; // obviously can change and should prompt updates
address originator;
CrewData crewData;
string website;
}
struct CrewData {
IERC721 crew;
bool isApprovedCrew;
IOwnable crewAdmin;
address crewTreasury;
string name;
}
struct CounterPtr {
uint256 tally;
}
struct FileBundle {
// delaySaveChunks is a safeguard against eip-7825 gas limitations on tx. allows to passively save chunks to break up a big transaction
bool delaySaveChunks;
bytes compressedFile;
bytes[] chunks;
}
enum JSONType {
STRING,
DATE,
NUMBER,
ARRAY,
OBJ
}
struct JSON {
JSONType t;
bytes data;
}
enum RevealedStatus {
PENDING,
REVEALED
}
Withdrawable.sol 23 lines
// SPDX-License-Identifier: HVPL - HEXED VIRAL PUBLIC LICENSE
pragma solidity ^0.8.25;
import "lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";
import "./LICENSE.sol";
abstract contract Withdrawable {
error FailedCall_error();
function withdrawETH(address to, uint256 amount) public virtual {
_onlyOwner();
(bool success,) = to.call{value: amount}("");
if (!success) revert FailedCall_error();
}
function withdrawERC20(IERC20 token, address to, uint256 amount) public {
_onlyOwner();
/// forge-lint: disable-next-line(erc20-unchecked-transfer)
token.transfer(to, amount);
}
function _onlyOwner() internal view virtual;
}
LibPack.sol 325 lines
// SPDX-License-Identifier: HVPL - HEXED VIRAL PUBLIC LICENSE
pragma solidity ^0.8.25;
import "lib/solady/src/utils/LibBit.sol";
import "lib/solady/src/utils/LibZip.sol";
import {LibDynamicBuffer} from "./LibDynamicThing.sol";
import "./Common.sol";
import "./Append.sol";
import "./Errors.sol";
import "./LICENSE.sol";
library LibPack {
function packBytesArrs(bytes[] memory arrs) internal pure returns (bytes memory ret) {
uint256[] memory positions = new uint256[](arrs.length);
uint256 position;
for (uint256 i; i < positions.length; ++i) {
positions[i] = position;
position += arrs[i].length;
}
bytes memory packedPositions = packUint256s(positions);
bytes memory bPackedPositionsLength = toBytes(bytes32(packedPositions.length));
LibDynamicBuffer.DynamicBuffer memory db = LibDynamicBuffer.newDynamicBuffer();
LibDynamicBuffer.p(db, bPackedPositionsLength);
LibDynamicBuffer.p(db, packedPositions);
for (uint256 i; i < arrs.length; ++i) {
LibDynamicBuffer.p(db, arrs[i]);
}
ret = LibDynamicBuffer.getBuffer(db);
}
function bytesAt(bytes memory input, uint256 idx) internal pure returns (bytes memory ret) {
if (input.length < 1) revert InvalidInput_error();
unchecked {
uint256 packedPositionsLength;
assembly {
packedPositionsLength := mload(add(input, 0x20))
}
bytes memory packedPositions = _allocateBytes(packedPositionsLength);
assembly {
mstore(packedPositions, 0)
}
uint256 start = 32;
uint256 end = start + packedPositionsLength;
_appendSubstring(packedPositions, input, start, end);
uint256[] memory positions = unpackBytesIntoUint256s(packedPositions);
// padding
packedPositionsLength += 32;
if (idx > positions.length - 1) revert InvalidInput_error();
start = packedPositionsLength + _at(positions, idx);
if (idx < positions.length - 1) {
end = packedPositionsLength + _at(positions, idx + 1);
} else {
end = input.length;
}
ret = _allocateBytes(end - start);
assembly {
mstore(ret, 0)
}
_appendSubstring(ret, input, start, end);
} //uc
}
function unpackBytesIntoBytesArrs(bytes memory input) internal pure returns (bytes[] memory ret) {
if (input.length < 1) revert InvalidInput_error();
unchecked {
uint256 packedPositionsLength;
assembly {
packedPositionsLength := mload(add(input, 0x20))
}
bytes memory packedPositions = _allocateBytes(packedPositionsLength);
assembly {
mstore(packedPositions, 0)
}
uint256 start = 32;
uint256 end = start + packedPositionsLength;
_appendSubstring(packedPositions, input, start, end);
uint256[] memory positions = unpackBytesIntoUint256s(packedPositions);
ret = _allocateArr(positions.length);
// padding
packedPositionsLength += 32;
uint256 bound = positions.length - 1;
bytes memory _ret;
for (uint256 i; i < bound; ++i) {
start = packedPositionsLength + _at(positions, i);
end = packedPositionsLength + _at(positions, i + 1);
_ret = _allocateBytes(end - start);
assembly {
mstore(_ret, 0)
}
_appendSubstring(_ret, input, start, end);
assembly {
mstore(add(ret, add(0x20, mul(i, 0x20))), _ret)
}
}
start = packedPositionsLength + _at(positions, bound);
end = input.length;
_ret = _allocateBytes(end - start);
assembly {
mstore(_ret, 0)
}
_appendSubstring(_ret, input, start, end);
assembly {
mstore(add(ret, add(0x20, mul(bound, 0x20))), _ret)
}
} //uc
}
function packUint256s(uint256[] memory arr) internal pure returns (bytes memory ret) {
/// forge-lint: disable-start(unsafe-typecast)
uint256 maxIdxMSB; // idx most significant bit
uint256 idxMSB;
unchecked {
for (uint256 i; i < arr.length; ++i) {
idxMSB = LibBit.fls(arr[i]);
if (idxMSB == 256) continue;
if (idxMSB > maxIdxMSB) maxIdxMSB = idxMSB;
}
uint256 bound = maxIdxMSB / 8 + 1;
ret = new bytes(arr.length * bound + 1);
uint256 retIdx;
ret[retIdx++] = bytes1(uint8(bound));
uint256 n;
for (uint256 i; i < arr.length; ++i) {
n = arr[i];
for (uint256 j; j < bound; ++j) {
ret[retIdx++] = bytes1(uint8(n >> (8 * j)));
}
}
} // uc
/// forge-lint: disable-end(unsafe-typecast)
}
function uint256At(bytes memory packed, uint256 idx) internal pure returns (uint256 ret) {
if (packed.length < 1) revert InvalidInput_error();
unchecked {
uint256 bound = uint256(uint8(packed[0]));
assembly {
idx := add(mul(idx, bound), 1)
for { let j := 0 } 1 {} {
let shift := mul(8, j)
let r := byte(0, mload(add(packed, add(0x20, idx))))
r := shl(shift, r)
ret := or(ret, r)
idx := add(idx, 1)
j := add(j, 1)
if iszero(lt(j, bound)) { break }
}
}
} // uc
}
function unpackBytesIntoUint256s(bytes memory packed) internal pure returns (uint256[] memory ret) {
uint256 idx;
if (packed.length < 1) revert InvalidInput_error();
unchecked {
uint256 bound = uint256(uint8(packed[idx++]));
ret = _allocateUintArr((packed.length - 1) / bound);
assembly {
let length := mload(ret)
for { let i := 0 } 1 {} {
let n := 0
for { let j := 0 } 1 {} {
let shift := mul(8, j)
let r := byte(0, mload(add(packed, add(0x20, idx))))
r := shl(shift, r)
n := or(n, r)
idx := add(idx, 1)
j := add(j, 1)
if iszero(lt(j, bound)) { break }
}
mstore(add(ret, add(0x20, mul(i, 0x20))), n)
i := add(i, 1)
if iszero(lt(i, length)) { break }
}
}
} // uc
// does this ...
/*
unchecked {
if (packed.length < ret.length * bound) revert InvalidInput_error();
uint256 n;
for (uint256 i; i < ret.length; ++i) {
n = 0;
for (uint256 j; j < bound; ++j) {
n |= (uint256(uint8(packed[idx++])) << (8 * j));
}
ret[i] = n;
}
} // uc
*/
}
function decomposeZ(int256 z) internal pure returns (bool negative, uint256 n) {
// forge-lint: disable-start(unsafe-typecast)
unchecked {
if (z < int256(0)) return (true, ~uint256(z) + 1);
return (false, uint256(z));
} // uc
// forge-lint: disable-end(unsafe-typecast)
}
function packInt256s(int256[] memory arr) internal pure returns (bytes memory ret) {
uint256 length = arr.length;
uint256[] memory us = new uint256[](length);
bool negative;
uint256 n;
for (uint256 i; i < arr.length; ++i) {
(negative, n) = decomposeZ(arr[i]);
n = n << 1;
us[i] = n;
if (negative) {
us[i] |= 0x1;
}
}
ret = packUint256s(us);
}
function int256At(bytes memory packed, uint256 idx) internal pure returns (int256 ret) {
uint256 n = uint256At(packed, idx);
// forge-lint: disable-next-line(unsafe-typecast)
ret = int256(n >> 1);
if (n & 0x1 == 0x1) ret *= -1;
}
function unpackBytesIntoInt256s(bytes memory packed) internal pure returns (int256[] memory ret) {
uint256 idx;
if (packed.length < 1) revert InvalidInput_error();
unchecked {
uint256 bound = uint256(uint8(packed[idx++]));
if (bound < 1) revert InvalidInput_error();
uint256 length = (packed.length - 1) / bound;
ret = _allocateIntArr(length);
assembly {
for { let i := 0 } 1 {} {
let n := 0
for { let j := 0 } 1 {} {
let shift := mul(8, j)
let r := byte(0, mload(add(packed, add(0x20, idx))))
r := shl(shift, r)
n := or(n, r)
idx := add(idx, 1)
j := add(j, 1)
if iszero(lt(j, bound)) { break }
}
let ptr := add(ret, add(0x20, mul(i, 0x20)))
mstore(ptr, shr(1, n))
if and(n, 0x1) { mstore(ptr, sub(0, mload(ptr))) }
i := add(i, 1)
if iszero(lt(i, length)) { break }
}
}
} // uc
/* // does this ...
uint256[] memory us = unpackBytesIntoUint256s(packed);
uint256 length = us.length;
uint256 n;
for (uint256 i; i < length; ++i) {
n = us[i];
ret[i] = int256(n >> 1);
if (n & 0x1 == 0x1) ret[i] *= -1;
}
*/
}
function packAddresses(address[] memory arr) internal pure returns (bytes memory ret) {
uint256 bound = 20;
ret = new bytes(arr.length * bound);
uint256 retIdx;
uint256 n;
for (uint256 i; i < arr.length; ++i) {
n = uint256(uint160(arr[i]));
for (uint256 j; j < bound; ++j) {
// forge-lint: disable-next-line(unsafe-typecast)
ret[retIdx++] = bytes1(uint8(n >> (8 * j)));
}
}
}
function addressAt(bytes memory packed, uint256 idx) internal pure returns (address ret) {
// forge-lint: disable-start(unsafe-typecast)
if (packed.length < 1) revert InvalidInput_error();
unchecked {
idx = idx * 20; // no +1 since know bound
uint256 n;
for (uint256 j; j < 20; ++j) {
n |= (uint256(uint8(packed[idx++])) << (8 * j));
}
ret = address(uint160(n));
} // uc
// forge-lint: disable-end(unsafe-typecast)
}
function unpackBytesIntoAddresses(bytes memory packed) internal pure returns (address[] memory ret) {
uint256 idx;
if (packed.length < 1) revert InvalidInput_error();
unchecked {
ret = new address[](packed.length / 20); // no +1 since know bound
if (packed.length < ret.length * 20) revert InvalidInput_error();
uint256 n;
for (uint256 i; i < ret.length; ++i) {
n = 0;
/// forge-lint: disable-start(unsafe-typecast)
for (uint256 j; j < 20; ++j) {
n |= (uint256(uint8(packed[idx++])) << (8 * j));
}
ret[i] = address(uint160(n));
/// forge-lint: disable-end(unsafe-typecast)
}
} // uc
}
}
Errors.sol 121 lines
// SPDX-License-Identifier: HVPL - HEXED VIRAL PUBLIC LICENSE pragma solidity ^0.8.25; import "./LICENSE.sol"; error AlreadyImmortalized_error(); error IDOrdering_error(); error MustRespectExclusivity_error(); error ExclusivityLapsed_error(); error InvalidExclusivityParams_error(); error InvalidOptionsLength_error(); error RevealTimeNotSet_error(); error RevealOrdering_error(); error NotOwner_error(); error NotApproved_error(); error InsufficientImmortalizeFee_error(); error FailedCall_error(); error ExceedsMaxFreeMintsPerTx_error(); error MalformedInputs_error(); error ZeroInput_error(); error RepeatedEncryptionReference_error(); error InvalidCommitment_error(); error InvalidKey_error(); error InvalidFeeNumerator_error(); error BadMintPrices_error(); error BadMintCheckpoints_error(); error TrivialMaxSupply_error(); error MintEconomicsOrderering_error(); error NotHub_error(); error NotDreams_error(); error NotRobustRenderer_error(); error NotImmortalizer_error(); error AlreadyRevealed_error(); error NotRevealed_error(); error InvalidInput_error(); error TrivialData_error(); error InvalidComponent_error(); error InvalidDimensions_error(); error InvalidResolution_error(); error InvalidData_error(); error InvalidColor_error(); error InvalidPaint_error(); error InvalidRune_error(); error InvalidTexture_error(); error InvalidCaller_error(); error TrivialFrame_error(); error NoWrapping_error(); error UnpermittedKey_error(); error NotReady_error(); error CollectionFinalized_error(); error FramePayees_error(); error TabPayees_error(); error CollectionPayees_error(); error NotCollection_error(); error Transfer_error(); error InvalidLengths_error(); error InvalidPath_error(); error PathsNotOrdered_error(); error InvalidFileBundle_error(); error InvalidTabFingerprint_error(); error InvalidFrameFingerprint_error(); error InvalidColorClassAliases_error(); error RefundFailed_error(); error InsufficientValue_error(uint256 expected); error ExceedsMaxSupply_error(); error MintNotStarted_error(); error MintEnded_error(); error DiscountAlreadyClaimed_error(); error CollectionNotDiscounted_error(); error BridgingNotCurrentlySupported_error(); error InvalidProof_error(); error NotDistinct_error(); error ReadFile_error(); error AlreadySet_error(); error InvalidCollection_error(); error InvalidBurnWindow_error(); error InvalidMintCheckpoints_error(); error InvalidNFTTemplateVersion_error(); error InvalidMaxFreeMintsPerTx_error(); error InvalidMintRevealBlockOffset_error(); error Overflow_error(); error CyclesDisallowed_error(); error AlreadyInitialized_error(); error OnlyMasterApproved_error(); error IsFrozen_error(); error Unsupported_error(); error UninitializedCacheMap_error(); error InvalidShading_error(); error InvalidHex_error(); error InvalidByte_error(); error InvalidType_error(); error InvalidBackground_error(); // from ethfs error SliceOutOfBounds(address pointer, uint32 codeSize, uint32 sliceStart, uint32 sliceEnd); error AssertShouldNotHappen_error(); error AlreadyPaid_error(); error AlreadyApprovedCrew_error(); error Poser_error(); error CrewNotApproved_error(); error HasBeenRevealed_error(); error CanStillBeRevealed_error(); error NameAlreadyExists_error(); error UnsafeClear_error();
Common.sol 532 lines
// SPDX-License-Identifier: HVPL - HEXED VIRAL PUBLIC LICENSE
pragma solidity ^0.8.25;
import "lib/solady/src/utils/FixedPointMathLib.sol";
import "lib/solady/src/utils/LibBytes.sol";
import "./Append.sol";
import "./Structs.sol";
import "./Errors.sol";
import "./MemoryMappings.sol";
import "./LibPack.sol";
import "./LibDynamicThing.sol";
import "./LICENSE.sol";
function splitAtFirst(bytes memory subject, bytes memory needle, uint256 starting)
pure
returns (bool ok, bytes[2] memory ret)
{
uint256 idx = LibBytes.indexOf(subject, needle, starting);
if (idx < 1 || idx > subject.length) {
return (ok, ret);
}
ok = true;
unchecked {
ret[0] = _substring(subject, starting, idx);
ret[1] = __tail(subject, idx + needle.length);
} // uc
}
function atoi(bytes memory a) pure returns (uint256 ret) {
unchecked {
uint256 c;
for (uint256 i; i < a.length; ++i) {
c = uint256(uint8(_at(a, i)));
if (!(48 <= c && c <= 57)) revert InvalidRune_error();
c -= 48;
ret += (c * (10 ** (a.length - i - 1)));
}
} // uc
}
function toBytes32(bytes memory b) pure returns (bytes32 ret) {
assembly {
ret := mload(add(b, 0x20))
}
}
function toBytes(bytes32 b32) pure returns (bytes memory ret) {
ret = _allocateBytes(32);
assembly {
mstore(add(ret, 0x20), b32)
}
}
function allAreDistinct(uint256[] memory arr) pure returns (bool ret) {
// note: may not be ordered! so have to use map!
MemoryMappings.MemoryMapping memory m = MemoryMappings.newMemoryMapping({overwrite: false, sorted: false});
unchecked {
for (uint256 i; i < arr.length; ++i) {
MemoryMappings.add(m, bytes32(_at(arr, i)), bytes32(0));
}
} // uc
return arr.length == m.totalKeys;
}
function hashPair(uint256 a, uint256 b) pure returns (uint256 result) {
assembly {
mstore(0x0, a)
mstore(0x20, b)
result := keccak256(0x0, 0x40)
}
}
// shoutout vectorized!
function hashArr(uint256[] memory arr) pure returns (bytes32 result) {
assembly {
result := keccak256(add(arr, 0x20), shl(5, mload(arr)))
}
}
function hashArr(bytes32[] memory arr) pure returns (bytes32 result) {
assembly {
result := keccak256(add(arr, 0x20), shl(5, mload(arr)))
}
}
function _concat(bytes memory a, bytes memory b) pure returns (bytes memory ret) {
unchecked {
ret = _allocateBytes(a.length + b.length);
assembly {
mstore(ret, 0)
}
_append(ret, a);
_append(ret, b);
} // uc
}
function _concat(bytes memory a, bytes memory b, bytes memory c) pure returns (bytes memory ret) {
unchecked {
ret = _allocateBytes(a.length + b.length + c.length);
assembly {
mstore(ret, 0)
}
_append(ret, a);
_append(ret, b);
_append(ret, c);
} // uc
}
function _concat(bytes memory a, bytes memory b, bytes memory c, bytes memory d) pure returns (bytes memory ret) {
unchecked {
ret = _allocateBytes(a.length + b.length + c.length + d.length);
assembly {
mstore(ret, 0)
}
_append(ret, a);
_append(ret, b);
_append(ret, c);
_append(ret, d);
} // uc
}
function typeToString(Type t) pure returns (string memory ret) {
if (t == Type.NONE) {
ret = "NONE";
} else if (t == Type.TAB) {
ret = "TAB";
} else if (t == Type.TAB_ENCRYPTED) {
ret = "TAB_ENCRYPTED";
} else if (t == Type.FRAME) {
ret = "FRAME";
} else if (t == Type.FRAME_ENCRYPTED) {
ret = "FRAME_ENCRYPTED";
} else if (t == Type.COLLECTION) {
ret = "COLLECTION";
} else if (t == Type.COLLECTION_ENCRYPTED) {
ret = "COLLECTION_ENCRYPTED";
} else {
revert Unsupported_error();
}
}
function typeToStringDECRYPTED(Type t) pure returns (string memory ret) {
if (t == Type.NONE) {
ret = "NONE";
} else if (t == Type.TAB) {
ret = "TAB";
} else if (t == Type.TAB_ENCRYPTED) {
ret = "TAB_DECRYPTED";
} else if (t == Type.FRAME) {
ret = "FRAME";
} else if (t == Type.FRAME_ENCRYPTED) {
ret = "FRAME_DECRYPTED";
} else if (t == Type.COLLECTION) {
ret = "COLLECTION";
} else if (t == Type.COLLECTION_ENCRYPTED) {
ret = "COLLECTION_DECRYPTED";
} else {
revert Unsupported_error();
}
}
function validityToString(Validity v) pure returns (string memory ret) {
if (v == Validity.UNKNOWN) {
ret = "UNKNOWN";
} else if (v == Validity.PENDING_DECRYPTION) {
ret = "PENDING_DECRYPTION";
} else if (v == Validity.VALID) {
ret = "VALID";
} else if (v == Validity.INVALID) {
ret = "INVALID";
} else {
revert Unsupported_error();
}
}
function boolToString(bool tf) pure returns (string memory ret) {
if (tf) return "true";
return "false";
}
// single split is more efficient than others
function _splitBy(bytes memory data, bytes8 rune) pure returns (bool ok, bytes[2] memory ret) {
unchecked {
for (uint256 i; i < data.length; ++i) {
if (_at(data, i) == rune) {
bytes memory start = _allocateBytes(i);
assembly {
mstore(start, 0)
}
_appendSubstring(start, data, 0, i);
ret[0] = start;
bytes memory finish = _allocateBytes(data.length - i);
assembly {
mstore(finish, 0)
}
_appendSubstring(finish, data, i + 1, data.length);
ret[1] = finish;
ok = true;
break;
}
}
} // uc
}
// not the most efficient but very useful for validating given the "ok"
function _splitByEvery(bytes memory data, bytes8 rune) pure returns (bool ok, bytes[] memory ret) {
LibDynamicBytesArr.LinkedBytes memory lb = LibDynamicBytesArr.newDynamicBytesArr();
uint256 lastIdx;
bytes memory b;
uint256 size;
unchecked {
for (uint256 i; i < data.length; ++i) {
if (_at(data, i) == rune) {
size = i - lastIdx;
if (size < 1) {
lastIdx = i;
continue;
}
b = _allocateBytes(size);
assembly {
mstore(b, 0)
}
_appendSubstring(b, data, lastIdx, i);
LibDynamicBytesArr.p(lb, b);
lastIdx = i;
ok = true;
}
}
size = data.length - lastIdx;
if (size > 0) {
// don't forget last section :)
b = _allocateBytes(size);
assembly {
mstore(b, 0)
}
_appendSubstring(b, data, lastIdx, data.length);
LibDynamicBytesArr.p(lb, b);
}
ret = LibDynamicBytesArr.dump(lb);
} // uc
}
// this protects ux from long msgs
function _cropWithDots(bytes memory _msg, uint256 maxLength) pure returns (bytes memory) {
if (_msg.length < maxLength) return _msg;
assembly {
mstore(_msg, maxLength)
let start := sub(maxLength, 3) // assumes maxLength is greater than 3
for { let i := start } 1 {} {
mstore8(add(_msg, add(0x20, i)), 0x2e)
i := add(i, 1)
if iszero(lt(i, maxLength)) { break }
}
}
return _msg;
}
function validateNumeral(bytes memory subject) pure returns (bool ok) {
unchecked {
uint256 c;
for (uint256 i; i < subject.length; ++i) {
c = uint256(uint8(_at(subject, i)));
if (!(48 <= c && c <= 57)) return false;
}
ok = true;
} // uc
}
function validateHexColor(bytes memory subject, uint256 startIdx) pure returns (bool ok) {
unchecked {
uint256 length = subject.length;
if (length != 6 + startIdx) return false; // the 7 - is safe within this contract
uint256 c;
for (uint256 i = startIdx; i < length; ++i) {
c = uint256(uint8(_at(subject, i)));
// [0-9] || [a-f]
if (!((48 <= c && c <= 57) || (97 <= c && c <= 102))) return false;
}
} // uc
ok = true;
}
function _getPaintType(bytes memory paintKey) pure returns (PaintType ret) {
// forge-lint: disable-start(unsafe-typecast)
if (paintKey.length < 1) revert InvalidPaint_error();
bytes1 first = _at(paintKey, 0);
if (first == bytes1("_")[0]) {
return PaintType.STANDARD;
} else if (first == bytes1("-")[0]) {
return PaintType.COLORCLASS;
} else if (first == bytes1("T")[0]) {
return PaintType.TEXTURE;
} else if (first == bytes1("B")[0]) {
return PaintType.BACKGROUND;
}
revert InvalidPaint_error();
// forge-lint: disable-end(unsafe-typecast)
}
function _getShadingType(bytes memory paintKey) pure returns (ShadingType ret) {
unchecked {
if (paintKey.length < 1) revert InvalidPaint_error();
bytes1 last = _at(paintKey, paintKey.length - 1);
// forge-lint: disable-next-line(unsafe-typecast)
if (last == bytes1("S")[0]) ret = ShadingType.SHADING;
} // uc
}
function _validateTexture(uint256 dimension, bytes[][2] memory paintRoots) pure {
if (!LibBit.isPo2(dimension)) revert InvalidDimensions_error();
bytes[] memory keys = _at(paintRoots, 0);
unchecked {
for (uint256 i; i < keys.length; ++i) {
bytes memory key = _at(keys, i);
PaintType t = _getPaintType(key);
if (t == PaintType.TEXTURE) revert InvalidPaint_error();
}
} // uc
}
function getDeclaredFingerprint(FileBundle memory compressed) pure returns (bytes32 ret) {
if (compressed.chunks.length == 0) revert InvalidFileBundle_error();
return toBytes32(LibPack.bytesAt(_at(compressed.chunks, 0), 0));
}
function _hashPrefixIfHexColor(bytes memory subject) pure returns (bytes memory ret) {
if (validateHexColor(subject, 0)) {
return _concat(bytes("#"), subject);
}
return subject;
}
function _formatIfTextureInstance(bytes memory subject) pure returns (bytes memory ret) {
if (subject.length < 2) return subject;
uint256 c = uint256(uint8(subject[1]));
if (!(47 < c && c < 58)) return subject;
return _formatTextureInstance(subject);
}
function _getTextureInstanceStringData(bytes memory subject) pure returns (TextureInstanceStringData memory ret) {
(bool ok, bytes[2] memory valueSplit) = splitAtFirst(_tail(subject), "t", 0);
if (!ok) revert InvalidTexture_error();
ret = TextureInstanceStringData({id: string(valueSplit[0]), backgroundColorClass: string(valueSplit[1])});
}
function _formatTextureInstance(bytes memory subject) pure returns (bytes memory ret) {
TextureInstanceStringData memory tisd = _getTextureInstanceStringData(subject);
bytes memory background = bytes(tisd.backgroundColorClass);
if (keccak256(background) == keccak256("none")) {
ret = bytes(tisd.id);
} else {
PaintType pt = _getPaintType(background);
background = _tail(background); // a form of colorclass, has been validated in rr
if (pt == PaintType.STANDARD) {
background = _concat("#", background);
}
LibDynamicBuffer.DynamicBuffer memory db = LibDynamicBuffer.newDynamicBuffer();
LibDynamicBuffer.p(db, bytes(tisd.id));
LibDynamicBuffer.p(db, " ");
LibDynamicBuffer.p(db, background);
ret = LibDynamicBuffer.getBuffer(db);
}
}
// returns 0 when trivial frame. caller must interpret this
function getFrameHash(Frame memory frame) pure returns (bytes32) {
unchecked {
MemoryMappings.MemoryMapping memory m = MemoryMappings.newMemoryMapping({overwrite: true, sorted: true});
uint256[] memory ids = frame.tabIds;
uint256[] memory spots = new uint256[](4);
bytes32 hash;
uint256 count = 1; // both compressFrame and decompressFrame checks "allAreDistinct" so count is 1..
for (uint256 i; i < ids.length; ++i) {
spots[0] = _at(ids, i);
//spots[1] = 0; // "origin"
//spots[2] = 0;
//spots[3] = 0;
hash = hashArr(spots);
MemoryMappings.add(m, hash, bytes32(count));
}
Fork[] memory forks = frame.forks;
Fork memory fork;
bool ok;
bytes32 b32Count;
for (uint256 i; i < forks.length; ++i) {
fork = forks[i];
ids = fork.frameIds;
for (uint256 j; j < ids.length; ++j) {
spots[0] = _at(ids, j);
spots[1] = _at(fork.footprints, j);
spots[2] = _at(fork.positions, 2 * j);
spots[3] = _at(fork.positions, 2 * j + 1);
hash = hashArr(spots);
(ok, b32Count) = MemoryMappings.get(m, hash);
if (ok) {
count = uint256(b32Count);
++count;
} else {
count = 1;
}
MemoryMappings.add(m, hash, bytes32(count));
}
}
(bytes32[] memory ks, bytes32[] memory vs) = MemoryMappings.dump(m);
if (ks.length == 0) return bytes32(0); // caller interprets this
if (vs.length == 1 && _at(vs, 0) == bytes32(uint256(1))) return bytes32(0); // caller interprets this
// this strategy is cheaper gas than keccak256(abi.encode(ks,vs)) lol
spots[0] = uint256(hashArr(ks));
spots[1] = uint256(hashArr(vs));
spots[2] = 0;
spots[3] = 0;
return hashArr(spots);
} // uc
}
function _byteFromHexRune(bytes1 hexRune) pure returns (uint256 ret) {
uint256 c = uint256(uint8(hexRune));
if (c >= 48 && c <= 57) {
// [0-9]
return c - 48;
} else if (c >= 87 && c <= 102) {
// [a-f]
return c - 87;
}
revert InvalidByte_error();
}
// assumes dev KNOWS idx is within byte length, so can be unchecked since bytelength wouldn't be type max (unless bytes data is corrupted.. in which case.. FML)
function _byteFromStringAt(bytes memory b, uint256 idx) pure returns (uint256 ret) {
unchecked {
ret = _byteFromHexRune(_at(b, idx++)) << 4;
ret |= _byteFromHexRune(_at(b, idx));
} // uc
}
// this stupid trick is 75% gas of normal access, AND it reduces contract size!!
// ASSUMES def knows index exists: will not revert if reads out of bounds
function _at(bytes memory b, uint256 i) pure returns (bytes1 ret) {
assembly {
ret := mload(add(b, add(0x20, i)))
}
}
// this stupid trick is 75% gas of normal access, AND it reduces contract size!!
// ASSUMES def knows index exists: will not revert if reads out of bounds
function _at(uint256[] memory ks, uint256 i) pure returns (uint256 ret) {
assembly {
ret := mload(add(ks, add(0x20, mul(i, 0x20))))
}
}
// this stupid trick is 75% gas of normal access, AND it reduces contract size!!
// ASSUMES def knows index exists: will not revert if reads out of bounds
function _at(bytes32[] memory ks, uint256 i) pure returns (bytes32 ret) {
assembly {
ret := mload(add(ks, add(0x20, mul(i, 0x20))))
}
}
// this stupid trick is 75% gas of normal access, AND it reduces contract size!!
// ASSUMES def knows index exists: will not revert if reads out of bounds
function _atB(bytes32[] memory ks, uint256 i) pure returns (bytes memory ret) {
assembly {
ret := mload(add(ks, add(0x20, mul(i, 0x20))))
}
}
// this stupid trick is 75% gas of normal access, AND it reduces contract size!!
// ASSUMES def knows index exists: will not revert if reads out of bounds
function _atU(bytes32[] memory ks, uint256 i) pure returns (uint256 ret) {
assembly {
ret := mload(add(ks, add(0x20, mul(i, 0x20))))
}
}
// this stupid trick is 75% gas of normal access, AND it reduces contract size!!
// ASSUMES def knows index exists: will not revert if reads out of bounds
function _at(bytes[] memory ks, uint256 i) pure returns (bytes memory ret) {
assembly {
ret := mload(add(ks, add(0x20, mul(i, 0x20))))
}
}
// this stupid trick is 75% gas of normal access, AND it reduces contract size!!
// ASSUMES def knows index exists: will not revert if reads out of bounds
function _at(string[] memory ks, uint256 i) pure returns (string memory ret) {
assembly {
ret := mload(add(ks, add(0x20, mul(i, 0x20))))
}
}
// this stupid trick is 75% gas of normal access, AND it reduces contract size!!
// ASSUMES def knows index exists: will not revert if reads out of bounds
function _at(JSON[] memory ks, uint256 i) pure returns (JSON memory ret) {
assembly {
ret := mload(add(ks, add(0x20, mul(i, 0x20))))
}
}
// this stupid trick is 75% gas of normal access, AND it reduces contract size!!
// ASSUMES def knows index exists: will not revert if reads out of bounds
function _at(JSON[][2] memory aakvs, uint256 i) pure returns (JSON[] memory ret) {
assembly {
ret := mload(add(aakvs, mul(i, 0x20)))
}
}
// this stupid trick is 75% gas of normal access, AND it reduces contract size!!
function _at(bytes[][2] memory kvs, uint256 i) pure returns (bytes[] memory ret) {
assembly {
ret := mload(add(kvs, mul(i, 0x20)))
}
}
// this stupid trick is 75% gas of normal access, AND it reduces contract size!!
function _at(string[][2] memory kvs, uint256 i) pure returns (string[] memory ret) {
assembly {
ret := mload(add(kvs, mul(i, 0x20)))
}
}
function _respectOrder(uint256 value, uint256[] memory arr, uint256 i, uint256 length) pure {
unchecked {
if (i < length - 1 && !(value < _at(arr, i + 1))) revert IDOrdering_error();
}
}
LICENSE.sol 37 lines
// SPDX-License-Identifier: HVPL - HEXED VIRAL PUBLIC LICENSE pragma solidity 0.8.28; /* HEXED VIRAL PUBLIC LICENSE Copyleft (c) 2025 DreamStack. This WORK protected by voodoo and is hereby relinquished of all associated ownership, attribution and copy rights, and redistribution or use of any kind, with or without modification, is permitted without restriction subject to the following conditions: 1. Redistributions of this WORK, or ANY work that makes use of ANY of the contents of this WORK by ANY kind of copying, dependency, linkage, or ANY other possible form of DERIVATION or COMBINATION, must retain the ENTIRETY of this license. 2. No further restrictions of ANY kind may be applied. 3. While not legally binding, tongue-in-cheek hex spells may be cast on anyone who intentionally forks a HVPL project with the intent to siphon or vampire attack the project's business, earnings, or influence. Fully permissive, viral software license. Like VPL (VIRAL PUBLIC LICENSE), the HVPL (HEXED VIRAL PUBLIC LICENSE) is designed to achieve and extend the GPL's strong copyleft without the burden of its obligations and restrictions, but with a dash of witchcraft nudging humans to copy or borrow with decency. The HVPL's sole restriction is its own viral continuity, allowing it to effectively and permanently infect any work it touches with absolute permissiveness, and reign supreme in its sorcery. **/
LibBit.sol 180 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Library for bit twiddling and boolean operations.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibBit.sol)
/// @author Inspired by (https://graphics.stanford.edu/~seander/bithacks.html)
library LibBit {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* BIT TWIDDLING OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Find last set.
/// Returns the index of the most significant bit of `x`,
/// counting from the least significant bit position.
/// If `x` is zero, returns 256.
function fls(uint256 x) internal pure returns (uint256 r) {
/// @solidity memory-safe-assembly
assembly {
r := or(shl(8, iszero(x)), shl(7, lt(0xffffffffffffffffffffffffffffffff, x)))
r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x))))
r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
r := or(r, shl(4, lt(0xffff, shr(r, x))))
r := or(r, shl(3, lt(0xff, shr(r, x))))
// forgefmt: disable-next-item
r := or(r, byte(and(0x1f, shr(shr(r, x), 0x8421084210842108cc6318c6db6d54be)),
0x0706060506020504060203020504030106050205030304010505030400000000))
}
}
/// @dev Count leading zeros.
/// Returns the number of zeros preceding the most significant one bit.
/// If `x` is zero, returns 256.
function clz(uint256 x) internal pure returns (uint256 r) {
/// @solidity memory-safe-assembly
assembly {
r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x))
r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x))))
r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
r := or(r, shl(4, lt(0xffff, shr(r, x))))
r := or(r, shl(3, lt(0xff, shr(r, x))))
// forgefmt: disable-next-item
r := add(xor(r, byte(and(0x1f, shr(shr(r, x), 0x8421084210842108cc6318c6db6d54be)),
0xf8f9f9faf9fdfafbf9fdfcfdfafbfcfef9fafdfafcfcfbfefafafcfbffffffff)), iszero(x))
}
}
/// @dev Find first set.
/// Returns the index of the least significant bit of `x`,
/// counting from the least significant bit position.
/// If `x` is zero, returns 256.
/// Equivalent to `ctz` (count trailing zeros), which gives
/// the number of zeros following the least significant one bit.
function ffs(uint256 x) internal pure returns (uint256 r) {
/// @solidity memory-safe-assembly
assembly {
// Isolate the least significant bit.
x := and(x, add(not(x), 1))
// For the upper 3 bits of the result, use a De Bruijn-like lookup.
// Credit to adhusson: https://blog.adhusson.com/cheap-find-first-set-evm/
// forgefmt: disable-next-item
r := shl(5, shr(252, shl(shl(2, shr(250, mul(x,
0xb6db6db6ddddddddd34d34d349249249210842108c6318c639ce739cffffffff))),
0x8040405543005266443200005020610674053026020000107506200176117077)))
// For the lower 5 bits of the result, use a De Bruijn lookup.
// forgefmt: disable-next-item
r := or(r, byte(and(div(0xd76453e0, shr(r, x)), 0x1f),
0x001f0d1e100c1d070f090b19131c1706010e11080a1a141802121b1503160405))
}
}
/// @dev Returns the number of set bits in `x`.
function popCount(uint256 x) internal pure returns (uint256 c) {
/// @solidity memory-safe-assembly
assembly {
let max := not(0)
let isMax := eq(x, max)
x := sub(x, and(shr(1, x), div(max, 3)))
x := add(and(x, div(max, 5)), and(shr(2, x), div(max, 5)))
x := and(add(x, shr(4, x)), div(max, 17))
c := or(shl(8, isMax), shr(248, mul(x, div(max, 255))))
}
}
/// @dev Returns whether `x` is a power of 2.
function isPo2(uint256 x) internal pure returns (bool result) {
/// @solidity memory-safe-assembly
assembly {
// Equivalent to `x && !(x & (x - 1))`.
result := iszero(add(and(x, sub(x, 1)), iszero(x)))
}
}
/// @dev Returns `x` reversed at the bit level.
function reverseBits(uint256 x) internal pure returns (uint256 r) {
uint256 m0 = 0x0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f;
uint256 m1 = m0 ^ (m0 << 2);
uint256 m2 = m1 ^ (m1 << 1);
r = reverseBytes(x);
r = (m2 & (r >> 1)) | ((m2 & r) << 1);
r = (m1 & (r >> 2)) | ((m1 & r) << 2);
r = (m0 & (r >> 4)) | ((m0 & r) << 4);
}
/// @dev Returns `x` reversed at the byte level.
function reverseBytes(uint256 x) internal pure returns (uint256 r) {
unchecked {
// Computing masks on-the-fly reduces bytecode size by about 200 bytes.
uint256 m0 = 0x100000000000000000000000000000001 * (~toUint(x == uint256(0)) >> 192);
uint256 m1 = m0 ^ (m0 << 32);
uint256 m2 = m1 ^ (m1 << 16);
uint256 m3 = m2 ^ (m2 << 8);
r = (m3 & (x >> 8)) | ((m3 & x) << 8);
r = (m2 & (r >> 16)) | ((m2 & r) << 16);
r = (m1 & (r >> 32)) | ((m1 & r) << 32);
r = (m0 & (r >> 64)) | ((m0 & r) << 64);
r = (r >> 128) | (r << 128);
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* BOOLEAN OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
// A Solidity bool on the stack or memory is represented as a 256-bit word.
// Non-zero values are true, zero is false.
// A clean bool is either 0 (false) or 1 (true) under the hood.
// Usually, if not always, the bool result of a regular Solidity expression,
// or the argument of a public/external function will be a clean bool.
// You can usually use the raw variants for more performance.
// If uncertain, test (best with exact compiler settings).
// Or use the non-raw variants (compiler can sometimes optimize out the double `iszero`s).
/// @dev Returns `x & y`. Inputs must be clean.
function rawAnd(bool x, bool y) internal pure returns (bool z) {
/// @solidity memory-safe-assembly
assembly {
z := and(x, y)
}
}
/// @dev Returns `x & y`.
function and(bool x, bool y) internal pure returns (bool z) {
/// @solidity memory-safe-assembly
assembly {
z := and(iszero(iszero(x)), iszero(iszero(y)))
}
}
/// @dev Returns `x | y`. Inputs must be clean.
function rawOr(bool x, bool y) internal pure returns (bool z) {
/// @solidity memory-safe-assembly
assembly {
z := or(x, y)
}
}
/// @dev Returns `x | y`.
function or(bool x, bool y) internal pure returns (bool z) {
/// @solidity memory-safe-assembly
assembly {
z := or(iszero(iszero(x)), iszero(iszero(y)))
}
}
/// @dev Returns 1 if `b` is true, else 0. Input must be clean.
function rawToUint(bool b) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := b
}
}
/// @dev Returns 1 if `b` is true, else 0.
function toUint(bool b) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := iszero(iszero(b))
}
}
}
ERC721.sol 912 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Simple ERC721 implementation with storage hitchhiking.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/tokens/ERC721.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC721.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/tree/master/contracts/token/ERC721/ERC721.sol)
///
/// @dev Note:
/// - The ERC721 standard allows for self-approvals.
/// For performance, this implementation WILL NOT revert for such actions.
/// Please add any checks with overrides if desired.
/// - For performance, methods are made payable where permitted by the ERC721 standard.
/// - The `safeTransfer` functions use the identity precompile (0x4)
/// to copy memory internally.
///
/// If you are overriding:
/// - NEVER violate the ERC721 invariant:
/// the balance of an owner MUST always be equal to their number of ownership slots.
/// The transfer functions do not have an underflow guard for user token balances.
/// - Make sure all variables written to storage are properly cleaned
// (e.g. the bool value for `isApprovedForAll` MUST be either 1 or 0 under the hood).
/// - Check that the overridden function is actually used in the function you want to
/// change the behavior of. Much of the code has been manually inlined for performance.
abstract contract ERC721 {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CONSTANTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev An account can hold up to 4294967295 tokens.
uint256 internal constant _MAX_ACCOUNT_BALANCE = 0xffffffff;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Only the token owner or an approved account can manage the token.
error NotOwnerNorApproved();
/// @dev The token does not exist.
error TokenDoesNotExist();
/// @dev The token already exists.
error TokenAlreadyExists();
/// @dev Cannot query the balance for the zero address.
error BalanceQueryForZeroAddress();
/// @dev Cannot mint or transfer to the zero address.
error TransferToZeroAddress();
/// @dev The token must be owned by `from`.
error TransferFromIncorrectOwner();
/// @dev The recipient's balance has overflowed.
error AccountBalanceOverflow();
/// @dev Cannot safely transfer to a contract that does not implement
/// the ERC721Receiver interface.
error TransferToNonERC721ReceiverImplementer();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* EVENTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Emitted when token `id` is transferred from `from` to `to`.
event Transfer(address indexed from, address indexed to, uint256 indexed id);
/// @dev Emitted when `owner` enables `account` to manage the `id` token.
event Approval(address indexed owner, address indexed account, uint256 indexed id);
/// @dev Emitted when `owner` enables or disables `operator` to manage all of their tokens.
event ApprovalForAll(address indexed owner, address indexed operator, bool isApproved);
/// @dev `keccak256(bytes("Transfer(address,address,uint256)"))`.
uint256 private constant _TRANSFER_EVENT_SIGNATURE =
0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef;
/// @dev `keccak256(bytes("Approval(address,address,uint256)"))`.
uint256 private constant _APPROVAL_EVENT_SIGNATURE =
0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925;
/// @dev `keccak256(bytes("ApprovalForAll(address,address,bool)"))`.
uint256 private constant _APPROVAL_FOR_ALL_EVENT_SIGNATURE =
0x17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* STORAGE */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The ownership data slot of `id` is given by:
/// ```
/// mstore(0x00, id)
/// mstore(0x1c, _ERC721_MASTER_SLOT_SEED)
/// let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20)))
/// ```
/// Bits Layout:
/// - [0..159] `addr`
/// - [160..255] `extraData`
///
/// The approved address slot is given by: `add(1, ownershipSlot)`.
///
/// See: https://notes.ethereum.org/%40vbuterin/verkle_tree_eip
///
/// The balance slot of `owner` is given by:
/// ```
/// mstore(0x1c, _ERC721_MASTER_SLOT_SEED)
/// mstore(0x00, owner)
/// let balanceSlot := keccak256(0x0c, 0x1c)
/// ```
/// Bits Layout:
/// - [0..31] `balance`
/// - [32..255] `aux`
///
/// The `operator` approval slot of `owner` is given by:
/// ```
/// mstore(0x1c, or(_ERC721_MASTER_SLOT_SEED, operator))
/// mstore(0x00, owner)
/// let operatorApprovalSlot := keccak256(0x0c, 0x30)
/// ```
uint256 private constant _ERC721_MASTER_SLOT_SEED = 0x7d8825530a5a2e7a << 192;
/// @dev Pre-shifted and pre-masked constant.
uint256 private constant _ERC721_MASTER_SLOT_SEED_MASKED = 0x0a5a2e7a00000000;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* ERC721 METADATA */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the token collection name.
function name() public view virtual returns (string memory);
/// @dev Returns the token collection symbol.
function symbol() public view virtual returns (string memory);
/// @dev Returns the Uniform Resource Identifier (URI) for token `id`.
function tokenURI(uint256 id) public view virtual returns (string memory);
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* ERC721 */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the owner of token `id`.
///
/// Requirements:
/// - Token `id` must exist.
function ownerOf(uint256 id) public view virtual returns (address result) {
result = _ownerOf(id);
/// @solidity memory-safe-assembly
assembly {
if iszero(result) {
mstore(0x00, 0xceea21b6) // `TokenDoesNotExist()`.
revert(0x1c, 0x04)
}
}
}
/// @dev Returns the number of tokens owned by `owner`.
///
/// Requirements:
/// - `owner` must not be the zero address.
function balanceOf(address owner) public view virtual returns (uint256 result) {
/// @solidity memory-safe-assembly
assembly {
// Revert if the `owner` is the zero address.
if iszero(owner) {
mstore(0x00, 0x8f4eb604) // `BalanceQueryForZeroAddress()`.
revert(0x1c, 0x04)
}
mstore(0x1c, _ERC721_MASTER_SLOT_SEED)
mstore(0x00, owner)
result := and(sload(keccak256(0x0c, 0x1c)), _MAX_ACCOUNT_BALANCE)
}
}
/// @dev Returns the account approved to manage token `id`.
///
/// Requirements:
/// - Token `id` must exist.
function getApproved(uint256 id) public view virtual returns (address result) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, id)
mstore(0x1c, _ERC721_MASTER_SLOT_SEED)
let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20)))
if iszero(shl(96, sload(ownershipSlot))) {
mstore(0x00, 0xceea21b6) // `TokenDoesNotExist()`.
revert(0x1c, 0x04)
}
result := sload(add(1, ownershipSlot))
}
}
/// @dev Sets `account` as the approved account to manage token `id`.
///
/// Requirements:
/// - Token `id` must exist.
/// - The caller must be the owner of the token,
/// or an approved operator for the token owner.
///
/// Emits an {Approval} event.
function approve(address account, uint256 id) public payable virtual {
_approve(msg.sender, account, id);
}
/// @dev Returns whether `operator` is approved to manage the tokens of `owner`.
function isApprovedForAll(address owner, address operator)
public
view
virtual
returns (bool result)
{
/// @solidity memory-safe-assembly
assembly {
mstore(0x1c, operator)
mstore(0x08, _ERC721_MASTER_SLOT_SEED_MASKED)
mstore(0x00, owner)
result := sload(keccak256(0x0c, 0x30))
}
}
/// @dev Sets whether `operator` is approved to manage the tokens of the caller.
///
/// Emits an {ApprovalForAll} event.
function setApprovalForAll(address operator, bool isApproved) public virtual {
/// @solidity memory-safe-assembly
assembly {
// Convert to 0 or 1.
isApproved := iszero(iszero(isApproved))
// Update the `isApproved` for (`msg.sender`, `operator`).
mstore(0x1c, operator)
mstore(0x08, _ERC721_MASTER_SLOT_SEED_MASKED)
mstore(0x00, caller())
sstore(keccak256(0x0c, 0x30), isApproved)
// Emit the {ApprovalForAll} event.
mstore(0x00, isApproved)
// forgefmt: disable-next-item
log3(0x00, 0x20, _APPROVAL_FOR_ALL_EVENT_SIGNATURE, caller(), shr(96, shl(96, operator)))
}
}
/// @dev Transfers token `id` from `from` to `to`.
///
/// Requirements:
///
/// - Token `id` must exist.
/// - `from` must be the owner of the token.
/// - `to` cannot be the zero address.
/// - The caller must be the owner of the token, or be approved to manage the token.
///
/// Emits a {Transfer} event.
function transferFrom(address from, address to, uint256 id) public payable virtual {
_beforeTokenTransfer(from, to, id);
/// @solidity memory-safe-assembly
assembly {
// Clear the upper 96 bits.
let bitmaskAddress := shr(96, not(0))
from := and(bitmaskAddress, from)
to := and(bitmaskAddress, to)
// Load the ownership data.
mstore(0x00, id)
mstore(0x1c, or(_ERC721_MASTER_SLOT_SEED, caller()))
let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20)))
let ownershipPacked := sload(ownershipSlot)
let owner := and(bitmaskAddress, ownershipPacked)
// Revert if the token does not exist, or if `from` is not the owner.
if iszero(mul(owner, eq(owner, from))) {
// `TokenDoesNotExist()`, `TransferFromIncorrectOwner()`.
mstore(shl(2, iszero(owner)), 0xceea21b6a1148100)
revert(0x1c, 0x04)
}
// Load, check, and update the token approval.
{
mstore(0x00, from)
let approvedAddress := sload(add(1, ownershipSlot))
// Revert if the caller is not the owner, nor approved.
if iszero(or(eq(caller(), from), eq(caller(), approvedAddress))) {
if iszero(sload(keccak256(0x0c, 0x30))) {
mstore(0x00, 0x4b6e7f18) // `NotOwnerNorApproved()`.
revert(0x1c, 0x04)
}
}
// Delete the approved address if any.
if approvedAddress { sstore(add(1, ownershipSlot), 0) }
}
// Update with the new owner.
sstore(ownershipSlot, xor(ownershipPacked, xor(from, to)))
// Decrement the balance of `from`.
{
let fromBalanceSlot := keccak256(0x0c, 0x1c)
sstore(fromBalanceSlot, sub(sload(fromBalanceSlot), 1))
}
// Increment the balance of `to`.
{
mstore(0x00, to)
let toBalanceSlot := keccak256(0x0c, 0x1c)
let toBalanceSlotPacked := add(sload(toBalanceSlot), 1)
// Revert if `to` is the zero address, or if the account balance overflows.
if iszero(mul(to, and(toBalanceSlotPacked, _MAX_ACCOUNT_BALANCE))) {
// `TransferToZeroAddress()`, `AccountBalanceOverflow()`.
mstore(shl(2, iszero(to)), 0xea553b3401336cea)
revert(0x1c, 0x04)
}
sstore(toBalanceSlot, toBalanceSlotPacked)
}
// Emit the {Transfer} event.
log4(codesize(), 0x00, _TRANSFER_EVENT_SIGNATURE, from, to, id)
}
_afterTokenTransfer(from, to, id);
}
/// @dev Equivalent to `safeTransferFrom(from, to, id, "")`.
function safeTransferFrom(address from, address to, uint256 id) public payable virtual {
transferFrom(from, to, id);
if (_hasCode(to)) _checkOnERC721Received(from, to, id, "");
}
/// @dev Transfers token `id` from `from` to `to`.
///
/// Requirements:
///
/// - Token `id` must exist.
/// - `from` must be the owner of the token.
/// - `to` cannot be the zero address.
/// - The caller must be the owner of the token, or be approved to manage the token.
/// - If `to` refers to a smart contract, it must implement
/// {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
///
/// Emits a {Transfer} event.
function safeTransferFrom(address from, address to, uint256 id, bytes calldata data)
public
payable
virtual
{
transferFrom(from, to, id);
if (_hasCode(to)) _checkOnERC721Received(from, to, id, data);
}
/// @dev Returns true if this contract implements the interface defined by `interfaceId`.
/// See: https://eips.ethereum.org/EIPS/eip-165
/// This function call must use less than 30000 gas.
function supportsInterface(bytes4 interfaceId) public view virtual returns (bool result) {
/// @solidity memory-safe-assembly
assembly {
let s := shr(224, interfaceId)
// ERC165: 0x01ffc9a7, ERC721: 0x80ac58cd, ERC721Metadata: 0x5b5e139f.
result := or(or(eq(s, 0x01ffc9a7), eq(s, 0x80ac58cd)), eq(s, 0x5b5e139f))
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* INTERNAL QUERY FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns if token `id` exists.
function _exists(uint256 id) internal view virtual returns (bool result) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, id)
mstore(0x1c, _ERC721_MASTER_SLOT_SEED)
result := iszero(iszero(shl(96, sload(add(id, add(id, keccak256(0x00, 0x20)))))))
}
}
/// @dev Returns the owner of token `id`.
/// Returns the zero address instead of reverting if the token does not exist.
function _ownerOf(uint256 id) internal view virtual returns (address result) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, id)
mstore(0x1c, _ERC721_MASTER_SLOT_SEED)
result := shr(96, shl(96, sload(add(id, add(id, keccak256(0x00, 0x20))))))
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* INTERNAL DATA HITCHHIKING FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
// For performance, no events are emitted for the hitchhiking setters.
// Please emit your own events if required.
/// @dev Returns the auxiliary data for `owner`.
/// Minting, transferring, burning the tokens of `owner` will not change the auxiliary data.
/// Auxiliary data can be set for any address, even if it does not have any tokens.
function _getAux(address owner) internal view virtual returns (uint224 result) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x1c, _ERC721_MASTER_SLOT_SEED)
mstore(0x00, owner)
result := shr(32, sload(keccak256(0x0c, 0x1c)))
}
}
/// @dev Set the auxiliary data for `owner` to `value`.
/// Minting, transferring, burning the tokens of `owner` will not change the auxiliary data.
/// Auxiliary data can be set for any address, even if it does not have any tokens.
function _setAux(address owner, uint224 value) internal virtual {
/// @solidity memory-safe-assembly
assembly {
mstore(0x1c, _ERC721_MASTER_SLOT_SEED)
mstore(0x00, owner)
let balanceSlot := keccak256(0x0c, 0x1c)
let packed := sload(balanceSlot)
sstore(balanceSlot, xor(packed, shl(32, xor(value, shr(32, packed)))))
}
}
/// @dev Returns the extra data for token `id`.
/// Minting, transferring, burning a token will not change the extra data.
/// The extra data can be set on a non-existent token.
function _getExtraData(uint256 id) internal view virtual returns (uint96 result) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, id)
mstore(0x1c, _ERC721_MASTER_SLOT_SEED)
result := shr(160, sload(add(id, add(id, keccak256(0x00, 0x20)))))
}
}
/// @dev Sets the extra data for token `id` to `value`.
/// Minting, transferring, burning a token will not change the extra data.
/// The extra data can be set on a non-existent token.
function _setExtraData(uint256 id, uint96 value) internal virtual {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, id)
mstore(0x1c, _ERC721_MASTER_SLOT_SEED)
let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20)))
let packed := sload(ownershipSlot)
sstore(ownershipSlot, xor(packed, shl(160, xor(value, shr(160, packed)))))
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* INTERNAL MINT FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Mints token `id` to `to`.
///
/// Requirements:
///
/// - Token `id` must not exist.
/// - `to` cannot be the zero address.
///
/// Emits a {Transfer} event.
function _mint(address to, uint256 id) internal virtual {
_beforeTokenTransfer(address(0), to, id);
/// @solidity memory-safe-assembly
assembly {
// Clear the upper 96 bits.
to := shr(96, shl(96, to))
// Load the ownership data.
mstore(0x00, id)
mstore(0x1c, _ERC721_MASTER_SLOT_SEED)
let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20)))
let ownershipPacked := sload(ownershipSlot)
// Revert if the token already exists.
if shl(96, ownershipPacked) {
mstore(0x00, 0xc991cbb1) // `TokenAlreadyExists()`.
revert(0x1c, 0x04)
}
// Update with the owner.
sstore(ownershipSlot, or(ownershipPacked, to))
// Increment the balance of the owner.
{
mstore(0x00, to)
let balanceSlot := keccak256(0x0c, 0x1c)
let balanceSlotPacked := add(sload(balanceSlot), 1)
// Revert if `to` is the zero address, or if the account balance overflows.
if iszero(mul(to, and(balanceSlotPacked, _MAX_ACCOUNT_BALANCE))) {
// `TransferToZeroAddress()`, `AccountBalanceOverflow()`.
mstore(shl(2, iszero(to)), 0xea553b3401336cea)
revert(0x1c, 0x04)
}
sstore(balanceSlot, balanceSlotPacked)
}
// Emit the {Transfer} event.
log4(codesize(), 0x00, _TRANSFER_EVENT_SIGNATURE, 0, to, id)
}
_afterTokenTransfer(address(0), to, id);
}
/// @dev Mints token `id` to `to`, and updates the extra data for token `id` to `value`.
/// Does NOT check if token `id` already exists (assumes `id` is auto-incrementing).
///
/// Requirements:
///
/// - `to` cannot be the zero address.
///
/// Emits a {Transfer} event.
function _mintAndSetExtraDataUnchecked(address to, uint256 id, uint96 value) internal virtual {
_beforeTokenTransfer(address(0), to, id);
/// @solidity memory-safe-assembly
assembly {
// Clear the upper 96 bits.
to := shr(96, shl(96, to))
// Update with the owner and extra data.
mstore(0x00, id)
mstore(0x1c, _ERC721_MASTER_SLOT_SEED)
sstore(add(id, add(id, keccak256(0x00, 0x20))), or(shl(160, value), to))
// Increment the balance of the owner.
{
mstore(0x00, to)
let balanceSlot := keccak256(0x0c, 0x1c)
let balanceSlotPacked := add(sload(balanceSlot), 1)
// Revert if `to` is the zero address, or if the account balance overflows.
if iszero(mul(to, and(balanceSlotPacked, _MAX_ACCOUNT_BALANCE))) {
// `TransferToZeroAddress()`, `AccountBalanceOverflow()`.
mstore(shl(2, iszero(to)), 0xea553b3401336cea)
revert(0x1c, 0x04)
}
sstore(balanceSlot, balanceSlotPacked)
}
// Emit the {Transfer} event.
log4(codesize(), 0x00, _TRANSFER_EVENT_SIGNATURE, 0, to, id)
}
_afterTokenTransfer(address(0), to, id);
}
/// @dev Equivalent to `_safeMint(to, id, "")`.
function _safeMint(address to, uint256 id) internal virtual {
_safeMint(to, id, "");
}
/// @dev Mints token `id` to `to`.
///
/// Requirements:
///
/// - Token `id` must not exist.
/// - `to` cannot be the zero address.
/// - If `to` refers to a smart contract, it must implement
/// {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
///
/// Emits a {Transfer} event.
function _safeMint(address to, uint256 id, bytes memory data) internal virtual {
_mint(to, id);
if (_hasCode(to)) _checkOnERC721Received(address(0), to, id, data);
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* INTERNAL BURN FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Equivalent to `_burn(address(0), id)`.
function _burn(uint256 id) internal virtual {
_burn(address(0), id);
}
/// @dev Destroys token `id`, using `by`.
///
/// Requirements:
///
/// - Token `id` must exist.
/// - If `by` is not the zero address,
/// it must be the owner of the token, or be approved to manage the token.
///
/// Emits a {Transfer} event.
function _burn(address by, uint256 id) internal virtual {
address owner = ownerOf(id);
_beforeTokenTransfer(owner, address(0), id);
/// @solidity memory-safe-assembly
assembly {
// Clear the upper 96 bits.
by := shr(96, shl(96, by))
// Load the ownership data.
mstore(0x00, id)
mstore(0x1c, or(_ERC721_MASTER_SLOT_SEED, by))
let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20)))
let ownershipPacked := sload(ownershipSlot)
// Reload the owner in case it is changed in `_beforeTokenTransfer`.
owner := shr(96, shl(96, ownershipPacked))
// Revert if the token does not exist.
if iszero(owner) {
mstore(0x00, 0xceea21b6) // `TokenDoesNotExist()`.
revert(0x1c, 0x04)
}
// Load and check the token approval.
{
mstore(0x00, owner)
let approvedAddress := sload(add(1, ownershipSlot))
// If `by` is not the zero address, do the authorization check.
// Revert if the `by` is not the owner, nor approved.
if iszero(or(iszero(by), or(eq(by, owner), eq(by, approvedAddress)))) {
if iszero(sload(keccak256(0x0c, 0x30))) {
mstore(0x00, 0x4b6e7f18) // `NotOwnerNorApproved()`.
revert(0x1c, 0x04)
}
}
// Delete the approved address if any.
if approvedAddress { sstore(add(1, ownershipSlot), 0) }
}
// Clear the owner.
sstore(ownershipSlot, xor(ownershipPacked, owner))
// Decrement the balance of `owner`.
{
let balanceSlot := keccak256(0x0c, 0x1c)
sstore(balanceSlot, sub(sload(balanceSlot), 1))
}
// Emit the {Transfer} event.
log4(codesize(), 0x00, _TRANSFER_EVENT_SIGNATURE, owner, 0, id)
}
_afterTokenTransfer(owner, address(0), id);
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* INTERNAL APPROVAL FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns whether `account` is the owner of token `id`, or is approved to manage it.
///
/// Requirements:
/// - Token `id` must exist.
function _isApprovedOrOwner(address account, uint256 id)
internal
view
virtual
returns (bool result)
{
/// @solidity memory-safe-assembly
assembly {
result := 1
// Clear the upper 96 bits.
account := shr(96, shl(96, account))
// Load the ownership data.
mstore(0x00, id)
mstore(0x1c, or(_ERC721_MASTER_SLOT_SEED, account))
let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20)))
let owner := shr(96, shl(96, sload(ownershipSlot)))
// Revert if the token does not exist.
if iszero(owner) {
mstore(0x00, 0xceea21b6) // `TokenDoesNotExist()`.
revert(0x1c, 0x04)
}
// Check if `account` is the `owner`.
if iszero(eq(account, owner)) {
mstore(0x00, owner)
// Check if `account` is approved to manage the token.
if iszero(sload(keccak256(0x0c, 0x30))) {
result := eq(account, sload(add(1, ownershipSlot)))
}
}
}
}
/// @dev Returns the account approved to manage token `id`.
/// Returns the zero address instead of reverting if the token does not exist.
function _getApproved(uint256 id) internal view virtual returns (address result) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, id)
mstore(0x1c, _ERC721_MASTER_SLOT_SEED)
result := sload(add(1, add(id, add(id, keccak256(0x00, 0x20)))))
}
}
/// @dev Equivalent to `_approve(address(0), account, id)`.
function _approve(address account, uint256 id) internal virtual {
_approve(address(0), account, id);
}
/// @dev Sets `account` as the approved account to manage token `id`, using `by`.
///
/// Requirements:
/// - Token `id` must exist.
/// - If `by` is not the zero address, `by` must be the owner
/// or an approved operator for the token owner.
///
/// Emits a {Approval} event.
function _approve(address by, address account, uint256 id) internal virtual {
assembly {
// Clear the upper 96 bits.
let bitmaskAddress := shr(96, not(0))
account := and(bitmaskAddress, account)
by := and(bitmaskAddress, by)
// Load the owner of the token.
mstore(0x00, id)
mstore(0x1c, or(_ERC721_MASTER_SLOT_SEED, by))
let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20)))
let owner := and(bitmaskAddress, sload(ownershipSlot))
// Revert if the token does not exist.
if iszero(owner) {
mstore(0x00, 0xceea21b6) // `TokenDoesNotExist()`.
revert(0x1c, 0x04)
}
// If `by` is not the zero address, do the authorization check.
// Revert if `by` is not the owner, nor approved.
if iszero(or(iszero(by), eq(by, owner))) {
mstore(0x00, owner)
if iszero(sload(keccak256(0x0c, 0x30))) {
mstore(0x00, 0x4b6e7f18) // `NotOwnerNorApproved()`.
revert(0x1c, 0x04)
}
}
// Sets `account` as the approved account to manage `id`.
sstore(add(1, ownershipSlot), account)
// Emit the {Approval} event.
log4(codesize(), 0x00, _APPROVAL_EVENT_SIGNATURE, owner, account, id)
}
}
/// @dev Approve or remove the `operator` as an operator for `by`,
/// without authorization checks.
///
/// Emits an {ApprovalForAll} event.
function _setApprovalForAll(address by, address operator, bool isApproved) internal virtual {
/// @solidity memory-safe-assembly
assembly {
// Clear the upper 96 bits.
by := shr(96, shl(96, by))
operator := shr(96, shl(96, operator))
// Convert to 0 or 1.
isApproved := iszero(iszero(isApproved))
// Update the `isApproved` for (`by`, `operator`).
mstore(0x1c, or(_ERC721_MASTER_SLOT_SEED, operator))
mstore(0x00, by)
sstore(keccak256(0x0c, 0x30), isApproved)
// Emit the {ApprovalForAll} event.
mstore(0x00, isApproved)
log3(0x00, 0x20, _APPROVAL_FOR_ALL_EVENT_SIGNATURE, by, operator)
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* INTERNAL TRANSFER FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Equivalent to `_transfer(address(0), from, to, id)`.
function _transfer(address from, address to, uint256 id) internal virtual {
_transfer(address(0), from, to, id);
}
/// @dev Transfers token `id` from `from` to `to`.
///
/// Requirements:
///
/// - Token `id` must exist.
/// - `from` must be the owner of the token.
/// - `to` cannot be the zero address.
/// - If `by` is not the zero address,
/// it must be the owner of the token, or be approved to manage the token.
///
/// Emits a {Transfer} event.
function _transfer(address by, address from, address to, uint256 id) internal virtual {
_beforeTokenTransfer(from, to, id);
/// @solidity memory-safe-assembly
assembly {
// Clear the upper 96 bits.
let bitmaskAddress := shr(96, not(0))
from := and(bitmaskAddress, from)
to := and(bitmaskAddress, to)
by := and(bitmaskAddress, by)
// Load the ownership data.
mstore(0x00, id)
mstore(0x1c, or(_ERC721_MASTER_SLOT_SEED, by))
let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20)))
let ownershipPacked := sload(ownershipSlot)
let owner := and(bitmaskAddress, ownershipPacked)
// Revert if the token does not exist, or if `from` is not the owner.
if iszero(mul(owner, eq(owner, from))) {
// `TokenDoesNotExist()`, `TransferFromIncorrectOwner()`.
mstore(shl(2, iszero(owner)), 0xceea21b6a1148100)
revert(0x1c, 0x04)
}
// Load, check, and update the token approval.
{
mstore(0x00, from)
let approvedAddress := sload(add(1, ownershipSlot))
// If `by` is not the zero address, do the authorization check.
// Revert if the `by` is not the owner, nor approved.
if iszero(or(iszero(by), or(eq(by, from), eq(by, approvedAddress)))) {
if iszero(sload(keccak256(0x0c, 0x30))) {
mstore(0x00, 0x4b6e7f18) // `NotOwnerNorApproved()`.
revert(0x1c, 0x04)
}
}
// Delete the approved address if any.
if approvedAddress { sstore(add(1, ownershipSlot), 0) }
}
// Update with the new owner.
sstore(ownershipSlot, xor(ownershipPacked, xor(from, to)))
// Decrement the balance of `from`.
{
let fromBalanceSlot := keccak256(0x0c, 0x1c)
sstore(fromBalanceSlot, sub(sload(fromBalanceSlot), 1))
}
// Increment the balance of `to`.
{
mstore(0x00, to)
let toBalanceSlot := keccak256(0x0c, 0x1c)
let toBalanceSlotPacked := add(sload(toBalanceSlot), 1)
// Revert if `to` is the zero address, or if the account balance overflows.
if iszero(mul(to, and(toBalanceSlotPacked, _MAX_ACCOUNT_BALANCE))) {
// `TransferToZeroAddress()`, `AccountBalanceOverflow()`.
mstore(shl(2, iszero(to)), 0xea553b3401336cea)
revert(0x1c, 0x04)
}
sstore(toBalanceSlot, toBalanceSlotPacked)
}
// Emit the {Transfer} event.
log4(codesize(), 0x00, _TRANSFER_EVENT_SIGNATURE, from, to, id)
}
_afterTokenTransfer(from, to, id);
}
/// @dev Equivalent to `_safeTransfer(from, to, id, "")`.
function _safeTransfer(address from, address to, uint256 id) internal virtual {
_safeTransfer(from, to, id, "");
}
/// @dev Transfers token `id` from `from` to `to`.
///
/// Requirements:
///
/// - Token `id` must exist.
/// - `from` must be the owner of the token.
/// - `to` cannot be the zero address.
/// - The caller must be the owner of the token, or be approved to manage the token.
/// - If `to` refers to a smart contract, it must implement
/// {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
///
/// Emits a {Transfer} event.
function _safeTransfer(address from, address to, uint256 id, bytes memory data)
internal
virtual
{
_transfer(address(0), from, to, id);
if (_hasCode(to)) _checkOnERC721Received(from, to, id, data);
}
/// @dev Equivalent to `_safeTransfer(by, from, to, id, "")`.
function _safeTransfer(address by, address from, address to, uint256 id) internal virtual {
_safeTransfer(by, from, to, id, "");
}
/// @dev Transfers token `id` from `from` to `to`.
///
/// Requirements:
///
/// - Token `id` must exist.
/// - `from` must be the owner of the token.
/// - `to` cannot be the zero address.
/// - If `by` is not the zero address,
/// it must be the owner of the token, or be approved to manage the token.
/// - If `to` refers to a smart contract, it must implement
/// {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
///
/// Emits a {Transfer} event.
function _safeTransfer(address by, address from, address to, uint256 id, bytes memory data)
internal
virtual
{
_transfer(by, from, to, id);
if (_hasCode(to)) _checkOnERC721Received(from, to, id, data);
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* HOOKS FOR OVERRIDING */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Hook that is called before any token transfers, including minting and burning.
function _beforeTokenTransfer(address from, address to, uint256 id) internal virtual {}
/// @dev Hook that is called after any token transfers, including minting and burning.
function _afterTokenTransfer(address from, address to, uint256 id) internal virtual {}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* PRIVATE HELPERS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns if `a` has bytecode of non-zero length.
function _hasCode(address a) private view returns (bool result) {
/// @solidity memory-safe-assembly
assembly {
result := extcodesize(a) // Can handle dirty upper bits.
}
}
/// @dev Perform a call to invoke {IERC721Receiver-onERC721Received} on `to`.
/// Reverts if the target does not support the function correctly.
function _checkOnERC721Received(address from, address to, uint256 id, bytes memory data)
private
{
/// @solidity memory-safe-assembly
assembly {
// Prepare the calldata.
let m := mload(0x40)
let onERC721ReceivedSelector := 0x150b7a02
mstore(m, onERC721ReceivedSelector)
mstore(add(m, 0x20), caller()) // The `operator`, which is always `msg.sender`.
mstore(add(m, 0x40), shr(96, shl(96, from)))
mstore(add(m, 0x60), id)
mstore(add(m, 0x80), 0x80)
let n := mload(data)
mstore(add(m, 0xa0), n)
if n { pop(staticcall(gas(), 4, add(data, 0x20), n, add(m, 0xc0), n)) }
// Revert if the call reverts.
if iszero(call(gas(), to, 0, add(m, 0x1c), add(n, 0xa4), m, 0x20)) {
if returndatasize() {
// Bubble up the revert if the call reverts.
returndatacopy(m, 0x00, returndatasize())
revert(m, returndatasize())
}
}
// Load the returndata and compare it.
if iszero(eq(mload(m), shl(224, onERC721ReceivedSelector))) {
mstore(0x00, 0xd1a57ed6) // `TransferToNonERC721ReceiverImplementer()`.
revert(0x1c, 0x04)
}
}
}
}
CreatorTokenBase.sol 268 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
import "lib/openzeppelin-contracts/contracts/interfaces/IERC165.sol";
import "./OwnablePermissions.sol";
import "./ICreatorToken.sol";
import "./ICreatorTokenTransferValidator.sol";
import "./TransferValidation.sol";
/**
* @title CreatorTokenBase
* @author Limit Break, Inc.
* @notice CreatorTokenBase is an abstract contract that provides basic functionality for managing token
* transfer policies through an implementation of ICreatorTokenTransferValidator. This contract is intended to be used
* as a base for creator-specific token contracts, enabling customizable transfer restrictions and security policies.
*
* <h4>Features:</h4>
* <ul>Ownable: This contract can have an owner who can set and update the transfer validator.</ul>
* <ul>TransferValidation: Implements the basic token transfer validation interface.</ul>
* <ul>ICreatorToken: Implements the interface for creator tokens, providing view functions for token security policies.</ul>
*
* <h4>Benefits:</h4>
* <ul>Provides a flexible and modular way to implement custom token transfer restrictions and security policies.</ul>
* <ul>Allows creators to enforce policies such as whitelisted operators and permitted contract receivers.</ul>
* <ul>Can be easily integrated into other token contracts as a base contract.</ul>
*
* <h4>Intended Usage:</h4>
* <ul>Use as a base contract for creator token implementations that require advanced transfer restrictions and
* security policies.</ul>
* <ul>Set and update the ICreatorTokenTransferValidator implementation contract to enforce desired policies for the
* creator token.</ul>
*/
abstract contract CreatorTokenBase is OwnablePermissions, TransferValidation, ICreatorToken {
error CreatorTokenBase__InvalidTransferValidatorContract();
error CreatorTokenBase__SetTransferValidatorFirst();
address public constant DEFAULT_TRANSFER_VALIDATOR = address(0x0000721C310194CcfC01E523fc93C9cCcFa2A0Ac);
TransferSecurityLevels public constant DEFAULT_TRANSFER_SECURITY_LEVEL = TransferSecurityLevels.One;
uint120 public constant DEFAULT_OPERATOR_WHITELIST_ID = uint120(1);
ICreatorTokenTransferValidator private transferValidator;
/**
* @notice Allows the contract owner to set the transfer validator to the official validator contract
* and set the security policy to the recommended default settings.
* @dev May be overridden to change the default behavior of an individual collection.
*/
function setToDefaultSecurityPolicy() public virtual {
_requireCallerIsContractOwner();
setTransferValidator(DEFAULT_TRANSFER_VALIDATOR);
ICreatorTokenTransferValidator(DEFAULT_TRANSFER_VALIDATOR)
.setTransferSecurityLevelOfCollection(address(this), DEFAULT_TRANSFER_SECURITY_LEVEL);
ICreatorTokenTransferValidator(DEFAULT_TRANSFER_VALIDATOR)
.setOperatorWhitelistOfCollection(address(this), DEFAULT_OPERATOR_WHITELIST_ID);
}
/**
* @notice Allows the contract owner to set the transfer validator to a custom validator contract
* and set the security policy to their own custom settings.
*/
function setToCustomValidatorAndSecurityPolicy(
address validator,
TransferSecurityLevels level,
uint120 operatorWhitelistId,
uint120 permittedContractReceiversAllowlistId
) public virtual {
_requireCallerIsContractOwner();
setTransferValidator(validator);
ICreatorTokenTransferValidator(validator).setTransferSecurityLevelOfCollection(address(this), level);
ICreatorTokenTransferValidator(validator).setOperatorWhitelistOfCollection(address(this), operatorWhitelistId);
ICreatorTokenTransferValidator(validator)
.setPermittedContractReceiverAllowlistOfCollection(address(this), permittedContractReceiversAllowlistId);
}
/**
* @notice Allows the contract owner to set the security policy to their own custom settings.
* @dev Reverts if the transfer validator has not been set.
*/
function setToCustomSecurityPolicy(
TransferSecurityLevels level,
uint120 operatorWhitelistId,
uint120 permittedContractReceiversAllowlistId
) public virtual {
_requireCallerIsContractOwner();
ICreatorTokenTransferValidator validator = getTransferValidator();
if (address(validator) == address(0)) {
revert CreatorTokenBase__SetTransferValidatorFirst();
}
validator.setTransferSecurityLevelOfCollection(address(this), level);
validator.setOperatorWhitelistOfCollection(address(this), operatorWhitelistId);
validator.setPermittedContractReceiverAllowlistOfCollection(
address(this), permittedContractReceiversAllowlistId
);
}
/**
* @notice Sets the transfer validator for the token contract.
*
* @dev Throws when provided validator contract is not the zero address and doesn't support
* the ICreatorTokenTransferValidator interface.
* @dev Throws when the caller is not the contract owner.
*
* @dev <h4>Postconditions:</h4>
* 1. The transferValidator address is updated.
* 2. The `TransferValidatorUpdated` event is emitted.
*
* @param transferValidator_ The address of the transfer validator contract.
*/
function setTransferValidator(address transferValidator_) public virtual {
_requireCallerIsContractOwner();
bool isValidTransferValidator = false;
if (transferValidator_.code.length > 0) {
try IERC165(transferValidator_)
.supportsInterface(type(ICreatorTokenTransferValidator).interfaceId) returns (
bool supportsInterface
) {
isValidTransferValidator = supportsInterface;
} catch {}
}
if (transferValidator_ != address(0) && !isValidTransferValidator) {
revert CreatorTokenBase__InvalidTransferValidatorContract();
}
emit TransferValidatorUpdated(address(transferValidator), transferValidator_);
transferValidator = ICreatorTokenTransferValidator(transferValidator_);
}
/**
* @notice Returns the transfer validator contract address for this token contract.
*/
function getTransferValidator() public view override returns (ICreatorTokenTransferValidator) {
return transferValidator;
}
/**
* @notice Returns the security policy for this token contract, which includes:
* Transfer security level, operator whitelist id, permitted contract receiver allowlist id.
*/
function getSecurityPolicy() public view override returns (CollectionSecurityPolicy memory) {
if (address(transferValidator) != address(0)) {
return transferValidator.getCollectionSecurityPolicy(address(this));
}
return CollectionSecurityPolicy({
transferSecurityLevel: TransferSecurityLevels.Zero, operatorWhitelistId: 0, permittedContractReceiversId: 0
});
}
/**
* @notice Returns the list of all whitelisted operators for this token contract.
* @dev This can be an expensive call and should only be used in view-only functions.
*/
function getWhitelistedOperators() public view override returns (address[] memory) {
if (address(transferValidator) != address(0)) {
return transferValidator.getWhitelistedOperators(
transferValidator.getCollectionSecurityPolicy(address(this)).operatorWhitelistId
);
}
return new address[](0);
}
/**
* @notice Returns the list of permitted contract receivers for this token contract.
* @dev This can be an expensive call and should only be used in view-only functions.
*/
function getPermittedContractReceivers() public view override returns (address[] memory) {
if (address(transferValidator) != address(0)) {
return transferValidator.getPermittedContractReceivers(
transferValidator.getCollectionSecurityPolicy(address(this)).permittedContractReceiversId
);
}
return new address[](0);
}
/**
* @notice Checks if an operator is whitelisted for this token contract.
* @param operator The address of the operator to check.
*/
function isOperatorWhitelisted(address operator) public view override returns (bool) {
if (address(transferValidator) != address(0)) {
return transferValidator.isOperatorWhitelisted(
transferValidator.getCollectionSecurityPolicy(address(this)).operatorWhitelistId, operator
);
}
return false;
}
/**
* @notice Checks if a contract receiver is permitted for this token contract.
* @param receiver The address of the receiver to check.
*/
function isContractReceiverPermitted(address receiver) public view override returns (bool) {
if (address(transferValidator) != address(0)) {
return transferValidator.isContractReceiverPermitted(
transferValidator.getCollectionSecurityPolicy(address(this)).permittedContractReceiversId, receiver
);
}
return false;
}
/**
* @notice Determines if a transfer is allowed based on the token contract's security policy. Use this function
* to simulate whether or not a transfer made by the specified `caller` from the `from` address to the `to`
* address would be allowed by this token's security policy.
*
* @notice This function only checks the security policy restrictions and does not check whether token ownership
* or approvals are in place.
*
* @param caller The address of the simulated caller.
* @param from The address of the sender.
* @param to The address of the receiver.
* @return True if the transfer is allowed, false otherwise.
*/
function isTransferAllowed(address caller, address from, address to) public view override returns (bool) {
if (address(transferValidator) != address(0)) {
try transferValidator.applyCollectionTransferPolicy(caller, from, to) {
return true;
} catch {
return false;
}
}
return true;
}
/**
* @dev Pre-validates a token transfer, reverting if the transfer is not allowed by this token's security policy.
* Inheriting contracts are responsible for overriding the _beforeTokenTransfer function, or its equivalent
* and calling _validateBeforeTransfer so that checks can be properly applied during token transfers.
*
* @dev Throws when the transfer doesn't comply with the collection's transfer policy, if the transferValidator is
* set to a non-zero address.
*
* @param caller The address of the caller.
* @param from The address of the sender.
* @param to The address of the receiver.
*/
function _preValidateTransfer(
address caller,
address from,
address to,
uint256,
/*tokenId*/
uint256 /*value*/
)
internal
virtual
override
{
if (address(transferValidator) != address(0)) {
transferValidator.applyCollectionTransferPolicy(caller, from, to);
}
}
}
ERC2981.sol 107 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/common/ERC2981.sol)
pragma solidity ^0.8.0;
import "../../interfaces/IERC2981.sol";
import "../../utils/introspection/ERC165.sol";
/**
* @dev Implementation of the NFT Royalty Standard, a standardized way to retrieve royalty payment information.
*
* Royalty information can be specified globally for all token ids via {_setDefaultRoyalty}, and/or individually for
* specific token ids via {_setTokenRoyalty}. The latter takes precedence over the first.
*
* Royalty is specified as a fraction of sale price. {_feeDenominator} is overridable but defaults to 10000, meaning the
* fee is specified in basis points by default.
*
* IMPORTANT: ERC-2981 only specifies a way to signal royalty information and does not enforce its payment. See
* https://eips.ethereum.org/EIPS/eip-2981#optional-royalty-payments[Rationale] in the EIP. Marketplaces are expected to
* voluntarily pay royalties together with sales, but note that this standard is not yet widely supported.
*
* _Available since v4.5._
*/
abstract contract ERC2981 is IERC2981, ERC165 {
struct RoyaltyInfo {
address receiver;
uint96 royaltyFraction;
}
RoyaltyInfo private _defaultRoyaltyInfo;
mapping(uint256 => RoyaltyInfo) private _tokenRoyaltyInfo;
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, ERC165) returns (bool) {
return interfaceId == type(IERC2981).interfaceId || super.supportsInterface(interfaceId);
}
/**
* @inheritdoc IERC2981
*/
function royaltyInfo(uint256 tokenId, uint256 salePrice) public view virtual override returns (address, uint256) {
RoyaltyInfo memory royalty = _tokenRoyaltyInfo[tokenId];
if (royalty.receiver == address(0)) {
royalty = _defaultRoyaltyInfo;
}
uint256 royaltyAmount = (salePrice * royalty.royaltyFraction) / _feeDenominator();
return (royalty.receiver, royaltyAmount);
}
/**
* @dev The denominator with which to interpret the fee set in {_setTokenRoyalty} and {_setDefaultRoyalty} as a
* fraction of the sale price. Defaults to 10000 so fees are expressed in basis points, but may be customized by an
* override.
*/
function _feeDenominator() internal pure virtual returns (uint96) {
return 10000;
}
/**
* @dev Sets the royalty information that all ids in this contract will default to.
*
* Requirements:
*
* - `receiver` cannot be the zero address.
* - `feeNumerator` cannot be greater than the fee denominator.
*/
function _setDefaultRoyalty(address receiver, uint96 feeNumerator) internal virtual {
require(feeNumerator <= _feeDenominator(), "ERC2981: royalty fee will exceed salePrice");
require(receiver != address(0), "ERC2981: invalid receiver");
_defaultRoyaltyInfo = RoyaltyInfo(receiver, feeNumerator);
}
/**
* @dev Removes default royalty information.
*/
function _deleteDefaultRoyalty() internal virtual {
delete _defaultRoyaltyInfo;
}
/**
* @dev Sets the royalty information for a specific token id, overriding the global default.
*
* Requirements:
*
* - `receiver` cannot be the zero address.
* - `feeNumerator` cannot be greater than the fee denominator.
*/
function _setTokenRoyalty(uint256 tokenId, address receiver, uint96 feeNumerator) internal virtual {
require(feeNumerator <= _feeDenominator(), "ERC2981: royalty fee will exceed salePrice");
require(receiver != address(0), "ERC2981: Invalid parameters");
_tokenRoyaltyInfo[tokenId] = RoyaltyInfo(receiver, feeNumerator);
}
/**
* @dev Resets royalty information for the token id back to the global default.
*/
function _resetTokenRoyalty(uint256 tokenId) internal virtual {
delete _tokenRoyaltyInfo[tokenId];
}
}
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);
}
TransferPolicy.sol 46 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
enum AllowlistTypes {
Operators,
PermittedContractReceivers
}
enum ReceiverConstraints {
None,
NoCode,
EOA
}
enum CallerConstraints {
None,
OperatorWhitelistEnableOTC,
OperatorWhitelistDisableOTC
}
enum StakerConstraints {
None,
CallerIsTxOrigin,
EOA
}
enum TransferSecurityLevels {
Zero,
One,
Two,
Three,
Four,
Five,
Six
}
struct TransferSecurityPolicy {
CallerConstraints callerConstraints;
ReceiverConstraints receiverConstraints;
}
struct CollectionSecurityPolicy {
TransferSecurityLevels transferSecurityLevel;
uint120 operatorWhitelistId;
uint120 permittedContractReceiversId;
}
FixedPoint.sol 106 lines
// SPDX-License-Identifier: HVPL - HEXED VIRAL PUBLIC LICENSE
pragma solidity ^0.8.25;
import "lib/solady/src/utils/FixedPointMathLib.sol";
import "lib/solady/src/utils/LibString.sol";
import {LibDynamicBuffer} from "./LibDynamicThing.sol";
import "./Common.sol";
import "./Append.sol";
import "./LICENSE.sol";
library FixedPoint {
struct FP {
uint256 value;
}
struct iFP {
int256 value;
}
function toFixedPointNumber(uint256 x) internal pure returns (FP memory fp) {
x *= 1 ether;
fp.value = x;
}
function toFixedPointNumberRaw(uint256 x) internal pure returns (FP memory fp) {
fp.value = x;
}
function toFixedPointNumberRaw(int256 x) internal pure returns (iFP memory fp) {
fp.value = x;
}
function toNaturalNumber(FixedPoint.FP memory fp) internal pure returns (uint256) {
unchecked {
return fp.value / 1 ether;
} // uc
}
function add(FP memory a, FP memory b) internal pure returns (FP memory res) {
res.value = a.value + b.value;
}
function sub(FP memory a, FP memory b) internal pure returns (FP memory res) {
res.value = a.value - b.value;
}
function mul(FP memory a, FP memory b) internal pure returns (FP memory res) {
res.value = a.value * b.value / 1 ether;
}
function div(FP memory a, FP memory b) internal pure returns (FP memory res) {
res.value = a.value * 1 ether / b.value;
}
function mulDiv(FP memory a, FP memory b, FP memory d) internal pure returns (FP memory res) {
return div(mul(a, b), d);
}
function eq(FP memory a, FP memory b) internal pure returns (bool) {
return a.value == b.value;
}
function toString(uint256 x) internal pure returns (string memory ret) {
return toString(toFixedPointNumberRaw(x));
}
function toString(FP memory x) internal pure returns (string memory ret) {
unchecked {
uint256 value = x.value;
LibDynamicBuffer.DynamicBuffer memory db = LibDynamicBuffer.newDynamicBuffer();
LibDynamicBuffer.p(db, bytes(LibString.toString(value / 1 ether)));
uint256 decimals = value % 1 ether;
if (decimals > 0) {
LibDynamicBuffer.p(db, bytes("."));
uint256 numZeros = 17 - FixedPointMathLib.log10(decimals);
LibDynamicBuffer.p(db, bytes(sZeros(numZeros)));
while (decimals > 1 && decimals % 10 == 0) {
decimals /= 10;
}
LibDynamicBuffer.p(db, bytes(LibString.toString(decimals)));
}
ret = string(LibDynamicBuffer.getBuffer(db));
} // uc
}
function toString(iFP memory x) internal pure returns (string memory ret) {
ret = toString(toFixedPointNumberRaw(FixedPointMathLib.abs(x.value)));
if (x.value < 0) ret = string(_concat("-", bytes(ret)));
}
function sZeros(uint256 numZeros) internal pure returns (string memory ret) {
ret = _allocateString(numZeros);
assembly {
for { let i := 0 } 1 {} {
mstore8(add(ret, add(0x20, i)), 48)
i := add(i, 1)
if iszero(lt(i, numZeros)) { break }
}
// 48 is utf8 for "0"
}
}
}
IBlockHashBeacon.sol 17 lines
// SPDX-License-Identifier: HVPL - HEXED VIRAL PUBLIC LICENSE
pragma solidity ^0.8.25;
import "./LICENSE.sol";
interface IBlockHashBeacon {
function blockHashAt(uint256 blockNumber) external view returns (bytes32);
function rewards(uint256 blockNumber) external view returns (uint256);
function rewardsInRange(uint256 from, uint256 to)
external
view
returns (bytes32[] memory blockHashes, uint256[] memory rewardsPerBlock);
function fundReward(uint256 blockNumber) external payable;
function etchBlockHash(uint256 blockNumber, address payable rewardTo) external;
function etchBlockHashBulk(uint256[] calldata blockNumbers, address payable rewardTo) external;
function etchBlockHashBulkRange(uint256 from, uint256 to, address payable rewardTo) external;
}
IDiscountDemon.sol 26 lines
// SPDX-License-Identifier: HVPL - HEXED VIRAL PUBLIC LICENSE
pragma solidity ^0.8.25;
import "lib/openzeppelin-contracts/contracts/token/ERC721/IERC721.sol";
import "./IERC173.sol";
import "./LICENSE.sol";
interface IDiscountDemon is IERC173, IERC165 {
function getDiscountData(address thing)
external
view
returns (IERC721[] memory collections, uint256[] memory factors);
function discountedCollections(address thing) external view returns (IERC721[] memory);
function collectionIsDiscounted(address thing, IERC721 collection) external view returns (bool);
function discountedCollectionFactors(address thing, IERC721 collection) external view returns (uint256);
function initializeDiscounts(IERC721[] calldata collections, uint256[] calldata discountFactors) external;
function discountClaimed(address thing, IERC721 collection, uint256 tokenId) external view returns (bool);
function processDiscount(
IERC721 collection,
address claimer,
uint256 claimingTokenId,
uint256 mintPrice,
uint256 divisor
) external returns (uint256 updatedMintPrice);
function claimDiscount(IERC721 collection, uint256 tokenId) external;
}
IERC721.sol 132 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC721/IERC721.sol)
pragma solidity ^0.8.0;
import "../../utils/introspection/IERC165.sol";
/**
* @dev Required interface of an ERC721 compliant contract.
*/
interface IERC721 is IERC165 {
/**
* @dev Emitted when `tokenId` token is transferred from `from` to `to`.
*/
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
/**
* @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
*/
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
/**
* @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
*/
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
/**
* @dev Returns the number of tokens in ``owner``'s account.
*/
function balanceOf(address owner) external view returns (uint256 balance);
/**
* @dev Returns the owner of the `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function ownerOf(uint256 tokenId) external view returns (address owner);
/**
* @dev Safely transfers `tokenId` token from `from` to `to`.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
*
* Emits a {Transfer} event.
*/
function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;
/**
* @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
* are aware of the ERC721 protocol to prevent tokens from being forever locked.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must have been allowed to move this token by either {approve} or {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
*
* Emits a {Transfer} event.
*/
function safeTransferFrom(address from, address to, uint256 tokenId) external;
/**
* @dev Transfers `tokenId` token from `from` to `to`.
*
* WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721
* or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must
* understand this adds an external call which potentially creates a reentrancy vulnerability.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 tokenId) external;
/**
* @dev Gives permission to `to` to transfer `tokenId` token to another account.
* The approval is cleared when the token is transferred.
*
* Only a single account can be approved at a time, so approving the zero address clears previous approvals.
*
* Requirements:
*
* - The caller must own the token or be an approved operator.
* - `tokenId` must exist.
*
* Emits an {Approval} event.
*/
function approve(address to, uint256 tokenId) external;
/**
* @dev Approve or remove `operator` as an operator for the caller.
* Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
*
* Requirements:
*
* - The `operator` cannot be the caller.
*
* Emits an {ApprovalForAll} event.
*/
function setApprovalForAll(address operator, bool approved) external;
/**
* @dev Returns the account approved for `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function getApproved(uint256 tokenId) external view returns (address operator);
/**
* @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
*
* See {setApprovalForAll}
*/
function isApprovedForAll(address owner, address operator) external view returns (bool);
}
LibZip.sol 275 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Library for compressing and decompressing bytes.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibZip.sol)
/// @author Calldata compression by clabby (https://github.com/clabby/op-kompressor)
/// @author FastLZ by ariya (https://github.com/ariya/FastLZ)
///
/// @dev Note:
/// The accompanying solady.js library includes implementations of
/// FastLZ and calldata operations for convenience.
library LibZip {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* FAST LZ OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
// LZ77 implementation based on FastLZ.
// Equivalent to level 1 compression and decompression at the following commit:
// https://github.com/ariya/FastLZ/commit/344eb4025f9ae866ebf7a2ec48850f7113a97a42
// Decompression is backwards compatible.
/// @dev Returns the compressed `data`.
function flzCompress(bytes memory data) internal pure returns (bytes memory result) {
/// @solidity memory-safe-assembly
assembly {
function ms8(d_, v_) -> _d {
mstore8(d_, v_)
_d := add(d_, 1)
}
function u24(p_) -> _u {
_u := mload(p_)
_u := or(shl(16, byte(2, _u)), or(shl(8, byte(1, _u)), byte(0, _u)))
}
function cmp(p_, q_, e_) -> _l {
for { e_ := sub(e_, q_) } lt(_l, e_) { _l := add(_l, 1) } {
e_ := mul(iszero(byte(0, xor(mload(add(p_, _l)), mload(add(q_, _l))))), e_)
}
}
function literals(runs_, src_, dest_) -> _o {
for { _o := dest_ } iszero(lt(runs_, 0x20)) { runs_ := sub(runs_, 0x20) } {
mstore(ms8(_o, 31), mload(src_))
_o := add(_o, 0x21)
src_ := add(src_, 0x20)
}
if iszero(runs_) { leave }
mstore(ms8(_o, sub(runs_, 1)), mload(src_))
_o := add(1, add(_o, runs_))
}
function mt(l_, d_, o_) -> _o {
for { d_ := sub(d_, 1) } iszero(lt(l_, 263)) { l_ := sub(l_, 262) } {
o_ := ms8(ms8(ms8(o_, add(224, shr(8, d_))), 253), and(0xff, d_))
}
if iszero(lt(l_, 7)) {
_o := ms8(ms8(ms8(o_, add(224, shr(8, d_))), sub(l_, 7)), and(0xff, d_))
leave
}
_o := ms8(ms8(o_, add(shl(5, l_), shr(8, d_))), and(0xff, d_))
}
function setHash(i_, v_) {
let p_ := add(mload(0x40), shl(2, i_))
mstore(p_, xor(mload(p_), shl(224, xor(shr(224, mload(p_)), v_))))
}
function getHash(i_) -> _h {
_h := shr(224, mload(add(mload(0x40), shl(2, i_))))
}
function hash(v_) -> _r {
_r := and(shr(19, mul(2654435769, v_)), 0x1fff)
}
function setNextHash(ip_, ipStart_) -> _ip {
setHash(hash(u24(ip_)), sub(ip_, ipStart_))
_ip := add(ip_, 1)
}
result := mload(0x40)
codecopy(result, codesize(), 0x8000) // Zeroize the hashmap.
let op := add(result, 0x8000)
let a := add(data, 0x20)
let ipStart := a
let ipLimit := sub(add(ipStart, mload(data)), 13)
for { let ip := add(2, a) } lt(ip, ipLimit) {} {
let r := 0
let d := 0
for {} 1 {} {
let s := u24(ip)
let h := hash(s)
r := add(ipStart, getHash(h))
setHash(h, sub(ip, ipStart))
d := sub(ip, r)
if iszero(lt(ip, ipLimit)) { break }
ip := add(ip, 1)
if iszero(gt(d, 0x1fff)) { if eq(s, u24(r)) { break } }
}
if iszero(lt(ip, ipLimit)) { break }
ip := sub(ip, 1)
if gt(ip, a) { op := literals(sub(ip, a), a, op) }
let l := cmp(add(r, 3), add(ip, 3), add(ipLimit, 9))
op := mt(l, d, op)
ip := setNextHash(setNextHash(add(ip, l), ipStart), ipStart)
a := ip
}
// Copy the result to compact the memory, overwriting the hashmap.
let end := sub(literals(sub(add(ipStart, mload(data)), a), a, op), 0x7fe0)
let o := add(result, 0x20)
mstore(result, sub(end, o)) // Store the length.
for {} iszero(gt(o, end)) { o := add(o, 0x20) } { mstore(o, mload(add(o, 0x7fe0))) }
mstore(end, 0) // Zeroize the slot after the string.
mstore(0x40, add(end, 0x20)) // Allocate the memory.
}
}
/// @dev Returns the decompressed `data`.
function flzDecompress(bytes memory data) internal pure returns (bytes memory result) {
/// @solidity memory-safe-assembly
assembly {
result := mload(0x40)
let op := add(result, 0x20)
let end := add(add(data, 0x20), mload(data))
for { data := add(data, 0x20) } lt(data, end) {} {
let w := mload(data)
let c := byte(0, w)
let t := shr(5, c)
if iszero(t) {
mstore(op, mload(add(data, 1)))
data := add(data, add(2, c))
op := add(op, add(1, c))
continue
}
for {
let g := eq(t, 7)
let l := add(2, xor(t, mul(g, xor(t, add(7, byte(1, w)))))) // M
let s := add(add(shl(8, and(0x1f, c)), byte(add(1, g), w)), 1) // R
let r := sub(op, s)
let f := xor(s, mul(gt(s, 0x20), xor(s, 0x20)))
let j := 0
} 1 {} {
mstore(add(op, j), mload(add(r, j)))
j := add(j, f)
if lt(j, l) { continue }
data := add(data, add(2, g))
op := add(op, l)
break
}
}
mstore(result, sub(op, add(result, 0x20))) // Store the length.
mstore(op, 0) // Zeroize the slot after the string.
mstore(0x40, add(op, 0x20)) // Allocate the memory.
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CALLDATA OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
// Calldata compression and decompression using selective run length encoding:
// - Sequences of 0x00 (up to 128 consecutive).
// - Sequences of 0xff (up to 32 consecutive).
//
// A run length encoded block consists of two bytes:
// (0) 0x00
// (1) A control byte with the following bit layout:
// - [7] `0: 0x00, 1: 0xff`.
// - [0..6] `runLength - 1`.
//
// The first 4 bytes are bitwise negated so that the compressed calldata
// can be dispatched into the `fallback` and `receive` functions.
/// @dev Returns the compressed `data`.
function cdCompress(bytes memory data) internal pure returns (bytes memory result) {
/// @solidity memory-safe-assembly
assembly {
function rle(v_, o_, d_) -> _o, _d {
mstore(o_, shl(240, or(and(0xff, add(d_, 0xff)), and(0x80, v_))))
_o := add(o_, 2)
}
result := mload(0x40)
let o := add(result, 0x20)
let z := 0 // Number of consecutive 0x00.
let y := 0 // Number of consecutive 0xff.
for { let end := add(data, mload(data)) } iszero(eq(data, end)) {} {
data := add(data, 1)
let c := byte(31, mload(data))
if iszero(c) {
if y { o, y := rle(0xff, o, y) }
z := add(z, 1)
if eq(z, 0x80) { o, z := rle(0x00, o, 0x80) }
continue
}
if eq(c, 0xff) {
if z { o, z := rle(0x00, o, z) }
y := add(y, 1)
if eq(y, 0x20) { o, y := rle(0xff, o, 0x20) }
continue
}
if y { o, y := rle(0xff, o, y) }
if z { o, z := rle(0x00, o, z) }
mstore8(o, c)
o := add(o, 1)
}
if y { o, y := rle(0xff, o, y) }
if z { o, z := rle(0x00, o, z) }
// Bitwise negate the first 4 bytes.
mstore(add(result, 4), not(mload(add(result, 4))))
mstore(result, sub(o, add(result, 0x20))) // Store the length.
mstore(o, 0) // Zeroize the slot after the string.
mstore(0x40, add(o, 0x20)) // Allocate the memory.
}
}
/// @dev Returns the decompressed `data`.
function cdDecompress(bytes memory data) internal pure returns (bytes memory result) {
/// @solidity memory-safe-assembly
assembly {
if mload(data) {
result := mload(0x40)
let o := add(result, 0x20)
let s := add(data, 4)
let v := mload(s)
let end := add(data, mload(data))
mstore(s, not(v)) // Bitwise negate the first 4 bytes.
for {} lt(data, end) {} {
data := add(data, 1)
let c := byte(31, mload(data))
if iszero(c) {
data := add(data, 1)
let d := byte(31, mload(data))
// Fill with either 0xff or 0x00.
mstore(o, not(0))
if iszero(gt(d, 0x7f)) { codecopy(o, codesize(), add(d, 1)) }
o := add(o, add(and(d, 0x7f), 1))
continue
}
mstore8(o, c)
o := add(o, 1)
}
mstore(s, v) // Restore the first 4 bytes.
mstore(result, sub(o, add(result, 0x20))) // Store the length.
mstore(o, 0) // Zeroize the slot after the string.
mstore(0x40, add(o, 0x20)) // Allocate the memory.
}
}
}
/// @dev To be called in the `fallback` function.
/// ```
/// fallback() external payable { LibZip.cdFallback(); }
/// receive() external payable {} // Silence compiler warning to add a `receive` function.
/// ```
/// For efficiency, this function will directly return the results, terminating the context.
/// If called internally, it must be called at the end of the function.
function cdFallback() internal {
assembly {
if iszero(calldatasize()) { return(calldatasize(), calldatasize()) }
let o := 0
let f := not(3) // For negating the first 4 bytes.
for { let i := 0 } lt(i, calldatasize()) {} {
let c := byte(0, xor(add(i, f), calldataload(i)))
i := add(i, 1)
if iszero(c) {
let d := byte(0, xor(add(i, f), calldataload(i)))
i := add(i, 1)
// Fill with either 0xff or 0x00.
mstore(o, not(0))
if iszero(gt(d, 0x7f)) { codecopy(o, codesize(), add(d, 1)) }
o := add(o, add(and(d, 0x7f), 1))
continue
}
mstore8(o, c)
o := add(o, 1)
}
let success := delegatecall(gas(), address(), 0x00, o, codesize(), 0x00)
returndatacopy(0x00, 0x00, returndatasize())
if iszero(success) { revert(0x00, returndatasize()) }
return(0x00, returndatasize())
}
}
}
LibDynamicThing.sol 399 lines
// SPDX-License-Identifier: HVPL - HEXED VIRAL PUBLIC LICENSE
pragma solidity ^0.8.25;
import "./Append.sol";
import "./Structs.sol";
import "./LICENSE.sol";
// by no_side666
library LibDynamicJSONKVArr {
struct LinkedKVs {
LibDynamicThing.LinkedThings lks;
LibDynamicThing.LinkedThings lvs;
}
function newDynamicKVArr() internal pure returns (LinkedKVs memory ret) {
ret = LinkedKVs(LibDynamicThing.newLinkedThings(), LibDynamicThing.newLinkedThings());
}
function p(LinkedKVs memory lkvs, bytes memory kData, bytes memory vData) internal pure {
p(lkvs, kData, vData, JSONType.STRING);
}
function p(LinkedKVs memory lkvs, bytes memory kData, bytes memory vData, JSONType valueType) internal pure {
if (vData.length < 1) return; // note: don't forget it ignores kvs with trivial value!!
JSON memory k = JSON(JSONType.STRING, kData);
JSON memory v = JSON(valueType, vData);
uint256 kPtr;
uint256 vPtr;
assembly {
kPtr := k
vPtr := v
}
LibDynamicThing.p(lkvs.lks, kPtr);
LibDynamicThing.p(lkvs.lvs, vPtr);
}
function dump(LinkedKVs memory lkvs) internal pure returns (JSON[][2] memory aakvs) {
uint256[] memory kPtrs = LibDynamicThing.dump(lkvs.lks);
uint256[] memory vPtrs = LibDynamicThing.dump(lkvs.lvs);
JSON[] memory ks;
JSON[] memory vs;
assembly {
ks := kPtrs
vs := vPtrs
}
aakvs[0] = ks;
aakvs[1] = vs;
}
}
library LibDynamicKVArr {
struct LinkedKVs {
LibDynamicThing.LinkedThings lks;
LibDynamicThing.LinkedThings lvs;
}
function newDynamicKVArr() internal pure returns (LinkedKVs memory ret) {
ret = LinkedKVs(LibDynamicThing.newLinkedThings(), LibDynamicThing.newLinkedThings());
}
function p(LinkedKVs memory lkvs, bytes memory kData, bytes memory vData) internal pure {
if (vData.length < 1) return; // note: don't forget it ignores kvs with trivial value!!
uint256 kPtr;
uint256 vPtr;
assembly {
kPtr := kData
vPtr := vData
}
LibDynamicThing.p(lkvs.lks, kPtr);
LibDynamicThing.p(lkvs.lvs, vPtr);
}
function dump(LinkedKVs memory lkvs) internal pure returns (bytes[][2] memory aakvs) {
uint256[] memory kPtrs = LibDynamicThing.dump(lkvs.lks);
uint256[] memory vPtrs = LibDynamicThing.dump(lkvs.lvs);
bytes[] memory ks;
bytes[] memory vs;
assembly {
ks := kPtrs
vs := vPtrs
}
aakvs[0] = ks;
aakvs[1] = vs;
}
}
library LibDynamicBytesArr {
struct LinkedBytes {
LibDynamicThing.LinkedThings ldt;
}
function newDynamicBytesArr() internal pure returns (LinkedBytes memory ret) {
return LinkedBytes(LibDynamicThing.newLinkedThings());
}
function p(LinkedBytes memory ls, bytes memory b) internal pure {
uint256 ptr;
assembly {
ptr := b
}
LibDynamicThing.p(ls.ldt, ptr);
}
function dump(LinkedBytes memory lb) internal pure returns (bytes[] memory ret) {
uint256[] memory ptrs = LibDynamicThing.dump(lb.ldt);
uint256 length = ptrs.length;
ret = _allocateArr(length);
assembly {
for { let i := 0 } 1 {} {
// hail solady
let ptr := mload(add(ptrs, add(0x20, mul(i, 0x20))))
mstore(add(ret, add(0x20, mul(i, 0x20))), ptr)
i := add(i, 1)
if iszero(lt(i, length)) { break }
}
}
/* // does this
uint256 length = ptrs.length;
ret = new string[](length);
uint256 ptr;
string memory str;
for (uint256 i; i < length; ++i) {
ptr = ptrs[i];
assembly {
str := ptr
}
ret[i] = str;
}
*/
}
function shift(LinkedBytes memory lb) internal pure returns (bytes memory ret) {
uint256 ptr = LibDynamicThing.shift(lb.ldt);
if (ptr == 0) return ret;
assembly {
ret := ptr
}
}
}
library LibDynamicStringArr {
struct LinkedStrings {
LibDynamicThing.LinkedThings ldt;
}
function newDynamicStringArr() internal pure returns (LinkedStrings memory ret) {
return LinkedStrings(LibDynamicThing.newLinkedThings());
}
function p(LinkedStrings memory ls, string memory str) internal pure {
uint256 ptr;
assembly {
ptr := str
}
LibDynamicThing.p(ls.ldt, ptr);
}
function dump(LinkedStrings memory ls) internal pure returns (string[] memory ret) {
uint256[] memory ptrs = LibDynamicThing.dump(ls.ldt);
uint256 length = ptrs.length;
ret = _allocateStringArr(length);
assembly {
for { let i := 0 } 1 {} {
// hail solady
let ptr := mload(add(ptrs, add(0x20, mul(i, 0x20))))
mstore(add(ret, add(0x20, mul(i, 0x20))), ptr)
i := add(i, 1)
if iszero(lt(i, length)) { break }
}
}
/* // does this
uint256 length = ptrs.length;
ret = new string[](length);
uint256 ptr;
string memory str;
for (uint256 i; i < length; ++i) {
ptr = ptrs[i];
assembly {
str := ptr
}
ret[i] = str;
}
*/
}
}
library LibDynamicUint256Arr {
struct LinkedUint256s {
LibDynamicThing.LinkedThings ldt;
}
function newDynamicUint256Arr() internal pure returns (LinkedUint256s memory ret) {
return LinkedUint256s(LibDynamicThing.newLinkedThings());
}
function p(LinkedUint256s memory ls, uint256 n) internal pure {
LibDynamicThing.p(ls.ldt, n);
}
function dump(LinkedUint256s memory ls) internal pure returns (uint256[] memory ret) {
ret = LibDynamicThing.dump(ls.ldt);
}
}
library LibDynamicInt256Arr {
struct LinkedInt256s {
LibDynamicThing.LinkedThings ldt;
}
function newDynamicInt256Arr() internal pure returns (LinkedInt256s memory ret) {
return LinkedInt256s(LibDynamicThing.newLinkedThings());
}
function p(LinkedInt256s memory ls, int256 n) internal pure {
// forge-lint: disable-next-line(unsafe-typecast)
LibDynamicThing.p(ls.ldt, uint256(n));
}
function dump(LinkedInt256s memory ls) internal pure returns (int256[] memory ret) {
uint256[] memory preRet = LibDynamicThing.dump(ls.ldt);
assembly {
ret := preRet
}
}
}
library LibDynamicBuffer {
struct DynamicBuffer {
uint256 numThings; // not used. but for some reason having it as buffer reduces gas!?
uint256 first;
uint256 last;
}
struct Thing {
uint256 ptr;
uint256 next;
}
function newDynamicBuffer() internal pure returns (DynamicBuffer memory ret) {
Thing memory first;
assembly {
mstore(add(ret, 0x20), first)
mstore(add(ret, 0x40), first)
}
}
function p(DynamicBuffer memory ls, bytes memory data) internal pure {
LibDynamicThing.Thing memory t;
assembly {
mstore(t, data)
let newPtr := t
let lastPtr := mload(add(ls, 0x40))
mstore(add(lastPtr, 0x20), newPtr)
mstore(add(ls, 0x40), newPtr)
}
}
// will need concat but had issues w it so deprecated it
function getBuffer(DynamicBuffer memory lts) internal pure returns (bytes memory ret) {
assembly {
ret := mload(0x40)
let len := 0x20 // offset
let nextPtr := mload(add(lts, 0x20)) // lt.first
for {} 1 {} {
nextPtr := mload(add(nextPtr, 0x20)) // ptr to next LinkedThing
if iszero(nextPtr) { break }
let ptr := mload(nextPtr) // ptr to actual thing
mcopy(add(ret, len), add(ptr, 0x20), mload(ptr))
len := add(len, mload(ptr))
}
mstore(0x40, add(ret, and(add(len, 0x1f), not(0x1f))))
mstore(ret, sub(len, 0x20))
}
}
}
library LibDynamicThing {
struct LinkedThings {
uint256 numThings;
uint256 first;
uint256 last;
}
struct Thing {
uint256 ptr;
uint256 next;
}
// will need concat but had issues w it so deprecated it
function newLinkedThings() internal pure returns (LinkedThings memory ret) {
Thing memory first;
assembly {
mstore(add(ret, 0x20), first)
mstore(add(ret, 0x40), first)
}
/* // it does this ...
Thing memory first;
uint256 ptr;
assembly {
ptr := first
}
ret.first = ptr;
ret.last = ptr;
*/
}
function p(LinkedThings memory lts, uint256 ptr) internal pure {
//Thing memory t;
assembly {
let t := mload(0x40)
mstore(0x40, add(t, and(add(0x40, 0x1f), not(0x1f))))
mstore(t, ptr)
let newPtr := t
let lastPtr := mload(add(lts, 0x40))
mstore(add(lastPtr, 0x20), newPtr)
mstore(add(lts, 0x40), newPtr)
//mstore(lts, add(mload(lts), 1)) // TODO deprecate numThings
}
/*// it does this ...
Thing memory t;
t.ptr = ptr;
uint256 newPtr;
assembly {
newPtr := t
}
uint256 lastPtr = lt.last;
Thing memory lastThing;
assembly {
lastThing := lastPtr
}
lastThing.next = newPtr;
lt.last = newPtr;
++lt.numThings;
*/
}
function dump(LinkedThings memory lts) internal pure returns (uint256[] memory ret) {
assembly {
ret := mload(0x40)
let len
let nextPtr := mload(add(lts, 0x20)) // lt.first
for {} 1 {} {
nextPtr := mload(add(nextPtr, 0x20))
if iszero(nextPtr) { break }
mstore(add(ret, add(0x20, mul(len, 0x20))), mload(nextPtr))
len := add(len, 1)
}
mstore(0x40, add(ret, and(add(add(mul(len, 0x20), 0x20), 0x1f), not(0x1f))))
mstore(ret, len)
}
/* // it does this ..
uint256 length = lts.numThings;
ret = new uint256[](length);
uint256 length = lt.numThings;
ret = new uint256[](length);
Thing memory t;
uint256 nextPtr = lt.first;
assembly {
t := nextPtr
}
for (uint256 i; i < length; ++i) {
nextPtr = t.next;
assembly {
t := nextPtr
}
ret[i] = t.ptr;
}
*/
}
function shift(LinkedThings memory lts) internal pure returns (uint256 ret) {
Thing memory t;
assembly {
t := mload(add(lts, 0x20)) // lts.first
}
ret = t.ptr;
if (ret == 0) return ret;
lts.first = t.next;
}
}
Append.sol 76 lines
// SPDX-License-Identifier: HVPL - HEXED VIRAL PUBLIC LICENSE
pragma solidity ^0.8.25;
import "./Allocate.sol";
import "./LICENSE.sol";
// cheaper than bytes concat :)
function _append(bytes memory dst, bytes memory src) pure {
assembly {
// resize
let priorLength := mload(dst)
mstore(dst, add(priorLength, mload(src)))
// copy
mcopy(add(dst, add(0x20, priorLength)), add(src, 0x20), mload(src))
}
}
error InvalidIndices_error();
// assumes dev is not stupid and startIdx < endIdx
function _appendSubstring(bytes memory dst, bytes memory src, uint256 startIdx, uint256 endIdx) pure {
if (endIdx < startIdx) revert InvalidIndices_error();
assembly {
// resize
let priorLength := mload(dst)
let substringLength := sub(endIdx, startIdx)
mstore(dst, add(priorLength, substringLength))
// copy
mcopy(add(dst, add(0x20, priorLength)), add(src, add(0x20, startIdx)), substringLength)
}
}
function _tail(bytes memory subject) pure returns (bytes memory ret) {
return __tail(subject, 1);
}
// we don't overload _tail since it is used as a fn ptr and confuses the poor compiler
function __tail(bytes memory subject, uint256 from) pure returns (bytes memory ret) {
uint256 length = subject.length;
if (!(from < length)) return ret;
unchecked {
ret = _allocateBytes(length - from);
} // uc
assembly {
mstore(ret, 0)
}
_appendSubstring(ret, subject, from, length);
}
function __head(bytes memory subject, uint256 to) pure returns (bytes memory ret) {
uint256 length = subject.length;
if (!(to < length)) return ret;
ret = _allocateBytes(to);
assembly {
mstore(ret, 0)
}
_appendSubstring(ret, subject, 0, to);
}
function _substring(bytes memory subject, uint256 from, uint256 to) pure returns (bytes memory ret) {
uint256 length = subject.length;
if (!(to < length)) return ret;
if (!(from < to)) revert InvalidIndices_error();
unchecked {
ret = _allocateBytes(to - from);
} // uc
assembly {
mstore(ret, 0)
}
_appendSubstring(ret, subject, from, to);
}
FixedPointMathLib.sol 1238 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Arithmetic library with operations for fixed-point numbers.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/FixedPointMathLib.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/FixedPointMathLib.sol)
library FixedPointMathLib {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The operation failed, as the output exceeds the maximum value of uint256.
error ExpOverflow();
/// @dev The operation failed, as the output exceeds the maximum value of uint256.
error FactorialOverflow();
/// @dev The operation failed, due to an overflow.
error RPowOverflow();
/// @dev The mantissa is too big to fit.
error MantissaOverflow();
/// @dev The operation failed, due to an multiplication overflow.
error MulWadFailed();
/// @dev The operation failed, due to an multiplication overflow.
error SMulWadFailed();
/// @dev The operation failed, either due to a multiplication overflow, or a division by a zero.
error DivWadFailed();
/// @dev The operation failed, either due to a multiplication overflow, or a division by a zero.
error SDivWadFailed();
/// @dev The operation failed, either due to a multiplication overflow, or a division by a zero.
error MulDivFailed();
/// @dev The division failed, as the denominator is zero.
error DivFailed();
/// @dev The full precision multiply-divide operation failed, either due
/// to the result being larger than 256 bits, or a division by a zero.
error FullMulDivFailed();
/// @dev The output is undefined, as the input is less-than-or-equal to zero.
error LnWadUndefined();
/// @dev The input outside the acceptable domain.
error OutOfDomain();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CONSTANTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The scalar of ETH and most ERC20s.
uint256 internal constant WAD = 1e18;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* SIMPLIFIED FIXED POINT OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Equivalent to `(x * y) / WAD` rounded down.
function mulWad(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
// Equivalent to `require(y == 0 || x <= type(uint256).max / y)`.
if gt(x, div(not(0), y)) {
if y {
mstore(0x00, 0xbac65e5b) // `MulWadFailed()`.
revert(0x1c, 0x04)
}
}
z := div(mul(x, y), WAD)
}
}
/// @dev Equivalent to `(x * y) / WAD` rounded down.
function sMulWad(int256 x, int256 y) internal pure returns (int256 z) {
/// @solidity memory-safe-assembly
assembly {
z := mul(x, y)
// Equivalent to `require((x == 0 || z / x == y) && !(x == -1 && y == type(int256).min))`.
if iszero(gt(or(iszero(x), eq(sdiv(z, x), y)), lt(not(x), eq(y, shl(255, 1))))) {
mstore(0x00, 0xedcd4dd4) // `SMulWadFailed()`.
revert(0x1c, 0x04)
}
z := sdiv(z, WAD)
}
}
/// @dev Equivalent to `(x * y) / WAD` rounded down, but without overflow checks.
function rawMulWad(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := div(mul(x, y), WAD)
}
}
/// @dev Equivalent to `(x * y) / WAD` rounded down, but without overflow checks.
function rawSMulWad(int256 x, int256 y) internal pure returns (int256 z) {
/// @solidity memory-safe-assembly
assembly {
z := sdiv(mul(x, y), WAD)
}
}
/// @dev Equivalent to `(x * y) / WAD` rounded up.
function mulWadUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := mul(x, y)
// Equivalent to `require(y == 0 || x <= type(uint256).max / y)`.
if iszero(eq(div(z, y), x)) {
if y {
mstore(0x00, 0xbac65e5b) // `MulWadFailed()`.
revert(0x1c, 0x04)
}
}
z := add(iszero(iszero(mod(z, WAD))), div(z, WAD))
}
}
/// @dev Equivalent to `(x * y) / WAD` rounded up, but without overflow checks.
function rawMulWadUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := add(iszero(iszero(mod(mul(x, y), WAD))), div(mul(x, y), WAD))
}
}
/// @dev Equivalent to `(x * WAD) / y` rounded down.
function divWad(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
// Equivalent to `require(y != 0 && x <= type(uint256).max / WAD)`.
if iszero(mul(y, lt(x, add(1, div(not(0), WAD))))) {
mstore(0x00, 0x7c5f487d) // `DivWadFailed()`.
revert(0x1c, 0x04)
}
z := div(mul(x, WAD), y)
}
}
/// @dev Equivalent to `(x * WAD) / y` rounded down.
function sDivWad(int256 x, int256 y) internal pure returns (int256 z) {
/// @solidity memory-safe-assembly
assembly {
z := mul(x, WAD)
// Equivalent to `require(y != 0 && ((x * WAD) / WAD == x))`.
if iszero(mul(y, eq(sdiv(z, WAD), x))) {
mstore(0x00, 0x5c43740d) // `SDivWadFailed()`.
revert(0x1c, 0x04)
}
z := sdiv(z, y)
}
}
/// @dev Equivalent to `(x * WAD) / y` rounded down, but without overflow and divide by zero checks.
function rawDivWad(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := div(mul(x, WAD), y)
}
}
/// @dev Equivalent to `(x * WAD) / y` rounded down, but without overflow and divide by zero checks.
function rawSDivWad(int256 x, int256 y) internal pure returns (int256 z) {
/// @solidity memory-safe-assembly
assembly {
z := sdiv(mul(x, WAD), y)
}
}
/// @dev Equivalent to `(x * WAD) / y` rounded up.
function divWadUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
// Equivalent to `require(y != 0 && x <= type(uint256).max / WAD)`.
if iszero(mul(y, lt(x, add(1, div(not(0), WAD))))) {
mstore(0x00, 0x7c5f487d) // `DivWadFailed()`.
revert(0x1c, 0x04)
}
z := add(iszero(iszero(mod(mul(x, WAD), y))), div(mul(x, WAD), y))
}
}
/// @dev Equivalent to `(x * WAD) / y` rounded up, but without overflow and divide by zero checks.
function rawDivWadUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := add(iszero(iszero(mod(mul(x, WAD), y))), div(mul(x, WAD), y))
}
}
/// @dev Equivalent to `x` to the power of `y`.
/// because `x ** y = (e ** ln(x)) ** y = e ** (ln(x) * y)`.
/// Note: This function is an approximation.
function powWad(int256 x, int256 y) internal pure returns (int256) {
// Using `ln(x)` means `x` must be greater than 0.
return expWad((lnWad(x) * y) / int256(WAD));
}
/// @dev Returns `exp(x)`, denominated in `WAD`.
/// Credit to Remco Bloemen under MIT license: https://2π.com/22/exp-ln
/// Note: This function is an approximation. Monotonically increasing.
function expWad(int256 x) internal pure returns (int256 r) {
unchecked {
// When the result is less than 0.5 we return zero.
// This happens when `x <= (log(1e-18) * 1e18) ~ -4.15e19`.
if (x <= -41446531673892822313) return r;
/// @solidity memory-safe-assembly
assembly {
// When the result is greater than `(2**255 - 1) / 1e18` we can not represent it as
// an int. This happens when `x >= floor(log((2**255 - 1) / 1e18) * 1e18) ≈ 135`.
if iszero(slt(x, 135305999368893231589)) {
mstore(0x00, 0xa37bfec9) // `ExpOverflow()`.
revert(0x1c, 0x04)
}
}
// `x` is now in the range `(-42, 136) * 1e18`. Convert to `(-42, 136) * 2**96`
// for more intermediate precision and a binary basis. This base conversion
// is a multiplication by 1e18 / 2**96 = 5**18 / 2**78.
x = (x << 78) / 5 ** 18;
// Reduce range of x to (-½ ln 2, ½ ln 2) * 2**96 by factoring out powers
// of two such that exp(x) = exp(x') * 2**k, where k is an integer.
// Solving this gives k = round(x / log(2)) and x' = x - k * log(2).
int256 k = ((x << 96) / 54916777467707473351141471128 + 2 ** 95) >> 96;
x = x - k * 54916777467707473351141471128;
// `k` is in the range `[-61, 195]`.
// Evaluate using a (6, 7)-term rational approximation.
// `p` is made monic, we'll multiply by a scale factor later.
int256 y = x + 1346386616545796478920950773328;
y = ((y * x) >> 96) + 57155421227552351082224309758442;
int256 p = y + x - 94201549194550492254356042504812;
p = ((p * y) >> 96) + 28719021644029726153956944680412240;
p = p * x + (4385272521454847904659076985693276 << 96);
// We leave `p` in `2**192` basis so we don't need to scale it back up for the division.
int256 q = x - 2855989394907223263936484059900;
q = ((q * x) >> 96) + 50020603652535783019961831881945;
q = ((q * x) >> 96) - 533845033583426703283633433725380;
q = ((q * x) >> 96) + 3604857256930695427073651918091429;
q = ((q * x) >> 96) - 14423608567350463180887372962807573;
q = ((q * x) >> 96) + 26449188498355588339934803723976023;
/// @solidity memory-safe-assembly
assembly {
// Div in assembly because solidity adds a zero check despite the unchecked.
// The q polynomial won't have zeros in the domain as all its roots are complex.
// No scaling is necessary because p is already `2**96` too large.
r := sdiv(p, q)
}
// r should be in the range `(0.09, 0.25) * 2**96`.
// We now need to multiply r by:
// - The scale factor `s ≈ 6.031367120`.
// - The `2**k` factor from the range reduction.
// - The `1e18 / 2**96` factor for base conversion.
// We do this all at once, with an intermediate result in `2**213`
// basis, so the final right shift is always by a positive amount.
r = int256(
(uint256(r) * 3822833074963236453042738258902158003155416615667) >> uint256(195 - k)
);
}
}
/// @dev Returns `ln(x)`, denominated in `WAD`.
/// Credit to Remco Bloemen under MIT license: https://2π.com/22/exp-ln
/// Note: This function is an approximation. Monotonically increasing.
function lnWad(int256 x) internal pure returns (int256 r) {
/// @solidity memory-safe-assembly
assembly {
// We want to convert `x` from `10**18` fixed point to `2**96` fixed point.
// We do this by multiplying by `2**96 / 10**18`. But since
// `ln(x * C) = ln(x) + ln(C)`, we can simply do nothing here
// and add `ln(2**96 / 10**18)` at the end.
// Compute `k = log2(x) - 96`, `r = 159 - k = 255 - log2(x) = 255 ^ log2(x)`.
r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x))
r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x))))
r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
r := or(r, shl(4, lt(0xffff, shr(r, x))))
r := or(r, shl(3, lt(0xff, shr(r, x))))
// We place the check here for more optimal stack operations.
if iszero(sgt(x, 0)) {
mstore(0x00, 0x1615e638) // `LnWadUndefined()`.
revert(0x1c, 0x04)
}
// forgefmt: disable-next-item
r := xor(r, byte(and(0x1f, shr(shr(r, x), 0x8421084210842108cc6318c6db6d54be)),
0xf8f9f9faf9fdfafbf9fdfcfdfafbfcfef9fafdfafcfcfbfefafafcfbffffffff))
// Reduce range of x to (1, 2) * 2**96
// ln(2^k * x) = k * ln(2) + ln(x)
x := shr(159, shl(r, x))
// Evaluate using a (8, 8)-term rational approximation.
// `p` is made monic, we will multiply by a scale factor later.
// forgefmt: disable-next-item
let p := sub( // This heavily nested expression is to avoid stack-too-deep for via-ir.
sar(96, mul(add(43456485725739037958740375743393,
sar(96, mul(add(24828157081833163892658089445524,
sar(96, mul(add(3273285459638523848632254066296,
x), x))), x))), x)), 11111509109440967052023855526967)
p := sub(sar(96, mul(p, x)), 45023709667254063763336534515857)
p := sub(sar(96, mul(p, x)), 14706773417378608786704636184526)
p := sub(mul(p, x), shl(96, 795164235651350426258249787498))
// We leave `p` in `2**192` basis so we don't need to scale it back up for the division.
// `q` is monic by convention.
let q := add(5573035233440673466300451813936, x)
q := add(71694874799317883764090561454958, sar(96, mul(x, q)))
q := add(283447036172924575727196451306956, sar(96, mul(x, q)))
q := add(401686690394027663651624208769553, sar(96, mul(x, q)))
q := add(204048457590392012362485061816622, sar(96, mul(x, q)))
q := add(31853899698501571402653359427138, sar(96, mul(x, q)))
q := add(909429971244387300277376558375, sar(96, mul(x, q)))
// `p / q` is in the range `(0, 0.125) * 2**96`.
// Finalization, we need to:
// - Multiply by the scale factor `s = 5.549…`.
// - Add `ln(2**96 / 10**18)`.
// - Add `k * ln(2)`.
// - Multiply by `10**18 / 2**96 = 5**18 >> 78`.
// The q polynomial is known not to have zeros in the domain.
// No scaling required because p is already `2**96` too large.
p := sdiv(p, q)
// Multiply by the scaling factor: `s * 5**18 * 2**96`, base is now `5**18 * 2**192`.
p := mul(1677202110996718588342820967067443963516166, p)
// Add `ln(2) * k * 5**18 * 2**192`.
// forgefmt: disable-next-item
p := add(mul(16597577552685614221487285958193947469193820559219878177908093499208371, sub(159, r)), p)
// Add `ln(2**96 / 10**18) * 5**18 * 2**192`.
p := add(600920179829731861736702779321621459595472258049074101567377883020018308, p)
// Base conversion: mul `2**18 / 2**192`.
r := sar(174, p)
}
}
/// @dev Returns `W_0(x)`, denominated in `WAD`.
/// See: https://en.wikipedia.org/wiki/Lambert_W_function
/// a.k.a. Product log function. This is an approximation of the principal branch.
/// Note: This function is an approximation. Monotonically increasing.
function lambertW0Wad(int256 x) internal pure returns (int256 w) {
// forgefmt: disable-next-item
unchecked {
if ((w = x) <= -367879441171442322) revert OutOfDomain(); // `x` less than `-1/e`.
(int256 wad, int256 p) = (int256(WAD), x);
uint256 c; // Whether we need to avoid catastrophic cancellation.
uint256 i = 4; // Number of iterations.
if (w <= 0x1ffffffffffff) {
if (-0x4000000000000 <= w) {
i = 1; // Inputs near zero only take one step to converge.
} else if (w <= -0x3ffffffffffffff) {
i = 32; // Inputs near `-1/e` take very long to converge.
}
} else if (uint256(w >> 63) == uint256(0)) {
/// @solidity memory-safe-assembly
assembly {
// Inline log2 for more performance, since the range is small.
let v := shr(49, w)
let l := shl(3, lt(0xff, v))
l := add(or(l, byte(and(0x1f, shr(shr(l, v), 0x8421084210842108cc6318c6db6d54be)),
0x0706060506020504060203020504030106050205030304010505030400000000)), 49)
w := sdiv(shl(l, 7), byte(sub(l, 31), 0x0303030303030303040506080c13))
c := gt(l, 60)
i := add(2, add(gt(l, 53), c))
}
} else {
int256 ll = lnWad(w = lnWad(w));
/// @solidity memory-safe-assembly
assembly {
// `w = ln(x) - ln(ln(x)) + b * ln(ln(x)) / ln(x)`.
w := add(sdiv(mul(ll, 1023715080943847266), w), sub(w, ll))
i := add(3, iszero(shr(68, x)))
c := iszero(shr(143, x))
}
if (c == uint256(0)) {
do { // If `x` is big, use Newton's so that intermediate values won't overflow.
int256 e = expWad(w);
/// @solidity memory-safe-assembly
assembly {
let t := mul(w, div(e, wad))
w := sub(w, sdiv(sub(t, x), div(add(e, t), wad)))
}
if (p <= w) break;
p = w;
} while (--i != uint256(0));
/// @solidity memory-safe-assembly
assembly {
w := sub(w, sgt(w, 2))
}
return w;
}
}
do { // Otherwise, use Halley's for faster convergence.
int256 e = expWad(w);
/// @solidity memory-safe-assembly
assembly {
let t := add(w, wad)
let s := sub(mul(w, e), mul(x, wad))
w := sub(w, sdiv(mul(s, wad), sub(mul(e, t), sdiv(mul(add(t, wad), s), add(t, t)))))
}
if (p <= w) break;
p = w;
} while (--i != c);
/// @solidity memory-safe-assembly
assembly {
w := sub(w, sgt(w, 2))
}
// For certain ranges of `x`, we'll use the quadratic-rate recursive formula of
// R. Iacono and J.P. Boyd for the last iteration, to avoid catastrophic cancellation.
if (c == uint256(0)) return w;
int256 t = w | 1;
/// @solidity memory-safe-assembly
assembly {
x := sdiv(mul(x, wad), t)
}
x = (t * (wad + lnWad(x)));
/// @solidity memory-safe-assembly
assembly {
w := sdiv(x, add(wad, t))
}
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* GENERAL NUMBER UTILITIES */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns `a * b == x * y`, with full precision.
function fullMulEq(uint256 a, uint256 b, uint256 x, uint256 y)
internal
pure
returns (bool result)
{
/// @solidity memory-safe-assembly
assembly {
result := and(eq(mul(a, b), mul(x, y)), eq(mulmod(x, y, not(0)), mulmod(a, b, not(0))))
}
}
/// @dev Calculates `floor(x * y / d)` with full precision.
/// Throws if result overflows a uint256 or when `d` is zero.
/// Credit to Remco Bloemen under MIT license: https://2π.com/21/muldiv
function fullMulDiv(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
// 512-bit multiply `[p1 p0] = x * y`.
// Compute the product mod `2**256` and mod `2**256 - 1`
// then use the Chinese Remainder Theorem to reconstruct
// the 512 bit result. The result is stored in two 256
// variables such that `product = p1 * 2**256 + p0`.
// Temporarily use `z` as `p0` to save gas.
z := mul(x, y) // Lower 256 bits of `x * y`.
for {} 1 {} {
// If overflows.
if iszero(mul(or(iszero(x), eq(div(z, x), y)), d)) {
let mm := mulmod(x, y, not(0))
let p1 := sub(mm, add(z, lt(mm, z))) // Upper 256 bits of `x * y`.
/*------------------- 512 by 256 division --------------------*/
// Make division exact by subtracting the remainder from `[p1 p0]`.
let r := mulmod(x, y, d) // Compute remainder using mulmod.
let t := and(d, sub(0, d)) // The least significant bit of `d`. `t >= 1`.
// Make sure `z` is less than `2**256`. Also prevents `d == 0`.
// Placing the check here seems to give more optimal stack operations.
if iszero(gt(d, p1)) {
mstore(0x00, 0xae47f702) // `FullMulDivFailed()`.
revert(0x1c, 0x04)
}
d := div(d, t) // Divide `d` by `t`, which is a power of two.
// Invert `d mod 2**256`
// Now that `d` is an odd number, it has an inverse
// modulo `2**256` such that `d * inv = 1 mod 2**256`.
// Compute the inverse by starting with a seed that is correct
// correct for four bits. That is, `d * inv = 1 mod 2**4`.
let inv := xor(2, mul(3, d))
// Now use Newton-Raphson iteration to improve the precision.
// Thanks to Hensel's lifting lemma, this also works in modular
// arithmetic, doubling the correct bits in each step.
inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**8
inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**16
inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**32
inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**64
inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**128
z :=
mul(
// Divide [p1 p0] by the factors of two.
// Shift in bits from `p1` into `p0`. For this we need
// to flip `t` such that it is `2**256 / t`.
or(mul(sub(p1, gt(r, z)), add(div(sub(0, t), t), 1)), div(sub(z, r), t)),
mul(sub(2, mul(d, inv)), inv) // inverse mod 2**256
)
break
}
z := div(z, d)
break
}
}
}
/// @dev Calculates `floor(x * y / d)` with full precision.
/// Behavior is undefined if `d` is zero or the final result cannot fit in 256 bits.
/// Performs the full 512 bit calculation regardless.
function fullMulDivUnchecked(uint256 x, uint256 y, uint256 d)
internal
pure
returns (uint256 z)
{
/// @solidity memory-safe-assembly
assembly {
z := mul(x, y)
let mm := mulmod(x, y, not(0))
let p1 := sub(mm, add(z, lt(mm, z)))
let t := and(d, sub(0, d))
let r := mulmod(x, y, d)
d := div(d, t)
let inv := xor(2, mul(3, d))
inv := mul(inv, sub(2, mul(d, inv)))
inv := mul(inv, sub(2, mul(d, inv)))
inv := mul(inv, sub(2, mul(d, inv)))
inv := mul(inv, sub(2, mul(d, inv)))
inv := mul(inv, sub(2, mul(d, inv)))
z :=
mul(
or(mul(sub(p1, gt(r, z)), add(div(sub(0, t), t), 1)), div(sub(z, r), t)),
mul(sub(2, mul(d, inv)), inv)
)
}
}
/// @dev Calculates `floor(x * y / d)` with full precision, rounded up.
/// Throws if result overflows a uint256 or when `d` is zero.
/// Credit to Uniswap-v3-core under MIT license:
/// https://github.com/Uniswap/v3-core/blob/main/contracts/libraries/FullMath.sol
function fullMulDivUp(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) {
z = fullMulDiv(x, y, d);
/// @solidity memory-safe-assembly
assembly {
if mulmod(x, y, d) {
z := add(z, 1)
if iszero(z) {
mstore(0x00, 0xae47f702) // `FullMulDivFailed()`.
revert(0x1c, 0x04)
}
}
}
}
/// @dev Calculates `floor(x * y / 2 ** n)` with full precision.
/// Throws if result overflows a uint256.
/// Credit to Philogy under MIT license:
/// https://github.com/SorellaLabs/angstrom/blob/main/contracts/src/libraries/X128MathLib.sol
function fullMulDivN(uint256 x, uint256 y, uint8 n) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
// Temporarily use `z` as `p0` to save gas.
z := mul(x, y) // Lower 256 bits of `x * y`. We'll call this `z`.
for {} 1 {} {
if iszero(or(iszero(x), eq(div(z, x), y))) {
let k := and(n, 0xff) // `n`, cleaned.
let mm := mulmod(x, y, not(0))
let p1 := sub(mm, add(z, lt(mm, z))) // Upper 256 bits of `x * y`.
// | p1 | z |
// Before: | p1_0 ¦ p1_1 | z_0 ¦ z_1 |
// Final: | 0 ¦ p1_0 | p1_1 ¦ z_0 |
// Check that final `z` doesn't overflow by checking that p1_0 = 0.
if iszero(shr(k, p1)) {
z := add(shl(sub(256, k), p1), shr(k, z))
break
}
mstore(0x00, 0xae47f702) // `FullMulDivFailed()`.
revert(0x1c, 0x04)
}
z := shr(and(n, 0xff), z)
break
}
}
}
/// @dev Returns `floor(x * y / d)`.
/// Reverts if `x * y` overflows, or `d` is zero.
function mulDiv(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := mul(x, y)
// Equivalent to `require(d != 0 && (y == 0 || x <= type(uint256).max / y))`.
if iszero(mul(or(iszero(x), eq(div(z, x), y)), d)) {
mstore(0x00, 0xad251c27) // `MulDivFailed()`.
revert(0x1c, 0x04)
}
z := div(z, d)
}
}
/// @dev Returns `ceil(x * y / d)`.
/// Reverts if `x * y` overflows, or `d` is zero.
function mulDivUp(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := mul(x, y)
// Equivalent to `require(d != 0 && (y == 0 || x <= type(uint256).max / y))`.
if iszero(mul(or(iszero(x), eq(div(z, x), y)), d)) {
mstore(0x00, 0xad251c27) // `MulDivFailed()`.
revert(0x1c, 0x04)
}
z := add(iszero(iszero(mod(z, d))), div(z, d))
}
}
/// @dev Returns `x`, the modular multiplicative inverse of `a`, such that `(a * x) % n == 1`.
function invMod(uint256 a, uint256 n) internal pure returns (uint256 x) {
/// @solidity memory-safe-assembly
assembly {
let g := n
let r := mod(a, n)
for { let y := 1 } 1 {} {
let q := div(g, r)
let t := g
g := r
r := sub(t, mul(r, q))
let u := x
x := y
y := sub(u, mul(y, q))
if iszero(r) { break }
}
x := mul(eq(g, 1), add(x, mul(slt(x, 0), n)))
}
}
/// @dev Returns `ceil(x / d)`.
/// Reverts if `d` is zero.
function divUp(uint256 x, uint256 d) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
if iszero(d) {
mstore(0x00, 0x65244e4e) // `DivFailed()`.
revert(0x1c, 0x04)
}
z := add(iszero(iszero(mod(x, d))), div(x, d))
}
}
/// @dev Returns `max(0, x - y)`.
function zeroFloorSub(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := mul(gt(x, y), sub(x, y))
}
}
/// @dev Returns `condition ? x : y`, without branching.
function ternary(bool condition, uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := xor(x, mul(xor(x, y), iszero(condition)))
}
}
/// @dev Exponentiate `x` to `y` by squaring, denominated in base `b`.
/// Reverts if the computation overflows.
function rpow(uint256 x, uint256 y, uint256 b) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := mul(b, iszero(y)) // `0 ** 0 = 1`. Otherwise, `0 ** n = 0`.
if x {
z := xor(b, mul(xor(b, x), and(y, 1))) // `z = isEven(y) ? scale : x`
let half := shr(1, b) // Divide `b` by 2.
// Divide `y` by 2 every iteration.
for { y := shr(1, y) } y { y := shr(1, y) } {
let xx := mul(x, x) // Store x squared.
let xxRound := add(xx, half) // Round to the nearest number.
// Revert if `xx + half` overflowed, or if `x ** 2` overflows.
if or(lt(xxRound, xx), shr(128, x)) {
mstore(0x00, 0x49f7642b) // `RPowOverflow()`.
revert(0x1c, 0x04)
}
x := div(xxRound, b) // Set `x` to scaled `xxRound`.
// If `y` is odd:
if and(y, 1) {
let zx := mul(z, x) // Compute `z * x`.
let zxRound := add(zx, half) // Round to the nearest number.
// If `z * x` overflowed or `zx + half` overflowed:
if or(xor(div(zx, x), z), lt(zxRound, zx)) {
// Revert if `x` is non-zero.
if x {
mstore(0x00, 0x49f7642b) // `RPowOverflow()`.
revert(0x1c, 0x04)
}
}
z := div(zxRound, b) // Return properly scaled `zxRound`.
}
}
}
}
}
/// @dev Returns the square root of `x`, rounded down.
function sqrt(uint256 x) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
// `floor(sqrt(2**15)) = 181`. `sqrt(2**15) - 181 = 2.84`.
z := 181 // The "correct" value is 1, but this saves a multiplication later.
// This segment is to get a reasonable initial estimate for the Babylonian method. With a bad
// start, the correct # of bits increases ~linearly each iteration instead of ~quadratically.
// Let `y = x / 2**r`. We check `y >= 2**(k + 8)`
// but shift right by `k` bits to ensure that if `x >= 256`, then `y >= 256`.
let r := shl(7, lt(0xffffffffffffffffffffffffffffffffff, x))
r := or(r, shl(6, lt(0xffffffffffffffffff, shr(r, x))))
r := or(r, shl(5, lt(0xffffffffff, shr(r, x))))
r := or(r, shl(4, lt(0xffffff, shr(r, x))))
z := shl(shr(1, r), z)
// Goal was to get `z*z*y` within a small factor of `x`. More iterations could
// get y in a tighter range. Currently, we will have y in `[256, 256*(2**16))`.
// We ensured `y >= 256` so that the relative difference between `y` and `y+1` is small.
// That's not possible if `x < 256` but we can just verify those cases exhaustively.
// Now, `z*z*y <= x < z*z*(y+1)`, and `y <= 2**(16+8)`, and either `y >= 256`, or `x < 256`.
// Correctness can be checked exhaustively for `x < 256`, so we assume `y >= 256`.
// Then `z*sqrt(y)` is within `sqrt(257)/sqrt(256)` of `sqrt(x)`, or about 20bps.
// For `s` in the range `[1/256, 256]`, the estimate `f(s) = (181/1024) * (s+1)`
// is in the range `(1/2.84 * sqrt(s), 2.84 * sqrt(s))`,
// with largest error when `s = 1` and when `s = 256` or `1/256`.
// Since `y` is in `[256, 256*(2**16))`, let `a = y/65536`, so that `a` is in `[1/256, 256)`.
// Then we can estimate `sqrt(y)` using
// `sqrt(65536) * 181/1024 * (a + 1) = 181/4 * (y + 65536)/65536 = 181 * (y + 65536)/2**18`.
// There is no overflow risk here since `y < 2**136` after the first branch above.
z := shr(18, mul(z, add(shr(r, x), 65536))) // A `mul()` is saved from starting `z` at 181.
// Given the worst case multiplicative error of 2.84 above, 7 iterations should be enough.
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
// If `x+1` is a perfect square, the Babylonian method cycles between
// `floor(sqrt(x))` and `ceil(sqrt(x))`. This statement ensures we return floor.
// See: https://en.wikipedia.org/wiki/Integer_square_root#Using_only_integer_division
z := sub(z, lt(div(x, z), z))
}
}
/// @dev Returns the cube root of `x`, rounded down.
/// Credit to bout3fiddy and pcaversaccio under AGPLv3 license:
/// https://github.com/pcaversaccio/snekmate/blob/main/src/utils/Math.vy
/// Formally verified by xuwinnie:
/// https://github.com/vectorized/solady/blob/main/audits/xuwinnie-solady-cbrt-proof.pdf
function cbrt(uint256 x) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
let r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x))
r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x))))
r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
r := or(r, shl(4, lt(0xffff, shr(r, x))))
r := or(r, shl(3, lt(0xff, shr(r, x))))
// Makeshift lookup table to nudge the approximate log2 result.
z := div(shl(div(r, 3), shl(lt(0xf, shr(r, x)), 0xf)), xor(7, mod(r, 3)))
// Newton-Raphson's.
z := div(add(add(div(x, mul(z, z)), z), z), 3)
z := div(add(add(div(x, mul(z, z)), z), z), 3)
z := div(add(add(div(x, mul(z, z)), z), z), 3)
z := div(add(add(div(x, mul(z, z)), z), z), 3)
z := div(add(add(div(x, mul(z, z)), z), z), 3)
z := div(add(add(div(x, mul(z, z)), z), z), 3)
z := div(add(add(div(x, mul(z, z)), z), z), 3)
// Round down.
z := sub(z, lt(div(x, mul(z, z)), z))
}
}
/// @dev Returns the square root of `x`, denominated in `WAD`, rounded down.
function sqrtWad(uint256 x) internal pure returns (uint256 z) {
unchecked {
if (x <= type(uint256).max / 10 ** 18) return sqrt(x * 10 ** 18);
z = (1 + sqrt(x)) * 10 ** 9;
z = (fullMulDivUnchecked(x, 10 ** 18, z) + z) >> 1;
}
/// @solidity memory-safe-assembly
assembly {
z := sub(z, gt(999999999999999999, sub(mulmod(z, z, x), 1))) // Round down.
}
}
/// @dev Returns the cube root of `x`, denominated in `WAD`, rounded down.
/// Formally verified by xuwinnie:
/// https://github.com/vectorized/solady/blob/main/audits/xuwinnie-solady-cbrt-proof.pdf
function cbrtWad(uint256 x) internal pure returns (uint256 z) {
unchecked {
if (x <= type(uint256).max / 10 ** 36) return cbrt(x * 10 ** 36);
z = (1 + cbrt(x)) * 10 ** 12;
z = (fullMulDivUnchecked(x, 10 ** 36, z * z) + z + z) / 3;
}
/// @solidity memory-safe-assembly
assembly {
let p := x
for {} 1 {} {
if iszero(shr(229, p)) {
if iszero(shr(199, p)) {
p := mul(p, 100000000000000000) // 10 ** 17.
break
}
p := mul(p, 100000000) // 10 ** 8.
break
}
if iszero(shr(249, p)) { p := mul(p, 100) }
break
}
let t := mulmod(mul(z, z), z, p)
z := sub(z, gt(lt(t, shr(1, p)), iszero(t))) // Round down.
}
}
/// @dev Returns the factorial of `x`.
function factorial(uint256 x) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := 1
if iszero(lt(x, 58)) {
mstore(0x00, 0xaba0f2a2) // `FactorialOverflow()`.
revert(0x1c, 0x04)
}
for {} x { x := sub(x, 1) } { z := mul(z, x) }
}
}
/// @dev Returns the log2 of `x`.
/// Equivalent to computing the index of the most significant bit (MSB) of `x`.
/// Returns 0 if `x` is zero.
function log2(uint256 x) internal pure returns (uint256 r) {
/// @solidity memory-safe-assembly
assembly {
r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x))
r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x))))
r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
r := or(r, shl(4, lt(0xffff, shr(r, x))))
r := or(r, shl(3, lt(0xff, shr(r, x))))
// forgefmt: disable-next-item
r := or(r, byte(and(0x1f, shr(shr(r, x), 0x8421084210842108cc6318c6db6d54be)),
0x0706060506020504060203020504030106050205030304010505030400000000))
}
}
/// @dev Returns the log2 of `x`, rounded up.
/// Returns 0 if `x` is zero.
function log2Up(uint256 x) internal pure returns (uint256 r) {
r = log2(x);
/// @solidity memory-safe-assembly
assembly {
r := add(r, lt(shl(r, 1), x))
}
}
/// @dev Returns the log10 of `x`.
/// Returns 0 if `x` is zero.
function log10(uint256 x) internal pure returns (uint256 r) {
/// @solidity memory-safe-assembly
assembly {
if iszero(lt(x, 100000000000000000000000000000000000000)) {
x := div(x, 100000000000000000000000000000000000000)
r := 38
}
if iszero(lt(x, 100000000000000000000)) {
x := div(x, 100000000000000000000)
r := add(r, 20)
}
if iszero(lt(x, 10000000000)) {
x := div(x, 10000000000)
r := add(r, 10)
}
if iszero(lt(x, 100000)) {
x := div(x, 100000)
r := add(r, 5)
}
r := add(r, add(gt(x, 9), add(gt(x, 99), add(gt(x, 999), gt(x, 9999)))))
}
}
/// @dev Returns the log10 of `x`, rounded up.
/// Returns 0 if `x` is zero.
function log10Up(uint256 x) internal pure returns (uint256 r) {
r = log10(x);
/// @solidity memory-safe-assembly
assembly {
r := add(r, lt(exp(10, r), x))
}
}
/// @dev Returns the log256 of `x`.
/// Returns 0 if `x` is zero.
function log256(uint256 x) internal pure returns (uint256 r) {
/// @solidity memory-safe-assembly
assembly {
r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x))
r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x))))
r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
r := or(r, shl(4, lt(0xffff, shr(r, x))))
r := or(shr(3, r), lt(0xff, shr(r, x)))
}
}
/// @dev Returns the log256 of `x`, rounded up.
/// Returns 0 if `x` is zero.
function log256Up(uint256 x) internal pure returns (uint256 r) {
r = log256(x);
/// @solidity memory-safe-assembly
assembly {
r := add(r, lt(shl(shl(3, r), 1), x))
}
}
/// @dev Returns the scientific notation format `mantissa * 10 ** exponent` of `x`.
/// Useful for compressing prices (e.g. using 25 bit mantissa and 7 bit exponent).
function sci(uint256 x) internal pure returns (uint256 mantissa, uint256 exponent) {
/// @solidity memory-safe-assembly
assembly {
mantissa := x
if mantissa {
if iszero(mod(mantissa, 1000000000000000000000000000000000)) {
mantissa := div(mantissa, 1000000000000000000000000000000000)
exponent := 33
}
if iszero(mod(mantissa, 10000000000000000000)) {
mantissa := div(mantissa, 10000000000000000000)
exponent := add(exponent, 19)
}
if iszero(mod(mantissa, 1000000000000)) {
mantissa := div(mantissa, 1000000000000)
exponent := add(exponent, 12)
}
if iszero(mod(mantissa, 1000000)) {
mantissa := div(mantissa, 1000000)
exponent := add(exponent, 6)
}
if iszero(mod(mantissa, 10000)) {
mantissa := div(mantissa, 10000)
exponent := add(exponent, 4)
}
if iszero(mod(mantissa, 100)) {
mantissa := div(mantissa, 100)
exponent := add(exponent, 2)
}
if iszero(mod(mantissa, 10)) {
mantissa := div(mantissa, 10)
exponent := add(exponent, 1)
}
}
}
}
/// @dev Convenience function for packing `x` into a smaller number using `sci`.
/// The `mantissa` will be in bits [7..255] (the upper 249 bits).
/// The `exponent` will be in bits [0..6] (the lower 7 bits).
/// Use `SafeCastLib` to safely ensure that the `packed` number is small
/// enough to fit in the desired unsigned integer type:
/// ```
/// uint32 packed = SafeCastLib.toUint32(FixedPointMathLib.packSci(777 ether));
/// ```
function packSci(uint256 x) internal pure returns (uint256 packed) {
(x, packed) = sci(x); // Reuse for `mantissa` and `exponent`.
/// @solidity memory-safe-assembly
assembly {
if shr(249, x) {
mstore(0x00, 0xce30380c) // `MantissaOverflow()`.
revert(0x1c, 0x04)
}
packed := or(shl(7, x), packed)
}
}
/// @dev Convenience function for unpacking a packed number from `packSci`.
function unpackSci(uint256 packed) internal pure returns (uint256 unpacked) {
unchecked {
unpacked = (packed >> 7) * 10 ** (packed & 0x7f);
}
}
/// @dev Returns the average of `x` and `y`. Rounds towards zero.
function avg(uint256 x, uint256 y) internal pure returns (uint256 z) {
unchecked {
z = (x & y) + ((x ^ y) >> 1);
}
}
/// @dev Returns the average of `x` and `y`. Rounds towards negative infinity.
function avg(int256 x, int256 y) internal pure returns (int256 z) {
unchecked {
z = (x >> 1) + (y >> 1) + (x & y & 1);
}
}
/// @dev Returns the absolute value of `x`.
function abs(int256 x) internal pure returns (uint256 z) {
unchecked {
z = (uint256(x) + uint256(x >> 255)) ^ uint256(x >> 255);
}
}
/// @dev Returns the absolute distance between `x` and `y`.
function dist(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := add(xor(sub(0, gt(x, y)), sub(y, x)), gt(x, y))
}
}
/// @dev Returns the absolute distance between `x` and `y`.
function dist(int256 x, int256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := add(xor(sub(0, sgt(x, y)), sub(y, x)), sgt(x, y))
}
}
/// @dev Returns the minimum of `x` and `y`.
function min(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := xor(x, mul(xor(x, y), lt(y, x)))
}
}
/// @dev Returns the minimum of `x` and `y`.
function min(int256 x, int256 y) internal pure returns (int256 z) {
/// @solidity memory-safe-assembly
assembly {
z := xor(x, mul(xor(x, y), slt(y, x)))
}
}
/// @dev Returns the maximum of `x` and `y`.
function max(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := xor(x, mul(xor(x, y), gt(y, x)))
}
}
/// @dev Returns the maximum of `x` and `y`.
function max(int256 x, int256 y) internal pure returns (int256 z) {
/// @solidity memory-safe-assembly
assembly {
z := xor(x, mul(xor(x, y), sgt(y, x)))
}
}
/// @dev Returns `x`, bounded to `minValue` and `maxValue`.
function clamp(uint256 x, uint256 minValue, uint256 maxValue)
internal
pure
returns (uint256 z)
{
/// @solidity memory-safe-assembly
assembly {
z := xor(x, mul(xor(x, minValue), gt(minValue, x)))
z := xor(z, mul(xor(z, maxValue), lt(maxValue, z)))
}
}
/// @dev Returns `x`, bounded to `minValue` and `maxValue`.
function clamp(int256 x, int256 minValue, int256 maxValue) internal pure returns (int256 z) {
/// @solidity memory-safe-assembly
assembly {
z := xor(x, mul(xor(x, minValue), sgt(minValue, x)))
z := xor(z, mul(xor(z, maxValue), slt(maxValue, z)))
}
}
/// @dev Returns greatest common divisor of `x` and `y`.
function gcd(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
for { z := x } y {} {
let t := y
y := mod(z, y)
z := t
}
}
}
/// @dev Returns `a + (b - a) * (t - begin) / (end - begin)`,
/// with `t` clamped between `begin` and `end` (inclusive).
/// Agnostic to the order of (`a`, `b`) and (`end`, `begin`).
/// If `begins == end`, returns `t <= begin ? a : b`.
function lerp(uint256 a, uint256 b, uint256 t, uint256 begin, uint256 end)
internal
pure
returns (uint256)
{
if (begin > end) (t, begin, end) = (~t, ~begin, ~end);
if (t <= begin) return a;
if (t >= end) return b;
unchecked {
if (b >= a) return a + fullMulDiv(b - a, t - begin, end - begin);
return a - fullMulDiv(a - b, t - begin, end - begin);
}
}
/// @dev Returns `a + (b - a) * (t - begin) / (end - begin)`.
/// with `t` clamped between `begin` and `end` (inclusive).
/// Agnostic to the order of (`a`, `b`) and (`end`, `begin`).
/// If `begins == end`, returns `t <= begin ? a : b`.
function lerp(int256 a, int256 b, int256 t, int256 begin, int256 end)
internal
pure
returns (int256)
{
if (begin > end) (t, begin, end) = (~t, ~begin, ~end);
if (t <= begin) return a;
if (t >= end) return b;
// forgefmt: disable-next-item
unchecked {
if (b >= a) return int256(uint256(a) + fullMulDiv(uint256(b - a),
uint256(t - begin), uint256(end - begin)));
return int256(uint256(a) - fullMulDiv(uint256(a - b),
uint256(t - begin), uint256(end - begin)));
}
}
/// @dev Returns if `x` is an even number. Some people may need this.
function isEven(uint256 x) internal pure returns ...
// [truncated — 53028 bytes total]
LibBytes.sol 696 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Library for byte related operations.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibBytes.sol)
library LibBytes {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* STRUCTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Goated bytes storage struct that totally MOGs, no cap, fr.
/// Uses less gas and bytecode than Solidity's native bytes storage. It's meta af.
/// Packs length with the first 31 bytes if <255 bytes, so it’s mad tight.
struct BytesStorage {
bytes32 _spacer;
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CONSTANTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The constant returned when the `search` is not found in the bytes.
uint256 internal constant NOT_FOUND = type(uint256).max;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* BYTE STORAGE OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Sets the value of the bytes storage `$` to `s`.
function set(BytesStorage storage $, bytes memory s) internal {
/// @solidity memory-safe-assembly
assembly {
let n := mload(s)
let packed := or(0xff, shl(8, n))
for { let i := 0 } 1 {} {
if iszero(gt(n, 0xfe)) {
i := 0x1f
packed := or(n, shl(8, mload(add(s, i))))
if iszero(gt(n, i)) { break }
}
let o := add(s, 0x20)
mstore(0x00, $.slot)
for { let p := keccak256(0x00, 0x20) } 1 {} {
sstore(add(p, shr(5, i)), mload(add(o, i)))
i := add(i, 0x20)
if iszero(lt(i, n)) { break }
}
break
}
sstore($.slot, packed)
}
}
/// @dev Sets the value of the bytes storage `$` to `s`.
function setCalldata(BytesStorage storage $, bytes calldata s) internal {
/// @solidity memory-safe-assembly
assembly {
let packed := or(0xff, shl(8, s.length))
for { let i := 0 } 1 {} {
if iszero(gt(s.length, 0xfe)) {
i := 0x1f
packed := or(s.length, shl(8, shr(8, calldataload(s.offset))))
if iszero(gt(s.length, i)) { break }
}
mstore(0x00, $.slot)
for { let p := keccak256(0x00, 0x20) } 1 {} {
sstore(add(p, shr(5, i)), calldataload(add(s.offset, i)))
i := add(i, 0x20)
if iszero(lt(i, s.length)) { break }
}
break
}
sstore($.slot, packed)
}
}
/// @dev Sets the value of the bytes storage `$` to the empty bytes.
function clear(BytesStorage storage $) internal {
delete $._spacer;
}
/// @dev Returns whether the value stored is `$` is the empty bytes "".
function isEmpty(BytesStorage storage $) internal view returns (bool) {
return uint256($._spacer) & 0xff == uint256(0);
}
/// @dev Returns the length of the value stored in `$`.
function length(BytesStorage storage $) internal view returns (uint256 result) {
result = uint256($._spacer);
/// @solidity memory-safe-assembly
assembly {
let n := and(0xff, result)
result := or(mul(shr(8, result), eq(0xff, n)), mul(n, iszero(eq(0xff, n))))
}
}
/// @dev Returns the value stored in `$`.
function get(BytesStorage storage $) internal view returns (bytes memory result) {
/// @solidity memory-safe-assembly
assembly {
result := mload(0x40)
let o := add(result, 0x20)
let packed := sload($.slot)
let n := shr(8, packed)
for { let i := 0 } 1 {} {
if iszero(eq(and(packed, 0xff), 0xff)) {
mstore(o, packed)
n := and(0xff, packed)
i := 0x1f
if iszero(gt(n, i)) { break }
}
mstore(0x00, $.slot)
for { let p := keccak256(0x00, 0x20) } 1 {} {
mstore(add(o, i), sload(add(p, shr(5, i))))
i := add(i, 0x20)
if iszero(lt(i, n)) { break }
}
break
}
mstore(result, n) // Store the length of the memory.
mstore(add(o, n), 0) // Zeroize the slot after the bytes.
mstore(0x40, add(add(o, n), 0x20)) // Allocate memory.
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* BYTES OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns `subject` all occurrences of `needle` replaced with `replacement`.
function replace(bytes memory subject, bytes memory needle, bytes memory replacement)
internal
pure
returns (bytes memory result)
{
/// @solidity memory-safe-assembly
assembly {
result := mload(0x40)
let needleLen := mload(needle)
let replacementLen := mload(replacement)
let d := sub(result, subject) // Memory difference.
let i := add(subject, 0x20) // Subject bytes pointer.
mstore(0x00, add(i, mload(subject))) // End of subject.
if iszero(gt(needleLen, mload(subject))) {
let subjectSearchEnd := add(sub(mload(0x00), needleLen), 1)
let h := 0 // The hash of `needle`.
if iszero(lt(needleLen, 0x20)) { h := keccak256(add(needle, 0x20), needleLen) }
let s := mload(add(needle, 0x20))
for { let m := shl(3, sub(0x20, and(needleLen, 0x1f))) } 1 {} {
let t := mload(i)
// Whether the first `needleLen % 32` bytes of `subject` and `needle` matches.
if iszero(shr(m, xor(t, s))) {
if h {
if iszero(eq(keccak256(i, needleLen), h)) {
mstore(add(i, d), t)
i := add(i, 1)
if iszero(lt(i, subjectSearchEnd)) { break }
continue
}
}
// Copy the `replacement` one word at a time.
for { let j := 0 } 1 {} {
mstore(add(add(i, d), j), mload(add(add(replacement, 0x20), j)))
j := add(j, 0x20)
if iszero(lt(j, replacementLen)) { break }
}
d := sub(add(d, replacementLen), needleLen)
if needleLen {
i := add(i, needleLen)
if iszero(lt(i, subjectSearchEnd)) { break }
continue
}
}
mstore(add(i, d), t)
i := add(i, 1)
if iszero(lt(i, subjectSearchEnd)) { break }
}
}
let end := mload(0x00)
let n := add(sub(d, add(result, 0x20)), end)
// Copy the rest of the bytes one word at a time.
for {} lt(i, end) { i := add(i, 0x20) } { mstore(add(i, d), mload(i)) }
let o := add(i, d)
mstore(o, 0) // Zeroize the slot after the bytes.
mstore(0x40, add(o, 0x20)) // Allocate memory.
mstore(result, n) // Store the length.
}
}
/// @dev Returns the byte index of the first location of `needle` in `subject`,
/// needleing from left to right, starting from `from`.
/// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `needle` is not found.
function indexOf(bytes memory subject, bytes memory needle, uint256 from)
internal
pure
returns (uint256 result)
{
/// @solidity memory-safe-assembly
assembly {
result := not(0) // Initialize to `NOT_FOUND`.
for { let subjectLen := mload(subject) } 1 {} {
if iszero(mload(needle)) {
result := from
if iszero(gt(from, subjectLen)) { break }
result := subjectLen
break
}
let needleLen := mload(needle)
let subjectStart := add(subject, 0x20)
subject := add(subjectStart, from)
let end := add(sub(add(subjectStart, subjectLen), needleLen), 1)
let m := shl(3, sub(0x20, and(needleLen, 0x1f)))
let s := mload(add(needle, 0x20))
if iszero(and(lt(subject, end), lt(from, subjectLen))) { break }
if iszero(lt(needleLen, 0x20)) {
for { let h := keccak256(add(needle, 0x20), needleLen) } 1 {} {
if iszero(shr(m, xor(mload(subject), s))) {
if eq(keccak256(subject, needleLen), h) {
result := sub(subject, subjectStart)
break
}
}
subject := add(subject, 1)
if iszero(lt(subject, end)) { break }
}
break
}
for {} 1 {} {
if iszero(shr(m, xor(mload(subject), s))) {
result := sub(subject, subjectStart)
break
}
subject := add(subject, 1)
if iszero(lt(subject, end)) { break }
}
break
}
}
}
/// @dev Returns the byte index of the first location of `needle` in `subject`,
/// needleing from left to right.
/// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `needle` is not found.
function indexOf(bytes memory subject, bytes memory needle) internal pure returns (uint256) {
return indexOf(subject, needle, 0);
}
/// @dev Returns the byte index of the first location of `needle` in `subject`,
/// needleing from right to left, starting from `from`.
/// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `needle` is not found.
function lastIndexOf(bytes memory subject, bytes memory needle, uint256 from)
internal
pure
returns (uint256 result)
{
/// @solidity memory-safe-assembly
assembly {
for {} 1 {} {
result := not(0) // Initialize to `NOT_FOUND`.
let needleLen := mload(needle)
if gt(needleLen, mload(subject)) { break }
let w := result
let fromMax := sub(mload(subject), needleLen)
if iszero(gt(fromMax, from)) { from := fromMax }
let end := add(add(subject, 0x20), w)
subject := add(add(subject, 0x20), from)
if iszero(gt(subject, end)) { break }
// As this function is not too often used,
// we shall simply use keccak256 for smaller bytecode size.
for { let h := keccak256(add(needle, 0x20), needleLen) } 1 {} {
if eq(keccak256(subject, needleLen), h) {
result := sub(subject, add(end, 1))
break
}
subject := add(subject, w) // `sub(subject, 1)`.
if iszero(gt(subject, end)) { break }
}
break
}
}
}
/// @dev Returns the byte index of the first location of `needle` in `subject`,
/// needleing from right to left.
/// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `needle` is not found.
function lastIndexOf(bytes memory subject, bytes memory needle)
internal
pure
returns (uint256)
{
return lastIndexOf(subject, needle, type(uint256).max);
}
/// @dev Returns true if `needle` is found in `subject`, false otherwise.
function contains(bytes memory subject, bytes memory needle) internal pure returns (bool) {
return indexOf(subject, needle) != NOT_FOUND;
}
/// @dev Returns whether `subject` starts with `needle`.
function startsWith(bytes memory subject, bytes memory needle)
internal
pure
returns (bool result)
{
/// @solidity memory-safe-assembly
assembly {
let n := mload(needle)
// Just using keccak256 directly is actually cheaper.
let t := eq(keccak256(add(subject, 0x20), n), keccak256(add(needle, 0x20), n))
result := lt(gt(n, mload(subject)), t)
}
}
/// @dev Returns whether `subject` ends with `needle`.
function endsWith(bytes memory subject, bytes memory needle)
internal
pure
returns (bool result)
{
/// @solidity memory-safe-assembly
assembly {
let n := mload(needle)
let notInRange := gt(n, mload(subject))
// `subject + 0x20 + max(subject.length - needle.length, 0)`.
let t := add(add(subject, 0x20), mul(iszero(notInRange), sub(mload(subject), n)))
// Just using keccak256 directly is actually cheaper.
result := gt(eq(keccak256(t, n), keccak256(add(needle, 0x20), n)), notInRange)
}
}
/// @dev Returns `subject` repeated `times`.
function repeat(bytes memory subject, uint256 times)
internal
pure
returns (bytes memory result)
{
/// @solidity memory-safe-assembly
assembly {
let l := mload(subject) // Subject length.
if iszero(or(iszero(times), iszero(l))) {
result := mload(0x40)
subject := add(subject, 0x20)
let o := add(result, 0x20)
for {} 1 {} {
// Copy the `subject` one word at a time.
for { let j := 0 } 1 {} {
mstore(add(o, j), mload(add(subject, j)))
j := add(j, 0x20)
if iszero(lt(j, l)) { break }
}
o := add(o, l)
times := sub(times, 1)
if iszero(times) { break }
}
mstore(o, 0) // Zeroize the slot after the bytes.
mstore(0x40, add(o, 0x20)) // Allocate memory.
mstore(result, sub(o, add(result, 0x20))) // Store the length.
}
}
}
/// @dev Returns a copy of `subject` sliced from `start` to `end` (exclusive).
/// `start` and `end` are byte offsets.
function slice(bytes memory subject, uint256 start, uint256 end)
internal
pure
returns (bytes memory result)
{
/// @solidity memory-safe-assembly
assembly {
let l := mload(subject) // Subject length.
if iszero(gt(l, end)) { end := l }
if iszero(gt(l, start)) { start := l }
if lt(start, end) {
result := mload(0x40)
let n := sub(end, start)
let i := add(subject, start)
let w := not(0x1f)
// Copy the `subject` one word at a time, backwards.
for { let j := and(add(n, 0x1f), w) } 1 {} {
mstore(add(result, j), mload(add(i, j)))
j := add(j, w) // `sub(j, 0x20)`.
if iszero(j) { break }
}
let o := add(add(result, 0x20), n)
mstore(o, 0) // Zeroize the slot after the bytes.
mstore(0x40, add(o, 0x20)) // Allocate memory.
mstore(result, n) // Store the length.
}
}
}
/// @dev Returns a copy of `subject` sliced from `start` to the end of the bytes.
/// `start` is a byte offset.
function slice(bytes memory subject, uint256 start)
internal
pure
returns (bytes memory result)
{
result = slice(subject, start, type(uint256).max);
}
/// @dev Returns a copy of `subject` sliced from `start` to `end` (exclusive).
/// `start` and `end` are byte offsets. Faster than Solidity's native slicing.
function sliceCalldata(bytes calldata subject, uint256 start, uint256 end)
internal
pure
returns (bytes calldata result)
{
/// @solidity memory-safe-assembly
assembly {
end := xor(end, mul(xor(end, subject.length), lt(subject.length, end)))
start := xor(start, mul(xor(start, subject.length), lt(subject.length, start)))
result.offset := add(subject.offset, start)
result.length := mul(lt(start, end), sub(end, start))
}
}
/// @dev Returns a copy of `subject` sliced from `start` to the end of the bytes.
/// `start` is a byte offset. Faster than Solidity's native slicing.
function sliceCalldata(bytes calldata subject, uint256 start)
internal
pure
returns (bytes calldata result)
{
/// @solidity memory-safe-assembly
assembly {
start := xor(start, mul(xor(start, subject.length), lt(subject.length, start)))
result.offset := add(subject.offset, start)
result.length := mul(lt(start, subject.length), sub(subject.length, start))
}
}
/// @dev Reduces the size of `subject` to `n`.
/// If `n` is greater than the size of `subject`, this will be a no-op.
function truncate(bytes memory subject, uint256 n)
internal
pure
returns (bytes memory result)
{
/// @solidity memory-safe-assembly
assembly {
result := subject
mstore(mul(lt(n, mload(result)), result), n)
}
}
/// @dev Returns a copy of `subject`, with the length reduced to `n`.
/// If `n` is greater than the size of `subject`, this will be a no-op.
function truncatedCalldata(bytes calldata subject, uint256 n)
internal
pure
returns (bytes calldata result)
{
/// @solidity memory-safe-assembly
assembly {
result.offset := subject.offset
result.length := xor(n, mul(xor(n, subject.length), lt(subject.length, n)))
}
}
/// @dev Returns all the indices of `needle` in `subject`.
/// The indices are byte offsets.
function indicesOf(bytes memory subject, bytes memory needle)
internal
pure
returns (uint256[] memory result)
{
/// @solidity memory-safe-assembly
assembly {
let searchLen := mload(needle)
if iszero(gt(searchLen, mload(subject))) {
result := mload(0x40)
let i := add(subject, 0x20)
let o := add(result, 0x20)
let subjectSearchEnd := add(sub(add(i, mload(subject)), searchLen), 1)
let h := 0 // The hash of `needle`.
if iszero(lt(searchLen, 0x20)) { h := keccak256(add(needle, 0x20), searchLen) }
let s := mload(add(needle, 0x20))
for { let m := shl(3, sub(0x20, and(searchLen, 0x1f))) } 1 {} {
let t := mload(i)
// Whether the first `searchLen % 32` bytes of `subject` and `needle` matches.
if iszero(shr(m, xor(t, s))) {
if h {
if iszero(eq(keccak256(i, searchLen), h)) {
i := add(i, 1)
if iszero(lt(i, subjectSearchEnd)) { break }
continue
}
}
mstore(o, sub(i, add(subject, 0x20))) // Append to `result`.
o := add(o, 0x20)
i := add(i, searchLen) // Advance `i` by `searchLen`.
if searchLen {
if iszero(lt(i, subjectSearchEnd)) { break }
continue
}
}
i := add(i, 1)
if iszero(lt(i, subjectSearchEnd)) { break }
}
mstore(result, shr(5, sub(o, add(result, 0x20)))) // Store the length of `result`.
// Allocate memory for result.
// We allocate one more word, so this array can be recycled for {split}.
mstore(0x40, add(o, 0x20))
}
}
}
/// @dev Returns a arrays of bytess based on the `delimiter` inside of the `subject` bytes.
function split(bytes memory subject, bytes memory delimiter)
internal
pure
returns (bytes[] memory result)
{
uint256[] memory indices = indicesOf(subject, delimiter);
/// @solidity memory-safe-assembly
assembly {
let w := not(0x1f)
let indexPtr := add(indices, 0x20)
let indicesEnd := add(indexPtr, shl(5, add(mload(indices), 1)))
mstore(add(indicesEnd, w), mload(subject))
mstore(indices, add(mload(indices), 1))
for { let prevIndex := 0 } 1 {} {
let index := mload(indexPtr)
mstore(indexPtr, 0x60)
if iszero(eq(index, prevIndex)) {
let element := mload(0x40)
let l := sub(index, prevIndex)
mstore(element, l) // Store the length of the element.
// Copy the `subject` one word at a time, backwards.
for { let o := and(add(l, 0x1f), w) } 1 {} {
mstore(add(element, o), mload(add(add(subject, prevIndex), o)))
o := add(o, w) // `sub(o, 0x20)`.
if iszero(o) { break }
}
mstore(add(add(element, 0x20), l), 0) // Zeroize the slot after the bytes.
// Allocate memory for the length and the bytes, rounded up to a multiple of 32.
mstore(0x40, add(element, and(add(l, 0x3f), w)))
mstore(indexPtr, element) // Store the `element` into the array.
}
prevIndex := add(index, mload(delimiter))
indexPtr := add(indexPtr, 0x20)
if iszero(lt(indexPtr, indicesEnd)) { break }
}
result := indices
if iszero(mload(delimiter)) {
result := add(indices, 0x20)
mstore(result, sub(mload(indices), 2))
}
}
}
/// @dev Returns a concatenated bytes of `a` and `b`.
/// Cheaper than `bytes.concat()` and does not de-align the free memory pointer.
function concat(bytes memory a, bytes memory b) internal pure returns (bytes memory result) {
/// @solidity memory-safe-assembly
assembly {
result := mload(0x40)
let w := not(0x1f)
let aLen := mload(a)
// Copy `a` one word at a time, backwards.
for { let o := and(add(aLen, 0x20), w) } 1 {} {
mstore(add(result, o), mload(add(a, o)))
o := add(o, w) // `sub(o, 0x20)`.
if iszero(o) { break }
}
let bLen := mload(b)
let output := add(result, aLen)
// Copy `b` one word at a time, backwards.
for { let o := and(add(bLen, 0x20), w) } 1 {} {
mstore(add(output, o), mload(add(b, o)))
o := add(o, w) // `sub(o, 0x20)`.
if iszero(o) { break }
}
let totalLen := add(aLen, bLen)
let last := add(add(result, 0x20), totalLen)
mstore(last, 0) // Zeroize the slot after the bytes.
mstore(result, totalLen) // Store the length.
mstore(0x40, add(last, 0x20)) // Allocate memory.
}
}
/// @dev Returns whether `a` equals `b`.
function eq(bytes memory a, bytes memory b) internal pure returns (bool result) {
/// @solidity memory-safe-assembly
assembly {
result := eq(keccak256(add(a, 0x20), mload(a)), keccak256(add(b, 0x20), mload(b)))
}
}
/// @dev Returns whether `a` equals `b`, where `b` is a null-terminated small bytes.
function eqs(bytes memory a, bytes32 b) internal pure returns (bool result) {
/// @solidity memory-safe-assembly
assembly {
// These should be evaluated on compile time, as far as possible.
let m := not(shl(7, div(not(iszero(b)), 255))) // `0x7f7f ...`.
let x := not(or(m, or(b, add(m, and(b, m)))))
let r := shl(7, iszero(iszero(shr(128, x))))
r := or(r, shl(6, iszero(iszero(shr(64, shr(r, x))))))
r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
r := or(r, shl(4, lt(0xffff, shr(r, x))))
r := or(r, shl(3, lt(0xff, shr(r, x))))
// forgefmt: disable-next-item
result := gt(eq(mload(a), add(iszero(x), xor(31, shr(3, r)))),
xor(shr(add(8, r), b), shr(add(8, r), mload(add(a, 0x20)))))
}
}
/// @dev Directly returns `a` without copying.
function directReturn(bytes memory a) internal pure {
assembly {
// Assumes that the bytes does not start from the scratch space.
let retStart := sub(a, 0x20)
let retUnpaddedSize := add(mload(a), 0x40)
// Right pad with zeroes. Just in case the bytes is produced
// by a method that doesn't zero right pad.
mstore(add(retStart, retUnpaddedSize), 0)
mstore(retStart, 0x20) // Store the return offset.
// End the transaction, returning the bytes.
return(retStart, and(not(0x1f), add(0x1f, retUnpaddedSize)))
}
}
/// @dev Directly returns `a` with minimal copying.
function directReturn(bytes[] memory a) internal pure {
assembly {
let n := mload(a) // `a.length`.
let o := add(a, 0x20) // Start of elements in `a`.
let u := a // Highest memory slot.
let w := not(0x1f)
for { let i := 0 } iszero(eq(i, n)) { i := add(i, 1) } {
let c := add(o, shl(5, i)) // Location of pointer to `a[i]`.
let s := mload(c) // `a[i]`.
let l := mload(s) // `a[i].length`.
let r := and(l, 0x1f) // `a[i].length % 32`.
let z := add(0x20, and(l, w)) // Offset of last word in `a[i]` from `s`.
// If `s` comes before `o`, or `s` is not zero right padded.
if iszero(lt(lt(s, o), or(iszero(r), iszero(shl(shl(3, r), mload(add(s, z))))))) {
let m := mload(0x40)
mstore(m, l) // Copy `a[i].length`.
for {} 1 {} {
mstore(add(m, z), mload(add(s, z))) // Copy `a[i]`, backwards.
z := add(z, w) // `sub(z, 0x20)`.
if iszero(z) { break }
}
let e := add(add(m, 0x20), l)
mstore(e, 0) // Zeroize the slot after the copied bytes.
mstore(0x40, add(e, 0x20)) // Allocate memory.
s := m
}
mstore(c, sub(s, o)) // Convert to calldata offset.
let t := add(l, add(s, 0x20))
if iszero(lt(t, u)) { u := t }
}
let retStart := add(a, w) // Assumes `a` doesn't start from scratch space.
mstore(retStart, 0x20) // Store the return offset.
return(retStart, add(0x40, sub(u, retStart))) // End the transaction.
}
}
/// @dev Returns the word at `offset`, without any bounds checks.
/// To load an address, you can use `address(bytes20(load(a, offset)))`.
function load(bytes memory a, uint256 offset) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
result := mload(add(add(a, 0x20), offset))
}
}
/// @dev Returns the word at `offset`, without any bounds checks.
/// To load an address, you can use `address(bytes20(loadCalldata(a, offset)))`.
function loadCalldata(bytes calldata a, uint256 offset)
internal
pure
returns (bytes32 result)
{
/// @solidity memory-safe-assembly
assembly {
result := calldataload(add(a.offset, offset))
}
}
/// @dev Returns empty calldata bytes. For silencing the compiler.
function emptyCalldata() internal pure returns (bytes calldata result) {
/// @solidity memory-safe-assembly
assembly {
result.length := 0
}
}
}
MemoryMappings.sol 261 lines
// SPDX-License-Identifier: HVPL - HEXED VIRAL PUBLIC LICENSE
pragma solidity ^0.8.25;
import "./Allocate.sol";
library MemoryMappings {
struct MemoryMapping {
bool sorted; // more efficient read/write when NOT sorted
// note sorted only for uint256/bytes32 NOT for bytes key
bool overwrite;
uint256 totalKeys;
Tree tree;
}
struct Tree {
bool exists;
bytes32 sortingKey;
bytes32 key;
bytes32 value;
Tree[] children;
}
function newMemoryMapping(bool sorted, bool overwrite) internal pure returns (MemoryMapping memory) {
Tree memory empty;
return MemoryMapping({sorted: sorted, overwrite: overwrite, totalKeys: 0, tree: empty});
}
function add(MemoryMapping memory mm, bytes32 key, bytes memory value) internal pure {
bytes32 sortingKey = key;
if (!mm.sorted) {
assembly {
mstore(0x0, sortingKey)
sortingKey := keccak256(0x0, 0x20)
}
}
_add(mm, sortingKey, key, value);
}
// note that won't be sorted if keys are bytes
function add(MemoryMapping memory mm, bytes memory key, bytes memory value) internal pure {
bytes32 keyHash;
bytes32 keyPtr;
assembly {
keyHash := keccak256(add(0x20, key), mload(key))
keyPtr := key
}
_add(mm, keyHash, keyPtr, value);
}
function add(MemoryMapping memory mm, bytes32 key, bytes32 value) internal pure {
bytes32 sortingKey = key;
if (!mm.sorted) {
assembly {
mstore(0x0, sortingKey)
sortingKey := keccak256(0x0, 0x20)
}
}
if (mm.totalKeys < 1) {
// bootstrapping
++mm.totalKeys;
Tree[] memory children = new Tree[](2);
mm.tree = Tree(true, sortingKey, key, value, children);
return;
}
bool newValue = _add(mm.tree, mm.overwrite, sortingKey, key, value);
if (newValue) ++mm.totalKeys;
}
function _add(MemoryMapping memory mm, bytes32 sortingKey, bytes32 key, bytes memory value) private pure {
bytes32 valuePtr;
assembly {
valuePtr := value
}
if (mm.totalKeys < 1) {
// bootstrapping
++mm.totalKeys;
Tree[] memory children = new Tree[](2);
mm.tree = Tree(true, sortingKey, key, valuePtr, children);
return;
}
bool newValue = _add(mm.tree, mm.overwrite, sortingKey, key, valuePtr);
if (newValue) ++mm.totalKeys;
}
function get(MemoryMapping memory mm, bytes32 key) internal pure returns (bool ok, bytes32 value) {
bytes32 sortingKey = key;
if (!mm.sorted) {
assembly {
mstore(0x0, sortingKey)
sortingKey := keccak256(0x0, 0x20)
}
}
return _get(mm, sortingKey);
}
function get(MemoryMapping memory mm, bytes memory key) internal pure returns (bool ok, bytes32 value) {
bytes32 keyHash;
assembly {
keyHash := keccak256(add(0x20, key), mload(key))
}
return _get(mm, keyHash);
}
function _get(MemoryMapping memory mm, bytes32 sortingKey) private pure returns (bool ok, bytes32 value) {
return _get(mm.tree, sortingKey);
}
function dump(MemoryMapping memory mm) internal pure returns (bytes32[] memory keys, bytes32[] memory values) {
if (mm.totalKeys < 1) return (keys, values);
keys = _allocateBytes32Arr(mm.totalKeys);
values = _allocateBytes32Arr(mm.totalKeys);
assembly {
mstore(keys, 0)
mstore(values, 0)
}
_dump(mm.tree, keys, values);
}
function dumpKeys(MemoryMapping memory mm) internal pure returns (bytes32[] memory keys) {
if (mm.totalKeys < 1) return keys;
keys = _allocateBytes32Arr(mm.totalKeys);
assembly {
mstore(keys, 0)
}
_dumpKeys(mm.tree, keys);
}
function dumpValues(MemoryMapping memory mm) internal pure returns (bytes32[] memory values) {
if (mm.totalKeys < 1) return values;
values = _allocateBytes32Arr(mm.totalKeys);
assembly {
mstore(values, 0)
}
_dumpValues(mm.tree, values);
}
function _add(Tree memory tree, bool overwrite, bytes32 sortingKey, bytes32 key, bytes32 value)
private
pure
returns (bool newValue)
{
Tree memory _tree = tree;
while (true) {
if (_tree.sortingKey == sortingKey) {
newValue = !_tree.exists;
if (overwrite || newValue) {
_tree.exists = true;
_tree.value = value;
if (newValue) _tree.children = new Tree[](2);
return newValue;
}
return newValue;
}
if (sortingKey < _tree.sortingKey) {
_tree = _tree.children[0];
} else {
_tree = _tree.children[1];
}
if (!_tree.exists) {
_tree.sortingKey = sortingKey;
_tree.key = key;
}
}
}
function _get(Tree memory tree, bytes32 sortingKey) private pure returns (bool ok, bytes32 value) {
Tree memory _tree = tree;
while (true) {
if (_tree.sortingKey == sortingKey) {
ok = true;
value = _tree.value;
return (ok, value);
}
//bool isNewNode = _tree.children.length < 1;
if (_tree.children.length < 1) {
// ok = false;
return (ok, value);
}
if (sortingKey < _tree.sortingKey) {
_tree = _tree.children[0];
} else {
_tree = _tree.children[1];
}
}
}
function _dump(Tree memory tree, bytes32[] memory keys, bytes32[] memory values) private pure {
Tree memory other = tree.children[0];
if (other.exists) _dump(other, keys, values); // left
// center
// assembly does this:
//uint256 idx = keys.length;
//keys[idx] = tree.key;
//values[idx++] = tree.value;
assembly {
// assumes arrays come in allocated BUT have their length initialized to 0 so will know how many added
let idx := mload(keys)
mstore(keys, add(idx, 1))
mstore(values, add(idx, 1))
mstore(add(keys, add(0x20, mul(idx, 0x20))), mload(add(tree, 0x40)))
mstore(add(values, add(0x20, mul(idx, 0x20))), mload(add(tree, 0x60)))
}
other = tree.children[1];
if (other.exists) _dump(other, keys, values); // right
}
function _dumpKeys(Tree memory tree, bytes32[] memory keys) private pure {
Tree memory other = tree.children[0];
if (other.exists) _dumpKeys(other, keys); // left
// center
// assembly does this:
//uint256 idx = keys.length;
//keys[idx] = tree.key;
assembly {
// assumes arrays come in allocated BUT have their length initialized to 0 so will know how many added
let idx := mload(keys)
mstore(keys, add(idx, 1))
mstore(add(keys, add(0x20, mul(idx, 0x20))), mload(add(tree, 0x40)))
}
other = tree.children[1];
if (other.exists) _dumpKeys(other, keys); // right
}
function _dumpValues(Tree memory tree, bytes32[] memory values) private pure {
Tree memory other = tree.children[0];
if (other.exists) _dumpValues(other, values); // left
// center
// assembly does this:
//uint256 idx = values.length;
//values[idx++] = tree.value;
assembly {
// assumes arrays come in allocated BUT have their length initialized to 0 so will know how many added
let idx := mload(values)
mstore(values, add(idx, 1))
mstore(add(values, add(0x20, mul(idx, 0x20))), mload(add(tree, 0x60)))
}
other = tree.children[1];
if (other.exists) _dumpValues(other, values); // right
}
}
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";
OwnablePermissions.sol 6 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
abstract contract OwnablePermissions {
function _requireCallerIsContractOwner() internal view virtual;
}
ICreatorToken.sol 16 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
import "./ICreatorTokenTransferValidator.sol";
interface ICreatorToken {
event TransferValidatorUpdated(address oldValidator, address newValidator);
function getTransferValidator() external view returns (ICreatorTokenTransferValidator);
function getSecurityPolicy() external view returns (CollectionSecurityPolicy memory);
function getWhitelistedOperators() external view returns (address[] memory);
function getPermittedContractReceivers() external view returns (address[] memory);
function isOperatorWhitelisted(address operator) external view returns (bool);
function isContractReceiverPermitted(address receiver) external view returns (bool);
function isTransferAllowed(address caller, address from, address to) external view returns (bool);
}
ICreatorTokenTransferValidator.sol 8 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
import "./IEOARegistry.sol";
import "./ITransferSecurityRegistry.sol";
import "./ITransferValidator.sol";
interface ICreatorTokenTransferValidator is ITransferSecurityRegistry, ITransferValidator, IEOARegistry {}
TransferValidation.sol 69 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
import "lib/openzeppelin-contracts/contracts/utils/Context.sol";
/**
* @title TransferValidation
* @author Limit Break, Inc.
* @notice A mix-in that can be combined with ERC-721 contracts to provide more granular hooks.
* Openzeppelin's ERC721 contract only provides hooks for before and after transfer. This allows
* developers to validate or customize transfers within the context of a mint, a burn, or a transfer.
*/
abstract contract TransferValidation is Context {
error ShouldNotMintToBurnAddress();
/// @dev Inheriting contracts should call this function in the _beforeTokenTransfer function to get more granular hooks.
function _validateBeforeTransfer(address from, address to, uint256 tokenId) internal virtual {
bool fromZeroAddress = from == address(0);
bool toZeroAddress = to == address(0);
if (fromZeroAddress && toZeroAddress) {
revert ShouldNotMintToBurnAddress();
} else if (fromZeroAddress) {
_preValidateMint(_msgSender(), to, tokenId, msg.value);
} else if (toZeroAddress) {
_preValidateBurn(_msgSender(), from, tokenId, msg.value);
} else {
_preValidateTransfer(_msgSender(), from, to, tokenId, msg.value);
}
}
/// @dev Inheriting contracts should call this function in the _afterTokenTransfer function to get more granular hooks.
function _validateAfterTransfer(address from, address to, uint256 tokenId) internal virtual {
bool fromZeroAddress = from == address(0);
bool toZeroAddress = to == address(0);
if (fromZeroAddress && toZeroAddress) {
revert ShouldNotMintToBurnAddress();
} else if (fromZeroAddress) {
_postValidateMint(_msgSender(), to, tokenId, msg.value);
} else if (toZeroAddress) {
_postValidateBurn(_msgSender(), from, tokenId, msg.value);
} else {
_postValidateTransfer(_msgSender(), from, to, tokenId, msg.value);
}
}
/// @dev Optional validation hook that fires before a mint
function _preValidateMint(address caller, address to, uint256 tokenId, uint256 value) internal virtual {}
/// @dev Optional validation hook that fires after a mint
function _postValidateMint(address caller, address to, uint256 tokenId, uint256 value) internal virtual {}
/// @dev Optional validation hook that fires before a burn
function _preValidateBurn(address caller, address from, uint256 tokenId, uint256 value) internal virtual {}
/// @dev Optional validation hook that fires after a burn
function _postValidateBurn(address caller, address from, uint256 tokenId, uint256 value) internal virtual {}
/// @dev Optional validation hook that fires before a transfer
function _preValidateTransfer(address caller, address from, address to, uint256 tokenId, uint256 value)
internal
virtual {}
/// @dev Optional validation hook that fires after a transfer
function _postValidateTransfer(address caller, address from, address to, uint256 tokenId, uint256 value)
internal
virtual {}
}
IERC2981.sol 25 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (interfaces/IERC2981.sol)
pragma solidity ^0.8.0;
import "../utils/introspection/IERC165.sol";
/**
* @dev Interface for the NFT Royalty Standard.
*
* A standardized way to retrieve royalty payment information for non-fungible tokens (NFTs) to enable universal
* support for royalty payments across all NFT marketplaces and ecosystem participants.
*
* _Available since v4.5._
*/
interface IERC2981 is IERC165 {
/**
* @dev Returns how much royalty is owed and to whom, based on a sale price that may be denominated in any unit of
* exchange. The royalty amount is denominated and should be paid in that same unit of exchange.
*/
function royaltyInfo(
uint256 tokenId,
uint256 salePrice
) external view returns (address receiver, uint256 royaltyAmount);
}
ERC165.sol 29 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)
pragma solidity ^0.8.0;
import "./IERC165.sol";
/**
* @dev Implementation of the {IERC165} interface.
*
* Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
* for the additional interface id that will be supported. For example:
*
* ```solidity
* function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
* return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
* }
* ```
*
* Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
*/
abstract contract ERC165 is IERC165 {
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IERC165).interfaceId;
}
}
LibString.sol 965 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
import {LibBytes} from "./LibBytes.sol";
/// @notice Library for converting numbers into strings and other string operations.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibString.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/LibString.sol)
///
/// @dev Note:
/// For performance and bytecode compactness, most of the string operations are restricted to
/// byte strings (7-bit ASCII), except where otherwise specified.
/// Usage of byte string operations on charsets with runes spanning two or more bytes
/// can lead to undefined behavior.
library LibString {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* STRUCTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Goated string storage struct that totally MOGs, no cap, fr.
/// Uses less gas and bytecode than Solidity's native string storage. It's meta af.
/// Packs length with the first 31 bytes if <255 bytes, so it’s mad tight.
struct StringStorage {
bytes32 _spacer;
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The length of the output is too small to contain all the hex digits.
error HexLengthInsufficient();
/// @dev The length of the string is more than 32 bytes.
error TooBigForSmallString();
/// @dev The input string must be a 7-bit ASCII.
error StringNot7BitASCII();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CONSTANTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The constant returned when the `search` is not found in the string.
uint256 internal constant NOT_FOUND = type(uint256).max;
/// @dev Lookup for '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'.
uint128 internal constant ALPHANUMERIC_7_BIT_ASCII = 0x7fffffe07fffffe03ff000000000000;
/// @dev Lookup for 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'.
uint128 internal constant LETTERS_7_BIT_ASCII = 0x7fffffe07fffffe0000000000000000;
/// @dev Lookup for 'abcdefghijklmnopqrstuvwxyz'.
uint128 internal constant LOWERCASE_7_BIT_ASCII = 0x7fffffe000000000000000000000000;
/// @dev Lookup for 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.
uint128 internal constant UPPERCASE_7_BIT_ASCII = 0x7fffffe0000000000000000;
/// @dev Lookup for '0123456789'.
uint128 internal constant DIGITS_7_BIT_ASCII = 0x3ff000000000000;
/// @dev Lookup for '0123456789abcdefABCDEF'.
uint128 internal constant HEXDIGITS_7_BIT_ASCII = 0x7e0000007e03ff000000000000;
/// @dev Lookup for '01234567'.
uint128 internal constant OCTDIGITS_7_BIT_ASCII = 0xff000000000000;
/// @dev Lookup for '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~ \t\n\r\x0b\x0c'.
uint128 internal constant PRINTABLE_7_BIT_ASCII = 0x7fffffffffffffffffffffff00003e00;
/// @dev Lookup for '!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~'.
uint128 internal constant PUNCTUATION_7_BIT_ASCII = 0x78000001f8000001fc00fffe00000000;
/// @dev Lookup for ' \t\n\r\x0b\x0c'.
uint128 internal constant WHITESPACE_7_BIT_ASCII = 0x100003e00;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* STRING STORAGE OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Sets the value of the string storage `$` to `s`.
function set(StringStorage storage $, string memory s) internal {
LibBytes.set(bytesStorage($), bytes(s));
}
/// @dev Sets the value of the string storage `$` to `s`.
function setCalldata(StringStorage storage $, string calldata s) internal {
LibBytes.setCalldata(bytesStorage($), bytes(s));
}
/// @dev Sets the value of the string storage `$` to the empty string.
function clear(StringStorage storage $) internal {
delete $._spacer;
}
/// @dev Returns whether the value stored is `$` is the empty string "".
function isEmpty(StringStorage storage $) internal view returns (bool) {
return uint256($._spacer) & 0xff == uint256(0);
}
/// @dev Returns the length of the value stored in `$`.
function length(StringStorage storage $) internal view returns (uint256) {
return LibBytes.length(bytesStorage($));
}
/// @dev Returns the value stored in `$`.
function get(StringStorage storage $) internal view returns (string memory) {
return string(LibBytes.get(bytesStorage($)));
}
/// @dev Helper to cast `$` to a `BytesStorage`.
function bytesStorage(StringStorage storage $)
internal
pure
returns (LibBytes.BytesStorage storage casted)
{
/// @solidity memory-safe-assembly
assembly {
casted.slot := $.slot
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* DECIMAL OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the base 10 decimal representation of `value`.
function toString(uint256 value) internal pure returns (string memory result) {
/// @solidity memory-safe-assembly
assembly {
// The maximum value of a uint256 contains 78 digits (1 byte per digit), but
// we allocate 0xa0 bytes to keep the free memory pointer 32-byte word aligned.
// We will need 1 word for the trailing zeros padding, 1 word for the length,
// and 3 words for a maximum of 78 digits.
result := add(mload(0x40), 0x80)
mstore(0x40, add(result, 0x20)) // Allocate memory.
mstore(result, 0) // Zeroize the slot after the string.
let end := result // Cache the end of the memory to calculate the length later.
let w := not(0) // Tsk.
// We write the string from rightmost digit to leftmost digit.
// The following is essentially a do-while loop that also handles the zero case.
for { let temp := value } 1 {} {
result := add(result, w) // `sub(result, 1)`.
// Store the character to the pointer.
// The ASCII index of the '0' character is 48.
mstore8(result, add(48, mod(temp, 10)))
temp := div(temp, 10) // Keep dividing `temp` until zero.
if iszero(temp) { break }
}
let n := sub(end, result)
result := sub(result, 0x20) // Move the pointer 32 bytes back to make room for the length.
mstore(result, n) // Store the length.
}
}
/// @dev Returns the base 10 decimal representation of `value`.
function toString(int256 value) internal pure returns (string memory result) {
if (value >= 0) return toString(uint256(value));
unchecked {
result = toString(~uint256(value) + 1);
}
/// @solidity memory-safe-assembly
assembly {
// We still have some spare memory space on the left,
// as we have allocated 3 words (96 bytes) for up to 78 digits.
let n := mload(result) // Load the string length.
mstore(result, 0x2d) // Store the '-' character.
result := sub(result, 1) // Move back the string pointer by a byte.
mstore(result, add(n, 1)) // Update the string length.
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* HEXADECIMAL OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the hexadecimal representation of `value`,
/// left-padded to an input length of `byteCount` bytes.
/// The output is prefixed with "0x" encoded using 2 hexadecimal digits per byte,
/// giving a total length of `byteCount * 2 + 2` bytes.
/// Reverts if `byteCount` is too small for the output to contain all the digits.
function toHexString(uint256 value, uint256 byteCount)
internal
pure
returns (string memory result)
{
result = toHexStringNoPrefix(value, byteCount);
/// @solidity memory-safe-assembly
assembly {
let n := add(mload(result), 2) // Compute the length.
mstore(result, 0x3078) // Store the "0x" prefix.
result := sub(result, 2) // Move the pointer.
mstore(result, n) // Store the length.
}
}
/// @dev Returns the hexadecimal representation of `value`,
/// left-padded to an input length of `byteCount` bytes.
/// The output is not prefixed with "0x" and is encoded using 2 hexadecimal digits per byte,
/// giving a total length of `byteCount * 2` bytes.
/// Reverts if `byteCount` is too small for the output to contain all the digits.
function toHexStringNoPrefix(uint256 value, uint256 byteCount)
internal
pure
returns (string memory result)
{
/// @solidity memory-safe-assembly
assembly {
// We need 0x20 bytes for the trailing zeros padding, `byteCount * 2` bytes
// for the digits, 0x02 bytes for the prefix, and 0x20 bytes for the length.
// We add 0x20 to the total and round down to a multiple of 0x20.
// (0x20 + 0x20 + 0x02 + 0x20) = 0x62.
result := add(mload(0x40), and(add(shl(1, byteCount), 0x42), not(0x1f)))
mstore(0x40, add(result, 0x20)) // Allocate memory.
mstore(result, 0) // Zeroize the slot after the string.
let end := result // Cache the end to calculate the length later.
// Store "0123456789abcdef" in scratch space.
mstore(0x0f, 0x30313233343536373839616263646566)
let start := sub(result, add(byteCount, byteCount))
let w := not(1) // Tsk.
let temp := value
// We write the string from rightmost digit to leftmost digit.
// The following is essentially a do-while loop that also handles the zero case.
for {} 1 {} {
result := add(result, w) // `sub(result, 2)`.
mstore8(add(result, 1), mload(and(temp, 15)))
mstore8(result, mload(and(shr(4, temp), 15)))
temp := shr(8, temp)
if iszero(xor(result, start)) { break }
}
if temp {
mstore(0x00, 0x2194895a) // `HexLengthInsufficient()`.
revert(0x1c, 0x04)
}
let n := sub(end, result)
result := sub(result, 0x20)
mstore(result, n) // Store the length.
}
}
/// @dev Returns the hexadecimal representation of `value`.
/// The output is prefixed with "0x" and encoded using 2 hexadecimal digits per byte.
/// As address are 20 bytes long, the output will left-padded to have
/// a length of `20 * 2 + 2` bytes.
function toHexString(uint256 value) internal pure returns (string memory result) {
result = toHexStringNoPrefix(value);
/// @solidity memory-safe-assembly
assembly {
let n := add(mload(result), 2) // Compute the length.
mstore(result, 0x3078) // Store the "0x" prefix.
result := sub(result, 2) // Move the pointer.
mstore(result, n) // Store the length.
}
}
/// @dev Returns the hexadecimal representation of `value`.
/// The output is prefixed with "0x".
/// The output excludes leading "0" from the `toHexString` output.
/// `0x00: "0x0", 0x01: "0x1", 0x12: "0x12", 0x123: "0x123"`.
function toMinimalHexString(uint256 value) internal pure returns (string memory result) {
result = toHexStringNoPrefix(value);
/// @solidity memory-safe-assembly
assembly {
let o := eq(byte(0, mload(add(result, 0x20))), 0x30) // Whether leading zero is present.
let n := add(mload(result), 2) // Compute the length.
mstore(add(result, o), 0x3078) // Store the "0x" prefix, accounting for leading zero.
result := sub(add(result, o), 2) // Move the pointer, accounting for leading zero.
mstore(result, sub(n, o)) // Store the length, accounting for leading zero.
}
}
/// @dev Returns the hexadecimal representation of `value`.
/// The output excludes leading "0" from the `toHexStringNoPrefix` output.
/// `0x00: "0", 0x01: "1", 0x12: "12", 0x123: "123"`.
function toMinimalHexStringNoPrefix(uint256 value)
internal
pure
returns (string memory result)
{
result = toHexStringNoPrefix(value);
/// @solidity memory-safe-assembly
assembly {
let o := eq(byte(0, mload(add(result, 0x20))), 0x30) // Whether leading zero is present.
let n := mload(result) // Get the length.
result := add(result, o) // Move the pointer, accounting for leading zero.
mstore(result, sub(n, o)) // Store the length, accounting for leading zero.
}
}
/// @dev Returns the hexadecimal representation of `value`.
/// The output is encoded using 2 hexadecimal digits per byte.
/// As address are 20 bytes long, the output will left-padded to have
/// a length of `20 * 2` bytes.
function toHexStringNoPrefix(uint256 value) internal pure returns (string memory result) {
/// @solidity memory-safe-assembly
assembly {
// We need 0x20 bytes for the trailing zeros padding, 0x20 bytes for the length,
// 0x02 bytes for the prefix, and 0x40 bytes for the digits.
// The next multiple of 0x20 above (0x20 + 0x20 + 0x02 + 0x40) is 0xa0.
result := add(mload(0x40), 0x80)
mstore(0x40, add(result, 0x20)) // Allocate memory.
mstore(result, 0) // Zeroize the slot after the string.
let end := result // Cache the end to calculate the length later.
mstore(0x0f, 0x30313233343536373839616263646566) // Store the "0123456789abcdef" lookup.
let w := not(1) // Tsk.
// We write the string from rightmost digit to leftmost digit.
// The following is essentially a do-while loop that also handles the zero case.
for { let temp := value } 1 {} {
result := add(result, w) // `sub(result, 2)`.
mstore8(add(result, 1), mload(and(temp, 15)))
mstore8(result, mload(and(shr(4, temp), 15)))
temp := shr(8, temp)
if iszero(temp) { break }
}
let n := sub(end, result)
result := sub(result, 0x20)
mstore(result, n) // Store the length.
}
}
/// @dev Returns the hexadecimal representation of `value`.
/// The output is prefixed with "0x", encoded using 2 hexadecimal digits per byte,
/// and the alphabets are capitalized conditionally according to
/// https://eips.ethereum.org/EIPS/eip-55
function toHexStringChecksummed(address value) internal pure returns (string memory result) {
result = toHexString(value);
/// @solidity memory-safe-assembly
assembly {
let mask := shl(6, div(not(0), 255)) // `0b010000000100000000 ...`
let o := add(result, 0x22)
let hashed := and(keccak256(o, 40), mul(34, mask)) // `0b10001000 ... `
let t := shl(240, 136) // `0b10001000 << 240`
for { let i := 0 } 1 {} {
mstore(add(i, i), mul(t, byte(i, hashed)))
i := add(i, 1)
if eq(i, 20) { break }
}
mstore(o, xor(mload(o), shr(1, and(mload(0x00), and(mload(o), mask)))))
o := add(o, 0x20)
mstore(o, xor(mload(o), shr(1, and(mload(0x20), and(mload(o), mask)))))
}
}
/// @dev Returns the hexadecimal representation of `value`.
/// The output is prefixed with "0x" and encoded using 2 hexadecimal digits per byte.
function toHexString(address value) internal pure returns (string memory result) {
result = toHexStringNoPrefix(value);
/// @solidity memory-safe-assembly
assembly {
let n := add(mload(result), 2) // Compute the length.
mstore(result, 0x3078) // Store the "0x" prefix.
result := sub(result, 2) // Move the pointer.
mstore(result, n) // Store the length.
}
}
/// @dev Returns the hexadecimal representation of `value`.
/// The output is encoded using 2 hexadecimal digits per byte.
function toHexStringNoPrefix(address value) internal pure returns (string memory result) {
/// @solidity memory-safe-assembly
assembly {
result := mload(0x40)
// Allocate memory.
// We need 0x20 bytes for the trailing zeros padding, 0x20 bytes for the length,
// 0x02 bytes for the prefix, and 0x28 bytes for the digits.
// The next multiple of 0x20 above (0x20 + 0x20 + 0x02 + 0x28) is 0x80.
mstore(0x40, add(result, 0x80))
mstore(0x0f, 0x30313233343536373839616263646566) // Store the "0123456789abcdef" lookup.
result := add(result, 2)
mstore(result, 40) // Store the length.
let o := add(result, 0x20)
mstore(add(o, 40), 0) // Zeroize the slot after the string.
value := shl(96, value)
// We write the string from rightmost digit to leftmost digit.
// The following is essentially a do-while loop that also handles the zero case.
for { let i := 0 } 1 {} {
let p := add(o, add(i, i))
let temp := byte(i, value)
mstore8(add(p, 1), mload(and(temp, 15)))
mstore8(p, mload(shr(4, temp)))
i := add(i, 1)
if eq(i, 20) { break }
}
}
}
/// @dev Returns the hex encoded string from the raw bytes.
/// The output is encoded using 2 hexadecimal digits per byte.
function toHexString(bytes memory raw) internal pure returns (string memory result) {
result = toHexStringNoPrefix(raw);
/// @solidity memory-safe-assembly
assembly {
let n := add(mload(result), 2) // Compute the length.
mstore(result, 0x3078) // Store the "0x" prefix.
result := sub(result, 2) // Move the pointer.
mstore(result, n) // Store the length.
}
}
/// @dev Returns the hex encoded string from the raw bytes.
/// The output is encoded using 2 hexadecimal digits per byte.
function toHexStringNoPrefix(bytes memory raw) internal pure returns (string memory result) {
/// @solidity memory-safe-assembly
assembly {
let n := mload(raw)
result := add(mload(0x40), 2) // Skip 2 bytes for the optional prefix.
mstore(result, add(n, n)) // Store the length of the output.
mstore(0x0f, 0x30313233343536373839616263646566) // Store the "0123456789abcdef" lookup.
let o := add(result, 0x20)
let end := add(raw, n)
for {} iszero(eq(raw, end)) {} {
raw := add(raw, 1)
mstore8(add(o, 1), mload(and(mload(raw), 15)))
mstore8(o, mload(and(shr(4, mload(raw)), 15)))
o := add(o, 2)
}
mstore(o, 0) // Zeroize the slot after the string.
mstore(0x40, add(o, 0x20)) // Allocate memory.
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* RUNE STRING OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the number of UTF characters in the string.
function runeCount(string memory s) internal pure returns (uint256 result) {
/// @solidity memory-safe-assembly
assembly {
if mload(s) {
mstore(0x00, div(not(0), 255))
mstore(0x20, 0x0202020202020202020202020202020202020202020202020303030304040506)
let o := add(s, 0x20)
let end := add(o, mload(s))
for { result := 1 } 1 { result := add(result, 1) } {
o := add(o, byte(0, mload(shr(250, mload(o)))))
if iszero(lt(o, end)) { break }
}
}
}
}
/// @dev Returns if this string is a 7-bit ASCII string.
/// (i.e. all characters codes are in [0..127])
function is7BitASCII(string memory s) internal pure returns (bool result) {
/// @solidity memory-safe-assembly
assembly {
result := 1
let mask := shl(7, div(not(0), 255))
let n := mload(s)
if n {
let o := add(s, 0x20)
let end := add(o, n)
let last := mload(end)
mstore(end, 0)
for {} 1 {} {
if and(mask, mload(o)) {
result := 0
break
}
o := add(o, 0x20)
if iszero(lt(o, end)) { break }
}
mstore(end, last)
}
}
}
/// @dev Returns if this string is a 7-bit ASCII string,
/// AND all characters are in the `allowed` lookup.
/// Note: If `s` is empty, returns true regardless of `allowed`.
function is7BitASCII(string memory s, uint128 allowed) internal pure returns (bool result) {
/// @solidity memory-safe-assembly
assembly {
result := 1
if mload(s) {
let allowed_ := shr(128, shl(128, allowed))
let o := add(s, 0x20)
for { let end := add(o, mload(s)) } 1 {} {
result := and(result, shr(byte(0, mload(o)), allowed_))
o := add(o, 1)
if iszero(and(result, lt(o, end))) { break }
}
}
}
}
/// @dev Converts the bytes in the 7-bit ASCII string `s` to
/// an allowed lookup for use in `is7BitASCII(s, allowed)`.
/// To save runtime gas, you can cache the result in an immutable variable.
function to7BitASCIIAllowedLookup(string memory s) internal pure returns (uint128 result) {
/// @solidity memory-safe-assembly
assembly {
if mload(s) {
let o := add(s, 0x20)
for { let end := add(o, mload(s)) } 1 {} {
result := or(result, shl(byte(0, mload(o)), 1))
o := add(o, 1)
if iszero(lt(o, end)) { break }
}
if shr(128, result) {
mstore(0x00, 0xc9807e0d) // `StringNot7BitASCII()`.
revert(0x1c, 0x04)
}
}
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* BYTE STRING OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
// For performance and bytecode compactness, byte string operations are restricted
// to 7-bit ASCII strings. All offsets are byte offsets, not UTF character offsets.
// Usage of byte string operations on charsets with runes spanning two or more bytes
// can lead to undefined behavior.
/// @dev Returns `subject` all occurrences of `needle` replaced with `replacement`.
function replace(string memory subject, string memory needle, string memory replacement)
internal
pure
returns (string memory)
{
return string(LibBytes.replace(bytes(subject), bytes(needle), bytes(replacement)));
}
/// @dev Returns the byte index of the first location of `needle` in `subject`,
/// needleing from left to right, starting from `from`.
/// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `needle` is not found.
function indexOf(string memory subject, string memory needle, uint256 from)
internal
pure
returns (uint256)
{
return LibBytes.indexOf(bytes(subject), bytes(needle), from);
}
/// @dev Returns the byte index of the first location of `needle` in `subject`,
/// needleing from left to right.
/// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `needle` is not found.
function indexOf(string memory subject, string memory needle) internal pure returns (uint256) {
return LibBytes.indexOf(bytes(subject), bytes(needle), 0);
}
/// @dev Returns the byte index of the first location of `needle` in `subject`,
/// needleing from right to left, starting from `from`.
/// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `needle` is not found.
function lastIndexOf(string memory subject, string memory needle, uint256 from)
internal
pure
returns (uint256)
{
return LibBytes.lastIndexOf(bytes(subject), bytes(needle), from);
}
/// @dev Returns the byte index of the first location of `needle` in `subject`,
/// needleing from right to left.
/// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `needle` is not found.
function lastIndexOf(string memory subject, string memory needle)
internal
pure
returns (uint256)
{
return LibBytes.lastIndexOf(bytes(subject), bytes(needle), type(uint256).max);
}
/// @dev Returns true if `needle` is found in `subject`, false otherwise.
function contains(string memory subject, string memory needle) internal pure returns (bool) {
return LibBytes.contains(bytes(subject), bytes(needle));
}
/// @dev Returns whether `subject` starts with `needle`.
function startsWith(string memory subject, string memory needle) internal pure returns (bool) {
return LibBytes.startsWith(bytes(subject), bytes(needle));
}
/// @dev Returns whether `subject` ends with `needle`.
function endsWith(string memory subject, string memory needle) internal pure returns (bool) {
return LibBytes.endsWith(bytes(subject), bytes(needle));
}
/// @dev Returns `subject` repeated `times`.
function repeat(string memory subject, uint256 times) internal pure returns (string memory) {
return string(LibBytes.repeat(bytes(subject), times));
}
/// @dev Returns a copy of `subject` sliced from `start` to `end` (exclusive).
/// `start` and `end` are byte offsets.
function slice(string memory subject, uint256 start, uint256 end)
internal
pure
returns (string memory)
{
return string(LibBytes.slice(bytes(subject), start, end));
}
/// @dev Returns a copy of `subject` sliced from `start` to the end of the string.
/// `start` is a byte offset.
function slice(string memory subject, uint256 start) internal pure returns (string memory) {
return string(LibBytes.slice(bytes(subject), start, type(uint256).max));
}
/// @dev Returns all the indices of `needle` in `subject`.
/// The indices are byte offsets.
function indicesOf(string memory subject, string memory needle)
internal
pure
returns (uint256[] memory)
{
return LibBytes.indicesOf(bytes(subject), bytes(needle));
}
/// @dev Returns a arrays of strings based on the `delimiter` inside of the `subject` string.
function split(string memory subject, string memory delimiter)
internal
pure
returns (string[] memory result)
{
bytes[] memory a = LibBytes.split(bytes(subject), bytes(delimiter));
/// @solidity memory-safe-assembly
assembly {
result := a
}
}
/// @dev Returns a concatenated string of `a` and `b`.
/// Cheaper than `string.concat()` and does not de-align the free memory pointer.
function concat(string memory a, string memory b) internal pure returns (string memory) {
return string(LibBytes.concat(bytes(a), bytes(b)));
}
/// @dev Returns a copy of the string in either lowercase or UPPERCASE.
/// WARNING! This function is only compatible with 7-bit ASCII strings.
function toCase(string memory subject, bool toUpper)
internal
pure
returns (string memory result)
{
/// @solidity memory-safe-assembly
assembly {
let n := mload(subject)
if n {
result := mload(0x40)
let o := add(result, 0x20)
let d := sub(subject, result)
let flags := shl(add(70, shl(5, toUpper)), 0x3ffffff)
for { let end := add(o, n) } 1 {} {
let b := byte(0, mload(add(d, o)))
mstore8(o, xor(and(shr(b, flags), 0x20), b))
o := add(o, 1)
if eq(o, end) { break }
}
mstore(result, n) // Store the length.
mstore(o, 0) // Zeroize the slot after the string.
mstore(0x40, add(o, 0x20)) // Allocate memory.
}
}
}
/// @dev Returns a string from a small bytes32 string.
/// `s` must be null-terminated, or behavior will be undefined.
function fromSmallString(bytes32 s) internal pure returns (string memory result) {
/// @solidity memory-safe-assembly
assembly {
result := mload(0x40)
let n := 0
for {} byte(n, s) { n := add(n, 1) } {} // Scan for '\0'.
mstore(result, n) // Store the length.
let o := add(result, 0x20)
mstore(o, s) // Store the bytes of the string.
mstore(add(o, n), 0) // Zeroize the slot after the string.
mstore(0x40, add(result, 0x40)) // Allocate memory.
}
}
/// @dev Returns the small string, with all bytes after the first null byte zeroized.
function normalizeSmallString(bytes32 s) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
for {} byte(result, s) { result := add(result, 1) } {} // Scan for '\0'.
mstore(0x00, s)
mstore(result, 0x00)
result := mload(0x00)
}
}
/// @dev Returns the string as a normalized null-terminated small string.
function toSmallString(string memory s) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
result := mload(s)
if iszero(lt(result, 33)) {
mstore(0x00, 0xec92f9a3) // `TooBigForSmallString()`.
revert(0x1c, 0x04)
}
result := shl(shl(3, sub(32, result)), mload(add(s, result)))
}
}
/// @dev Returns a lowercased copy of the string.
/// WARNING! This function is only compatible with 7-bit ASCII strings.
function lower(string memory subject) internal pure returns (string memory result) {
result = toCase(subject, false);
}
/// @dev Returns an UPPERCASED copy of the string.
/// WARNING! This function is only compatible with 7-bit ASCII strings.
function upper(string memory subject) internal pure returns (string memory result) {
result = toCase(subject, true);
}
/// @dev Escapes the string to be used within HTML tags.
function escapeHTML(string memory s) internal pure returns (string memory result) {
/// @solidity memory-safe-assembly
assembly {
result := mload(0x40)
let end := add(s, mload(s))
let o := add(result, 0x20)
// Store the bytes of the packed offsets and strides into the scratch space.
// `packed = (stride << 5) | offset`. Max offset is 20. Max stride is 6.
mstore(0x1f, 0x900094)
mstore(0x08, 0xc0000000a6ab)
// Store ""&'<>" into the scratch space.
mstore(0x00, shl(64, 0x2671756f743b26616d703b262333393b266c743b2667743b))
for {} iszero(eq(s, end)) {} {
s := add(s, 1)
let c := and(mload(s), 0xff)
// Not in `["\"","'","&","<",">"]`.
if iszero(and(shl(c, 1), 0x500000c400000000)) {
mstore8(o, c)
o := add(o, 1)
continue
}
let t := shr(248, mload(c))
mstore(o, mload(and(t, 0x1f)))
o := add(o, shr(5, t))
}
mstore(o, 0) // Zeroize the slot after the string.
mstore(result, sub(o, add(result, 0x20))) // Store the length.
mstore(0x40, add(o, 0x20)) // Allocate memory.
}
}
/// @dev Escapes the string to be used within double-quotes in a JSON.
/// If `addDoubleQuotes` is true, the result will be enclosed in double-quotes.
function escapeJSON(string memory s, bool addDoubleQuotes)
internal
pure
returns (string memory result)
{
/// @solidity memory-safe-assembly
assembly {
result := mload(0x40)
let o := add(result, 0x20)
if addDoubleQuotes {
mstore8(o, 34)
o := add(1, o)
}
// Store "\\u0000" in scratch space.
// Store "0123456789abcdef" in scratch space.
// Also, store `{0x08:"b", 0x09:"t", 0x0a:"n", 0x0c:"f", 0x0d:"r"}`.
// into the scratch space.
mstore(0x15, 0x5c75303030303031323334353637383961626364656662746e006672)
// Bitmask for detecting `["\"","\\"]`.
let e := or(shl(0x22, 1), shl(0x5c, 1))
for { let end := add(s, mload(s)) } iszero(eq(s, end)) {} {
s := add(s, 1)
let c := and(mload(s), 0xff)
if iszero(lt(c, 0x20)) {
if iszero(and(shl(c, 1), e)) {
// Not in `["\"","\\"]`.
mstore8(o, c)
o := add(o, 1)
continue
}
mstore8(o, 0x5c) // "\\".
mstore8(add(o, 1), c)
o := add(o, 2)
continue
}
if iszero(and(shl(c, 1), 0x3700)) {
// Not in `["\b","\t","\n","\f","\d"]`.
mstore8(0x1d, mload(shr(4, c))) // Hex value.
mstore8(0x1e, mload(and(c, 15))) // Hex value.
mstore(o, mload(0x19)) // "\\u00XX".
o := add(o, 6)
continue
}
mstore8(o, 0x5c) // "\\".
mstore8(add(o, 1), mload(add(c, 8)))
o := add(o, 2)
}
if addDoubleQuotes {
mstore8(o, 34)
o := add(1, o)
}
mstore(o, 0) // Zeroize the slot after the string.
mstore(result, sub(o, add(result, 0x20))) // Store the length.
mstore(0x40, add(o, 0x20)) // Allocate memory.
}
}
/// @dev Escapes the string to be used within double-quotes in a JSON.
function escapeJSON(string memory s) internal pure returns (string memory result) {
result = escapeJSON(s, false);
}
/// @dev Encodes `s` so that it can be safely used in a URI,
/// just like `encodeURIComponent` in JavaScript.
/// See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent
/// See: https://datatracker.ietf.org/doc/html/rfc2396
/// See: https://datatracker.ietf.org/doc/html/rfc3986
function encodeURIComponent(string memory s) internal pure returns (string memory result) {
/// @solidity memory-safe-assembly
assembly {
result := mload(0x40)
// Store "0123456789ABCDEF" in scratch space.
// Uppercased to be consistent with JavaScript's implementation.
mstore(0x0f, 0x30313233343536373839414243444546)
let o := add(result, 0x20)
for { let end := add(s, mload(s)) } iszero(eq(s, end)) {} {
s := add(s, 1)
let c := and(mload(s), 0xff)
// If not in `[0-9A-Z-a-z-_.!~*'()]`.
if iszero(and(1, shr(c, 0x47fffffe87fffffe03ff678200000000))) {
mstore8(o, 0x25) // '%'.
mstore8(add(o, 1), mload(and(shr(4, c), 15)))
mstore8(add(o, 2), mload(and(c, 15)))
o := add(o, 3)
continue
}
mstore8(o, c)
o := add(o, 1)
}
mstore(result, sub(o, add(result, 0x20))) // Store the length.
mstore(o, 0) // Zeroize the slot after the string.
mstore(0x40, add(o, 0x20)) // Allocate memory.
}
}
/// @dev Returns whether `a` equals `b`.
function eq(string memory a, string memory b) internal pure returns (bool result) {
/// @solidity memory-safe-assembly
assembly {
result := eq(keccak256(add(a, 0x20), mload(a)), keccak256(add(b, 0x20), mload(b)))
}
}
/// @dev Returns whether `a` equals `b`, where `b` is a null-terminated small string.
function eqs(string memory a, bytes32 b) internal pure returns (bool result) {
/// @solidity memory-safe-assembly
assembly {
// These should be evaluated on compile time, as far as possible.
let m := not(shl(7, div(not(iszero(b)), 255))) // `0x7f7f ...`.
let x := not(or(m, or(b, add(m, and(b, m)))))
let r := shl(7, iszero(iszero(shr(128, x))))
r := or(r, shl(6, iszero(iszero(shr(64, shr(r, x))))))
r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
r := or(r, shl(4, lt(0xffff, shr(r, x))))
r := or(r, shl(3, lt(0xff, shr(r, x))))
// forgefmt: disable-next-item
result := gt(eq(mload(a), add(iszero(x), xor(31, shr(3, r)))),
xor(shr(add(8, r), b), shr(add(8, r), mload(add(a, 0x20)))))
}
}
/// @dev Packs a single string with its length into a single word.
/// Returns `bytes32(0)` if the length is zero or greater than 31.
function packOne(string memory a) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
// We don't need to zero right pad the string,
// since this is our own custom non-standard packing scheme.
result :=
mul(
// Load the length and the bytes.
mload(add(a, 0x1f)),
// `length != 0 && length < 32`. Abuses underflow.
// Assumes that the length is valid and within the block gas limit.
lt(sub(mload(a), 1), 0x1f)
)
}
}
/// @dev Unpacks a string packed using {packOne}.
/// Returns the empty string if `packed` is `bytes32(0)`.
/// If `packed` is not an output of {packOne}, the output behavior is undefined.
function unpackOne(bytes32 packed) internal pure returns (string memory result) {
/// @solidity memory-safe-assembly
assembly {
result := mload(0x40) // Grab the free memory pointer.
mstore(0x40, add(result, 0x40)) // Allocate 2 words (1 for the length, 1 for the bytes).
mstore(result, 0) // Zeroize the length slot.
mstore(add(result, 0x1f), packed) // Store the length and bytes.
mstore(add(add(result, 0x20), mload(result)), 0) // Right pad with zeroes.
}
}
/// @dev Packs two strings with their lengths into a single word.
/// Returns `bytes32(0)` if combined length is zero or greater than 30.
function packTwo(string memory a, string memory b) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
let aLen := mload(a)
// We don't need to zero right pad the strings,
// since this is our own custom non-standard packing scheme.
result :=
mul(
or( // Load the length and the bytes of `a` and `b`.
shl(shl(3, sub(0x1f, aLen)), mload(add(a, aLen))), mload(sub(add(b, 0x1e), aLen))),
// `totalLen != 0 && totalLen < 31`. Abuses underflow.
// Assumes that the lengths are valid and within the block gas limit.
lt(sub(add(aLen, mload(b)), 1), 0x1e)
)
}
}
/// @dev Unpacks strings packed using {packTwo}.
/// Returns the empty strings if `packed` is `bytes32(0)`.
/// If `packed` is not an output of {packTwo}, the output behavior is undefined.
function unpackTwo(bytes32 packed)
internal
pure
returns (string memory resultA, string memory resultB)
{
/// @solidity memory-safe-assembly
assembly {
resultA := mload(0x40) // Grab the free memory pointer.
resultB := add(resultA, 0x40)
// Allocate 2 words for each string (1 for the length, 1 for the byte). Total 4 words.
mstore(0x40, add(resultB, 0x40))
// Zeroize the length slots.
mstore(resultA, 0)
mstore(resultB, 0)
// Store the lengths and bytes.
mstore(add(resultA, 0x1f), packed)
mstore(add(resultB, 0x1f), mload(add(add(resultA, 0x20), mload(resultA))))
// Right pad with zeroes.
mstore(add(add(resultA, 0x20), mload(resultA)), 0)
mstore(add(add(resultB, 0x20), mload(resultB)), 0)
}
}
/// @dev Directly returns `a` without copying.
function directReturn(string memory a) internal pure {
assembly {
// Assumes that the string does not start from the scratch space.
let retStart := sub(a, 0x20)
let retUnpaddedSize := add(mload(a), 0x40)
// Right pad with zeroes. Just in case the string is produced
// by a method that doesn't zero right pad.
mstore(add(retStart, retUnpaddedSize), 0)
mstore(retStart, 0x20) // Store the return offset.
// End the transaction, returning the string.
return(retStart, and(not(0x1f), add(0x1f, retUnpaddedSize)))
}
}
}
IERC173.sol 7 lines
// SPDX-License-Identifier: HVPL - HEXED VIRAL PUBLIC LICENSE
pragma solidity ^0.8.25;
interface IERC173 {
function owner() external view returns (address);
function transferOwnership(address _newOwner) external payable;
}
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);
}
Allocate.sol 90 lines
// SPDX-License-Identifier: HVPL - HEXED VIRAL PUBLIC LICENSE
pragma solidity ^0.8.25;
import "./LICENSE.sol";
function _allocateBytes(uint256 len) pure returns (bytes memory ret) {
assembly {
ret := mload(0x40)
// new "memory end" including padding
mstore(0x40, add(ret, and(add(add(len, 0x20), 0x1f), not(0x1f))))
mstore(ret, len)
}
}
function _allocateString(uint256 len) pure returns (string memory ret) {
assembly {
ret := mload(0x40)
// new "memory end" including padding
mstore(0x40, add(ret, and(add(add(len, 0x20), 0x1f), not(0x1f))))
mstore(ret, len)
}
}
function _allocateArr(uint256 len) pure returns (bytes[] memory ret) {
assembly {
ret := mload(0x40)
// new "memory end" including padding
mstore(0x40, add(ret, and(add(add(mul(len, 0x20), 0x20), 0x1f), not(0x1f))))
mstore(ret, len)
}
}
function _allocateStringArr(uint256 len) pure returns (string[] memory ret) {
assembly {
ret := mload(0x40)
// new "memory end" including padding
mstore(0x40, add(ret, and(add(add(mul(len, 0x20), 0x20), 0x1f), not(0x1f))))
mstore(ret, len)
}
}
function _allocateUintArr(uint256 len) pure returns (uint256[] memory ret) {
assembly {
ret := mload(0x40)
// new "memory end" including padding
mstore(0x40, add(ret, and(add(add(mul(len, 0x20), 0x20), 0x1f), not(0x1f))))
mstore(ret, len)
}
}
function _allocateBytes32Arr(uint256 len) pure returns (bytes32[] memory ret) {
assembly {
ret := mload(0x40)
// new "memory end" including padding
mstore(0x40, add(ret, and(add(add(mul(len, 0x20), 0x20), 0x1f), not(0x1f))))
mstore(ret, len)
}
}
function _allocateIntArr(uint256 len) pure returns (int256[] memory ret) {
assembly {
ret := mload(0x40)
// new "memory end" including padding
mstore(0x40, add(ret, and(add(add(mul(len, 0x20), 0x20), 0x1f), not(0x1f))))
mstore(ret, len)
}
}
function _allocateAddressArr(uint256 len) pure returns (address[] memory ret) {
assembly {
ret := mload(0x40)
// new "memory end" including padding
mstore(0x40, add(ret, and(add(add(mul(len, 0x20), 0x20), 0x1f), not(0x1f))))
mstore(ret, len)
}
}
IEOARegistry.sol 8 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
import "lib/openzeppelin-contracts/contracts/utils/introspection/IERC165.sol";
interface IEOARegistry is IERC165 {
function isVerifiedEOA(address account) external view returns (bool);
}
ITransferSecurityRegistry.sol 32 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
import "./TransferPolicy.sol";
interface ITransferSecurityRegistry {
event AddedToAllowlist(AllowlistTypes indexed kind, uint256 indexed id, address indexed account);
event CreatedAllowlist(AllowlistTypes indexed kind, uint256 indexed id, string indexed name);
event ReassignedAllowlistOwnership(AllowlistTypes indexed kind, uint256 indexed id, address indexed newOwner);
event RemovedFromAllowlist(AllowlistTypes indexed kind, uint256 indexed id, address indexed account);
event SetAllowlist(AllowlistTypes indexed kind, address indexed collection, uint120 indexed id);
event SetTransferSecurityLevel(address indexed collection, TransferSecurityLevels level);
function createOperatorWhitelist(string calldata name) external returns (uint120);
function createPermittedContractReceiverAllowlist(string calldata name) external returns (uint120);
function reassignOwnershipOfOperatorWhitelist(uint120 id, address newOwner) external;
function reassignOwnershipOfPermittedContractReceiverAllowlist(uint120 id, address newOwner) external;
function renounceOwnershipOfOperatorWhitelist(uint120 id) external;
function renounceOwnershipOfPermittedContractReceiverAllowlist(uint120 id) external;
function setTransferSecurityLevelOfCollection(address collection, TransferSecurityLevels level) external;
function setOperatorWhitelistOfCollection(address collection, uint120 id) external;
function setPermittedContractReceiverAllowlistOfCollection(address collection, uint120 id) external;
function addOperatorToWhitelist(uint120 id, address operator) external;
function addPermittedContractReceiverToAllowlist(uint120 id, address receiver) external;
function removeOperatorFromWhitelist(uint120 id, address operator) external;
function removePermittedContractReceiverFromAllowlist(uint120 id, address receiver) external;
function getCollectionSecurityPolicy(address collection) external view returns (CollectionSecurityPolicy memory);
function getWhitelistedOperators(uint120 id) external view returns (address[] memory);
function getPermittedContractReceivers(uint120 id) external view returns (address[] memory);
function isOperatorWhitelisted(uint120 id, address operator) external view returns (bool);
function isContractReceiverPermitted(uint120 id, address receiver) external view returns (bool);
}
ITransferValidator.sol 8 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
import "./TransferPolicy.sol";
interface ITransferValidator {
function applyCollectionTransferPolicy(address caller, address from, address to) external view;
}
Context.sol 28 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.4) (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;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}
Read Contract
DEFAULT_OPERATOR_WHITELIST_ID 0x5d4c1d46 → uint120
DEFAULT_TRANSFER_SECURITY_LEVEL 0x1c33b328 → uint8
DEFAULT_TRANSFER_VALIDATOR 0x01463546 → address
HUB_DIVISOR 0x70b921dc → uint256
HUB_OWNER_TOKENID 0xd4263a53 → uint256
accountIncentivizedReleaseOptedOut 0x222b4989 → bool
allCollections 0x167ef495 → address
allCollectionsLength 0x3d759a92 → uint256
balanceOf 0x70a08231 → uint256
beneficiariesOf 0xa278d7c0 → uint256, address
blockHashBeacon 0xd94911a6 → address
blockHashBeaconRewardPercentage 0xec33bca9 → uint256
bridging 0x34cfb56c → address
burned 0x23250cae → bool
collectionCurationTokenId 0x012b23f5 → uint256
computeEncryptionReference 0x73550776 → bytes32
contractURI 0xe8a3d485 → string
crews 0x87d14763 → address
curationTokenFramedOptedIn 0x4f1c25ca → bool
discountDemon 0x07f3aa69 → address
dreams 0xbb3df8b8 → address
exists 0x4f558e79 → bool
framesAnimated 0x0fb54386 → bool
freelancerPercentage 0x8de65ce4 → uint256
frozen 0x054f7d9c → bool
getApproved 0x081812fc → address
getCollection 0x5a1f3c28 → address
getPermittedContractReceivers 0xd007af5c → address[]
getSecurityPolicy 0xbe537f43 → tuple
getTransferValidator 0x098144d4 → address
getWhitelistedOperators 0x495c8bf9 → address[]
hubPercentage 0x7a9a44cf → uint256
hubRoyalty 0x7c71035e → uint256
immortalStorage 0xff11343a → address
immortalizeFee 0xf29f3357 → uint256
immortalizer 0xb17535b8 → address
isApprovedForAll 0xe985e9c5 → bool
isContractReceiverPermitted 0x9d645a44 → bool
isOperatorWhitelisted 0x2e8da829 → bool
isPaymentFilterIniter 0x2f45dcfe → bool
isTransferAllowed 0x1b25b077 → bool
maxBurnWindow 0xfbe05451 → uint256
minFeeNumerator 0xdaea31c0 → uint96
multiTokenURI 0xcb287656 → string[]
name 0x06fdde03 → string
nftTemplates 0x9b82fb9c → address
nftTemplatesLength 0x21fdde67 → uint256
originator 0xd9760434 → address
owner 0x8da5cb5b → address
ownerApprovedTokenWrapper 0xcdf1bcc6 → bool
ownerOf 0x6352211e → address
paidImmortalizeFee 0xad3e63d8 → bool
paymentFilterer 0x4499e42c → address
platformApprovedWrapper 0x41e0ae23 → bool
pledgedRevealTimestamp 0x5a940077 → uint256
pledgedRevealTimestamps 0xe96b6c74 → uint256
premierAccess 0xff7da91e → address
prover 0x32a8f30f → address
refundAvailable 0x6a9412bd → uint256
refunder 0xdf4f5a72 → address
restrictedViewing 0x8750a4b4 → bool
robustRenderer 0x4dc12f07 → address
royaltyInfo 0x2a55205a → address, uint256
supportsInterface 0x01ffc9a7 → bool
symbol 0x95d89b41 → string
tokenURI 0xc87b56dd → string
totalMinted 0xa2309ff8 → uint256
totalSupply 0x18160ddd → uint256
uriRenderer 0x1dd64057 → address
validityLens 0x491bb66a → address
Write Contract 42 functions
These functions modify contract state and require a wallet transaction to execute.
addNewNFTTemplate 0x00381703
address newNFTTemplate
approve 0x095ea7b3
address account
uint256 id
burn 0x42966c68
uint256 tokenId
claimRefund 0xb5545a3c
No parameters
emitBatchMetadataUpdate 0xa4830114
uint256 fromTokenId
uint256 toTokenId
emitUpdated 0x5d95af2d
uint256 curationTokenId
freeze 0x62a5af3b
No parameters
immortalizeCollection 0x6f511c3e
tuple icd
returns: uint256
immortalizeCollectionCombined 0x2a881924
bytes32 encryptionPre
uint256 pledgedRevealTimestamp_
tuple icd
returns: bytes32, uint256
immortalizeFrame 0x1726dae2
tuple icd
returns: uint256
immortalizeTab 0x7d4c83a7
tuple icd
returns: uint256
immortalizeTabBulk 0x5fc49cd1
tuple[] icds
returns: uint256[]
optInCurationFramed 0x2eb4287a
uint256 curationTokenId
bool optIn
optOutIncentivizedRelease 0xb3b3390c
bool optOut
retroactivelyPayImmortalizeFee 0xb035866b
uint256 curationTokenId
reveal 0xa2d66137
bytes32 key
uint256 optionalCurationTokenId
safeTransferFrom 0x42842e0e
address from
address to
uint256 id
safeTransferFrom 0xb88d4fde
address from
address to
uint256 id
bytes data
setApprovalForAll 0xa22cb465
address operator
bool isApproved
setApprovedTokenWrapper 0xe66121ed
uint256 curationTokenId
address wrapper
setApprovedWrapper 0x63d425c8
address wrapper
setBlockHashBeaconRewardPercentage 0x7aa4244d
uint256 blockHashBeaconRewardPercentage_
setBridging 0xfcc088ac
address bridging_
address prover_
setColorClassOverrides 0x3ca5fe2d
uint256 curationTokenId
tuple compressedColorClassOverrides
setComponents 0x577d4113
address premierAccess_
address unifiedPaymentFilterer_
address immortalizer_
address immortalStorage_
address robustRenderer_
address validityLens_
address uri_
address refunder_
address blockHashBeacon_
address discountDemon_
address crews_
address dreams_
setDefaultRoyalty 0x6ff60210
No parameters
setEncryptedRevealTime 0x8405d4a1
bytes32 encryptionPre
uint256 pledgedRevealTimestamp_
returns: bytes32
setFramesAnimated 0x5fc51560
bool animateTF
setFreelancerPercentage 0x8cfdfd59
uint256 newPercentage
setHubValues 0xe7642c5c
uint256 hubPercentage_
uint256 hubRoyalty_
setImmortalizeFee 0x8c912796
uint8 t
uint256 newFee
setMaxBurnWindow 0x9a2fb2db
uint256 newMax
setMinFeeNumerator 0xb875ad99
uint96 newFeeNumerator
setRestrictedViewing 0x49c17642
uint256 curationTokenId
bool tf
setToCustomSecurityPolicy 0x61347162
uint8 level
uint120 operatorWhitelistId
uint120 permittedContractReceiversAllowlistId
setToCustomValidatorAndSecurityPolicy 0xfd762d92
address validator
uint8 level
uint120 operatorWhitelistId
uint120 permittedContractReceiversAllowlistId
setToDefaultSecurityPolicy 0x6c3b8699
No parameters
setTransferValidator 0xa9fc664e
address transferValidator_
transferFrom 0x23b872dd
address from
address to
uint256 id
updateContractURIImage 0x396286ea
uint256 curationTokenId
tuple imageData
address customRenderer
withdrawERC20 0x44004cc1
address token
address to
uint256 amount
withdrawETH 0x4782f779
address to
uint256 amount
Recent Transactions
No transactions found for this address