Cryo Explorer Ethereum Mainnet

Address Contract Verified

Address 0x02cf8C9fBa24d79886dAc40cb620f0930C6E8eC0
Balance 0 ETH
Nonce 1
Code Size 10574 bytes
Indexed Transactions 0
External Etherscan · Sourcify

Contract Bytecode

10574 bytes
0x608080604052600436101561001357600080fd5b60003560e01c90816306fdde031461184a575080630e9472351461179e5780630fce34151461175157806310b07b71146116a057806314043209146114fc57806318969a2d14611410578063224242ca1461136957806338e3e01314611301578063393e5ede1461124c5780633bee58f9146111fe57806344baa5fd146111555780634ca299231461111a5780634fce7a2a146110cf57806357de26a41461101d5780635b2f752514610ffb5780635ffcbba714610f0e578063635d4acd14610e7957806365c4ce7a14610e3f57806365fae35e14610df15780638265eed614610dcb5780638575738614610af75780638cf6a8f114610a755780639954b0dc146109b25780639c52a7f114610964578063a7260f9f14610834578063bf353dbb146107ea578063cb8370a31461072b578063ceed3ef214610674578063e32da6e8146104b6578063e891c09514610494578063ef4097cc146103d6578063f0b76dd71461029a578063f29c29c41461025e5763fc8717c31461019557600080fd5b34610259576020600319360112610259576101ae6119c0565b61010081101561022a576101c79060011b600401611c94565b80516020820151171561022157604073ffffffffffffffffffffffffffffffffffffffff91201660015b60408051911515825273ffffffffffffffffffffffffffffffffffffffff909216602082015290819081015b0390f35b506000806101f1565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600080fd5b346102595760206003193601126102595760043573ffffffffffffffffffffffffffffffffffffffff811681036102595761029890611f32565b005b346102595760406003193601126102595760243567ffffffffffffffff8111610259578060040190604060031982360301126102595761031460209260246102e28280611adb565b6103098760405183819483830196873781016000838201520301601f198101835282611ab8565b519020930190611adb565b61033b8460405183819483830196873781016000838201520301601f198101835282611ab8565b519020604051908382019260043584527f9f4828881b0f7a05d2743eba15bd9a0fcb8000f31ac25e121d78bf50e8b4c23a6040840152606083015260808201526080815261038a60a082611ab8565b519020604051828101917f194368726f6e69636c65205369676e6564204d6573736167653a0a33320000008352603d820152603d81526103cb605d82611ab8565b519020604051908152f35b34610259576020600319360112610259576103ef6119c0565b336000526000602052604060002054156104825760ff8116908115610259576103055460ff811683810361041f57005b6040805160ff92831681529390911660208401527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff009233917ff1f337dab208fa554bd26f24051a905078905ccd741043ca59e486a3b9e03f4c91a2161761030555005b634a0bfec1600052336020526024601cfd5b3461025957600060031936011261025957602060ff6102045416604051908152f35b346102595760206003193601126102595760043567ffffffffffffffff8111610259573660238201121561025957806004013567ffffffffffffffff8111610259573660248260061b8401011161025957336000526000602052604060002054156104825760005b81811015610298576000908060061b840160407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc8236030112610670576040519261056884611a51565b604460248301359283865201356020850192818452171561064057604084209173ffffffffffffffffffffffffffffffffffffffff83169261010060ff8260981c1610156106435760971c6101fe1690600482016105c581611c94565b9687516020890151171560001461061257600197509060059291519055519101557f189ea7fe6fa9a5be238df17366f57d37c1943a76871a92123a439f66bc489335339180a35b0161051e565b505050509192604073ffffffffffffffffffffffffffffffffffffffff91201603610640575060019061060c565b80fd5b6024837f4e487b710000000000000000000000000000000000000000000000000000000081526032600452fd5b8280fd5b346102595760006003193601126102595773ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000005d44916e0db13ecd661b20df4d645904e57589c8163303610705575b60606106f065ffffffffffff600354166fffffffffffffffffffffffffffffffff61030854169080151592565b90604051921515835260208301526040820152f35b336000526103066020526040600020546106c35763d957b595600052336020526024601cfd5b346102595760206003193601126102595760043573ffffffffffffffffffffffffffffffffffffffff81168082036102595761010060ff8360981c16101561022a576107a97401fffffffffffffffffffffffffffffffffffffffe6101fe6d01fffffffffffffffffffffffffe60209560971c161616600401611c94565b9081518383015117151591826107c6575b50506040519015158152f35b6040902073ffffffffffffffffffffffffffffffffffffffff1614905082806107ba565b346102595760206003193601126102595760043573ffffffffffffffffffffffffffffffffffffffff81168091036102595760005260006020526020604060002054604051908152f35b346102595760206003193601126102595760043567ffffffffffffffff8111610259576108659036906004016119d0565b90336000526000602052604060002054156104825760005b82811061088657005b610899610894828585611b54565b611f11565b9073ffffffffffffffffffffffffffffffffffffffff82169182156102595760ff9060981c1691610100831015928361022a57610205019273ffffffffffffffffffffffffffffffffffffffff845416801560001461094f575061022a5782817fffffffffffffffffffffffff00000000000000000000000000000000000000006001955416179055337fafb0e3c5f69ca0548e193d035ea46175485ad76d40ebac3f49531a414447047f600080a35b0161087d565b92935050600091036106405750600190610949565b346102595760206003193601126102595760043573ffffffffffffffffffffffffffffffffffffffff8116810361025957336000526000602052604060002054156104825761029890611eaf565b34610259576000600319360112610259576109cf61030754611b8a565b60009060005b8151811015610a62578073ffffffffffffffffffffffffffffffffffffffff6109ff600193611bd6565b90549060031b1c166000526103066020528160406000205414610a23575b016109d5565b73ffffffffffffffffffffffffffffffffffffffff610a4182611bd6565b90549060031b1c16610a5c610a5586611c0b565b9585611c67565b52610a1d565b5061021d91815260405191829182611a01565b346102595760206003193601126102595760043573ffffffffffffffffffffffffffffffffffffffff8116908181036102595760981c60ff16908015159081610ac6575b6020826040519015158152f35b905061010082101561022a5773ffffffffffffffffffffffffffffffffffffffff6020926102050154161482610ab9565b346102595760406003193601126102595760043567ffffffffffffffff8111610259578060040190604060031982360301126102595760243567ffffffffffffffff811161025957366023820112156102595780600401359067ffffffffffffffff8211610259573660246060840283010111610259577fffffffff0000000000000000000000000000000000000000000000000000000091610cbc91602480610ba18880611adb565b610bc9602060405183819483830196873781016000838201520301601f198101835282611ab8565b519020960195610bd98789611adb565b610c01602060405183819483830196873781016000838201520301601f198101835282611ab8565b5190206040519060208201927f454344534100000000000000000000000000000000000000000000000000000084527f9f4828881b0f7a05d2743eba15bd9a0fcb8000f31ac25e121d78bf50e8b4c23a60408401526060830152608082015260808152610c6f60a082611ab8565b51902060405160208101917f194368726f6e69636c65205369676e6564204d6573736167653a0a33320000008352603d820152603d8152610cb1605d82611ab8565b519020910190612543565b1680610d9e577fffffffff00000000000000000000000000000000000000000000000000000000610d37610d3185610d287f16f91548bee0e0722f10b5b21a55dae299cd9024229347fd3063ffe2eee8ea34610d188884611adb565b6040513394909283929083611b2c565b0390a280611adb565b90612421565b1680610d7157600380547fffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000164265ffffffffffff16179055005b7fda45eb1c0000000000000000000000000000000000000000000000000000000060005260045260246000fd5b7fc61f8ee60000000000000000000000000000000000000000000000000000000060005260045260246000fd5b3461025957600060031936011261025957602065ffffffffffff60035416604051908152f35b346102595760206003193601126102595760043573ffffffffffffffffffffffffffffffffffffffff8116810361025957336000526000602052604060002054156104825761029890611dee565b346102595760206003193601126102595760043573ffffffffffffffffffffffffffffffffffffffff811681036102595761029890611cb2565b3461025957600060031936011261025957604051601f19612020610e9d8184611ab8565b6101008352013660208301376000805b610100811015610efd5760019073ffffffffffffffffffffffffffffffffffffffff8161020501541680610ee3575b5001610ead565b610ef6610eef85611c0b565b9486611c67565b5284610edc565b8183526040518061021d8582611a01565b346102595760206003193601126102595760043567ffffffffffffffff811161025957610f3f9036906004016119d0565b90336000526000602052604060002054156104825760005b828110610f6057005b610f73610f6e828585611b54565b611b64565b9061010082101561022a57600191821b80600401610f9081611c94565b908151602083015117610fa7575b50505001610f57565b73ffffffffffffffffffffffffffffffffffffffff9160409160056000958680935501552016907f52ce30f1a5a6d183f1c399b08dbbebaa6bcd0e2a3f6da47ff3b6984b863d28de339180a3848080610f9e565b3461025957600060031936011261025957602060ff6103055416604051908152f35b346102595760006003193601126102595773ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000005d44916e0db13ecd661b20df4d645904e57589c81633036110a9575b61109765ffffffffffff600354166fffffffffffffffffffffffffffffffff61030854169080151592565b50901561025957602090604051908152f35b3360005261030660205260406000205461106c5763d957b595600052336020526024601cfd5b346102595760206003193601126102595760043573ffffffffffffffffffffffffffffffffffffffff8116809103610259576000526103066020526020604060002054604051908152f35b346102595760006003193601126102595760206040517f9f4828881b0f7a05d2743eba15bd9a0fcb8000f31ac25e121d78bf50e8b4c23a8152f35b3461025957600060031936011261025957604051601f196120206111798184611ab8565b610100835201366020830137604051600061119382611a51565b80825260208201525060009060005b610100811015610a62576001906111bd81831b600401611c94565b80516020820151176111d1575b50016111a2565b604073ffffffffffffffffffffffffffffffffffffffff9120166111f7610a5586611c0b565b52846111ca565b346102595760206003193601126102595760043573ffffffffffffffffffffffffffffffffffffffff8116809103610259576000526103066020526020600160406000205414604051908152f35b346102595760006003193601126102595773ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000005d44916e0db13ecd661b20df4d645904e57589c81633036112db575b6112c665ffffffffffff600354166fffffffffffffffffffffffffffffffff61030854169080151592565b91156102595760409182519182526020820152f35b3360005261030660205260406000205461129b5763d957b595600052336020526024601cfd5b346102595760206003193601126102595760043561010081101561022a57610205015473ffffffffffffffffffffffffffffffffffffffff16801561022157604080516001815273ffffffffffffffffffffffffffffffffffffffff92909216602083015290f35b3461025957600060031936011261025957611385600154611b8a565b60009060005b8151811015610a62578073ffffffffffffffffffffffffffffffffffffffff6113b5600193611bbb565b90549060031b1c16600052600060205281604060002054146113d8575b0161138b565b73ffffffffffffffffffffffffffffffffffffffff6113f682611bbb565b90549060031b1c1661140a610a5586611c0b565b526113d2565b346102595760206003193601126102595760043567ffffffffffffffff8111610259576114419036906004016119d0565b90336000526000602052604060002054156104825760005b82811061146257005b611470610f6e828585611b54565b9061010082101561022a576001916102050173ffffffffffffffffffffffffffffffffffffffff81541690816114a9575b505001611459565b80547fffffffffffffffffffffffff0000000000000000000000000000000000000000169055337f7d553b93bbf05f357fad190f996038832a2de9be7a99c6647f6b047cf3284fc9600080a384806114a1565b346102595760406003193601126102595760043567ffffffffffffffff8111610259578060040190604060031982360301126102595760243567ffffffffffffffff8111610259576060600319823603011261025957610cbc7fffffffff000000000000000000000000000000000000000000000000000000009160246115838680611adb565b6115ab602060405183819483830196873781016000838201520301601f198101835282611ab8565b5190209401936115bb8587611adb565b6115e3602060405183819483830196873781016000838201520301601f198101835282611ab8565b5190206040519060208201927f5343484e4f52520000000000000000000000000000000000000000000000000084527f9f4828881b0f7a05d2743eba15bd9a0fcb8000f31ac25e121d78bf50e8b4c23a6040840152606083015260808201526080815261165160a082611ab8565b51902060405160208101917f194368726f6e69636c65205369676e6564204d6573736167653a0a33320000008352603d820152603d8152611693605d82611ab8565b5190209060040190611ffb565b346102595760006003193601126102595773ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000005d44916e0db13ecd661b20df4d645904e57589c816330361172b575b604061171c65ffffffffffff600354166fffffffffffffffffffffffffffffffff61030854169080151592565b50825191151582526020820152f35b336000526103066020526040600020546116ef5763d957b595600052336020526024601cfd5b346102595760206003193601126102595760043573ffffffffffffffffffffffffffffffffffffffff81168091036102595760005260006020526020600160406000205414604051908152f35b34610259576020600319360112610259576117b76119c0565b336000526000602052604060002054156104825760ff8116908115610259576102045460ff81168381036117e757005b6040805160ff92831681529390911660208401527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff009233917f6725eac4d9463d79b15ccb65e620936de0a75d44795151a2fc5a6f62f1dc376c91a2161761020455005b34610259576000600319360112610259576000600254908160011c916001811680156119b6575b60208410811461198957838552849291811561194c57506001146118eb575b61189c92500382611ab8565b60405190602082528181519182602083015260005b8381106118d3575050601f19601f836000604080968601015201168101030190f35b602082820181015160408784010152859350016118b1565b509060026000527f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace906000915b81831061193057505090602061189c92820101611890565b6020919350806001915483858801015201910190918392611918565b6020925061189c9491507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001682840152151560051b820101611890565b6024837f4e487b710000000000000000000000000000000000000000000000000000000081526022600452fd5b92607f1692611871565b6004359060ff8216820361025957565b9181601f840112156102595782359167ffffffffffffffff8311610259576020808501948460051b01011161025957565b602060408183019282815284518094520192019060005b818110611a255750505090565b825173ffffffffffffffffffffffffffffffffffffffff16845260209384019390920191600101611a18565b6040810190811067ffffffffffffffff821117611a6d57604052565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6060810190811067ffffffffffffffff821117611a6d57604052565b90601f601f19910116810190811067ffffffffffffffff821117611a6d57604052565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe181360301821215610259570180359067ffffffffffffffff82116102595760200191813603831361025957565b90601f83604094601f1993602086528160208701528686013760008582860101520116010190565b919081101561022a5760051b0190565b3560ff811681036102595790565b67ffffffffffffffff8111611a6d5760051b60200190565b90611b9482611b72565b611ba16040519182611ab8565b828152601f19611bb18294611b72565b0190602036910137565b60015481101561022a57600160005260206000200190600090565b6103075481101561022a5761030760005260206000200190600090565b805482101561022a5760005260206000200190600090565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8114611c385760010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b805182101561022a5760209160051b010190565b60405190611c8882611a51565b60006020838281520152565b90604051611ca181611a51565b602060018294805484520154910152565b336000526000602052604060002054156104825773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000005d44916e0db13ecd661b20df4d645904e57589c8168114611d6a578060005261030660205260406000205415611d67578060005261030660205260006040812055337fdadd1471db1ea2f303654fb1bdcc010e5a664214ab41934c0f752aabca88a491600080a3565b50565b60846040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f546f6c6c57697468496d6d757461626c65526f757465723a2063616e6e6f742060448201527f6469737320726f757465720000000000000000000000000000000000000000006064820152fd5b73ffffffffffffffffffffffffffffffffffffffff811690816000526000602052600160406000205414611eab5781600052600060205260016040600020556001549068010000000000000000821015611a6d57611e57826001611e8394016001556001611bf3565b90919073ffffffffffffffffffffffffffffffffffffffff8084549260031b9316831b921b1916179055565b337fe31c10b0adbedd0c6e5d024286c6eeead7761e65a67608dcf0b67604c0da7e2f600080a3565b5050565b73ffffffffffffffffffffffffffffffffffffffff1680600052600060205260406000205415611d675780600052600060205260006040812055337f58466e5837b54e559819c9ba8a5d7c77c97c985d1aabf4bdc5f41069fa5d65a0600080a3565b3573ffffffffffffffffffffffffffffffffffffffff811681036102595790565b336000526000602052604060002054156104825773ffffffffffffffffffffffffffffffffffffffff81169081600052610306602052600160406000205414611eab57816000526103066020526001604060002055610307549068010000000000000000821015611a6d57611e57826001611fb4940161030755610307611bf3565b337f75d30ca40c7bcd48e685894b82b864808b9cb566090efc53444a2e61742f18a3600080a3565b60405190611fe982611a9c565b60006040838281528260208201520152565b9190600092612008611c7b565b50612011611fdc565b5060ff61020454169060408301948261202a8786611adb565b9050036123f85761203b8685611adb565b156123cb57358060f81c9161010083101561237357506101fe806120669260f71c1616600401611c94565b928351916020850192835117156123a0576001909791971b93612087611fdc565b50519151966040519261209984611a9c565b8352602083019788526040830194600186526001926000935b8381106121cf5750505050506120c6611c7b565b50604051926120d484611a51565b60008452600060208501525195600096600190807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f915b61219257505050957ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f8092612164979882808580098093510987525192099009602083015261215c60208401611f11565b9235916126ff565b1561216e57600090565b7fcbc4755f0000000000000000000000000000000000000000000000000000000090565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f8183949b930492818c850982039008929091820290038061210b565b6121d9828a611adb565b6000908310156123735750810135908160f81c9161010083101561022a576101fe8061220c9260f71c1616600401611c94565b93845192602086019384511715612342576001901b9081811661231157179387518d51978b51906122e2576001947ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f83818f818f978f9982809b818b8180809e819a82809a818085819609809c510990820390089d8e91838b818080878009928184600409998a988d0893820390820308918009089052099b099b5192820394099009086002099481808086600209810383820308818880090880985260009e09600209820394820390089009088c52016120b2565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b507fe7908552000000000000000000000000000000000000000000000000000000009c505050505050505050505050565b507f552e5639000000000000000000000000000000000000000000000000000000009c505050505050505050505050565b807f4e487b7100000000000000000000000000000000000000000000000000000000602492526032600452fd5b507f552e56390000000000000000000000000000000000000000000000000000000096505050505050565b6024827f4e487b710000000000000000000000000000000000000000000000000000000081526032600452fd5b507f19877c60000000000000000000000000000000000000000000000000000000009450505050565b9081604091810103126102595760405160009161243d82611a51565b80359065ffffffffffff82169182810361253f5790602091845201356fffffffffffffffffffffffffffffffff81169283820361253f576020015265ffffffffffff60035416811115612518574281116124f157817fffffffffffffffffffffffffffffffff000000000000000000000000000000006103085416176103085560405191825260208201527f9f8b8154174dc4a9ed8bbe5a985ecf6d8b2db481a1c64a02c8d94ae7929e0ba660403392a290565b5050507f5507d1ac0000000000000000000000000000000000000000000000000000000090565b5050507f4ef874670000000000000000000000000000000000000000000000000000000090565b8480fd5b61254b611fdc565b5060009160ff6103055416938481036126d6576000935b85851061257457505050505050600090565b8185101561022a576060850283016060813603126102595760405161259881611a9c565b81359160ff8316808403610259576125ee8360209560009552604086850135948588840152013590816040820152506040519384938b859094939260ff6060936080840197845216602083015260408201520152565b838052039060015afa156126ca5760005160ff73ffffffffffffffffffffffffffffffffffffffff82169160981c1690801590811561269a575b5061266f576001901b8082166126445760019117940193612562565b505050505050507fe79085520000000000000000000000000000000000000000000000000000000090565b505050505050507fcbc4755f0000000000000000000000000000000000000000000000000000000090565b905061010082101561022a5773ffffffffffffffffffffffffffffffffffffffff82610205015416141538612628565b6040513d6000823e3d90fd5b50505050507f19877c600000000000000000000000000000000000000000000000000000000090565b9091801580156128fa575b6128f157602082019081517ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f80600781875181818009900908918009036128e7577ffffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd03641418110156128e7576128b983601b60017ffffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd03641418060209960009951908951907f0100000000000000000000000000000000000000000000000000000000000000604051928e840194855260f81b16604083015260418201527fffffffffffffffffffffffffffffffffffffffff0000000000000000000000008d60601b1660618201526055815261281b607582611ab8565b5190200695845190097ffffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd036414103955116019051809460ff7ffffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd03641416040519788970991865290921660ff16602085015260408401527ffffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd03641410360608301526080820190565b838052039060015afa156126ca5773ffffffffffffffffffffffffffffffffffffffff806000511691161490565b5050505050600090565b50505050600090565b5073ffffffffffffffffffffffffffffffffffffffff84161561270a56fea2646970667358221220f3eaf1beb5bbb19e9d557463121675e4feda6ebe9384a6be9926cf9ef984f2dd64736f6c634300081c0033

Verified Source Code Full Match

Compiler: v0.8.28+commit.7893614a EVM: paris Optimization: Yes (10000 runs)
Consumer.sol 159 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.28;

import {Toll} from "chronicle-std@v2/toll/Toll.sol";

import {UScribe} from "uscribe@v1/UScribe.sol";

import {IChronicleVAO_Centrifuge_JAAA_Reader as IReader} from "./IReader.sol";

import {TollWithImmutableRouter} from "./TollWithImmutableRouter.sol";

// The poke struct is the single struct that is being poked.
struct PokeData {
    uint48 age;
    uint128 val;
}

/**
 * @title ChronicleVAO_Centrifuge_JAAA_Consumer_1
 * @custom:uscribe-version 1.3.0
 *
 * @author Chronicle Labs, Inc
 * @custom:security-contact [email protected]
 */
contract ChronicleVAO_Centrifuge_JAAA_Consumer_1 is
    UScribe,
    TollWithImmutableRouter,
    IReader
{

    /// @notice Thrown if attempted to poke a stale message.
    error StaleMessage();

    /// @notice Thrown if attempted to poke a future message.
    error FutureMessage();

    /// @notice Emitted when a poke is received.
    /// @param caller The caller's address.
    /// @param val The value poked.
    /// @param age The age of the value poked.
    event Poked(address indexed caller, uint128 val, uint48 age);

    //--------------------------------------------------------------------------
    // Storage

    uint128 internal _val;

    //--------------------------------------------------------------------------
    // Constructor

    constructor(address initialAuthed, address router)
        UScribe(initialAuthed, "VAO::Centrifuge_JAAA")
        TollWithImmutableRouter(router)
    {}

    /// @dev Defines authorization for IToll's authenticated functions.
    function toll_auth() internal override(TollWithImmutableRouter) auth {}

    //--------------------------------------------------------------------------
    // Poke Functionality

    /// @dev Function to handle state update.
    ///
    ///      This function is being called by the upstream uScribe
    ///      implementation iff the poke's signature verification succeeded.
    ///
    ///      Therefore, this function MUST only verify the payload contains
    ///      correctly encoded, non-stale and non-future data before committing a state update.
    ///
    /// @dev Reverts if:
    ///      - Decoding payload to expected format fails
    ///
    /// @dev Returns error if:
    ///      - Payload is stale
    ///      - Payload is signed at a future date to block timestamp
    ///
    /// @dev The payload is expected to be encoded using the PokeData struct
    ///      containing the fields:
    ///
    ///      - age: uint48
    ///      - val: uint128
    ///
    ///  @dev The age stored after a poke is equal to the current block time,
    ///       not the age sent in the poke.
    function _poke(bytes calldata payload)
        internal
        override(UScribe)
        returns (bytes4)
    {
        PokeData memory pokeData = abi.decode(payload, (PokeData));

        // Fail if payload stale.
        if (pokeData.age <= latestPoke()) {
            return StaleMessage.selector;
        }

        // Fail if payload from the future.
        if (pokeData.age > block.timestamp) {
            return FutureMessage.selector;
        }

        // Store the poke's val.
        _val = pokeData.val;

        emit Poked(msg.sender, pokeData.val, pokeData.age);

        return _NO_ERR;
    }

    //--------------------------------------------------------------------------
    // IReader Functionality
    //
    // Note that read functions differ from Scribe with regards to `val = 0` not
    // being a failure code. Read functions only revert/fail if
    // `latestPoke() = 0`, implying no valid poke occured yet.
    //
    // Side effect of this is that the read functions cannot be disabled.

    //// @inheritdoc IVAOReader
    function read() public view toll returns (uint val) {
        bool ok;
        (ok, val,) = _tryReadWithAge();
        require(ok);
    }

    //// @inheritdoc IVAOReader
    function tryRead() public view toll returns (bool ok, uint val) {
        (ok, val,) = _tryReadWithAge();
        // assert(ok || val == 0);
    }

    //// @inheritdoc IVAOReader
    function readWithAge() public view toll returns (uint val, uint age) {
        bool ok;
        (ok, val, age) = _tryReadWithAge();
        require(ok);
    }

    //// @inheritdoc IVAOReader
    function tryReadWithAge()
        public
        view
        toll
        returns (bool ok, uint val, uint age)
    {
        (ok, val, age) = _tryReadWithAge();
    }

    function _tryReadWithAge()
        internal
        view
        returns (bool ok, uint val, uint age)
    {
        age = latestPoke();
        val = _val;
        ok = age != 0;
        // assert(ok || val == 0);
    }
}
Toll.sol 125 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.16;

import {IToll} from "./IToll.sol";

/**
 * @title Toll Module
 *
 * @notice "Toll paid, we kiss - but dissension looms, maybe diss?"
 *
 * @dev The `Toll` contract module provides a basic access control mechanism,
 *      where a set of addresses are granted access to protected functions.
 *      These addresses are said the be _tolled_.
 *
 *      Initially, no address is tolled. Through the `kiss(address)` and
 *      `diss(address)` functions, auth'ed callers are able to toll/de-toll
 *      addresses. Authentication for these functions is defined via the
 *      downstream implemented `toll_auth()` function.
 *
 *      This module is used through inheritance. It will make available the
 *      modifier `toll`, which can be applied to functions to restrict their
 *      use to only tolled callers.
 */
abstract contract Toll is IToll {
    /// @dev Mapping storing whether address is tolled.
    /// @custom:invariant Image of mapping is {0, 1}.
    ///                     ∀x ∊ Address: _buds[x] ∊ {0, 1}
    /// @custom:invariant Only functions `kiss` and `diss` may mutate the mapping's state.
    ///                     ∀x ∊ Address: preTx(_buds[x]) != postTx(_buds[x])
    ///                                     → (msg.sig == "kiss" ∨ msg.sig == "diss")
    /// @custom:invariant Mapping's state may only be mutated by authenticated caller.
    ///                     ∀x ∊ Address: preTx(_buds[x]) != postTx(_buds[x])
    ///                                     → toll_auth()
    mapping(address => uint) private _buds;

    /// @dev List of addresses possibly being tolled.
    /// @dev May contain duplicates.
    /// @dev May contain addresses not being tolled anymore.
    /// @custom:invariant Every address being tolled once is element of the list.
    ///                     ∀x ∊ Address: tolled(x) → x ∊ _budsTouched
    address[] private _budsTouched;

    /// @dev Ensures caller is tolled.
    modifier toll() {
        assembly ("memory-safe") {
            // Compute slot of _buds[msg.sender].
            mstore(0x00, caller())
            mstore(0x20, _buds.slot)
            let slot := keccak256(0x00, 0x40)

            // Revert if caller not tolled.
            let isTolled := sload(slot)
            if iszero(isTolled) {
                // Store selector of `NotTolled(address)`.
                mstore(0x00, 0xd957b595)
                // Store msg.sender.
                mstore(0x20, caller())
                // Revert with (offset, size).
                revert(0x1c, 0x24)
            }
        }
        _;
    }

    /// @dev Reverts if caller not allowed to access protected function.
    /// @dev Must be implemented in downstream contract.
    function toll_auth() internal virtual;

    /// @inheritdoc IToll
    function kiss(address who) external {
        toll_auth();

        if (_buds[who] == 1) return;

        _buds[who] = 1;
        _budsTouched.push(who);
        emit TollGranted(msg.sender, who);
    }

    /// @inheritdoc IToll
    function diss(address who) external {
        toll_auth();

        if (_buds[who] == 0) return;

        _buds[who] = 0;
        emit TollRenounced(msg.sender, who);
    }

    /// @inheritdoc IToll
    function tolled(address who) public view returns (bool) {
        return _buds[who] == 1;
    }

    /// @inheritdoc IToll
    /// @custom:invariant Only contains tolled addresses.
    ///                     ∀x ∊ tolled(): _tolled[x]
    /// @custom:invariant Contains all tolled addresses.
    ///                     ∀x ∊ Address: _tolled[x] == 1 → x ∊ tolled()
    function tolled() public view returns (address[] memory) {
        // Initiate array with upper limit length.
        address[] memory budsList = new address[](_budsTouched.length);

        // Iterate through all possible tolled addresses.
        uint ctr;
        for (uint i; i < budsList.length; i++) {
            // Add address only if still tolled.
            if (_buds[_budsTouched[i]] == 1) {
                budsList[ctr++] = _budsTouched[i];
            }
        }

        // Set length of array to number of tolled addresses actually included.
        assembly ("memory-safe") {
            mstore(budsList, ctr)
        }

        return budsList;
    }

    /// @inheritdoc IToll
    function bud(address who) public view returns (uint) {
        return _buds[who];
    }
}
UScribe.sol 523 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.24;

import {Auth} from "chronicle-std@v2/auth/Auth.sol";

import {IUScribe} from "./IUScribe.sol";
import {UPokeData, SchnorrData, ECDSAData} from "./Types.sol";

import {LibSchnorr} from "./libs/LibSchnorr.sol";
import {LibSecp256k1} from "./libs/LibSecp256k1.sol";

/**
 * @title UScribe
 * @custom:version 1.2.0
 *
 * @notice A universal Oracle
 *
 * @author Chronicle Labs, Inc
 * @custom:security-contact [email protected]
 */
abstract contract UScribe is IUScribe, Auth {
    using LibSchnorr for LibSecp256k1.Point;
    using LibSecp256k1 for LibSecp256k1.Point;
    using LibSecp256k1 for LibSecp256k1.JacobianPoint;

    //--------------------------------------------------------------------------
    // Constants and Immutables

    bytes4 internal constant _NO_ERR = bytes4(0);

    /// @inheritdoc IUScribe
    bytes32 public immutable wat;

    // Note that strings cannot be marked as immutable.
    // @custom:invariant Is immutable.
    string private _name;

    //--------------------------------------------------------------------------
    // Storage

    uint48 private _latestPoke;

    struct SchnorrStorage {
        LibSecp256k1.Point[256] pubKeys;
        uint8 bar;
    }

    SchnorrStorage private __schnorrStorage;

    struct ECDSAStorage {
        address[256] validators;
        uint8 bar;
    }

    ECDSAStorage private __ecdsaStorage;

    //--------------------------------------------------------------------------
    // Constructor

    constructor(address initialAuthed, string memory name_)
        Auth(initialAuthed)
    {
        require(bytes(name_).length != 0);

        _name = name_;
        wat = keccak256(bytes(name_));

        // Note to not have bars of zero.
        __schnorrStorage.bar = type(uint8).max;
        __ecdsaStorage.bar = type(uint8).max;
    }

    /// @inheritdoc IUScribe
    function name() external view returns (string memory) {
        return _name;
    }

    //--------------------------------------------------------------------------
    // Consumer Implemented Functionality

    /// @dev Function implemented in downstream consumer contract to handle
    ///      application specific state update.
    ///
    /// @dev The implementation MUST deserialize the payload and perform
    ///      necessary sanity checks.
    ///
    ///      It SHOULD NOT revert but instead return the error types' selector
    ///      whenever possible. This allows uScribe to wrap the application
    ///      specific error into a `PokeError_ConsumerRejectedPayload()` error.
    ///
    ///      To indicate a successful poke, the function MUST return the
    ///      `_NO_ERR = bytes4(0)` constant.
    ///
    /// @dev Note that this function is vulnerable to replay attacks.
    ///
    ///      Consumers MUST implement application specific logic to prevent
    ///      replayability issues. uScribe only verifies that the respective
    ///      validators attested to the payload at some point in time, ie
    ///      uScribe performs a stateless signature verification.
    ///
    ///      Note that this requires the payload to contain sufficient data for
    ///      the consumer logic to protect against replayability issues.
    ///
    ///      Protections against replayability issues MAY be including a nonce
    ///      in the payload or only accepting payloads with strictly
    ///      monotonically increasing timestamps. The `latestPoke()(uint)`
    ///      function can be used by consumers to access the timestamp of the
    ///      last poke.
    ///
    ///      To protect against cross-chain replayability issues the payload
    ///      MAY be expected to include the chain's id.
    ///
    /// @param payload The verified payload blob.
    /// @return bytes4 `_NO_ERR` if poke successful, application's error type
    ///                selector otherwise.
    function _poke(bytes calldata payload) internal virtual returns (bytes4);

    //--------------------------------------------------------------------------
    // Poke Functionality

    /// @inheritdoc IUScribe
    function poke(UPokeData calldata uPokeData, SchnorrData calldata schnorr)
        external
    {
        bytes4 err;
        bytes32 message;

        // Construct Schnorr Chronicle Signed Message of uPokeData.
        message = constructChronicleSignedMessage({
            scheme: bytes32("SCHNORR"),
            uPokeData: uPokeData
        });

        // Verify Schnorr signature.
        err = __verifySchnorrSignature(message, schnorr);
        if (err != _NO_ERR) {
            revert PokeError_VerificationFailed(err);
        }

        // Poke's security verified.
        emit UPoked(msg.sender, uPokeData.proofURI);

        // Forward payload to consumer.
        err = _poke(uPokeData.payload);
        if (err != _NO_ERR) {
            revert PokeError_ConsumerRejectedPayload(err);
        }

        // Update latest poke timestamp once poke accepted by consumer.
        _latestPoke = uint48(block.timestamp);
    }

    /// @inheritdoc IUScribe
    function poke(UPokeData calldata uPokeData, ECDSAData[] calldata ecdsas)
        external
    {
        bytes4 err;
        bytes32 message;

        // Construct ECDSA Chronicle Signed Message of uPokeData.
        message = constructChronicleSignedMessage({
            scheme: bytes32("ECDSA"),
            uPokeData: uPokeData
        });

        // Verify ECDSA signatures.
        err = __verifyECDSASignatures(message, ecdsas);
        if (err != _NO_ERR) {
            revert PokeError_VerificationFailed(err);
        }

        // Poke's security verified.
        emit UPoked(msg.sender, uPokeData.proofURI);

        // Forward payload to consumer.
        err = _poke(uPokeData.payload);
        if (err != _NO_ERR) {
            revert PokeError_ConsumerRejectedPayload(err);
        }

        // Update latest poke timestamp once poke accepted by consumer.
        _latestPoke = uint48(block.timestamp);
    }

    //--------------------------------------------------------------------------
    // Chronicle Signed Message Functionality

    /// @inheritdoc IUScribe
    function constructChronicleSignedMessage(
        bytes32 scheme,
        UPokeData calldata uPokeData
    ) public view returns (bytes32) {
        return keccak256(
            abi.encodePacked(
                "\x19Chronicle Signed Message:\n32",
                keccak256(
                    abi.encodePacked(
                        scheme,
                        wat,
                        keccak256(abi.encodePacked(uPokeData.payload)),
                        keccak256(abi.encodePacked(uPokeData.proofURI))
                    )
                )
            )
        );
    }

    //--------------------------------------------------------------------------
    // Signature Verification Functionality

    /// @custom:invariant Reverts iff out of gas.
    /// @custom:invariant Runtime is O(__schnorrStorage.bar).
    function __verifySchnorrSignature(
        bytes32 message,
        SchnorrData calldata schnorr
    ) private view returns (bytes4) {
        // Let pubKey be the currently processed validator's public key.
        LibSecp256k1.Point memory pubKey;
        // Let id be the currently processed validator's id.
        uint8 id;
        // Let aggPubKey be the sum of processed validator public keys.
        // Note that Jacobian coordinates are used.
        LibSecp256k1.JacobianPoint memory aggPubKey;
        // Let bloom be a bloom filter to guarantee signer uniqueness.
        uint bloom;

        // Fail if number of validators unequal to bar.
        //
        // Note that requiring equality constrains the verification's runtime
        // from Ω(bar) to Θ(bar).
        uint bar = __schnorrStorage.bar;
        if (schnorr.validatorIds.length != bar) {
            return VerificationError_BarNotReached.selector;
        }

        // Initiate validator variables.
        id = uint8(schnorr.validatorIds[0]);
        pubKey = __schnorrStorage.pubKeys[id];
        if (pubKey.isZeroPoint()) {
            return VerificationError_ValidatorInvalid.selector;
        }

        // Initiate bloom filter and aggPubKey.
        bloom = 1 << id;
        aggPubKey = pubKey.toJacobian();

        // Aggregation loop.
        for (uint i = 1; i < bar; i++) {
            // Update validator variables.
            id = uint8(schnorr.validatorIds[i]);
            pubKey = __schnorrStorage.pubKeys[id];
            if (pubKey.isZeroPoint()) {
                return VerificationError_ValidatorInvalid.selector;
            }

            // Fail if double signing attempted.
            if (bloom & (1 << id) != 0) {
                return VerificationError_DoubleSigningAttempted.selector;
            }

            // Update bloom filter.
            bloom |= 1 << id;

            // Add pubKey to aggPubKey.
            aggPubKey.addAffinePoint(pubKey);
        }

        // Perform signature verification.
        bool ok = aggPubKey.toAffine().verifySignature(
            message, schnorr.signature, schnorr.commitment
        );
        if (!ok) {
            return VerificationError_SignatureInvalid.selector;
        }

        return _NO_ERR;
    }

    /// @custom:invariant Reverts iff out of gas.
    /// @custom:invariant Runtime is O(__ecdsaStorage.bar).
    function __verifyECDSASignatures(
        bytes32 message,
        ECDSAData[] calldata ecdsas
    ) private view returns (bytes4) {
        // Let ecdsa be the currently processed ECDSA signature.
        ECDSAData memory ecdsa;
        // Let signer be the currently processed ECDSA signature's signer.
        address signer;
        // Let id the the currently processed ECDSA signature's signer id.
        uint id;
        // Let bloom be a bloom filter to guarantee signer uniqueness.
        uint bloom;

        // Fail if number of validators unequal to bar.
        //
        // Note that requiring equality constrains the verification's runtime
        // from Ω(bar) to Θ(bar).
        uint bar = __ecdsaStorage.bar;
        if (ecdsas.length != bar) {
            return VerificationError_BarNotReached.selector;
        }

        for (uint i; i < bar; i++) {
            // Update ECDSA variables.
            ecdsa = ecdsas[i];
            signer = ecrecover(message, ecdsa.v, ecdsa.r, ecdsa.s);
            id = uint160(signer) >> 152;

            // Fail if signature invalid or signer not a validator.
            // forgefmt: disable-next-item
            if (signer == address(0) || __ecdsaStorage.validators[id] != signer) {
                return VerificationError_SignatureInvalid.selector;
            }

            // Fail if double signing attempted.
            if (bloom & (1 << id) != 0) {
                return VerificationError_DoubleSigningAttempted.selector;
            }

            // Update bloom filter.
            bloom |= 1 << id;
        }

        return _NO_ERR;
    }

    //--------------------------------------------------------------------------
    // Auth'ed Functionality

    //----------------------------------
    // SchnorrStorage

    /// @inheritdoc IUScribe
    function liftSchnorr(LibSecp256k1.Point[] calldata pubKeys) external auth {
        for (uint i; i < pubKeys.length; i++) {
            LibSecp256k1.Point memory pubKey = pubKeys[i];
            require(!pubKey.isZeroPoint());
            // assert(pubKey.toAddress() != address(0));

            address validator = pubKey.toAddress();
            uint id = uint160(validator) >> 152;

            LibSecp256k1.Point memory sPubKey = __schnorrStorage.pubKeys[id];
            if (sPubKey.isZeroPoint()) {
                __schnorrStorage.pubKeys[id] = pubKey;
                emit ValidatorLiftedSchnorr(msg.sender, validator);
            } else {
                require(validator == sPubKey.toAddress());
            }
        }
    }

    /// @inheritdoc IUScribe
    function dropSchnorr(uint8[] calldata ids) external auth {
        for (uint i; i < ids.length; i++) {
            uint8 id = ids[i];

            LibSecp256k1.Point memory pubKey = __schnorrStorage.pubKeys[id];
            if (!pubKey.isZeroPoint()) {
                delete __schnorrStorage.pubKeys[id];
                emit ValidatorDroppedSchnorr(msg.sender, pubKey.toAddress());
            }
        }
    }

    /// @inheritdoc IUScribe
    function setBarSchnorr(uint8 bar) external auth {
        require(bar != 0);

        if (__schnorrStorage.bar != bar) {
            emit BarUpdatedSchnorr(msg.sender, __schnorrStorage.bar, bar);
            __schnorrStorage.bar = bar;
        }
    }

    //----------------------------------
    // ECDSAStorage

    /// @inheritdoc IUScribe
    function liftECDSA(address[] calldata validators) external auth {
        for (uint i; i < validators.length; i++) {
            address validator = validators[i];
            require(validator != address(0));

            uint id = uint160(validator) >> 152;

            if (__ecdsaStorage.validators[id] == address(0)) {
                __ecdsaStorage.validators[id] = validator;
                emit ValidatorLiftedECDSA(msg.sender, validator);
            } else {
                require(__ecdsaStorage.validators[id] == validator);
            }
        }
    }

    /// @inheritdoc IUScribe
    function dropECDSA(uint8[] calldata ids) external auth {
        for (uint i; i < ids.length; i++) {
            uint8 id = ids[i];
            address validator = __ecdsaStorage.validators[id];

            if (validator != address(0)) {
                // assert(uint160(validator) >> 152 == id);
                delete __ecdsaStorage.validators[id];
                emit ValidatorDroppedECDSA(msg.sender, validator);
            }
        }
    }

    /// @inheritdoc IUScribe
    function setBarECDSA(uint8 bar) external auth {
        require(bar != 0);

        if (__ecdsaStorage.bar != bar) {
            emit BarUpdatedECDSA(msg.sender, __ecdsaStorage.bar, bar);
            __ecdsaStorage.bar = bar;
        }
    }

    //--------------------------------------------------------------------------
    // Public View Functions

    /// @inheritdoc IUScribe
    ///
    /// @dev Note that function is public to grant read-only access to
    ///      downstream consumer.
    function latestPoke() public view returns (uint) {
        return _latestPoke;
    }

    //----------------------------------
    // SchnorrStorage

    /// @inheritdoc IUScribe
    function validatorsSchnorr(address who) external view returns (bool) {
        uint id = uint160(who) >> 152;
        LibSecp256k1.Point memory pubKey = __schnorrStorage.pubKeys[id];

        return !pubKey.isZeroPoint() && pubKey.toAddress() == who;
    }

    /// @inheritdoc IUScribe
    function validatorsSchnorr(uint8 id)
        external
        view
        returns (bool, address)
    {
        LibSecp256k1.Point memory pubKey = __schnorrStorage.pubKeys[id];

        return !pubKey.isZeroPoint()
            ? (true, pubKey.toAddress())
            : (false, address(0));
    }

    /// @inheritdoc IUScribe
    function validatorsSchnorr() external view returns (address[] memory) {
        address[] memory validators = new address[](256);

        LibSecp256k1.Point memory pubKey;
        uint ctr;
        for (uint i; i < 256; i++) {
            pubKey = __schnorrStorage.pubKeys[i];

            if (!pubKey.isZeroPoint()) {
                validators[ctr++] = pubKey.toAddress();
            }
        }

        assembly ("memory-safe") {
            mstore(validators, ctr)
        }

        return validators;
    }

    /// @inheritdoc IUScribe
    function barSchnorr() external view returns (uint8) {
        return __schnorrStorage.bar;
    }

    //----------------------------------
    // ECDSAStorage

    /// @inheritdoc IUScribe
    function validatorsECDSA(address who) external view returns (bool) {
        uint id = uint160(who) >> 152;

        return who != address(0) && __ecdsaStorage.validators[id] == who;
    }

    /// @inheritdoc IUScribe
    function validatorsECDSA(uint id) external view returns (bool, address) {
        address validator = __ecdsaStorage.validators[id];

        return validator != address(0) ? (true, validator) : (false, address(0));
    }

    /// @inheritdoc IUScribe
    function validatorsECDSA() external view returns (address[] memory) {
        address[] memory validators = new address[](256);

        address validator;
        uint ctr;
        for (uint i; i < 256; i++) {
            validator = __ecdsaStorage.validators[i];

            if (validator != address(0)) {
                validators[ctr++] = validator;
            }
        }

        assembly ("memory-safe") {
            mstore(validators, ctr)
        }

        return validators;
    }

    /// @inheritdoc IUScribe
    function barECDSA() external view returns (uint8) {
        return __ecdsaStorage.bar;
    }
}
IReader.sol 31 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

interface IChronicleVAO_Centrifuge_JAAA_Reader {
    /// @notice Returns the oracle's current value.
    /// @dev Reverts if:
    ///      - Not yet poked
    /// @return val The oracle's current value.
    function read() external view returns (uint val);

    /// @notice Returns the oracle's current value.
    /// @return ok True if previously poked, false otherwise.
    /// @return val The oracle's current value if it exists, zero otherwise.
    function tryRead() external view returns (bool ok, uint val);

    /// @notice Returns the oracle's current value and its age.
    /// @dev Reverts if:
    ///      - Not yet poked
    /// @return val The oracle's current value.
    /// @return age The value's age.
    function readWithAge() external view returns (uint val, uint age);

    /// @notice Returns the oracle's current value and its age.
    /// @return ok True if previously poked, false otherwise.
    /// @return val The oracle's current value if it exists, zero otherwise.
    /// @return age The value's age if value exists, zero otherwise.
    function tryReadWithAge()
        external
        view
        returns (bool ok, uint val, uint age);
}
TollWithImmutableRouter.sol 149 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

import {IToll} from "chronicle-std@v2/toll/IToll.sol";

/**
 * @title TollWithImmutableRouter
 *
 * @notice "Toll paid, we kiss - but dissension looms, maybe diss?"
 *
 * @dev The `TollWithImmutableRouter` contract module provides a basic access
 *      control mechanism, where a set of addresses are granted access to
 *      protected functions.
 *      These addresses are said the be _tolled_.
 *
 *      Note that this module differs from chronicle-std/Toll in that a router
 *      address given at construction is immutably tolled.
 *
 *      This path reduces gas costs for customers connecting to the router
 *      instead of directly reading from the uscribe consumer.
 *
 *      Initially, only the router is tolled. Through the `kiss(address)` and
 *      `diss(address)` functions, auth'ed callers are able to toll/de-toll
 *      addresses. Authentication for these functions is defined via the
 *      downstream implemented `toll_auth()` function.
 *
 *      This module is used through inheritance. It will make available the
 *      modifier `toll`, which can be applied to functions to restrict their
 *      use to only tolled callers.
 */
abstract contract TollWithImmutableRouter is IToll {
    /// @dev Address of the immutably tolled router.
    address private immutable _router;

    /// @dev Mapping storing whether address is tolled.
    /// @custom:invariant Image of mapping is {0, 1}.
    ///                     ∀x ∊ Address: _buds[x] ∊ {0, 1}
    /// @custom:invariant Only functions `kiss`, `diss` or constructor may
    ///                   mutate the mapping's state.
    ///                     ∀x ∊ Address: preTx(_buds[x]) != postTx(_buds[x])
    ///                                     → (msg.sig == "kiss" ∨ msg.sig == "diss" ∨ msg.sig == "")
    /// @custom:invariant Mapping's state may only be mutated by authenticated
    ///                   caller or during construction.
    ///                     ∀x ∊ Address: preTx(_buds[x]) != postTx(_buds[x])
    ///                                     → (toll_auth() ∨ msg.sig == "")
    mapping(address => uint) private _buds;

    /// @dev List of addresses possibly being tolled.
    /// @dev May contain duplicates.
    /// @dev May contain addresses not being tolled anymore.
    /// @custom:invariant Every address being tolled once is element of the list.
    ///                     ∀x ∊ Address: tolled(x) → x ∊ _budsTouched
    address[] private _budsTouched;

    constructor(address router) {
        _router = router;

        _buds[router] = 1;
        _budsTouched.push(router);
        emit TollGranted(msg.sender, router);
    }

    /// @dev Ensures caller is tolled.
    modifier toll() {
        if (msg.sender != _router) {
            assembly ("memory-safe") {
                // Compute slot of _buds[msg.sender].
                mstore(0x00, caller())
                mstore(0x20, _buds.slot)
                let slot := keccak256(0x00, 0x40)

                // Revert if caller not tolled.
                let isTolled := sload(slot)
                if iszero(isTolled) {
                    // Store selector of `NotTolled(address)`.
                    mstore(0x00, 0xd957b595)
                    // Store msg.sender.
                    mstore(0x20, caller())
                    // Revert with (offset, size).
                    revert(0x1c, 0x24)
                }
            }
        }
        _;
    }

    /// @dev Reverts if caller not allowed to access protected function.
    /// @dev Must be implemented in downstream contract.
    function toll_auth() internal virtual;

    /// @inheritdoc IToll
    function kiss(address who) external {
        toll_auth();

        if (_buds[who] == 1) return;

        _buds[who] = 1;
        _budsTouched.push(who);
        emit TollGranted(msg.sender, who);
    }

    /// @inheritdoc IToll
    function diss(address who) external {
        toll_auth();

        require(who != _router, "TollWithImmutableRouter: cannot diss router");

        if (_buds[who] == 0) return;

        _buds[who] = 0;
        emit TollRenounced(msg.sender, who);
    }

    /// @inheritdoc IToll
    function tolled(address who) public view returns (bool) {
        return _buds[who] == 1;
    }

    /// @inheritdoc IToll
    /// @custom:invariant Only contains tolled addresses.
    ///                     ∀x ∊ tolled(): _buds[x] == 1
    /// @custom:invariant Contains all tolled addresses.
    ///                     ∀x ∊ Address: _buds[x] == 1 → x ∊ tolled()
    function tolled() public view returns (address[] memory) {
        // Initiate array with upper limit length.
        address[] memory budsList = new address[](_budsTouched.length);

        // Iterate through all possible tolled addresses.
        uint ctr;
        for (uint i; i < budsList.length; i++) {
            // Add address only if still tolled.
            if (_buds[_budsTouched[i]] == 1) {
                budsList[ctr++] = _budsTouched[i];
            }
        }

        // Set length of array to number of tolled addresses actually included.
        assembly ("memory-safe") {
            mstore(budsList, ctr)
        }

        return budsList;
    }

    /// @inheritdoc IToll
    function bud(address who) public view returns (uint) {
        return _buds[who];
    }
}
IToll.sol 44 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.16;

interface IToll {
    /// @notice Thrown by protected function if caller not tolled.
    /// @param caller The caller's address.
    error NotTolled(address caller);

    /// @notice Emitted when toll granted to address.
    /// @param caller The caller's address.
    /// @param who The address toll got granted to.
    event TollGranted(address indexed caller, address indexed who);

    /// @notice Emitted when toll renounced from address.
    /// @param caller The caller's address.
    /// @param who The address toll got renounced from.
    event TollRenounced(address indexed caller, address indexed who);

    /// @notice Grants address `who` toll.
    /// @dev Only callable by auth'ed address.
    /// @param who The address to grant toll.
    function kiss(address who) external;

    /// @notice Renounces address `who`'s toll.
    /// @dev Only callable by auth'ed address.
    /// @param who The address to renounce toll.
    function diss(address who) external;

    /// @notice Returns whether address `who` is tolled.
    /// @param who The address to check.
    /// @return True if `who` is tolled, false otherwise.
    function tolled(address who) external view returns (bool);

    /// @notice Returns full list of addresses tolled.
    /// @dev May contain duplicates.
    /// @return List of addresses tolled.
    function tolled() external view returns (address[] memory);

    /// @notice Returns whether address `who` is tolled.
    /// @custom:deprecated Use `tolled(address)(bool)` instead.
    /// @param who The address to check.
    /// @return 1 if `who` is tolled, 0 otherwise.
    function bud(address who) external view returns (uint);
}
Auth.sol 124 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.16;

import {IAuth} from "./IAuth.sol";

/**
 * @title Auth Module
 *
 * @dev The `Auth` contract module provides a basic access control mechanism,
 *      where a set of addresses are granted access to protected functions.
 *      These addresses are said to be _auth'ed_.
 *
 *      Initially, the address given as constructor argument is the only address
 *      auth'ed. Through the `rely(address)` and `deny(address)` functions,
 *      auth'ed callers are able to grant/renounce auth to/from addresses.
 *
 *      This module is used through inheritance. It will make available the
 *      modifier `auth`, which can be applied to functions to restrict their
 *      use to only auth'ed callers.
 */
abstract contract Auth is IAuth {
    /// @dev Mapping storing whether address is auth'ed.
    /// @custom:invariant Image of mapping is {0, 1}.
    ///                     ∀x ∊ Address: _wards[x] ∊ {0, 1}
    /// @custom:invariant Only address given as constructor argument is authenticated after deployment.
    ///                     deploy(initialAuthed) → (∀x ∊ Address: _wards[x] == 1 → x == initialAuthed)
    /// @custom:invariant Only functions `rely` and `deny` may mutate the mapping's state.
    ///                     ∀x ∊ Address: preTx(_wards[x]) != postTx(_wards[x])
    ///                                     → (msg.sig == "rely" ∨ msg.sig == "deny")
    /// @custom:invariant Mapping's state may only be mutated by authenticated caller.
    ///                     ∀x ∊ Address: preTx(_wards[x]) != postTx(_wards[x]) → _wards[msg.sender] = 1
    mapping(address => uint) private _wards;

    /// @dev List of addresses possibly being auth'ed.
    /// @dev May contain duplicates.
    /// @dev May contain addresses not being auth'ed anymore.
    /// @custom:invariant Every address being auth'ed once is element of the list.
    ///                     ∀x ∊ Address: authed(x) -> x ∊ _wardsTouched
    address[] private _wardsTouched;

    /// @dev Ensures caller is auth'ed.
    modifier auth() {
        assembly ("memory-safe") {
            // Compute slot of _wards[msg.sender].
            mstore(0x00, caller())
            mstore(0x20, _wards.slot)
            let slot := keccak256(0x00, 0x40)

            // Revert if caller not auth'ed.
            let isAuthed := sload(slot)
            if iszero(isAuthed) {
                // Store selector of `NotAuthorized(address)`.
                mstore(0x00, 0x4a0bfec1)
                // Store msg.sender.
                mstore(0x20, caller())
                // Revert with (offset, size).
                revert(0x1c, 0x24)
            }
        }
        _;
    }

    constructor(address initialAuthed) {
        _wards[initialAuthed] = 1;
        _wardsTouched.push(initialAuthed);

        // Note to use address(0) as caller to indicate address was auth'ed
        // during deployment.
        emit AuthGranted(address(0), initialAuthed);
    }

    /// @inheritdoc IAuth
    function rely(address who) external auth {
        if (_wards[who] == 1) return;

        _wards[who] = 1;
        _wardsTouched.push(who);
        emit AuthGranted(msg.sender, who);
    }

    /// @inheritdoc IAuth
    function deny(address who) external auth {
        if (_wards[who] == 0) return;

        _wards[who] = 0;
        emit AuthRenounced(msg.sender, who);
    }

    /// @inheritdoc IAuth
    function authed(address who) public view returns (bool) {
        return _wards[who] == 1;
    }

    /// @inheritdoc IAuth
    /// @custom:invariant Only contains auth'ed addresses.
    ///                     ∀x ∊ authed(): _wards[x] == 1
    /// @custom:invariant Contains all auth'ed addresses.
    ///                     ∀x ∊ Address: _wards[x] == 1 → x ∊ authed()
    function authed() public view returns (address[] memory) {
        // Initiate array with upper limit length.
        address[] memory wardsList = new address[](_wardsTouched.length);

        // Iterate through all possible auth'ed addresses.
        uint ctr;
        for (uint i; i < wardsList.length; i++) {
            // Add address only if still auth'ed.
            if (_wards[_wardsTouched[i]] == 1) {
                wardsList[ctr++] = _wardsTouched[i];
            }
        }

        // Set length of array to number of auth'ed addresses actually included.
        assembly ("memory-safe") {
            mstore(wardsList, ctr)
        }

        return wardsList;
    }

    /// @inheritdoc IAuth
    function wards(address who) public view returns (uint) {
        return _wards[who];
    }
}
IUScribe.sol 220 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import {UPokeData, SchnorrData, ECDSAData} from "./Types.sol";
import {LibSecp256k1} from "./libs/LibSecp256k1.sol";

interface IUScribe {
    //--------------------------------------------------------------------------
    // Errors

    /// @notice Thrown if poke verification failed.
    /// @param err The verification error.
    error PokeError_VerificationFailed(bytes4 err);

    /// @notice Thrown if poke rejected by consumer.
    /// @param err The consumer error.
    error PokeError_ConsumerRejectedPayload(bytes4 err);

    //----------------------------------
    // Verification Errors

    /// @notice Verification error thrown via `PokeError_VerificationFailed` if
    ///         poke did not reach bar threshold.
    error VerificationError_BarNotReached();

    /// @notice Verification error thrown via `PokeError_VerificationFailed` if
    ///         poke attempted double signing.
    error VerificationError_DoubleSigningAttempted();

    /// @notice Verification error thrown via `PokeError_VerificationFailed` if
    ///         poke included invalid validator.
    error VerificationError_ValidatorInvalid();

    /// @notice Verification error thrown via `PokeError_VerificationFailed` if
    ///         poke included invalid signature.
    error VerificationError_SignatureInvalid();

    //--------------------------------------------------------------------------
    // Events

    /// @notice Emitted when oracle was successfully poked.
    /// @param caller The caller's address.
    /// @param proofURI Optional URI to validity proof.
    event UPoked(address indexed caller, string proofURI);

    /// @notice Emitted when new validator lifted for Schnorr verification.
    /// @param caller The caller's address.
    /// @param validator The validator address lifted.
    event ValidatorLiftedSchnorr(
        address indexed caller, address indexed validator
    );

    /// @notice Emitted when validator dropped from Schnorr verification.
    /// @param caller The caller's address.
    /// @param validator The validator address dropped.
    event ValidatorDroppedSchnorr(
        address indexed caller, address indexed validator
    );

    /// @notice Emitted when bar for Schnorr verification updated.
    /// @param caller The caller's address.
    /// @param oldBar The old bar's value.
    /// @param newBar The new bar's value.
    event BarUpdatedSchnorr(address indexed caller, uint8 oldBar, uint8 newBar);

    /// @notice Emitted when new validator lifted for ECDSA verification.
    /// @param caller The caller's address.
    /// @param validator The validator address lifted.
    event ValidatorLiftedECDSA(
        address indexed caller, address indexed validator
    );

    /// @notice Emitted when validator dropped from ECDSA verification.
    /// @param caller The caller's address.
    /// @param validator The validator address dropped.
    event ValidatorDroppedECDSA(
        address indexed caller, address indexed validator
    );

    /// @notice Emitted when bar for ECDSA verification updated.
    /// @param caller The caller's address.
    /// @param oldBar The old bar's value.
    /// @param newBar The new bar's value.
    event BarUpdatedECDSA(address indexed caller, uint8 oldBar, uint8 newBar);

    //--------------------------------------------------------------------------
    // Poke Functionality

    /// @notice Pokes the oracle using Schnorr verification.
    /// @param uPokeData The UPokeData being poked.
    /// @param schnorrData The SchnorrData proving the `uPokeData`'s integrity.
    function poke(
        UPokeData calldata uPokeData,
        SchnorrData calldata schnorrData
    ) external;

    /// @notice Pokes the oracle using ECDSA verification.
    /// @param uPokeData The UPokeData being poked.
    /// @param ecdsaDatas The list of ECDSAData proving the `uPokeData`'s
    ///                   integrity.
    function poke(UPokeData calldata uPokeData, ECDSAData[] calldata ecdsaDatas)
        external;

    /// @notice Construct a Chronicle Signed Message.
    /// @param scheme The scheme to construct the message for.
    /// @param uPokeData The UPokeData to inlcude in the message.
    function constructChronicleSignedMessage(
        bytes32 scheme,
        UPokeData calldata uPokeData
    ) external view returns (bytes32);

    //--------------------------------------------------------------------------
    // Auth'ed Functionality

    /// @notice Lifts list of public keys `pubKeys` for Schnorr verification.
    /// @dev Only callable by auth'ed address.
    /// @param pubKeys The list of public keys to lift.
    function liftSchnorr(LibSecp256k1.Point[] calldata pubKeys) external;

    /// @notice Drops list of validator ids `ids` from Schnorr verification.
    /// @dev Only callable by auth'ed address.
    /// @param ids The list of validator ids to drop.
    function dropSchnorr(uint8[] calldata ids) external;

    /// @notice Updates the bar security parameter for Schnorr verification.
    /// @dev Only callable by auth'ed address.
    /// @param bar The value to update bar to.
    function setBarSchnorr(uint8 bar) external;

    /// @notice Lifts list of addresses `validators` for ECDSA verification.
    /// @dev Only callable by auth'ed address.
    /// @param validators The list of addresses to lift.
    function liftECDSA(address[] calldata validators) external;

    /// @notice Drops list of validator ids `ids` from ECDSA verification.
    /// @dev Only callable by auth'ed address.
    /// @param ids The list of validator ids to drop.
    function dropECDSA(uint8[] calldata ids) external;

    /// @notice Updates the bar security parameter for ECDSA verification.
    /// @dev Only callable by auth'ed address.
    /// @param bar The value to update bar to.
    function setBarECDSA(uint8 bar) external;

    //--------------------------------------------------------------------------
    // Public View Functions

    /// @notice Returns the oracle's identifier.
    /// @dev The wat is derived from `name()`:
    ///
    ///      ```solidity
    ///      assert(wat() = keccak256(bytes(name())));
    ///      ```
    /// @return bytes32 The oracle's identifier.
    function wat() external view returns (bytes32);

    /// @notice Returns the oracle's name.
    /// @return string The oracle's name.
    function name() external view returns (string memory);

    /// @notice Returns the timestamp of the latest poke.
    /// @dev This timestamp is updated on every poke and allows uniform
    ///      staleness monitoring accross uscribe instances.
    /// @return uint The timestamp of the latest poke.
    function latestPoke() external view returns (uint);

    /// @notice Returns whether address `who` is a validator for Schnorr
    ///         verification.
    /// @param who The address to check.
    /// @return bool True if `who` is a validator for Schnorr verification,
    ///              false otherwise.
    function validatorsSchnorr(address who) external view returns (bool);

    /// @notice Returns whether validator id `id` is a validator for Schnorr
    ///         verification and, if so, the validator's address.
    /// @param id The validator id to check.
    /// @return bool True if `who` is a validator for Schnorr verification,
    ///              false otherwise.
    /// @return address Address of the validator if `id` is a validator for
    ///                 Schnorr verification, zero address otherwise.
    function validatorsSchnorr(uint8 id)
        external
        view
        returns (bool, address);

    /// @notice Returns the lift of validator addresses for Schnorr
    ///         verification.
    /// @return address[] The lift of validator addresses for Schnorr
    ///                   verification.
    function validatorsSchnorr() external view returns (address[] memory);

    /// @notice Returns the bar security parameter for Schnorr verification.
    /// @return uint8 The bar security parameter for Schnorr verification.
    function barSchnorr() external view returns (uint8);

    /// @notice Returns whether address `who` is a validator for ECDSA
    ///         verification.
    /// @param who The address to check.
    /// @return bool True if `who` is a validator for ECDSA verification, false
    ///              otherwise.
    function validatorsECDSA(address who) external view returns (bool);

    /// @notice Returns whether validator id `id` is a validator for ECDSA
    ///         verification and, if so, the validator's address.
    /// @param id The validator id to check.
    /// @return bool True if `who` is a validator for ECDSA verification,
    ///              false otherwise.
    /// @return address Address of the validator if `id` is a validator for
    ///                 ECDSA verification, zero address otherwise.
    function validatorsECDSA(uint id) external view returns (bool, address);

    /// @notice Returns the lift of validator addresses for ECDSA verification.
    /// @return address[] The lift of validator addresses for ECDSA
    ///                   verification.
    function validatorsECDSA() external view returns (address[] memory);

    /// @notice Returns the bar security parameter for ECDSA verification.
    /// @return uint8 The bar security parameter for ECDSA verification.
    function barECDSA() external view returns (uint8);
}
Types.sol 19 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

struct UPokeData {
    bytes payload;
    string proofURI;
}

struct SchnorrData {
    bytes32 signature;
    address commitment;
    bytes validatorIds;
}

struct ECDSAData {
    uint8 v;
    bytes32 r;
    bytes32 s;
}
LibSchnorr.sol 111 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.16;

import {LibSecp256k1} from "./LibSecp256k1.sol";

///////////////////////////////////////////////////////////////////////////////
//
// Library copied from [email protected]
//
// Reference: https://github.com/chronicleprotocol/scribe/blob/7d2106a265a8d82f259b506c4c8fec42002546ef/src/libs/LibSchnorr.sol
//
///////////////////////////////////////////////////////////////////////////////

/**
 * @title LibSchnorr
 *
 * @notice Custom-purpose library for Schnorr signature verification on the
 *         secp256k1 curve
 */
library LibSchnorr {
    using LibSecp256k1 for LibSecp256k1.Point;

    /// @dev Returns whether `signature` and `commitment` sign via `pubKey`
    ///      message `message`.
    ///
    /// @custom:invariant Reverts iff out of gas.
    /// @custom:invariant Uses constant amount of gas.
    function verifySignature(
        LibSecp256k1.Point memory pubKey,
        bytes32 message,
        bytes32 signature,
        address commitment
    ) internal pure returns (bool) {
        // Return false if signature or commitment is zero.
        if (signature == 0 || commitment == address(0)) {
            return false;
        }

        // Note to enforce pubKey is valid secp256k1 point.
        //
        // While the Scribe contract ensures to only verify signatures for valid
        // public keys, this check is enabled as an additional defense
        // mechanism.
        if (!pubKey.isOnCurve()) {
            return false;
        }

        // Note to enforce signature is less than Q to prevent signature
        // malleability.
        //
        // While the Scribe contract only accepts messages with strictly
        // monotonically increasing timestamps, circumventing replay attack
        // vectors and therefore also signature malleability issues at a higher
        // level, this check is enabled as an additional defense mechanism.
        if (uint(signature) >= LibSecp256k1.Q()) {
            return false;
        }

        // Construct challenge = H(Pₓ ‖ Pₚ ‖ m ‖ Rₑ) mod Q
        uint challenge = uint(
            keccak256(
                abi.encodePacked(
                    pubKey.x, uint8(pubKey.yParity()), message, commitment
                )
            )
        ) % LibSecp256k1.Q();

        // Compute msgHash = -sig * Pₓ      (mod Q)
        //                 = Q - (sig * Pₓ) (mod Q)
        //
        // Unchecked because the only protected operation performed is the
        // subtraction from Q where the subtrahend is the result of a (mod Q)
        // computation, i.e. the subtrahend is guaranteed to be less than Q.
        uint msgHash;
        unchecked {
            msgHash = LibSecp256k1.Q()
                - mulmod(uint(signature), pubKey.x, LibSecp256k1.Q());
        }

        // Compute v = Pₚ + 27
        //
        // Unchecked because pubKey.yParity() ∊ {0, 1} which cannot overflow
        // by adding 27.
        uint v;
        unchecked {
            v = pubKey.yParity() + 27;
        }

        // Set r = Pₓ
        uint r = pubKey.x;

        // Compute s = Q - (e * Pₓ) (mod Q)
        //
        // Unchecked because the only protected operation performed is the
        // subtraction from Q where the subtrahend is the result of a (mod Q)
        // computation, i.e. the subtrahend is guaranteed to be less than Q.
        uint s;
        unchecked {
            s = LibSecp256k1.Q() - mulmod(challenge, pubKey.x, LibSecp256k1.Q());
        }

        // Compute ([s]G - [e]P)ₑ via ecrecover.
        address recovered =
            ecrecover(bytes32(msgHash), uint8(v), bytes32(r), bytes32(s));

        // Verification succeeds iff ([s]G - [e]P)ₑ = Rₑ.
        //
        // Note that commitment is guaranteed to not be zero.
        return commitment == recovered;
    }
}
LibSecp256k1.sol 369 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.16;

///////////////////////////////////////////////////////////////////////////////
//
// Library copied from [email protected]
//
// Reference: https://github.com/chronicleprotocol/scribe/blob/7d2106a265a8d82f259b506c4c8fec42002546ef/src/libs/LibSecp256k1.sol
//
///////////////////////////////////////////////////////////////////////////////

/**
 * @title LibSecp256k1
 *
 * @notice Library for secp256k1 elliptic curve computations
 *
 * @dev This library was developed to efficiently compute aggregated public
 *      keys for Schnorr signatures based on secp256k1, i.e. it is _not_ a
 *      general purpose elliptic curve library!
 *
 *      References to the Ethereum Yellow Paper are based on the following
 *      version: "BERLIN VERSION beacfbd – 2022-10-24".
 */
library LibSecp256k1 {
    using LibSecp256k1 for LibSecp256k1.Point;
    using LibSecp256k1 for LibSecp256k1.JacobianPoint;

    uint private constant ADDRESS_MASK =
        0x000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF;

    // -- Secp256k1 Constants --
    //
    // Taken from https://www.secg.org/sec2-v2.pdf.
    // See section 2.4.1 "Recommended Parameters secp256k1".

    uint private constant _A = 0;
    uint private constant _B = 7;
    uint private constant _P =
        0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F;

    /// @dev Returns the order of the group.
    function Q() internal pure returns (uint) {
        return
            0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141;
    }

    /// @dev Returns the generator G.
    ///      Note that the generator is also called base point.
    function G() internal pure returns (Point memory) {
        return Point({
            x: 0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798,
            y: 0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8
        });
    }

    /// @dev Returns the zero point.
    function ZERO_POINT() internal pure returns (Point memory) {
        return Point({x: 0, y: 0});
    }

    // -- (Affine) Point --

    /// @dev Point encapsulates a secp256k1 point in Affine coordinates.
    struct Point {
        uint x;
        uint y;
    }

    /// @dev Returns the Ethereum address of `self`.
    ///
    /// @dev An Ethereum address is defined as the rightmost 160 bits of the
    ///      keccak256 hash of the concatenation of the hex-encoded x and y
    ///      coordinates of the corresponding ECDSA public key.
    ///      See "Appendix F: Signing Transactions" §134 in the Yellow Paper.
    function toAddress(Point memory self) internal pure returns (address) {
        address addr;
        // Functionally equivalent Solidity code:
        // addr = address(uint160(uint(keccak256(abi.encode(self.x, self.y)))));
        assembly ("memory-safe") {
            addr := and(keccak256(self, 0x40), ADDRESS_MASK)
        }
        return addr;
    }

    /// @dev Returns Affine point `self` in Jacobian coordinates.
    function toJacobian(Point memory self)
        internal
        pure
        returns (JacobianPoint memory)
    {
        return JacobianPoint({x: self.x, y: self.y, z: 1});
    }

    /// @dev Returns whether `self` is the zero point.
    function isZeroPoint(Point memory self) internal pure returns (bool) {
        return (self.x | self.y) == 0;
    }

    /// @dev Returns whether `self` is a point on the curve.
    ///
    /// @dev The secp256k1 curve is specified as y² ≡ x³ + ax + b (mod P)
    ///      where:
    ///         a = 0
    ///         b = 7
    function isOnCurve(Point memory self) internal pure returns (bool) {
        uint left = mulmod(self.y, self.y, _P);
        // Note that adding a * x can be waived as ∀x: a * x = 0.
        uint right =
            addmod(mulmod(self.x, mulmod(self.x, self.x, _P), _P), _B, _P);

        return left == right;
    }

    /// @dev Returns the parity of `self`'s y coordinate.
    ///
    /// @dev The value 0 represents an even y value and 1 represents an odd y
    ///      value.
    ///      See "Appendix F: Signing Transactions" in the Yellow Paper.
    function yParity(Point memory self) internal pure returns (uint) {
        return self.y & 1;
    }

    // -- Jacobian Point --

    /// @dev JacobianPoint encapsulates a secp256k1 point in Jacobian
    ///      coordinates.
    struct JacobianPoint {
        uint x;
        uint y;
        uint z;
    }

    /// @dev Returns Jacobian point `self` in Affine coordinates.
    ///
    /// @custom:invariant Reverts iff out of gas.
    /// @custom:invariant Does not run into an infinite loop.
    function toAffine(JacobianPoint memory self)
        internal
        pure
        returns (Point memory)
    {
        Point memory result;

        // Compute z⁻¹, i.e. the modular inverse of self.z.
        uint zInv = _invMod(self.z);

        // Compute (z⁻¹)² (mod P)
        uint zInv_2 = mulmod(zInv, zInv, _P);

        // Compute self.x * (z⁻¹)² (mod P), i.e. the x coordinate of given
        // Jacobian point in Affine representation.
        result.x = mulmod(self.x, zInv_2, _P);

        // Compute self.y * (z⁻¹)³ (mod P), i.e. the y coordinate of given
        // Jacobian point in Affine representation.
        result.y = mulmod(self.y, mulmod(zInv, zInv_2, _P), _P);

        return result;
    }

    /// @dev Adds Affine point `p` to Jacobian point `self`.
    ///
    ///      It is the caller's responsibility to ensure given points are on the
    ///      curve!
    ///
    ///      Computation based on: https://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian.html#addition-madd-2007-bl.
    ///
    ///      Note that the formula assumes z2 = 1, which always holds if z2's
    ///      point is given in Affine coordinates.
    ///
    ///      Note that eventhough the function is marked as pure, to be
    ///      understood as only being dependent on the input arguments, it
    ///      nevertheless has side effects by writing the result into the
    ///      `self` memory variable.
    ///
    /// @custom:invariant Only mutates `self` memory variable.
    /// @custom:invariant Reverts iff out of gas.
    /// @custom:invariant Uses constant amount of gas.
    function addAffinePoint(JacobianPoint memory self, Point memory p)
        internal
        pure
    {
        // Addition formula:
        //      x = r² - j - (2 * v)             (mod P)
        //      y = (r * (v - x)) - (2 * y1 * j) (mod P)
        //      z = (z1 + h)² - z1² - h²         (mod P)
        //
        // where:
        //      r = 2 * (s - y1) (mod P)
        //      j = h * i        (mod P)
        //      v = x1 * i       (mod P)
        //      h = u - x1       (mod P)
        //      s = y2 * z1³     (mod P)       Called s2 in reference
        //      i = 4 * h²       (mod P)
        //      u = x2 * z1²     (mod P)       Called u2 in reference
        //
        // and:
        //      x1 = self.x
        //      y1 = self.y
        //      z1 = self.z
        //      x2 = p.x
        //      y2 = p.y
        //
        // Note that in order to save memory allocations the result is stored
        // in the self variable, i.e. the following holds true after the
        // functions execution:
        //      x = self.x
        //      y = self.y
        //      z = self.z

        // Cache self's coordinates on stack.
        uint x1 = self.x;
        uint y1 = self.y;
        uint z1 = self.z;

        // Compute z1_2 = z1²     (mod P)
        //              = z1 * z1 (mod P)
        uint z1_2 = mulmod(z1, z1, _P);

        // Compute h = u        - x1       (mod P)
        //           = u        + (P - x1) (mod P)
        //           = x2 * z1² + (P - x1) (mod P)
        //
        // Unchecked because the only protected operation performed is P - x1
        // where x1 is guaranteed by the caller to be an x coordinate belonging
        // to a point on the curve, i.e. being less than P.
        uint h;
        unchecked {
            h = addmod(mulmod(p.x, z1_2, _P), _P - x1, _P);
        }

        // Compute h_2 = h²    (mod P)
        //             = h * h (mod P)
        uint h_2 = mulmod(h, h, _P);

        // Compute i = 4 * h² (mod P)
        uint i = mulmod(4, h_2, _P);

        // Compute z = (z1 + h)² - z1²       - h²       (mod P)
        //           = (z1 + h)² - z1²       + (P - h²) (mod P)
        //           = (z1 + h)² + (P - z1²) + (P - h²) (mod P)
        //             ╰───────╯   ╰───────╯   ╰──────╯
        //               left         mid       right
        //
        // Unchecked because the only protected operations performed are
        // subtractions from P where the subtrahend is the result of a (mod P)
        // computation, i.e. the subtrahend being guaranteed to be less than P.
        unchecked {
            uint left = mulmod(addmod(z1, h, _P), addmod(z1, h, _P), _P);
            uint mid = _P - z1_2;
            uint right = _P - h_2;

            self.z = addmod(left, addmod(mid, right, _P), _P);
        }

        // Compute v = x1 * i (mod P)
        uint v = mulmod(x1, i, _P);

        // Compute j = h * i (mod P)
        uint j = mulmod(h, i, _P);

        // Compute r = 2 * (s               - y1)       (mod P)
        //           = 2 * (s               + (P - y1)) (mod P)
        //           = 2 * ((y2 * z1³)      + (P - y1)) (mod P)
        //           = 2 * ((y2 * z1² * z1) + (P - y1)) (mod P)
        //
        // Unchecked because the only protected operation performed is P - y1
        // where y1 is guaranteed by the caller to be an y coordinate belonging
        // to a point on the curve, i.e. being less than P.
        uint r;
        unchecked {
            r = mulmod(
                2,
                addmod(mulmod(p.y, mulmod(z1_2, z1, _P), _P), _P - y1, _P),
                _P
            );
        }

        // Compute x = r² - j - (2 * v)             (mod P)
        //           = r² - j + (P - (2 * v))       (mod P)
        //           = r² + (P - j) + (P - (2 * v)) (mod P)
        //                  ╰─────╯   ╰───────────╯
        //                    mid         right
        //
        // Unchecked because the only protected operations performed are
        // subtractions from P where the subtrahend is the result of a (mod P)
        // computation, i.e. the subtrahend being guaranteed to be less than P.
        unchecked {
            uint r_2 = mulmod(r, r, _P);
            uint mid = _P - j;
            uint right = _P - mulmod(2, v, _P);

            self.x = addmod(r_2, addmod(mid, right, _P), _P);
        }

        // Compute y = (r * (v - x))       - (2 * y1 * j)       (mod P)
        //           = (r * (v - x))       + (P - (2 * y1 * j)) (mod P)
        //           = (r * (v + (P - x))) + (P - (2 * y1 * j)) (mod P)
        //             ╰─────────────────╯   ╰────────────────╯
        //                    left                 right
        //
        // Unchecked because the only protected operations performed are
        // subtractions from P where the subtrahend is the result of a (mod P)
        // computation, i.e. the subtrahend being guaranteed to be less than P.
        unchecked {
            uint left = mulmod(r, addmod(v, _P - self.x, _P), _P);
            uint right = _P - mulmod(2, mulmod(y1, j, _P), _P);

            self.y = addmod(left, right, _P);
        }
    }

    // -- Private Helpers --

    /// @dev Returns the modular inverse of `x` for modulo `_P`.
    ///
    ///      It is the caller's responsibility to ensure `x` is less than `_P`!
    ///
    ///      The modular inverse of `x` is x⁻¹ such that x * x⁻¹ ≡ 1 (mod P).
    ///
    /// @dev Modified from Jordi Baylina's [ecsol](https://github.com/jbaylina/ecsol/blob/c2256afad126b7500e6f879a9369b100e47d435d/ec.sol#L51-L67).
    ///
    /// @custom:invariant Reverts iff out of gas.
    /// @custom:invariant Does not run into an infinite loop.
    function _invMod(uint x) private pure returns (uint) {
        uint t;
        uint q;
        uint newT = 1;
        uint r = _P;

        assembly ("memory-safe") {
            // Implemented in assembly to circumvent division-by-zero
            // and over-/underflow protection.
            //
            // Functionally equivalent Solidity code:
            //      while (x != 0) {
            //          q = r / x;
            //          (t, newT) = (newT, addmod(t, (_P - mulmod(q, newT, _P)), _P));
            //          (r, x) = (x, r - (q * x));
            //      }
            //
            // For the division r / x, x is guaranteed to not be zero via the
            // loop condition.
            //
            // The subtraction of form P - mulmod(_, _, P) is guaranteed to not
            // underflow due to the subtrahend being a (mod P) result,
            // i.e. the subtrahend being guaranteed to be less than P.
            //
            // The subterm q * x is guaranteed to not overflow because
            // q * x ≤ r due to q = ⎣r / x⎦.
            //
            // The term r - (q * x) is guaranteed to not underflow because
            // q * x ≤ r and therefore r - (q * x) ≥ 0.
            for {} x {} {
                q := div(r, x)

                let tmp := t
                t := newT
                newT := addmod(tmp, sub(_P, mulmod(q, newT, _P)), _P)

                tmp := r
                r := x
                x := sub(tmp, mul(q, x))
            }
        }

        return t;
    }
}
IAuth.sol 44 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.16;

interface IAuth {
    /// @notice Thrown by protected function if caller not auth'ed.
    /// @param caller The caller's address.
    error NotAuthorized(address caller);

    /// @notice Emitted when auth granted to address.
    /// @param caller The caller's address.
    /// @param who The address auth got granted to.
    event AuthGranted(address indexed caller, address indexed who);

    /// @notice Emitted when auth renounced from address.
    /// @param caller The caller's address.
    /// @param who The address auth got renounced from.
    event AuthRenounced(address indexed caller, address indexed who);

    /// @notice Grants address `who` auth.
    /// @dev Only callable by auth'ed address.
    /// @param who The address to grant auth.
    function rely(address who) external;

    /// @notice Renounces address `who`'s auth.
    /// @dev Only callable by auth'ed address.
    /// @param who The address to renounce auth.
    function deny(address who) external;

    /// @notice Returns whether address `who` is auth'ed.
    /// @param who The address to check.
    /// @return True if `who` is auth'ed, false otherwise.
    function authed(address who) external view returns (bool);

    /// @notice Returns full list of addresses granted auth.
    /// @dev May contain duplicates.
    /// @return List of addresses granted auth.
    function authed() external view returns (address[] memory);

    /// @notice Returns whether address `who` is auth'ed.
    /// @custom:deprecated Use `authed(address)(bool)` instead.
    /// @param who The address to check.
    /// @return 1 if `who` is auth'ed, 0 otherwise.
    function wards(address who) external view returns (uint);
}

Read Contract

authed 0x0fce3415 → bool
authed 0x224242ca → address[]
barECDSA 0x5b2f7525 → uint8
barSchnorr 0xe891c095 → uint8
bud 0x4fce7a2a → uint256
constructChronicleSignedMessage 0xda7b7209 → bytes32
latestPoke 0x8265eed6 → uint256
name 0x06fdde03 → string
read 0x57de26a4 → uint256
readWithAge 0x393e5ede → uint256, uint256
tolled 0x3bee58f9 → bool
tolled 0x9954b0dc → address[]
tryRead 0x10b07b71 → bool, uint256
tryReadWithAge 0xceed3ef2 → bool, uint256, uint256
validatorsECDSA 0x38e3e013 → bool, address
validatorsECDSA 0x635d4acd → address[]
validatorsECDSA 0x8cf6a8f1 → bool
validatorsSchnorr 0x44baa5fd → address[]
validatorsSchnorr 0xcb8370a3 → bool
validatorsSchnorr 0xfc8717c3 → bool, address
wards 0xbf353dbb → uint256
wat 0x4ca29923 → bytes32

Write Contract 12 functions

These functions modify contract state and require a wallet transaction to execute.

deny 0x9c52a7f1
address who
diss 0x65c4ce7a
address who
dropECDSA 0x18969a2d
uint8[] ids
dropSchnorr 0x5ffcbba7
uint8[] ids
kiss 0xf29c29c4
address who
liftECDSA 0xa7260f9f
address[] validators
liftSchnorr 0x9476090e
tuple[] pubKeys
poke 0x6b24406f
tuple uPokeData
tuple schnorr
poke 0xc679f27d
tuple uPokeData
tuple[] ecdsas
rely 0x65fae35e
address who
setBarECDSA 0xef4097cc
uint8 bar
setBarSchnorr 0x0e947235
uint8 bar

Recent Transactions

No transactions found for this address