├── .config └── config.toml ├── .github ├── CODEOWNERS └── workflows │ └── rust-ci.yml ├── .gitignore ├── Cargo.toml ├── Makefile ├── README.md ├── abi ├── BundleHelper.json ├── HostOrders.json ├── Passage.json ├── RollupOrders.json ├── RollupPassage.json ├── Transactor.json └── Zenith.json ├── clippy.toml ├── rustfmt.toml └── src ├── bindings.rs ├── block.rs ├── bundle.rs ├── lib.rs ├── orders ├── agg.rs ├── mod.rs └── signed.rs ├── req.rs └── resp.rs /.config/config.toml: -------------------------------------------------------------------------------- 1 | [net] 2 | retry = 5 3 | git-fetch-with-cli = true 4 | 5 | [http] 6 | proxy = "" -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | *: @Evalir @prestwich @dylanlott 2 | abi/: @anna-carroll 3 | .github/: @rswanson -------------------------------------------------------------------------------- /.github/workflows/rust-ci.yml: -------------------------------------------------------------------------------- 1 | name: Rust CI 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: [main] 7 | pull_request: 8 | 9 | # simplest example of using the rust-base action 10 | jobs: 11 | rust-base: 12 | uses: init4tech/actions/.github/workflows/rust-library-base.yml@main 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | .vscode/launch.json 4 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "zenith-types" 3 | description = "Types for the zenith smart contracts" 4 | 5 | version = "0.15.0" 6 | edition = "2021" 7 | rust-version = "1.82" 8 | authors = ["Zenith Contributors"] 9 | homepage = "https://github.com/init4tech/zenith" 10 | repository = "https://github.com/init4tech/zenith" 11 | license = "AGPL-3.0" 12 | 13 | [dependencies] 14 | alloy = { version = "=0.11.1", features = ["full", "json-rpc", "signer-aws", "rpc-types-mev", "rlp"] } 15 | 16 | serde = { version = "1.0.197", features = ["derive"] } 17 | 18 | [dev-dependencies] 19 | serde_json = "1.0.94" 20 | tokio = { version = "1.37.0", features = ["macros"] } 21 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: build 2 | build: 3 | @cargo b --release 4 | 5 | .PHONY: clean 6 | clean: 7 | @cargo clean 8 | 9 | .PHONY: check 10 | check: 11 | @cargo check 12 | 13 | .PHONY: test 14 | test: 15 | @cargo test 16 | 17 | .PHONY: fmt 18 | fmt: 19 | @cargo +nightly fmt --all 20 | 21 | .PHONY: clippy 22 | clippy: 23 | @cargo clippy --all-targets --all-features -D warnings 24 | 25 | tidy: 26 | @cargo clippy --all-targets --all-features -D warnings && cargo +nightly fmt --all -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # THIS REPO HAS BEEN MERGED WITH [SIGNET-SDK] 2 | 3 | [SIGNET-SDK]: https://github.com/init4tech/signet-sdk 4 | 5 | # zenith-rs 6 | 7 | Rust types & utilities for working with [Zenith](https://github.com/init4tech/zenith). 8 | 9 | ![rust](https://github.com/init4tech/zenith-rs/actions/workflows/rust-ci.yml/badge.svg) ![ecr](https://github.com/init4tech/zenith-rs/actions/workflows/ecr-cd.yml/badge.svg) 10 | 11 | ## Development 12 | 13 | This project requires Rust 1.82.0 or newer. 14 | 15 | To build the project, run `make build`. To run tests, run `make test`. Before committing code, make sure to run `make tidy`, which will run clippy and format the whole project. 16 | -------------------------------------------------------------------------------- /abi/BundleHelper.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "constructor", 4 | "inputs": [ 5 | { 6 | "name": "_zenith", 7 | "type": "address", 8 | "internalType": "address" 9 | }, 10 | { 11 | "name": "_orders", 12 | "type": "address", 13 | "internalType": "address" 14 | } 15 | ], 16 | "stateMutability": "nonpayable" 17 | }, 18 | { 19 | "type": "function", 20 | "name": "orders", 21 | "inputs": [], 22 | "outputs": [ 23 | { 24 | "name": "", 25 | "type": "address", 26 | "internalType": "contract HostOrders" 27 | } 28 | ], 29 | "stateMutability": "view" 30 | }, 31 | { 32 | "type": "function", 33 | "name": "submit", 34 | "inputs": [ 35 | { 36 | "name": "fills", 37 | "type": "tuple[]", 38 | "internalType": "struct BundleHelper.FillPermit2[]", 39 | "components": [ 40 | { 41 | "name": "outputs", 42 | "type": "tuple[]", 43 | "internalType": "struct IOrders.Output[]", 44 | "components": [ 45 | { 46 | "name": "token", 47 | "type": "address", 48 | "internalType": "address" 49 | }, 50 | { 51 | "name": "amount", 52 | "type": "uint256", 53 | "internalType": "uint256" 54 | }, 55 | { 56 | "name": "recipient", 57 | "type": "address", 58 | "internalType": "address" 59 | }, 60 | { 61 | "name": "chainId", 62 | "type": "uint32", 63 | "internalType": "uint32" 64 | } 65 | ] 66 | }, 67 | { 68 | "name": "permit2", 69 | "type": "tuple", 70 | "internalType": "struct UsesPermit2.Permit2Batch", 71 | "components": [ 72 | { 73 | "name": "permit", 74 | "type": "tuple", 75 | "internalType": "struct ISignatureTransfer.PermitBatchTransferFrom", 76 | "components": [ 77 | { 78 | "name": "permitted", 79 | "type": "tuple[]", 80 | "internalType": "struct ISignatureTransfer.TokenPermissions[]", 81 | "components": [ 82 | { 83 | "name": "token", 84 | "type": "address", 85 | "internalType": "address" 86 | }, 87 | { 88 | "name": "amount", 89 | "type": "uint256", 90 | "internalType": "uint256" 91 | } 92 | ] 93 | }, 94 | { 95 | "name": "nonce", 96 | "type": "uint256", 97 | "internalType": "uint256" 98 | }, 99 | { 100 | "name": "deadline", 101 | "type": "uint256", 102 | "internalType": "uint256" 103 | } 104 | ] 105 | }, 106 | { 107 | "name": "owner", 108 | "type": "address", 109 | "internalType": "address" 110 | }, 111 | { 112 | "name": "signature", 113 | "type": "bytes", 114 | "internalType": "bytes" 115 | } 116 | ] 117 | } 118 | ] 119 | }, 120 | { 121 | "name": "header", 122 | "type": "tuple", 123 | "internalType": "struct Zenith.BlockHeader", 124 | "components": [ 125 | { 126 | "name": "rollupChainId", 127 | "type": "uint256", 128 | "internalType": "uint256" 129 | }, 130 | { 131 | "name": "hostBlockNumber", 132 | "type": "uint256", 133 | "internalType": "uint256" 134 | }, 135 | { 136 | "name": "gasLimit", 137 | "type": "uint256", 138 | "internalType": "uint256" 139 | }, 140 | { 141 | "name": "rewardAddress", 142 | "type": "address", 143 | "internalType": "address" 144 | }, 145 | { 146 | "name": "blockDataHash", 147 | "type": "bytes32", 148 | "internalType": "bytes32" 149 | } 150 | ] 151 | }, 152 | { 153 | "name": "v", 154 | "type": "uint8", 155 | "internalType": "uint8" 156 | }, 157 | { 158 | "name": "r", 159 | "type": "bytes32", 160 | "internalType": "bytes32" 161 | }, 162 | { 163 | "name": "s", 164 | "type": "bytes32", 165 | "internalType": "bytes32" 166 | } 167 | ], 168 | "outputs": [], 169 | "stateMutability": "nonpayable" 170 | }, 171 | { 172 | "type": "function", 173 | "name": "zenith", 174 | "inputs": [], 175 | "outputs": [ 176 | { 177 | "name": "", 178 | "type": "address", 179 | "internalType": "contract Zenith" 180 | } 181 | ], 182 | "stateMutability": "view" 183 | } 184 | ] -------------------------------------------------------------------------------- /abi/HostOrders.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "constructor", 4 | "inputs": [ 5 | { "name": "_permit2", "type": "address", "internalType": "address" } 6 | ], 7 | "stateMutability": "nonpayable" 8 | }, 9 | { 10 | "type": "function", 11 | "name": "fill", 12 | "inputs": [ 13 | { 14 | "name": "outputs", 15 | "type": "tuple[]", 16 | "internalType": "struct IOrders.Output[]", 17 | "components": [ 18 | { "name": "token", "type": "address", "internalType": "address" }, 19 | { "name": "amount", "type": "uint256", "internalType": "uint256" }, 20 | { "name": "recipient", "type": "address", "internalType": "address" }, 21 | { "name": "chainId", "type": "uint32", "internalType": "uint32" } 22 | ] 23 | } 24 | ], 25 | "outputs": [], 26 | "stateMutability": "payable" 27 | }, 28 | { 29 | "type": "function", 30 | "name": "fillPermit2", 31 | "inputs": [ 32 | { 33 | "name": "outputs", 34 | "type": "tuple[]", 35 | "internalType": "struct IOrders.Output[]", 36 | "components": [ 37 | { "name": "token", "type": "address", "internalType": "address" }, 38 | { "name": "amount", "type": "uint256", "internalType": "uint256" }, 39 | { "name": "recipient", "type": "address", "internalType": "address" }, 40 | { "name": "chainId", "type": "uint32", "internalType": "uint32" } 41 | ] 42 | }, 43 | { 44 | "name": "permit2", 45 | "type": "tuple", 46 | "internalType": "struct UsesPermit2.Permit2Batch", 47 | "components": [ 48 | { 49 | "name": "permit", 50 | "type": "tuple", 51 | "internalType": "struct ISignatureTransfer.PermitBatchTransferFrom", 52 | "components": [ 53 | { 54 | "name": "permitted", 55 | "type": "tuple[]", 56 | "internalType": "struct ISignatureTransfer.TokenPermissions[]", 57 | "components": [ 58 | { 59 | "name": "token", 60 | "type": "address", 61 | "internalType": "address" 62 | }, 63 | { 64 | "name": "amount", 65 | "type": "uint256", 66 | "internalType": "uint256" 67 | } 68 | ] 69 | }, 70 | { "name": "nonce", "type": "uint256", "internalType": "uint256" }, 71 | { 72 | "name": "deadline", 73 | "type": "uint256", 74 | "internalType": "uint256" 75 | } 76 | ] 77 | }, 78 | { "name": "owner", "type": "address", "internalType": "address" }, 79 | { "name": "signature", "type": "bytes", "internalType": "bytes" } 80 | ] 81 | } 82 | ], 83 | "outputs": [], 84 | "stateMutability": "nonpayable" 85 | }, 86 | { 87 | "type": "function", 88 | "name": "outputWitness", 89 | "inputs": [ 90 | { 91 | "name": "outputs", 92 | "type": "tuple[]", 93 | "internalType": "struct IOrders.Output[]", 94 | "components": [ 95 | { "name": "token", "type": "address", "internalType": "address" }, 96 | { "name": "amount", "type": "uint256", "internalType": "uint256" }, 97 | { "name": "recipient", "type": "address", "internalType": "address" }, 98 | { "name": "chainId", "type": "uint32", "internalType": "uint32" } 99 | ] 100 | } 101 | ], 102 | "outputs": [ 103 | { 104 | "name": "_witness", 105 | "type": "tuple", 106 | "internalType": "struct UsesPermit2.Witness", 107 | "components": [ 108 | { 109 | "name": "witnessHash", 110 | "type": "bytes32", 111 | "internalType": "bytes32" 112 | }, 113 | { 114 | "name": "witnessTypeString", 115 | "type": "string", 116 | "internalType": "string" 117 | } 118 | ] 119 | } 120 | ], 121 | "stateMutability": "pure" 122 | }, 123 | { 124 | "type": "event", 125 | "name": "Filled", 126 | "inputs": [ 127 | { 128 | "name": "outputs", 129 | "type": "tuple[]", 130 | "indexed": false, 131 | "internalType": "struct IOrders.Output[]", 132 | "components": [ 133 | { "name": "token", "type": "address", "internalType": "address" }, 134 | { "name": "amount", "type": "uint256", "internalType": "uint256" }, 135 | { "name": "recipient", "type": "address", "internalType": "address" }, 136 | { "name": "chainId", "type": "uint32", "internalType": "uint32" } 137 | ] 138 | } 139 | ], 140 | "anonymous": false 141 | }, 142 | { 143 | "type": "error", 144 | "name": "AddressEmptyCode", 145 | "inputs": [ 146 | { "name": "target", "type": "address", "internalType": "address" } 147 | ] 148 | }, 149 | { "type": "error", "name": "FailedCall", "inputs": [] }, 150 | { 151 | "type": "error", 152 | "name": "InsufficientBalance", 153 | "inputs": [ 154 | { "name": "balance", "type": "uint256", "internalType": "uint256" }, 155 | { "name": "needed", "type": "uint256", "internalType": "uint256" } 156 | ] 157 | }, 158 | { "type": "error", "name": "LengthMismatch", "inputs": [] }, 159 | { "type": "error", "name": "OutputMismatch", "inputs": [] }, 160 | { "type": "error", "name": "ReentrancyGuardReentrantCall", "inputs": [] }, 161 | { 162 | "type": "error", 163 | "name": "SafeERC20FailedOperation", 164 | "inputs": [ 165 | { "name": "token", "type": "address", "internalType": "address" } 166 | ] 167 | } 168 | ] 169 | -------------------------------------------------------------------------------- /abi/Passage.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "constructor", 4 | "inputs": [ 5 | { 6 | "name": "_defaultRollupChainId", 7 | "type": "uint256", 8 | "internalType": "uint256" 9 | }, 10 | { "name": "_tokenAdmin", "type": "address", "internalType": "address" }, 11 | { 12 | "name": "initialEnterTokens", 13 | "type": "address[]", 14 | "internalType": "address[]" 15 | }, 16 | { "name": "_permit2", "type": "address", "internalType": "address" } 17 | ], 18 | "stateMutability": "nonpayable" 19 | }, 20 | { "type": "fallback", "stateMutability": "payable" }, 21 | { "type": "receive", "stateMutability": "payable" }, 22 | { 23 | "type": "function", 24 | "name": "canEnter", 25 | "inputs": [{ "name": "", "type": "address", "internalType": "address" }], 26 | "outputs": [{ "name": "", "type": "bool", "internalType": "bool" }], 27 | "stateMutability": "view" 28 | }, 29 | { 30 | "type": "function", 31 | "name": "configureEnter", 32 | "inputs": [ 33 | { "name": "token", "type": "address", "internalType": "address" }, 34 | { "name": "_canEnter", "type": "bool", "internalType": "bool" } 35 | ], 36 | "outputs": [], 37 | "stateMutability": "nonpayable" 38 | }, 39 | { 40 | "type": "function", 41 | "name": "defaultRollupChainId", 42 | "inputs": [], 43 | "outputs": [{ "name": "", "type": "uint256", "internalType": "uint256" }], 44 | "stateMutability": "view" 45 | }, 46 | { 47 | "type": "function", 48 | "name": "enter", 49 | "inputs": [ 50 | { 51 | "name": "rollupRecipient", 52 | "type": "address", 53 | "internalType": "address" 54 | } 55 | ], 56 | "outputs": [], 57 | "stateMutability": "payable" 58 | }, 59 | { 60 | "type": "function", 61 | "name": "enter", 62 | "inputs": [ 63 | { "name": "rollupChainId", "type": "uint256", "internalType": "uint256" }, 64 | { 65 | "name": "rollupRecipient", 66 | "type": "address", 67 | "internalType": "address" 68 | } 69 | ], 70 | "outputs": [], 71 | "stateMutability": "payable" 72 | }, 73 | { 74 | "type": "function", 75 | "name": "enterToken", 76 | "inputs": [ 77 | { "name": "rollupChainId", "type": "uint256", "internalType": "uint256" }, 78 | { 79 | "name": "rollupRecipient", 80 | "type": "address", 81 | "internalType": "address" 82 | }, 83 | { "name": "token", "type": "address", "internalType": "address" }, 84 | { "name": "amount", "type": "uint256", "internalType": "uint256" } 85 | ], 86 | "outputs": [], 87 | "stateMutability": "nonpayable" 88 | }, 89 | { 90 | "type": "function", 91 | "name": "enterToken", 92 | "inputs": [ 93 | { 94 | "name": "rollupRecipient", 95 | "type": "address", 96 | "internalType": "address" 97 | }, 98 | { "name": "token", "type": "address", "internalType": "address" }, 99 | { "name": "amount", "type": "uint256", "internalType": "uint256" } 100 | ], 101 | "outputs": [], 102 | "stateMutability": "nonpayable" 103 | }, 104 | { 105 | "type": "function", 106 | "name": "enterTokenPermit2", 107 | "inputs": [ 108 | { "name": "rollupChainId", "type": "uint256", "internalType": "uint256" }, 109 | { 110 | "name": "rollupRecipient", 111 | "type": "address", 112 | "internalType": "address" 113 | }, 114 | { 115 | "name": "permit2", 116 | "type": "tuple", 117 | "internalType": "struct UsesPermit2.Permit2", 118 | "components": [ 119 | { 120 | "name": "permit", 121 | "type": "tuple", 122 | "internalType": "struct ISignatureTransfer.PermitTransferFrom", 123 | "components": [ 124 | { 125 | "name": "permitted", 126 | "type": "tuple", 127 | "internalType": "struct ISignatureTransfer.TokenPermissions", 128 | "components": [ 129 | { 130 | "name": "token", 131 | "type": "address", 132 | "internalType": "address" 133 | }, 134 | { 135 | "name": "amount", 136 | "type": "uint256", 137 | "internalType": "uint256" 138 | } 139 | ] 140 | }, 141 | { "name": "nonce", "type": "uint256", "internalType": "uint256" }, 142 | { 143 | "name": "deadline", 144 | "type": "uint256", 145 | "internalType": "uint256" 146 | } 147 | ] 148 | }, 149 | { "name": "owner", "type": "address", "internalType": "address" }, 150 | { "name": "signature", "type": "bytes", "internalType": "bytes" } 151 | ] 152 | } 153 | ], 154 | "outputs": [], 155 | "stateMutability": "nonpayable" 156 | }, 157 | { 158 | "type": "function", 159 | "name": "enterWitness", 160 | "inputs": [ 161 | { "name": "rollupChainId", "type": "uint256", "internalType": "uint256" }, 162 | { 163 | "name": "rollupRecipient", 164 | "type": "address", 165 | "internalType": "address" 166 | } 167 | ], 168 | "outputs": [ 169 | { 170 | "name": "_witness", 171 | "type": "tuple", 172 | "internalType": "struct UsesPermit2.Witness", 173 | "components": [ 174 | { 175 | "name": "witnessHash", 176 | "type": "bytes32", 177 | "internalType": "bytes32" 178 | }, 179 | { 180 | "name": "witnessTypeString", 181 | "type": "string", 182 | "internalType": "string" 183 | } 184 | ] 185 | } 186 | ], 187 | "stateMutability": "pure" 188 | }, 189 | { 190 | "type": "function", 191 | "name": "exitWitness", 192 | "inputs": [ 193 | { "name": "hostRecipient", "type": "address", "internalType": "address" } 194 | ], 195 | "outputs": [ 196 | { 197 | "name": "_witness", 198 | "type": "tuple", 199 | "internalType": "struct UsesPermit2.Witness", 200 | "components": [ 201 | { 202 | "name": "witnessHash", 203 | "type": "bytes32", 204 | "internalType": "bytes32" 205 | }, 206 | { 207 | "name": "witnessTypeString", 208 | "type": "string", 209 | "internalType": "string" 210 | } 211 | ] 212 | } 213 | ], 214 | "stateMutability": "pure" 215 | }, 216 | { 217 | "type": "function", 218 | "name": "tokenAdmin", 219 | "inputs": [], 220 | "outputs": [{ "name": "", "type": "address", "internalType": "address" }], 221 | "stateMutability": "view" 222 | }, 223 | { 224 | "type": "function", 225 | "name": "withdraw", 226 | "inputs": [ 227 | { "name": "token", "type": "address", "internalType": "address" }, 228 | { "name": "recipient", "type": "address", "internalType": "address" }, 229 | { "name": "amount", "type": "uint256", "internalType": "uint256" } 230 | ], 231 | "outputs": [], 232 | "stateMutability": "nonpayable" 233 | }, 234 | { 235 | "type": "event", 236 | "name": "Enter", 237 | "inputs": [ 238 | { 239 | "name": "rollupChainId", 240 | "type": "uint256", 241 | "indexed": true, 242 | "internalType": "uint256" 243 | }, 244 | { 245 | "name": "rollupRecipient", 246 | "type": "address", 247 | "indexed": true, 248 | "internalType": "address" 249 | }, 250 | { 251 | "name": "amount", 252 | "type": "uint256", 253 | "indexed": false, 254 | "internalType": "uint256" 255 | } 256 | ], 257 | "anonymous": false 258 | }, 259 | { 260 | "type": "event", 261 | "name": "EnterConfigured", 262 | "inputs": [ 263 | { 264 | "name": "token", 265 | "type": "address", 266 | "indexed": true, 267 | "internalType": "address" 268 | }, 269 | { 270 | "name": "canEnter", 271 | "type": "bool", 272 | "indexed": true, 273 | "internalType": "bool" 274 | } 275 | ], 276 | "anonymous": false 277 | }, 278 | { 279 | "type": "event", 280 | "name": "EnterToken", 281 | "inputs": [ 282 | { 283 | "name": "rollupChainId", 284 | "type": "uint256", 285 | "indexed": true, 286 | "internalType": "uint256" 287 | }, 288 | { 289 | "name": "rollupRecipient", 290 | "type": "address", 291 | "indexed": true, 292 | "internalType": "address" 293 | }, 294 | { 295 | "name": "token", 296 | "type": "address", 297 | "indexed": true, 298 | "internalType": "address" 299 | }, 300 | { 301 | "name": "amount", 302 | "type": "uint256", 303 | "indexed": false, 304 | "internalType": "uint256" 305 | } 306 | ], 307 | "anonymous": false 308 | }, 309 | { 310 | "type": "event", 311 | "name": "Withdrawal", 312 | "inputs": [ 313 | { 314 | "name": "token", 315 | "type": "address", 316 | "indexed": true, 317 | "internalType": "address" 318 | }, 319 | { 320 | "name": "recipient", 321 | "type": "address", 322 | "indexed": true, 323 | "internalType": "address" 324 | }, 325 | { 326 | "name": "amount", 327 | "type": "uint256", 328 | "indexed": false, 329 | "internalType": "uint256" 330 | } 331 | ], 332 | "anonymous": false 333 | }, 334 | { 335 | "type": "error", 336 | "name": "AddressEmptyCode", 337 | "inputs": [ 338 | { "name": "target", "type": "address", "internalType": "address" } 339 | ] 340 | }, 341 | { 342 | "type": "error", 343 | "name": "DisallowedEnter", 344 | "inputs": [ 345 | { "name": "token", "type": "address", "internalType": "address" } 346 | ] 347 | }, 348 | { "type": "error", "name": "FailedCall", "inputs": [] }, 349 | { 350 | "type": "error", 351 | "name": "InsufficientBalance", 352 | "inputs": [ 353 | { "name": "balance", "type": "uint256", "internalType": "uint256" }, 354 | { "name": "needed", "type": "uint256", "internalType": "uint256" } 355 | ] 356 | }, 357 | { "type": "error", "name": "OnlyTokenAdmin", "inputs": [] }, 358 | { "type": "error", "name": "ReentrancyGuardReentrantCall", "inputs": [] }, 359 | { 360 | "type": "error", 361 | "name": "SafeERC20FailedOperation", 362 | "inputs": [ 363 | { "name": "token", "type": "address", "internalType": "address" } 364 | ] 365 | } 366 | ] 367 | -------------------------------------------------------------------------------- /abi/RollupOrders.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "constructor", 4 | "inputs": [ 5 | { "name": "_permit2", "type": "address", "internalType": "address" } 6 | ], 7 | "stateMutability": "nonpayable" 8 | }, 9 | { 10 | "type": "function", 11 | "name": "fill", 12 | "inputs": [ 13 | { 14 | "name": "outputs", 15 | "type": "tuple[]", 16 | "internalType": "struct IOrders.Output[]", 17 | "components": [ 18 | { "name": "token", "type": "address", "internalType": "address" }, 19 | { "name": "amount", "type": "uint256", "internalType": "uint256" }, 20 | { "name": "recipient", "type": "address", "internalType": "address" }, 21 | { "name": "chainId", "type": "uint32", "internalType": "uint32" } 22 | ] 23 | } 24 | ], 25 | "outputs": [], 26 | "stateMutability": "payable" 27 | }, 28 | { 29 | "type": "function", 30 | "name": "fillPermit2", 31 | "inputs": [ 32 | { 33 | "name": "outputs", 34 | "type": "tuple[]", 35 | "internalType": "struct IOrders.Output[]", 36 | "components": [ 37 | { "name": "token", "type": "address", "internalType": "address" }, 38 | { "name": "amount", "type": "uint256", "internalType": "uint256" }, 39 | { "name": "recipient", "type": "address", "internalType": "address" }, 40 | { "name": "chainId", "type": "uint32", "internalType": "uint32" } 41 | ] 42 | }, 43 | { 44 | "name": "permit2", 45 | "type": "tuple", 46 | "internalType": "struct UsesPermit2.Permit2Batch", 47 | "components": [ 48 | { 49 | "name": "permit", 50 | "type": "tuple", 51 | "internalType": "struct ISignatureTransfer.PermitBatchTransferFrom", 52 | "components": [ 53 | { 54 | "name": "permitted", 55 | "type": "tuple[]", 56 | "internalType": "struct ISignatureTransfer.TokenPermissions[]", 57 | "components": [ 58 | { 59 | "name": "token", 60 | "type": "address", 61 | "internalType": "address" 62 | }, 63 | { 64 | "name": "amount", 65 | "type": "uint256", 66 | "internalType": "uint256" 67 | } 68 | ] 69 | }, 70 | { "name": "nonce", "type": "uint256", "internalType": "uint256" }, 71 | { 72 | "name": "deadline", 73 | "type": "uint256", 74 | "internalType": "uint256" 75 | } 76 | ] 77 | }, 78 | { "name": "owner", "type": "address", "internalType": "address" }, 79 | { "name": "signature", "type": "bytes", "internalType": "bytes" } 80 | ] 81 | } 82 | ], 83 | "outputs": [], 84 | "stateMutability": "nonpayable" 85 | }, 86 | { 87 | "type": "function", 88 | "name": "initiate", 89 | "inputs": [ 90 | { "name": "deadline", "type": "uint256", "internalType": "uint256" }, 91 | { 92 | "name": "inputs", 93 | "type": "tuple[]", 94 | "internalType": "struct IOrders.Input[]", 95 | "components": [ 96 | { "name": "token", "type": "address", "internalType": "address" }, 97 | { "name": "amount", "type": "uint256", "internalType": "uint256" } 98 | ] 99 | }, 100 | { 101 | "name": "outputs", 102 | "type": "tuple[]", 103 | "internalType": "struct IOrders.Output[]", 104 | "components": [ 105 | { "name": "token", "type": "address", "internalType": "address" }, 106 | { "name": "amount", "type": "uint256", "internalType": "uint256" }, 107 | { "name": "recipient", "type": "address", "internalType": "address" }, 108 | { "name": "chainId", "type": "uint32", "internalType": "uint32" } 109 | ] 110 | } 111 | ], 112 | "outputs": [], 113 | "stateMutability": "payable" 114 | }, 115 | { 116 | "type": "function", 117 | "name": "initiatePermit2", 118 | "inputs": [ 119 | { 120 | "name": "tokenRecipient", 121 | "type": "address", 122 | "internalType": "address" 123 | }, 124 | { 125 | "name": "outputs", 126 | "type": "tuple[]", 127 | "internalType": "struct IOrders.Output[]", 128 | "components": [ 129 | { "name": "token", "type": "address", "internalType": "address" }, 130 | { "name": "amount", "type": "uint256", "internalType": "uint256" }, 131 | { "name": "recipient", "type": "address", "internalType": "address" }, 132 | { "name": "chainId", "type": "uint32", "internalType": "uint32" } 133 | ] 134 | }, 135 | { 136 | "name": "permit2", 137 | "type": "tuple", 138 | "internalType": "struct UsesPermit2.Permit2Batch", 139 | "components": [ 140 | { 141 | "name": "permit", 142 | "type": "tuple", 143 | "internalType": "struct ISignatureTransfer.PermitBatchTransferFrom", 144 | "components": [ 145 | { 146 | "name": "permitted", 147 | "type": "tuple[]", 148 | "internalType": "struct ISignatureTransfer.TokenPermissions[]", 149 | "components": [ 150 | { 151 | "name": "token", 152 | "type": "address", 153 | "internalType": "address" 154 | }, 155 | { 156 | "name": "amount", 157 | "type": "uint256", 158 | "internalType": "uint256" 159 | } 160 | ] 161 | }, 162 | { "name": "nonce", "type": "uint256", "internalType": "uint256" }, 163 | { 164 | "name": "deadline", 165 | "type": "uint256", 166 | "internalType": "uint256" 167 | } 168 | ] 169 | }, 170 | { "name": "owner", "type": "address", "internalType": "address" }, 171 | { "name": "signature", "type": "bytes", "internalType": "bytes" } 172 | ] 173 | } 174 | ], 175 | "outputs": [], 176 | "stateMutability": "nonpayable" 177 | }, 178 | { 179 | "type": "function", 180 | "name": "outputWitness", 181 | "inputs": [ 182 | { 183 | "name": "outputs", 184 | "type": "tuple[]", 185 | "internalType": "struct IOrders.Output[]", 186 | "components": [ 187 | { "name": "token", "type": "address", "internalType": "address" }, 188 | { "name": "amount", "type": "uint256", "internalType": "uint256" }, 189 | { "name": "recipient", "type": "address", "internalType": "address" }, 190 | { "name": "chainId", "type": "uint32", "internalType": "uint32" } 191 | ] 192 | } 193 | ], 194 | "outputs": [ 195 | { 196 | "name": "_witness", 197 | "type": "tuple", 198 | "internalType": "struct UsesPermit2.Witness", 199 | "components": [ 200 | { 201 | "name": "witnessHash", 202 | "type": "bytes32", 203 | "internalType": "bytes32" 204 | }, 205 | { 206 | "name": "witnessTypeString", 207 | "type": "string", 208 | "internalType": "string" 209 | } 210 | ] 211 | } 212 | ], 213 | "stateMutability": "pure" 214 | }, 215 | { 216 | "type": "function", 217 | "name": "sweep", 218 | "inputs": [ 219 | { "name": "recipient", "type": "address", "internalType": "address" }, 220 | { "name": "token", "type": "address", "internalType": "address" }, 221 | { "name": "amount", "type": "uint256", "internalType": "uint256" } 222 | ], 223 | "outputs": [], 224 | "stateMutability": "nonpayable" 225 | }, 226 | { 227 | "type": "event", 228 | "name": "Filled", 229 | "inputs": [ 230 | { 231 | "name": "outputs", 232 | "type": "tuple[]", 233 | "indexed": false, 234 | "internalType": "struct IOrders.Output[]", 235 | "components": [ 236 | { "name": "token", "type": "address", "internalType": "address" }, 237 | { "name": "amount", "type": "uint256", "internalType": "uint256" }, 238 | { "name": "recipient", "type": "address", "internalType": "address" }, 239 | { "name": "chainId", "type": "uint32", "internalType": "uint32" } 240 | ] 241 | } 242 | ], 243 | "anonymous": false 244 | }, 245 | { 246 | "type": "event", 247 | "name": "Order", 248 | "inputs": [ 249 | { 250 | "name": "deadline", 251 | "type": "uint256", 252 | "indexed": false, 253 | "internalType": "uint256" 254 | }, 255 | { 256 | "name": "inputs", 257 | "type": "tuple[]", 258 | "indexed": false, 259 | "internalType": "struct IOrders.Input[]", 260 | "components": [ 261 | { "name": "token", "type": "address", "internalType": "address" }, 262 | { "name": "amount", "type": "uint256", "internalType": "uint256" } 263 | ] 264 | }, 265 | { 266 | "name": "outputs", 267 | "type": "tuple[]", 268 | "indexed": false, 269 | "internalType": "struct IOrders.Output[]", 270 | "components": [ 271 | { "name": "token", "type": "address", "internalType": "address" }, 272 | { "name": "amount", "type": "uint256", "internalType": "uint256" }, 273 | { "name": "recipient", "type": "address", "internalType": "address" }, 274 | { "name": "chainId", "type": "uint32", "internalType": "uint32" } 275 | ] 276 | } 277 | ], 278 | "anonymous": false 279 | }, 280 | { 281 | "type": "event", 282 | "name": "Sweep", 283 | "inputs": [ 284 | { 285 | "name": "recipient", 286 | "type": "address", 287 | "indexed": true, 288 | "internalType": "address" 289 | }, 290 | { 291 | "name": "token", 292 | "type": "address", 293 | "indexed": true, 294 | "internalType": "address" 295 | }, 296 | { 297 | "name": "amount", 298 | "type": "uint256", 299 | "indexed": false, 300 | "internalType": "uint256" 301 | } 302 | ], 303 | "anonymous": false 304 | }, 305 | { 306 | "type": "error", 307 | "name": "AddressEmptyCode", 308 | "inputs": [ 309 | { "name": "target", "type": "address", "internalType": "address" } 310 | ] 311 | }, 312 | { "type": "error", "name": "FailedCall", "inputs": [] }, 313 | { 314 | "type": "error", 315 | "name": "InsufficientBalance", 316 | "inputs": [ 317 | { "name": "balance", "type": "uint256", "internalType": "uint256" }, 318 | { "name": "needed", "type": "uint256", "internalType": "uint256" } 319 | ] 320 | }, 321 | { "type": "error", "name": "LengthMismatch", "inputs": [] }, 322 | { "type": "error", "name": "OrderExpired", "inputs": [] }, 323 | { "type": "error", "name": "OutputMismatch", "inputs": [] }, 324 | { "type": "error", "name": "ReentrancyGuardReentrantCall", "inputs": [] }, 325 | { 326 | "type": "error", 327 | "name": "SafeERC20FailedOperation", 328 | "inputs": [ 329 | { "name": "token", "type": "address", "internalType": "address" } 330 | ] 331 | } 332 | ] 333 | -------------------------------------------------------------------------------- /abi/RollupPassage.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "constructor", 4 | "inputs": [ 5 | { "name": "_permit2", "type": "address", "internalType": "address" } 6 | ], 7 | "stateMutability": "nonpayable" 8 | }, 9 | { "type": "fallback", "stateMutability": "payable" }, 10 | { "type": "receive", "stateMutability": "payable" }, 11 | { 12 | "type": "function", 13 | "name": "enterWitness", 14 | "inputs": [ 15 | { "name": "rollupChainId", "type": "uint256", "internalType": "uint256" }, 16 | { 17 | "name": "rollupRecipient", 18 | "type": "address", 19 | "internalType": "address" 20 | } 21 | ], 22 | "outputs": [ 23 | { 24 | "name": "_witness", 25 | "type": "tuple", 26 | "internalType": "struct UsesPermit2.Witness", 27 | "components": [ 28 | { 29 | "name": "witnessHash", 30 | "type": "bytes32", 31 | "internalType": "bytes32" 32 | }, 33 | { 34 | "name": "witnessTypeString", 35 | "type": "string", 36 | "internalType": "string" 37 | } 38 | ] 39 | } 40 | ], 41 | "stateMutability": "pure" 42 | }, 43 | { 44 | "type": "function", 45 | "name": "exit", 46 | "inputs": [ 47 | { "name": "hostRecipient", "type": "address", "internalType": "address" } 48 | ], 49 | "outputs": [], 50 | "stateMutability": "payable" 51 | }, 52 | { 53 | "type": "function", 54 | "name": "exitToken", 55 | "inputs": [ 56 | { "name": "hostRecipient", "type": "address", "internalType": "address" }, 57 | { "name": "token", "type": "address", "internalType": "address" }, 58 | { "name": "amount", "type": "uint256", "internalType": "uint256" } 59 | ], 60 | "outputs": [], 61 | "stateMutability": "nonpayable" 62 | }, 63 | { 64 | "type": "function", 65 | "name": "exitTokenPermit2", 66 | "inputs": [ 67 | { "name": "hostRecipient", "type": "address", "internalType": "address" }, 68 | { 69 | "name": "permit2", 70 | "type": "tuple", 71 | "internalType": "struct UsesPermit2.Permit2", 72 | "components": [ 73 | { 74 | "name": "permit", 75 | "type": "tuple", 76 | "internalType": "struct ISignatureTransfer.PermitTransferFrom", 77 | "components": [ 78 | { 79 | "name": "permitted", 80 | "type": "tuple", 81 | "internalType": "struct ISignatureTransfer.TokenPermissions", 82 | "components": [ 83 | { 84 | "name": "token", 85 | "type": "address", 86 | "internalType": "address" 87 | }, 88 | { 89 | "name": "amount", 90 | "type": "uint256", 91 | "internalType": "uint256" 92 | } 93 | ] 94 | }, 95 | { "name": "nonce", "type": "uint256", "internalType": "uint256" }, 96 | { 97 | "name": "deadline", 98 | "type": "uint256", 99 | "internalType": "uint256" 100 | } 101 | ] 102 | }, 103 | { "name": "owner", "type": "address", "internalType": "address" }, 104 | { "name": "signature", "type": "bytes", "internalType": "bytes" } 105 | ] 106 | } 107 | ], 108 | "outputs": [], 109 | "stateMutability": "nonpayable" 110 | }, 111 | { 112 | "type": "function", 113 | "name": "exitWitness", 114 | "inputs": [ 115 | { "name": "hostRecipient", "type": "address", "internalType": "address" } 116 | ], 117 | "outputs": [ 118 | { 119 | "name": "_witness", 120 | "type": "tuple", 121 | "internalType": "struct UsesPermit2.Witness", 122 | "components": [ 123 | { 124 | "name": "witnessHash", 125 | "type": "bytes32", 126 | "internalType": "bytes32" 127 | }, 128 | { 129 | "name": "witnessTypeString", 130 | "type": "string", 131 | "internalType": "string" 132 | } 133 | ] 134 | } 135 | ], 136 | "stateMutability": "pure" 137 | }, 138 | { 139 | "type": "event", 140 | "name": "Exit", 141 | "inputs": [ 142 | { 143 | "name": "hostRecipient", 144 | "type": "address", 145 | "indexed": true, 146 | "internalType": "address" 147 | }, 148 | { 149 | "name": "amount", 150 | "type": "uint256", 151 | "indexed": false, 152 | "internalType": "uint256" 153 | } 154 | ], 155 | "anonymous": false 156 | }, 157 | { 158 | "type": "event", 159 | "name": "ExitToken", 160 | "inputs": [ 161 | { 162 | "name": "hostRecipient", 163 | "type": "address", 164 | "indexed": true, 165 | "internalType": "address" 166 | }, 167 | { 168 | "name": "token", 169 | "type": "address", 170 | "indexed": true, 171 | "internalType": "address" 172 | }, 173 | { 174 | "name": "amount", 175 | "type": "uint256", 176 | "indexed": false, 177 | "internalType": "uint256" 178 | } 179 | ], 180 | "anonymous": false 181 | }, 182 | { 183 | "type": "error", 184 | "name": "AddressEmptyCode", 185 | "inputs": [ 186 | { "name": "target", "type": "address", "internalType": "address" } 187 | ] 188 | }, 189 | { "type": "error", "name": "FailedCall", "inputs": [] }, 190 | { 191 | "type": "error", 192 | "name": "InsufficientBalance", 193 | "inputs": [ 194 | { "name": "balance", "type": "uint256", "internalType": "uint256" }, 195 | { "name": "needed", "type": "uint256", "internalType": "uint256" } 196 | ] 197 | }, 198 | { "type": "error", "name": "ReentrancyGuardReentrantCall", "inputs": [] }, 199 | { 200 | "type": "error", 201 | "name": "SafeERC20FailedOperation", 202 | "inputs": [ 203 | { "name": "token", "type": "address", "internalType": "address" } 204 | ] 205 | } 206 | ] 207 | -------------------------------------------------------------------------------- /abi/Transactor.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "constructor", 4 | "inputs": [ 5 | { 6 | "name": "_defaultRollupChainId", 7 | "type": "uint256", 8 | "internalType": "uint256" 9 | }, 10 | { "name": "_gasAdmin", "type": "address", "internalType": "address" }, 11 | { 12 | "name": "_passage", 13 | "type": "address", 14 | "internalType": "contract Passage" 15 | }, 16 | { 17 | "name": "_perBlockGasLimit", 18 | "type": "uint256", 19 | "internalType": "uint256" 20 | }, 21 | { 22 | "name": "_perTransactGasLimit", 23 | "type": "uint256", 24 | "internalType": "uint256" 25 | } 26 | ], 27 | "stateMutability": "nonpayable" 28 | }, 29 | { 30 | "type": "function", 31 | "name": "configureGas", 32 | "inputs": [ 33 | { "name": "perBlock", "type": "uint256", "internalType": "uint256" }, 34 | { "name": "perTransact", "type": "uint256", "internalType": "uint256" } 35 | ], 36 | "outputs": [], 37 | "stateMutability": "nonpayable" 38 | }, 39 | { 40 | "type": "function", 41 | "name": "defaultRollupChainId", 42 | "inputs": [], 43 | "outputs": [{ "name": "", "type": "uint256", "internalType": "uint256" }], 44 | "stateMutability": "view" 45 | }, 46 | { 47 | "type": "function", 48 | "name": "enterTransact", 49 | "inputs": [ 50 | { "name": "rollupChainId", "type": "uint256", "internalType": "uint256" }, 51 | { 52 | "name": "etherRecipient", 53 | "type": "address", 54 | "internalType": "address" 55 | }, 56 | { "name": "to", "type": "address", "internalType": "address" }, 57 | { "name": "data", "type": "bytes", "internalType": "bytes" }, 58 | { "name": "value", "type": "uint256", "internalType": "uint256" }, 59 | { "name": "gas", "type": "uint256", "internalType": "uint256" }, 60 | { "name": "maxFeePerGas", "type": "uint256", "internalType": "uint256" } 61 | ], 62 | "outputs": [], 63 | "stateMutability": "payable" 64 | }, 65 | { 66 | "type": "function", 67 | "name": "gasAdmin", 68 | "inputs": [], 69 | "outputs": [{ "name": "", "type": "address", "internalType": "address" }], 70 | "stateMutability": "view" 71 | }, 72 | { 73 | "type": "function", 74 | "name": "passage", 75 | "inputs": [], 76 | "outputs": [ 77 | { "name": "", "type": "address", "internalType": "contract Passage" } 78 | ], 79 | "stateMutability": "view" 80 | }, 81 | { 82 | "type": "function", 83 | "name": "perBlockGasLimit", 84 | "inputs": [], 85 | "outputs": [{ "name": "", "type": "uint256", "internalType": "uint256" }], 86 | "stateMutability": "view" 87 | }, 88 | { 89 | "type": "function", 90 | "name": "perTransactGasLimit", 91 | "inputs": [], 92 | "outputs": [{ "name": "", "type": "uint256", "internalType": "uint256" }], 93 | "stateMutability": "view" 94 | }, 95 | { 96 | "type": "function", 97 | "name": "transact", 98 | "inputs": [ 99 | { "name": "to", "type": "address", "internalType": "address" }, 100 | { "name": "data", "type": "bytes", "internalType": "bytes" }, 101 | { "name": "value", "type": "uint256", "internalType": "uint256" }, 102 | { "name": "gas", "type": "uint256", "internalType": "uint256" }, 103 | { "name": "maxFeePerGas", "type": "uint256", "internalType": "uint256" } 104 | ], 105 | "outputs": [], 106 | "stateMutability": "payable" 107 | }, 108 | { 109 | "type": "function", 110 | "name": "transact", 111 | "inputs": [ 112 | { "name": "rollupChainId", "type": "uint256", "internalType": "uint256" }, 113 | { "name": "to", "type": "address", "internalType": "address" }, 114 | { "name": "data", "type": "bytes", "internalType": "bytes" }, 115 | { "name": "value", "type": "uint256", "internalType": "uint256" }, 116 | { "name": "gas", "type": "uint256", "internalType": "uint256" }, 117 | { "name": "maxFeePerGas", "type": "uint256", "internalType": "uint256" } 118 | ], 119 | "outputs": [], 120 | "stateMutability": "payable" 121 | }, 122 | { 123 | "type": "function", 124 | "name": "transactGasUsed", 125 | "inputs": [ 126 | { "name": "", "type": "uint256", "internalType": "uint256" }, 127 | { "name": "", "type": "uint256", "internalType": "uint256" } 128 | ], 129 | "outputs": [{ "name": "", "type": "uint256", "internalType": "uint256" }], 130 | "stateMutability": "view" 131 | }, 132 | { 133 | "type": "event", 134 | "name": "GasConfigured", 135 | "inputs": [ 136 | { 137 | "name": "perBlock", 138 | "type": "uint256", 139 | "indexed": false, 140 | "internalType": "uint256" 141 | }, 142 | { 143 | "name": "perTransact", 144 | "type": "uint256", 145 | "indexed": false, 146 | "internalType": "uint256" 147 | } 148 | ], 149 | "anonymous": false 150 | }, 151 | { 152 | "type": "event", 153 | "name": "Transact", 154 | "inputs": [ 155 | { 156 | "name": "rollupChainId", 157 | "type": "uint256", 158 | "indexed": true, 159 | "internalType": "uint256" 160 | }, 161 | { 162 | "name": "sender", 163 | "type": "address", 164 | "indexed": true, 165 | "internalType": "address" 166 | }, 167 | { 168 | "name": "to", 169 | "type": "address", 170 | "indexed": true, 171 | "internalType": "address" 172 | }, 173 | { 174 | "name": "data", 175 | "type": "bytes", 176 | "indexed": false, 177 | "internalType": "bytes" 178 | }, 179 | { 180 | "name": "value", 181 | "type": "uint256", 182 | "indexed": false, 183 | "internalType": "uint256" 184 | }, 185 | { 186 | "name": "gas", 187 | "type": "uint256", 188 | "indexed": false, 189 | "internalType": "uint256" 190 | }, 191 | { 192 | "name": "maxFeePerGas", 193 | "type": "uint256", 194 | "indexed": false, 195 | "internalType": "uint256" 196 | } 197 | ], 198 | "anonymous": false 199 | }, 200 | { "type": "error", "name": "OnlyGasAdmin", "inputs": [] }, 201 | { "type": "error", "name": "PerBlockTransactGasLimit", "inputs": [] }, 202 | { "type": "error", "name": "PerTransactGasLimit", "inputs": [] } 203 | ] 204 | -------------------------------------------------------------------------------- /abi/Zenith.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "constructor", 4 | "inputs": [ 5 | { 6 | "name": "_sequencerAdmin", 7 | "type": "address", 8 | "internalType": "address" 9 | } 10 | ], 11 | "stateMutability": "nonpayable" 12 | }, 13 | { 14 | "type": "function", 15 | "name": "addSequencer", 16 | "inputs": [ 17 | { "name": "sequencer", "type": "address", "internalType": "address" } 18 | ], 19 | "outputs": [], 20 | "stateMutability": "nonpayable" 21 | }, 22 | { 23 | "type": "function", 24 | "name": "blockCommitment", 25 | "inputs": [ 26 | { 27 | "name": "header", 28 | "type": "tuple", 29 | "internalType": "struct Zenith.BlockHeader", 30 | "components": [ 31 | { 32 | "name": "rollupChainId", 33 | "type": "uint256", 34 | "internalType": "uint256" 35 | }, 36 | { 37 | "name": "hostBlockNumber", 38 | "type": "uint256", 39 | "internalType": "uint256" 40 | }, 41 | { "name": "gasLimit", "type": "uint256", "internalType": "uint256" }, 42 | { 43 | "name": "rewardAddress", 44 | "type": "address", 45 | "internalType": "address" 46 | }, 47 | { 48 | "name": "blockDataHash", 49 | "type": "bytes32", 50 | "internalType": "bytes32" 51 | } 52 | ] 53 | } 54 | ], 55 | "outputs": [ 56 | { "name": "commit", "type": "bytes32", "internalType": "bytes32" } 57 | ], 58 | "stateMutability": "view" 59 | }, 60 | { 61 | "type": "function", 62 | "name": "deployBlockNumber", 63 | "inputs": [], 64 | "outputs": [{ "name": "", "type": "uint256", "internalType": "uint256" }], 65 | "stateMutability": "view" 66 | }, 67 | { 68 | "type": "function", 69 | "name": "isSequencer", 70 | "inputs": [{ "name": "", "type": "address", "internalType": "address" }], 71 | "outputs": [{ "name": "", "type": "bool", "internalType": "bool" }], 72 | "stateMutability": "view" 73 | }, 74 | { 75 | "type": "function", 76 | "name": "lastSubmittedAtBlock", 77 | "inputs": [{ "name": "", "type": "uint256", "internalType": "uint256" }], 78 | "outputs": [{ "name": "", "type": "uint256", "internalType": "uint256" }], 79 | "stateMutability": "view" 80 | }, 81 | { 82 | "type": "function", 83 | "name": "removeSequencer", 84 | "inputs": [ 85 | { "name": "sequencer", "type": "address", "internalType": "address" } 86 | ], 87 | "outputs": [], 88 | "stateMutability": "nonpayable" 89 | }, 90 | { 91 | "type": "function", 92 | "name": "sequencerAdmin", 93 | "inputs": [], 94 | "outputs": [{ "name": "", "type": "address", "internalType": "address" }], 95 | "stateMutability": "view" 96 | }, 97 | { 98 | "type": "function", 99 | "name": "submitBlock", 100 | "inputs": [ 101 | { 102 | "name": "header", 103 | "type": "tuple", 104 | "internalType": "struct Zenith.BlockHeader", 105 | "components": [ 106 | { 107 | "name": "rollupChainId", 108 | "type": "uint256", 109 | "internalType": "uint256" 110 | }, 111 | { 112 | "name": "hostBlockNumber", 113 | "type": "uint256", 114 | "internalType": "uint256" 115 | }, 116 | { "name": "gasLimit", "type": "uint256", "internalType": "uint256" }, 117 | { 118 | "name": "rewardAddress", 119 | "type": "address", 120 | "internalType": "address" 121 | }, 122 | { 123 | "name": "blockDataHash", 124 | "type": "bytes32", 125 | "internalType": "bytes32" 126 | } 127 | ] 128 | }, 129 | { "name": "v", "type": "uint8", "internalType": "uint8" }, 130 | { "name": "r", "type": "bytes32", "internalType": "bytes32" }, 131 | { "name": "s", "type": "bytes32", "internalType": "bytes32" }, 132 | { "name": "", "type": "bytes", "internalType": "bytes" } 133 | ], 134 | "outputs": [], 135 | "stateMutability": "nonpayable" 136 | }, 137 | { 138 | "type": "event", 139 | "name": "BlockSubmitted", 140 | "inputs": [ 141 | { 142 | "name": "sequencer", 143 | "type": "address", 144 | "indexed": true, 145 | "internalType": "address" 146 | }, 147 | { 148 | "name": "rollupChainId", 149 | "type": "uint256", 150 | "indexed": true, 151 | "internalType": "uint256" 152 | }, 153 | { 154 | "name": "gasLimit", 155 | "type": "uint256", 156 | "indexed": false, 157 | "internalType": "uint256" 158 | }, 159 | { 160 | "name": "rewardAddress", 161 | "type": "address", 162 | "indexed": false, 163 | "internalType": "address" 164 | }, 165 | { 166 | "name": "blockDataHash", 167 | "type": "bytes32", 168 | "indexed": false, 169 | "internalType": "bytes32" 170 | } 171 | ], 172 | "anonymous": false 173 | }, 174 | { 175 | "type": "event", 176 | "name": "SequencerSet", 177 | "inputs": [ 178 | { 179 | "name": "sequencer", 180 | "type": "address", 181 | "indexed": true, 182 | "internalType": "address" 183 | }, 184 | { 185 | "name": "permissioned", 186 | "type": "bool", 187 | "indexed": true, 188 | "internalType": "bool" 189 | } 190 | ], 191 | "anonymous": false 192 | }, 193 | { 194 | "type": "error", 195 | "name": "BadSignature", 196 | "inputs": [ 197 | { 198 | "name": "derivedSequencer", 199 | "type": "address", 200 | "internalType": "address" 201 | } 202 | ] 203 | }, 204 | { "type": "error", "name": "IncorrectHostBlock", "inputs": [] }, 205 | { "type": "error", "name": "OneRollupBlockPerHostBlock", "inputs": [] }, 206 | { "type": "error", "name": "OnlySequencerAdmin", "inputs": [] } 207 | ] 208 | -------------------------------------------------------------------------------- /clippy.toml: -------------------------------------------------------------------------------- 1 | msrv = "1.82" 2 | too-large-for-stack = 128 3 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | reorder_imports = true 2 | use_field_init_shorthand = true 3 | use_small_heuristics = "Max" 4 | -------------------------------------------------------------------------------- /src/bindings.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::too_many_arguments)] 2 | #![allow(missing_docs)] 3 | use alloy::primitives::{Address, Bytes, FixedBytes, U256}; 4 | mod mint { 5 | alloy::sol!( 6 | #[derive(Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)] 7 | function mint(address to, uint256 amount); 8 | ); 9 | } 10 | pub use mint::mintCall; 11 | 12 | mod zenith { 13 | use super::*; 14 | 15 | alloy::sol!( 16 | #[derive(Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)] 17 | #[sol(rpc)] 18 | Zenith, 19 | "abi/Zenith.json" 20 | ); 21 | 22 | impl Copy for Zenith::BlockHeader {} 23 | impl Copy for Zenith::BlockSubmitted {} 24 | impl Copy for Zenith::SequencerSet {} 25 | impl Copy for Zenith::BadSignature {} 26 | impl Copy for Zenith::OneRollupBlockPerHostBlock {} 27 | impl Copy for Zenith::OnlySequencerAdmin {} 28 | impl Copy for Zenith::IncorrectHostBlock {} 29 | 30 | impl Zenith::BlockSubmitted { 31 | /// Get the sequencer address that signed the block. 32 | pub const fn sequencer(&self) -> Address { 33 | self.sequencer 34 | } 35 | 36 | /// Get the chain id of the rollup. 37 | pub const fn rollup_chain_id(&self) -> u64 { 38 | self.rollupChainId.as_limbs()[0] 39 | } 40 | 41 | /// Get the gas limit of the block 42 | pub const fn gas_limit(&self) -> u64 { 43 | self.gasLimit.as_limbs()[0] 44 | } 45 | 46 | /// Get the reward address of the block. 47 | pub const fn reward_address(&self) -> Address { 48 | self.rewardAddress 49 | } 50 | 51 | /// Get the block data hash, i.e. the committment to the data of the block. 52 | pub const fn block_data_hash(&self) -> FixedBytes<32> { 53 | self.blockDataHash 54 | } 55 | 56 | /// Convert the BlockSubmitted event to a BlockHeader with the given host 57 | /// block number. 58 | pub const fn to_header(self, host_block_number: U256) -> Zenith::BlockHeader { 59 | Zenith::BlockHeader::from_block_submitted(self, host_block_number) 60 | } 61 | } 62 | 63 | impl Zenith::BlockHeader { 64 | /// Create a BlockHeader from a BlockSubmitted event with the given host 65 | /// block number 66 | pub const fn from_block_submitted( 67 | host_block_submitted: Zenith::BlockSubmitted, 68 | host_block_number: U256, 69 | ) -> Zenith::BlockHeader { 70 | Zenith::BlockHeader { 71 | rollupChainId: host_block_submitted.rollupChainId, 72 | hostBlockNumber: host_block_number, 73 | gasLimit: host_block_submitted.gasLimit, 74 | rewardAddress: host_block_submitted.rewardAddress, 75 | blockDataHash: host_block_submitted.blockDataHash, 76 | } 77 | } 78 | 79 | /// Get the host block number of the block 80 | pub const fn host_block_number(&self) -> u64 { 81 | self.hostBlockNumber.as_limbs()[0] 82 | } 83 | 84 | /// Get the chain ID of the block (discarding high bytes). 85 | pub const fn chain_id(&self) -> u64 { 86 | self.rollupChainId.as_limbs()[0] 87 | } 88 | 89 | /// Get the gas limit of the block (discarding high bytes). 90 | pub const fn gas_limit(&self) -> u64 { 91 | self.gasLimit.as_limbs()[0] 92 | } 93 | 94 | /// Get the reward address of the block. 95 | pub const fn reward_address(&self) -> Address { 96 | self.rewardAddress 97 | } 98 | 99 | /// Get the block data hash, i.e. the committment to the data of the block. 100 | pub const fn block_data_hash(&self) -> FixedBytes<32> { 101 | self.blockDataHash 102 | } 103 | } 104 | } 105 | 106 | mod passage { 107 | use super::*; 108 | 109 | alloy::sol!( 110 | #[derive(Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)] 111 | #[sol(rpc)] 112 | Passage, 113 | "abi/Passage.json" 114 | ); 115 | 116 | impl Copy for Passage::EnterConfigured {} 117 | impl Copy for Passage::Withdrawal {} 118 | impl Copy for Passage::OnlyTokenAdmin {} 119 | impl Copy for Passage::Enter {} 120 | impl Copy for Passage::EnterToken {} 121 | impl Copy for Passage::DisallowedEnter {} 122 | impl Copy for Passage::FailedCall {} 123 | impl Copy for Passage::InsufficientBalance {} 124 | impl Copy for Passage::SafeERC20FailedOperation {} 125 | impl Copy for Passage::AddressEmptyCode {} 126 | 127 | impl Copy for Passage::PassageEvents {} 128 | 129 | impl Clone for Passage::PassageEvents { 130 | fn clone(&self) -> Self { 131 | *self 132 | } 133 | } 134 | 135 | impl Passage::EnterToken { 136 | /// Get the chain ID of the event (discarding high bytes), returns `None` 137 | /// if the event has no associated chain id. 138 | pub const fn rollup_chain_id(&self) -> u64 { 139 | self.rollupChainId.as_limbs()[0] 140 | } 141 | 142 | /// Get the token address of the event. 143 | pub const fn token(&self) -> Address { 144 | self.token 145 | } 146 | 147 | /// Get the recipient of the event. 148 | pub const fn recipient(&self) -> Address { 149 | self.rollupRecipient 150 | } 151 | 152 | /// Get the amount of the event. 153 | pub const fn amount(&self) -> U256 { 154 | self.amount 155 | } 156 | } 157 | 158 | impl Passage::Enter { 159 | /// Get the chain ID of the event (discarding high bytes), returns `None` 160 | /// if the event has no associated chain id. 161 | pub const fn rollup_chain_id(&self) -> u64 { 162 | self.rollupChainId.as_limbs()[0] 163 | } 164 | 165 | /// Get the recipient of the event. 166 | pub const fn recipient(&self) -> Address { 167 | self.rollupRecipient 168 | } 169 | 170 | /// Get the amount of the event. 171 | pub const fn amount(&self) -> U256 { 172 | self.amount 173 | } 174 | } 175 | 176 | impl Passage::Withdrawal { 177 | /// Get the token address of the request. 178 | pub const fn token(&self) -> Address { 179 | self.token 180 | } 181 | 182 | /// Get the recipient of the request. 183 | pub const fn recipient(&self) -> Address { 184 | self.recipient 185 | } 186 | 187 | /// Get the amount of the request. 188 | pub const fn amount(&self) -> U256 { 189 | self.amount 190 | } 191 | } 192 | 193 | impl Passage::EnterConfigured { 194 | /// Get the token address of the event. 195 | pub const fn token(&self) -> Address { 196 | self.token 197 | } 198 | 199 | /// Get if the token has been configured to allow or disallow enters. 200 | pub const fn can_enter(&self) -> bool { 201 | self.canEnter 202 | } 203 | } 204 | } 205 | 206 | mod orders { 207 | use super::*; 208 | 209 | alloy::sol!( 210 | #[derive(Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)] 211 | #[sol(rpc)] 212 | Orders, 213 | "abi/RollupOrders.json" 214 | ); 215 | 216 | impl Copy for IOrders::Input {} 217 | impl Copy for IOrders::Output {} 218 | impl Copy for Orders::Sweep {} 219 | impl Copy for Orders::InsufficientBalance {} 220 | impl Copy for Orders::AddressEmptyCode {} 221 | impl Copy for Orders::LengthMismatch {} 222 | impl Copy for Orders::OrderExpired {} 223 | impl Copy for Orders::OutputMismatch {} 224 | impl Copy for Orders::SafeERC20FailedOperation {} 225 | 226 | impl Clone for Orders::OrdersEvents { 227 | fn clone(&self) -> Self { 228 | match self { 229 | Self::Order(event) => Self::Order(event.clone()), 230 | Self::Sweep(event) => Self::Sweep(*event), 231 | Self::Filled(event) => Self::Filled(event.clone()), 232 | } 233 | } 234 | } 235 | 236 | impl IOrders::Input { 237 | pub const fn token(&self) -> Address { 238 | self.token 239 | } 240 | 241 | pub const fn amount(&self) -> u64 { 242 | self.amount.as_limbs()[0] 243 | } 244 | } 245 | 246 | impl IOrders::Output { 247 | pub const fn token(&self) -> Address { 248 | self.token 249 | } 250 | 251 | pub const fn amount(&self) -> u64 { 252 | self.amount.as_limbs()[0] 253 | } 254 | 255 | pub const fn recipient(&self) -> Address { 256 | self.recipient 257 | } 258 | 259 | pub const fn chain_id(&self) -> u32 { 260 | self.chainId 261 | } 262 | } 263 | 264 | impl Orders::Order { 265 | /// Get the inputs of the order. 266 | pub fn inputs(&self) -> &[IOrders::Input] { 267 | &self.inputs 268 | } 269 | 270 | /// Get the outputs of the order. 271 | pub fn outputs(&self) -> &[IOrders::Output] { 272 | &self.outputs 273 | } 274 | 275 | /// Get the deadline of the order. 276 | pub const fn deadline(&self) -> u64 { 277 | self.deadline.as_limbs()[0] 278 | } 279 | } 280 | 281 | impl Orders::Sweep { 282 | pub const fn recipient(&self) -> Address { 283 | self.recipient 284 | } 285 | 286 | pub const fn token(&self) -> Address { 287 | self.token 288 | } 289 | 290 | pub const fn amount(&self) -> u64 { 291 | self.amount.as_limbs()[0] 292 | } 293 | } 294 | 295 | impl Orders::Filled { 296 | pub fn outputs(&self) -> &[IOrders::Output] { 297 | self.outputs.as_slice() 298 | } 299 | } 300 | } 301 | 302 | mod transactor { 303 | use super::*; 304 | 305 | alloy::sol!( 306 | #[derive(Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)] 307 | #[sol(rpc)] 308 | Transactor, 309 | "abi/Transactor.json" 310 | ); 311 | 312 | impl Copy for Transactor::GasConfigured {} 313 | 314 | impl Clone for Transactor::TransactorEvents { 315 | fn clone(&self) -> Self { 316 | match self { 317 | Transactor::TransactorEvents::Transact(event) => { 318 | Transactor::TransactorEvents::Transact(event.clone()) 319 | } 320 | Transactor::TransactorEvents::GasConfigured(event) => { 321 | Transactor::TransactorEvents::GasConfigured(*event) 322 | } 323 | } 324 | } 325 | } 326 | 327 | impl Transactor::Transact { 328 | pub const fn rollup_chain_id(&self) -> u64 { 329 | self.rollupChainId.as_limbs()[0] 330 | } 331 | 332 | pub const fn sender(&self) -> Address { 333 | self.sender 334 | } 335 | 336 | pub const fn to(&self) -> Address { 337 | self.to 338 | } 339 | 340 | pub const fn data(&self) -> &Bytes { 341 | &self.data 342 | } 343 | 344 | pub const fn value(&self) -> U256 { 345 | self.value 346 | } 347 | 348 | pub fn max_fee_per_gas(&self) -> u128 { 349 | self.maxFeePerGas.to::() 350 | } 351 | 352 | pub fn gas(&self) -> u128 { 353 | self.gas.to::() 354 | } 355 | } 356 | } 357 | 358 | mod rollup_passage { 359 | 360 | alloy::sol!( 361 | #[derive(Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)] 362 | #[sol(rpc)] 363 | RollupPassage, 364 | "abi/RollupPassage.json" 365 | ); 366 | 367 | impl Copy for RollupPassage::Exit {} 368 | impl Copy for RollupPassage::ExitToken {} 369 | impl Copy for RollupPassage::AddressEmptyCode {} 370 | impl Copy for RollupPassage::InsufficientBalance {} 371 | impl Copy for RollupPassage::SafeERC20FailedOperation {} 372 | 373 | impl Copy for RollupPassage::RollupPassageEvents {} 374 | 375 | impl Clone for RollupPassage::RollupPassageEvents { 376 | fn clone(&self) -> Self { 377 | *self 378 | } 379 | } 380 | } 381 | 382 | mod bundle_helper { 383 | alloy::sol!( 384 | #[derive(Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)] 385 | #[sol(rpc)] 386 | BundleHelper, 387 | "abi/BundleHelper.json" 388 | ); 389 | } 390 | 391 | pub use zenith::Zenith; 392 | 393 | /// Contract Bindings for the RollupOrders contract. 394 | #[allow(non_snake_case)] 395 | pub mod RollupOrders { 396 | pub use super::orders::Orders::*; 397 | 398 | pub use super::orders::IOrders::*; 399 | pub use super::orders::ISignatureTransfer::*; 400 | pub use super::orders::UsesPermit2::*; 401 | 402 | pub use super::orders::Orders::OrdersCalls as RollupOrdersCalls; 403 | pub use super::orders::Orders::OrdersErrors as RollupOrdersErrors; 404 | pub use super::orders::Orders::OrdersEvents as RollupOrdersEvents; 405 | pub use super::orders::Orders::OrdersInstance as RollupOrdersInstance; 406 | } 407 | 408 | /// Contract Bindings for the HostOrders contract. 409 | #[allow(non_snake_case)] 410 | pub mod HostOrders { 411 | pub use super::orders::Orders::*; 412 | 413 | pub use super::orders::IOrders::*; 414 | pub use super::orders::ISignatureTransfer::*; 415 | pub use super::orders::UsesPermit2::*; 416 | 417 | pub use super::orders::Orders::OrdersCalls as HostOrdersCalls; 418 | pub use super::orders::Orders::OrdersErrors as HostOrdersErrors; 419 | pub use super::orders::Orders::OrdersEvents as HostOrdersEvents; 420 | pub use super::orders::Orders::OrdersInstance as HostOrdersInstance; 421 | } 422 | 423 | /// Contract Bindings for the Passage contract. 424 | #[allow(non_snake_case)] 425 | pub mod Passage { 426 | pub use super::passage::Passage::*; 427 | 428 | pub use super::passage::ISignatureTransfer::*; 429 | pub use super::passage::UsesPermit2::*; 430 | } 431 | 432 | pub use transactor::Transactor; 433 | 434 | /// Contract Bindings for the RollupPassage contract. 435 | #[allow(non_snake_case)] 436 | pub mod RollupPassage { 437 | pub use super::rollup_passage::RollupPassage::*; 438 | 439 | pub use super::rollup_passage::ISignatureTransfer::*; 440 | pub use super::rollup_passage::UsesPermit2::*; 441 | } 442 | 443 | /// Contract Bindings for the BundleHelper contract. 444 | #[allow(non_snake_case)] 445 | pub mod BundleHelper { 446 | pub use super::bundle_helper::BundleHelper::*; 447 | pub use super::bundle_helper::Zenith::BlockHeader; 448 | } 449 | -------------------------------------------------------------------------------- /src/block.rs: -------------------------------------------------------------------------------- 1 | use std::{marker::PhantomData, sync::OnceLock}; 2 | 3 | use crate::Zenith::BlockHeader as ZenithHeader; 4 | use alloy::consensus::TxEnvelope; 5 | use alloy::eips::eip2718::{Decodable2718, Encodable2718}; 6 | use alloy::primitives::{keccak256, Address, B256}; 7 | use alloy::rlp::Decodable; 8 | 9 | /// Zenith processes normal Ethereum txns. 10 | pub type ZenithTransaction = TxEnvelope; 11 | 12 | /// Encode/Decode trait for inner tx type 13 | pub trait Coder { 14 | /// The inner tx type. 15 | type Tx: std::fmt::Debug + Clone + PartialEq + Eq; 16 | 17 | /// Encode the tx. 18 | fn encode(t: &Self::Tx) -> Vec; 19 | 20 | /// Decode the tx. 21 | fn decode(buf: &mut &[u8]) -> Option 22 | where 23 | Self: Sized; 24 | } 25 | 26 | /// Coder for [`encode_txns`] and [`decode_txns`] that operates on 27 | /// [`TxEnvelope`]. 28 | #[derive(Copy, Clone, Debug)] 29 | pub struct Alloy2718Coder; 30 | 31 | impl Coder for Alloy2718Coder { 32 | type Tx = ZenithTransaction; 33 | 34 | fn encode(t: &ZenithTransaction) -> Vec { 35 | t.encoded_2718() 36 | } 37 | 38 | fn decode(buf: &mut &[u8]) -> Option 39 | where 40 | Self: Sized, 41 | { 42 | ZenithTransaction::decode_2718(buf).ok() 43 | } 44 | } 45 | 46 | /// A Zenith block is just a list of transactions. 47 | #[derive(Debug, Clone, PartialEq, Eq)] 48 | pub struct ZenithBlock { 49 | /// The zenith block header, which may be extracted from a 50 | /// [`crate::Zenith::BlockSubmitted`] event. 51 | header: ZenithHeader, 52 | /// The transactions in the block, which are extracted from the calldata or 53 | /// blob data. 54 | transactions: Vec<::Tx>, 55 | 56 | // memoization fields 57 | encoded: OnceLock>, 58 | block_data_hash: OnceLock, 59 | 60 | /// The coder 61 | _pd: std::marker::PhantomData, 62 | } 63 | 64 | impl ZenithBlock 65 | where 66 | C: Coder, 67 | { 68 | /// Create a new zenith block. 69 | pub const fn new(header: ZenithHeader, transactions: Vec<::Tx>) -> Self { 70 | ZenithBlock { 71 | header, 72 | transactions, 73 | encoded: OnceLock::new(), 74 | block_data_hash: OnceLock::new(), 75 | _pd: PhantomData, 76 | } 77 | } 78 | 79 | /// Decode tx data in the block. 80 | /// 81 | /// This will perform the following steps: 82 | /// - Attempt to decode the data as an RLP list 83 | /// - On failure, discard all data, returning an empty tx list 84 | /// - Attempt to decode each item in the list as a transaction 85 | /// - On failure, discard the item 86 | /// - Return a list of succesfully decoded transactions 87 | pub fn from_header_and_data(header: ZenithHeader, buf: impl AsRef<[u8]>) -> Self { 88 | let b = buf.as_ref(); 89 | let transactions = decode_txns::(b); 90 | let h = keccak256(b); 91 | ZenithBlock { 92 | header, 93 | transactions, 94 | encoded: b.to_owned().into(), 95 | block_data_hash: h.into(), 96 | _pd: PhantomData, 97 | } 98 | } 99 | 100 | /// Break the block into its parts. 101 | pub fn into_parts(self) -> (ZenithHeader, Vec) { 102 | (self.header, self.transactions) 103 | } 104 | 105 | /// Encode the transactions in the block. 106 | pub fn encoded_txns(&self) -> &[u8] { 107 | self.seal(); 108 | self.encoded.get().unwrap().as_slice() 109 | } 110 | 111 | /// The hash of the encoded transactions. 112 | pub fn block_data_hash(&self) -> B256 { 113 | self.seal(); 114 | *self.block_data_hash.get().unwrap() 115 | } 116 | 117 | /// Push a transaction into the block. 118 | pub fn push_transaction(&mut self, tx: C::Tx) { 119 | self.unseal(); 120 | self.transactions.push(tx); 121 | } 122 | 123 | /// Access to the transactions. 124 | pub fn transactions(&self) -> &[C::Tx] { 125 | &self.transactions 126 | } 127 | 128 | /// Mutable access to the transactions. 129 | pub fn transactions_mut(&mut self) -> &mut Vec { 130 | self.unseal(); 131 | &mut self.transactions 132 | } 133 | 134 | /// Iterate over the transactions. 135 | pub fn transactions_iter(&self) -> std::slice::Iter<'_, C::Tx> { 136 | self.transactions.iter() 137 | } 138 | 139 | /// Iterate over mut transactions. 140 | pub fn transactions_iter_mut(&mut self) -> std::slice::IterMut<'_, C::Tx> { 141 | self.unseal(); 142 | self.transactions.iter_mut() 143 | } 144 | 145 | /// Access to the header. 146 | pub const fn header(&self) -> &ZenithHeader { 147 | &self.header 148 | } 149 | 150 | /// Mutable access to the header. 151 | pub fn header_mut(&mut self) -> &mut ZenithHeader { 152 | &mut self.header 153 | } 154 | 155 | fn seal(&self) { 156 | let encoded = self.encoded.get_or_init(|| encode_txns::(&self.transactions)); 157 | self.block_data_hash.get_or_init(|| keccak256(encoded)); 158 | } 159 | 160 | fn unseal(&mut self) { 161 | self.encoded.take(); 162 | self.block_data_hash.take(); 163 | } 164 | 165 | /// Get the chain ID of the block (discarding high bytes). 166 | pub const fn chain_id(&self) -> u64 { 167 | self.header.chain_id() 168 | } 169 | 170 | /// Gets the block height according to the header 171 | pub const fn block_height(&self) -> u64 { 172 | self.header.host_block_number() 173 | } 174 | 175 | /// Get the gas limit of the block (discarding high bytes). 176 | pub const fn gas_limit(&self) -> u64 { 177 | self.header.gas_limit() 178 | } 179 | 180 | /// Get the reward address of the block. 181 | pub const fn reward_address(&self) -> Address { 182 | self.header.reward_address() 183 | } 184 | } 185 | 186 | /// Decode transactions. 187 | /// 188 | /// A transaction is an RLP-encoded list of EIP-2718-encoded transaction 189 | /// envelopes. 190 | /// 191 | /// The function is generic over the coder type, which is used to decode the 192 | /// transactions. This allows for different transaction types to be decoded 193 | /// using different coders. 194 | pub fn decode_txns(block_data: impl AsRef<[u8]>) -> Vec 195 | where 196 | C: Coder, 197 | { 198 | let mut bd = block_data.as_ref(); 199 | 200 | Vec::>::decode(&mut bd) 201 | .map(|rlp| rlp.into_iter().flat_map(|buf| C::decode(&mut buf.as_slice())).collect()) 202 | .ok() 203 | .unwrap_or_default() 204 | } 205 | 206 | /// Encode a set of transactions into a single RLP-encoded buffer. 207 | /// 208 | /// The function is generic over the coder type, which is used to encode the 209 | /// transactions. This allows for different transaction types to be encoded 210 | /// using different encodings. 211 | pub fn encode_txns<'a, C>(transactions: impl IntoIterator) -> Vec 212 | where 213 | C: Coder, 214 | C::Tx: 'a, 215 | { 216 | let encoded_txns = transactions.into_iter().map(|tx| C::encode(tx)).collect::>>(); 217 | 218 | let mut buf = Vec::new(); 219 | alloy::rlp::Encodable::encode(&encoded_txns, &mut buf); 220 | buf 221 | } 222 | 223 | #[cfg(test)] 224 | mod test { 225 | use alloy::consensus::{Signed, TxEip1559}; 226 | use alloy::primitives::{b256, bytes, Address, PrimitiveSignature, U256}; 227 | 228 | use super::*; 229 | 230 | #[test] 231 | fn encode_decode() { 232 | let sig = PrimitiveSignature::from_scalars_and_parity( 233 | b256!("840cfc572845f5786e702984c2a582528cad4b49b2a10b9db1be7fca90058565"), 234 | b256!("25e7109ceb98168d95b09b18bbf6b685130e0562f233877d492b94eee0c5b6d1"), 235 | false, 236 | ); 237 | 238 | let tx = ZenithTransaction::Eip1559(Signed::new_unchecked( 239 | TxEip1559 { 240 | chain_id: 1, 241 | nonce: 2, 242 | gas_limit: 3, 243 | max_fee_per_gas: 4, 244 | max_priority_fee_per_gas: 5, 245 | to: Address::repeat_byte(6).into(), 246 | value: U256::from(7), 247 | access_list: Default::default(), 248 | input: bytes!("08090a0b0c0d0e0f"), 249 | }, 250 | sig, 251 | b256!("87fdda4563f2f98ac9c3f076bca48a59309df94f13fb8abf8471b3b8b51a2816"), 252 | )); 253 | 254 | let mut txs = vec![tx.clone()]; 255 | let encoded = encode_txns::(&txs); 256 | let decoded = decode_txns::(encoded); 257 | 258 | assert_eq!(txs, decoded); 259 | 260 | txs.push(tx.clone()); 261 | let encoded = encode_txns::(&txs); 262 | let decoded = decode_txns::(encoded); 263 | 264 | assert_eq!(txs, decoded); 265 | } 266 | 267 | #[test] 268 | fn graceful_junk() { 269 | let junk = vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; 270 | let decoded = decode_txns::(&junk); 271 | assert!(decoded.is_empty()); 272 | } 273 | } 274 | -------------------------------------------------------------------------------- /src/bundle.rs: -------------------------------------------------------------------------------- 1 | use alloy::primitives::{keccak256, Address, Bytes, B256, U256}; 2 | use alloy::{ 3 | eips::{eip2718::Encodable2718, BlockNumberOrTag}, 4 | rpc::types::mev::{EthCallBundle, EthCallBundleResponse, EthSendBundle}, 5 | }; 6 | use serde::{Deserialize, Serialize}; 7 | use std::collections::BTreeMap; 8 | 9 | use crate::SignedOrder; 10 | 11 | /// Wraps a flashbots style EthSendBundle with host fills to make a Zenith compatible bundle 12 | #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] 13 | #[serde(rename_all = "camelCase")] 14 | pub struct ZenithEthBundle { 15 | /// The bundle of transactions to simulate. Same structure as a Flashbots [EthSendBundle] bundle. 16 | /// see 17 | #[serde(flatten)] 18 | pub bundle: EthSendBundle, 19 | /// Host fills to be applied with the bundle, represented as a signed permit2 order. 20 | pub host_fills: Option, 21 | } 22 | 23 | impl ZenithEthBundle { 24 | /// Returns the transactions in this bundle. 25 | pub fn txs(&self) -> &[Bytes] { 26 | &self.bundle.txs 27 | } 28 | 29 | /// Returns the block number for this bundle. 30 | pub const fn block_number(&self) -> u64 { 31 | self.bundle.block_number 32 | } 33 | 34 | /// Returns the minimum timestamp for this bundle. 35 | pub const fn min_timestamp(&self) -> Option { 36 | self.bundle.min_timestamp 37 | } 38 | 39 | /// Returns the maximum timestamp for this bundle. 40 | pub const fn max_timestamp(&self) -> Option { 41 | self.bundle.max_timestamp 42 | } 43 | 44 | /// Returns the reverting tx hashes for this bundle. 45 | pub fn reverting_tx_hashes(&self) -> &[B256] { 46 | self.bundle.reverting_tx_hashes.as_slice() 47 | } 48 | 49 | /// Returns the replacement uuid for this bundle. 50 | pub fn replacement_uuid(&self) -> Option<&str> { 51 | self.bundle.replacement_uuid.as_deref() 52 | } 53 | } 54 | 55 | /// Response for `zenith_sendBundle` 56 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] 57 | #[serde(rename_all = "camelCase")] 58 | pub struct ZenithEthBundleResponse { 59 | /// The bundle hash of the sent bundle. 60 | /// 61 | /// This is calculated as keccak256(tx_hashes) where tx_hashes are the concatenated transaction hashes. 62 | pub bundle_hash: B256, 63 | } 64 | 65 | /// Bundle of transactions for `zenith_callBundle` 66 | #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] 67 | #[serde(rename_all = "camelCase")] 68 | pub struct ZenithCallBundle { 69 | /// The bundle of transactions to simulate. Same structure as a Flashbots [EthCallBundle] bundle. 70 | /// see 71 | #[serde(flatten)] 72 | pub bundle: EthCallBundle, 73 | /// Host fills to be applied to the bundle for simulation. The mapping corresponds 74 | /// to asset => user => amount. 75 | pub host_fills: BTreeMap>, 76 | } 77 | 78 | impl ZenithCallBundle { 79 | /// Returns the host fills for this bundle. 80 | pub const fn host_fills(&self) -> &BTreeMap> { 81 | &self.host_fills 82 | } 83 | 84 | /// Returns the transactions in this bundle. 85 | pub fn txs(&self) -> &[Bytes] { 86 | &self.bundle.txs 87 | } 88 | 89 | /// Returns the block number for this bundle. 90 | pub const fn block_number(&self) -> u64 { 91 | self.bundle.block_number 92 | } 93 | 94 | /// Returns the state block number for this bundle. 95 | pub const fn state_block_number(&self) -> BlockNumberOrTag { 96 | self.bundle.state_block_number 97 | } 98 | 99 | /// Returns the timestamp for this bundle. 100 | pub const fn timestamp(&self) -> Option { 101 | self.bundle.timestamp 102 | } 103 | 104 | /// Returns the gas limit for this bundle. 105 | pub const fn gas_limit(&self) -> Option { 106 | self.bundle.gas_limit 107 | } 108 | 109 | /// Returns the difficulty for this bundle. 110 | pub const fn difficulty(&self) -> Option { 111 | self.bundle.difficulty 112 | } 113 | 114 | /// Returns the base fee for this bundle. 115 | pub const fn base_fee(&self) -> Option { 116 | self.bundle.base_fee 117 | } 118 | 119 | /// Creates a new bundle from the given [`Encodable2718`] transactions. 120 | pub fn from_2718_and_host_fills( 121 | txs: I, 122 | host_fills: BTreeMap>, 123 | ) -> Self 124 | where 125 | I: IntoIterator, 126 | T: Encodable2718, 127 | { 128 | Self::from_raw_txs_and_host_fills(txs.into_iter().map(|tx| tx.encoded_2718()), host_fills) 129 | } 130 | 131 | /// Creates a new bundle with the given transactions and host fills. 132 | pub fn from_raw_txs_and_host_fills( 133 | txs: I, 134 | host_fills: BTreeMap>, 135 | ) -> Self 136 | where 137 | I: IntoIterator, 138 | T: Into, 139 | { 140 | Self { 141 | bundle: EthCallBundle { 142 | txs: txs.into_iter().map(Into::into).collect(), 143 | ..Default::default() 144 | }, 145 | host_fills, 146 | } 147 | } 148 | 149 | /// Adds an [`Encodable2718`] transaction to the bundle. 150 | pub fn append_2718_tx(self, tx: impl Encodable2718) -> Self { 151 | self.append_raw_tx(tx.encoded_2718()) 152 | } 153 | 154 | /// Adds an EIP-2718 envelope to the bundle. 155 | pub fn append_raw_tx(mut self, tx: impl Into) -> Self { 156 | self.bundle.txs.push(tx.into()); 157 | self 158 | } 159 | 160 | /// Adds multiple [`Encodable2718`] transactions to the bundle. 161 | pub fn extend_2718_txs(self, tx: I) -> Self 162 | where 163 | I: IntoIterator, 164 | T: Encodable2718, 165 | { 166 | self.extend_raw_txs(tx.into_iter().map(|tx| tx.encoded_2718())) 167 | } 168 | 169 | /// Adds multiple calls to the block. 170 | pub fn extend_raw_txs(mut self, txs: I) -> Self 171 | where 172 | I: IntoIterator, 173 | T: Into, 174 | { 175 | self.bundle.txs.extend(txs.into_iter().map(Into::into)); 176 | self 177 | } 178 | 179 | /// Sets the block number for the bundle. 180 | pub const fn with_block_number(mut self, block_number: u64) -> Self { 181 | self.bundle.block_number = block_number; 182 | self 183 | } 184 | 185 | /// Sets the state block number for the bundle. 186 | pub fn with_state_block_number( 187 | mut self, 188 | state_block_number: impl Into, 189 | ) -> Self { 190 | self.bundle.state_block_number = state_block_number.into(); 191 | self 192 | } 193 | 194 | /// Sets the timestamp for the bundle. 195 | pub const fn with_timestamp(mut self, timestamp: u64) -> Self { 196 | self.bundle.timestamp = Some(timestamp); 197 | self 198 | } 199 | 200 | /// Sets the gas limit for the bundle. 201 | pub const fn with_gas_limit(mut self, gas_limit: u64) -> Self { 202 | self.bundle.gas_limit = Some(gas_limit); 203 | self 204 | } 205 | 206 | /// Sets the difficulty for the bundle. 207 | pub const fn with_difficulty(mut self, difficulty: U256) -> Self { 208 | self.bundle.difficulty = Some(difficulty); 209 | self 210 | } 211 | 212 | /// Sets the base fee for the bundle. 213 | pub const fn with_base_fee(mut self, base_fee: u128) -> Self { 214 | self.bundle.base_fee = Some(base_fee); 215 | self 216 | } 217 | 218 | /// Make a bundle hash from the given deserialized transaction array and host fills from this bundle. 219 | /// The hash is calculated as keccak256(tx_preimage + host_preimage). 220 | /// The tx_preimage is calculated as `keccak(tx_hash1 + tx_hash2 + ... + tx_hashn)`. 221 | /// The host_preimage is calculated as 222 | /// `keccak(NUM_OF_ASSETS_LE + asset1 + NUM_OF_FILLS_LE + asset1_user1 + user1_amount2 + ... + asset1_usern + asset1_amountn + ...)`. 223 | /// For the number of users/fills and amounts in the host_preimage, the amounts are serialized as little-endian U256 slice. 224 | pub fn bundle_hash(&self) -> B256 { 225 | let mut hasher = alloy::primitives::Keccak256::new(); 226 | 227 | // Concatenate the transaction hashes, to then hash them. This is the tx_preimage. 228 | for tx in self.bundle.txs.iter() { 229 | // Calculate the tx hash (keccak256(encoded_signed_tx)) and append it to the tx_bytes. 230 | hasher.update(keccak256(tx).as_slice()); 231 | } 232 | let tx_preimage = hasher.finalize(); 233 | 234 | // Now, let's build the host_preimage. We do it in steps: 235 | // 1. Prefix the number of assets, encoded as a little-endian U256 slice. 236 | // 2. For each asset: 237 | // 3. Concatenate the asset address. 238 | // 4. Prefix the number of fills. 239 | // 5. For each fill, concatenate the user and amount, the latter encoded as a little-endian U256 slice. 240 | let mut hasher = alloy::primitives::Keccak256::new(); 241 | 242 | // Prefix the list of users with the number of assets. 243 | hasher.update(U256::from(self.host_fills.len()).as_le_slice()); 244 | 245 | for (asset, fills) in self.host_fills.iter() { 246 | // Concatenate the asset address. 247 | hasher.update(asset.as_slice()); 248 | 249 | // Prefix the list of fills with the number of fills 250 | hasher.update(U256::from(fills.len()).as_le_slice()); 251 | 252 | for (user, amount) in fills.iter() { 253 | // Concatenate the user address and amount for each fill. 254 | hasher.update(user.as_slice()); 255 | hasher.update(amount.as_le_slice()); 256 | } 257 | } 258 | 259 | // Hash the host pre-image. 260 | let host_preimage = hasher.finalize(); 261 | 262 | let mut pre_image = alloy::primitives::Keccak256::new(); 263 | pre_image.update(tx_preimage.as_slice()); 264 | pre_image.update(host_preimage.as_slice()); 265 | 266 | // Hash both tx and host hashes to get the final bundle hash. 267 | pre_image.finalize() 268 | } 269 | } 270 | 271 | /// Response for `zenith_callBundle` 272 | #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)] 273 | #[serde(rename_all = "camelCase")] 274 | pub struct ZenithCallBundleResponse { 275 | /// The flattened "vanilla" response which comes from `eth_callBundle` 276 | #[serde(flatten)] 277 | pub response: EthCallBundleResponse, 278 | } 279 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![doc = include_str!("../README.md")] 2 | #![warn( 3 | missing_copy_implementations, 4 | missing_debug_implementations, 5 | missing_docs, 6 | unreachable_pub, 7 | clippy::missing_const_for_fn, 8 | rustdoc::all 9 | )] 10 | #![cfg_attr(not(test), warn(unused_crate_dependencies))] 11 | #![deny(unused_must_use, rust_2018_idioms)] 12 | #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] 13 | 14 | mod bindings; 15 | pub use bindings::{ 16 | mintCall, BundleHelper, HostOrders, Passage, RollupOrders, RollupPassage, Transactor, Zenith, 17 | }; 18 | 19 | mod block; 20 | pub use block::{decode_txns, encode_txns, Alloy2718Coder, Coder, ZenithBlock, ZenithTransaction}; 21 | 22 | mod orders; 23 | pub use orders::{AggregateOrders, SignedOrder}; 24 | 25 | mod bundle; 26 | pub use bundle::{ 27 | ZenithCallBundle, ZenithCallBundleResponse, ZenithEthBundle, ZenithEthBundleResponse, 28 | }; 29 | 30 | mod req; 31 | pub use req::SignRequest; 32 | 33 | mod resp; 34 | pub use resp::SignResponse; 35 | 36 | use alloy::primitives::{address, Address}; 37 | 38 | /// System address with permission to mint tokens on pre-deploys. 39 | pub const MINTER_ADDRESS: Address = address!("00000000000000000000746f6b656e61646d696e"); 40 | 41 | /// A [`RequestSigner`] signs [`SignRequest`]s by delegating to an 42 | /// [`alloy::signers::Signer`]. 43 | pub trait RequestSigner { 44 | /// Signs a [`SignRequest`] and returns the [`alloy::primitives::Signature`]. 45 | fn sign_request( 46 | &self, 47 | request: &SignRequest, 48 | ) -> impl std::future::Future< 49 | Output = Result, 50 | > + Send; 51 | } 52 | 53 | impl RequestSigner for T 54 | where 55 | T: alloy::signers::Signer + Send + Sync, 56 | { 57 | async fn sign_request( 58 | &self, 59 | request: &SignRequest, 60 | ) -> Result { 61 | let hash = request.signing_hash(); 62 | self.sign_hash(&hash).await 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/orders/agg.rs: -------------------------------------------------------------------------------- 1 | use crate::RollupOrders; 2 | use alloy::primitives::{Address, U256}; 3 | use std::collections::HashMap; 4 | 5 | /// Aggregated orders for a transaction or set of transactions. 6 | #[derive(Debug, Default, Clone, Eq, PartialEq)] 7 | pub struct AggregateOrders { 8 | /// Outputs to be transferred to the user. These may be on the rollup or 9 | /// the host or potentially elsewhere in the future. 10 | pub outputs: HashMap<(u64, Address), HashMap>, 11 | /// Inputs to be transferred to the filler. These are always on the 12 | /// rollup. 13 | pub inputs: HashMap, 14 | } 15 | 16 | impl AggregateOrders { 17 | /// Instantiate a new [`AggregateOrders`]. 18 | pub fn new() -> Self { 19 | Default::default() 20 | } 21 | 22 | /// Instantiate a new [`AggregateOrders`] with a custom capacity. The 23 | /// capcity is for the number of assets in inputs or outputs. 24 | pub fn with_capacity(capacity: usize) -> Self { 25 | Self { outputs: HashMap::with_capacity(capacity), inputs: HashMap::with_capacity(capacity) } 26 | } 27 | 28 | /// Ingest an output into the aggregate orders. 29 | fn ingest_output(&mut self, output: &RollupOrders::Output) { 30 | let entry = self 31 | .outputs 32 | .entry((output.chain_id() as u64, output.token)) 33 | .or_default() 34 | .entry(output.recipient) 35 | .or_default(); 36 | *entry = entry.saturating_add(output.amount); 37 | } 38 | 39 | /// Ingest an input into the aggregate orders. 40 | fn ingest_input(&mut self, input: &RollupOrders::Input) { 41 | let entry = self.inputs.entry(input.token).or_default(); 42 | *entry = entry.saturating_add(input.amount); 43 | } 44 | 45 | /// Ingest a new order into the aggregate orders. 46 | pub fn ingest(&mut self, order: &RollupOrders::Order) { 47 | order.outputs.iter().for_each(|o| self.ingest_output(o)); 48 | order.inputs.iter().for_each(|i| self.ingest_input(i)); 49 | } 50 | 51 | /// Extend the orders with a new set of orders. 52 | pub fn extend<'a>(&mut self, orders: impl IntoIterator) { 53 | for order in orders { 54 | self.ingest(order); 55 | } 56 | } 57 | } 58 | 59 | impl<'a> FromIterator<&'a RollupOrders::Order> for AggregateOrders { 60 | fn from_iter>(iter: T) -> Self { 61 | let mut orders = AggregateOrders::new(); 62 | orders.extend(iter); 63 | orders 64 | } 65 | } 66 | 67 | #[cfg(test)] 68 | mod test { 69 | use super::*; 70 | use alloy::primitives::{Address, U256}; 71 | 72 | const ASSET_A: Address = Address::repeat_byte(1); 73 | const ASSET_B: Address = Address::repeat_byte(2); 74 | const ASSET_C: Address = Address::repeat_byte(3); 75 | 76 | const USER_A: Address = Address::repeat_byte(4); 77 | const USER_B: Address = Address::repeat_byte(5); 78 | const USER_C: Address = Address::repeat_byte(6); 79 | 80 | fn input(asset: Address, amount: u64) -> RollupOrders::Input { 81 | RollupOrders::Input { token: asset, amount: U256::from(amount) } 82 | } 83 | 84 | fn output(asset: Address, recipient: Address, amount: u64) -> RollupOrders::Output { 85 | RollupOrders::Output { chainId: 1, token: asset, recipient, amount: U256::from(amount) } 86 | } 87 | 88 | #[test] 89 | fn test_single_order() { 90 | let order = RollupOrders::Order { 91 | inputs: vec![input(ASSET_A, 100), input(ASSET_B, 200)], 92 | outputs: vec![ 93 | output(ASSET_A, USER_A, 50), 94 | output(ASSET_A, USER_B, 50), 95 | output(ASSET_B, USER_B, 100), 96 | output(ASSET_C, USER_C, 200), 97 | output(ASSET_C, USER_C, 200), 98 | ], 99 | deadline: U256::ZERO, 100 | }; 101 | 102 | let agg: AggregateOrders = [&order].into_iter().collect(); 103 | assert_eq!(agg.inputs.get(&ASSET_A), Some(&U256::from(100)), "ASSET_A input"); 104 | assert_eq!(agg.inputs.get(&ASSET_B), Some(&U256::from(200)), "ASSET_B input"); 105 | 106 | assert_eq!( 107 | agg.outputs.get(&(1, ASSET_A)).map(|m| m.get(&USER_A)), 108 | Some(Some(&U256::from(50))), 109 | "ASSET_A USER_A output" 110 | ); 111 | assert_eq!( 112 | agg.outputs.get(&(1, ASSET_A)).map(|m| m.get(&USER_B)), 113 | Some(Some(&U256::from(50))), 114 | "ASSET_A USER_B output" 115 | ); 116 | assert_eq!( 117 | agg.outputs.get(&(1, ASSET_B)).map(|m| m.get(&USER_B)), 118 | Some(Some(&U256::from(100))), 119 | "ASSET_B USER_B output" 120 | ); 121 | assert_eq!( 122 | agg.outputs.get(&(1, ASSET_C)).map(|m| m.get(&USER_C)), 123 | Some(Some(&U256::from(400))), 124 | "ASSET_C USER_C output" 125 | ); 126 | } 127 | 128 | #[test] 129 | fn test_two_orders() { 130 | let order_1 = RollupOrders::Order { 131 | inputs: vec![input(ASSET_A, 100), input(ASSET_B, 200)], 132 | outputs: vec![ 133 | output(ASSET_A, USER_A, 50), 134 | output(ASSET_A, USER_B, 50), 135 | output(ASSET_B, USER_B, 100), 136 | output(ASSET_C, USER_C, 200), 137 | output(ASSET_C, USER_C, 200), 138 | ], 139 | deadline: U256::ZERO, 140 | }; 141 | let order_2 = RollupOrders::Order { 142 | inputs: vec![input(ASSET_A, 50), input(ASSET_C, 100)], 143 | outputs: vec![ 144 | output(ASSET_A, USER_A, 50), 145 | output(ASSET_B, USER_B, 100), 146 | output(ASSET_C, USER_C, 100), 147 | ], 148 | deadline: U256::ZERO, 149 | }; 150 | 151 | let agg: AggregateOrders = [&order_1, &order_2].into_iter().collect(); 152 | 153 | assert_eq!(agg.inputs.get(&ASSET_A), Some(&U256::from(150)), "ASSET_A input"); 154 | assert_eq!(agg.inputs.get(&ASSET_B), Some(&U256::from(200)), "ASSET_B input"); 155 | assert_eq!(agg.inputs.get(&ASSET_C), Some(&U256::from(100)), "ASSET_C input"); 156 | 157 | assert_eq!( 158 | agg.outputs.get(&(1, ASSET_A)).map(|m| m.get(&USER_A)), 159 | Some(Some(&U256::from(100))), 160 | "ASSET_A USER_A output" 161 | ); 162 | assert_eq!( 163 | agg.outputs.get(&(1, ASSET_A)).map(|m| m.get(&USER_B)), 164 | Some(Some(&U256::from(50))), 165 | "ASSET_A USER_B output" 166 | ); 167 | assert_eq!( 168 | agg.outputs.get(&(1, ASSET_B)).map(|m| m.get(&USER_B)), 169 | Some(Some(&U256::from(200))), 170 | "ASSET_B USER_B output" 171 | ); 172 | assert_eq!( 173 | agg.outputs.get(&(1, ASSET_C)).map(|m| m.get(&USER_C)), 174 | Some(Some(&U256::from(500))), 175 | "ASSET_C USER_C output" 176 | ); 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /src/orders/mod.rs: -------------------------------------------------------------------------------- 1 | mod agg; 2 | pub use agg::AggregateOrders; 3 | 4 | mod signed; 5 | pub use signed::SignedOrder; 6 | -------------------------------------------------------------------------------- /src/orders/signed.rs: -------------------------------------------------------------------------------- 1 | use crate::bindings::HostOrders::{Output, Permit2Batch}; 2 | use serde::{Deserialize, Serialize}; 3 | 4 | /// A signed order. 5 | /// TODO: Link to docs. 6 | #[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)] 7 | pub struct SignedOrder { 8 | /// The permit batch. 9 | #[serde(flatten)] 10 | pub permit: Permit2Batch, 11 | /// The desired outputs. 12 | pub outputs: Vec, 13 | } 14 | 15 | impl SignedOrder { 16 | /// Creates a new signed order. 17 | pub const fn new(permit: Permit2Batch, outputs: Vec) -> Self { 18 | Self { permit, outputs } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/req.rs: -------------------------------------------------------------------------------- 1 | use alloy::primitives::{Address, Keccak256, B256, U256}; 2 | use serde::{Deserialize, Serialize}; 3 | 4 | /// The domain binding for the signing service. 5 | const DOMAIN_BINDING: &str = "init4.sequencer.v0"; 6 | 7 | /// A request to sign a rollup block. 8 | #[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)] 9 | #[serde(rename_all = "camelCase")] 10 | pub struct SignRequest { 11 | /// The block number of the host. 12 | pub host_block_number: U256, 13 | /// The chain ID of the host. 14 | pub host_chain_id: U256, 15 | /// The chain ID of the rollup. 16 | pub ru_chain_id: U256, 17 | /// The gas limit of the rollup block. 18 | pub gas_limit: U256, 19 | /// The reward address for the builder. 20 | pub ru_reward_address: Address, 21 | /// Encoded transactions to be signed 22 | pub contents: B256, 23 | } 24 | 25 | impl SignRequest { 26 | /// Compute the signing hash for this sig request 27 | pub fn signing_hash(&self) -> B256 { 28 | let mut hasher = Keccak256::new(); 29 | hasher.update(DOMAIN_BINDING); 30 | hasher.update(self.host_chain_id.to_be_bytes::<32>()); 31 | hasher.update(self.ru_chain_id.to_be_bytes::<32>()); 32 | hasher.update(self.host_block_number.to_be_bytes::<32>()); 33 | hasher.update(self.gas_limit.to_be_bytes::<32>()); 34 | hasher.update(self.ru_reward_address); 35 | hasher.update(self.contents); 36 | hasher.finalize() 37 | } 38 | } 39 | 40 | impl core::fmt::Display for SignRequest { 41 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 42 | write!( 43 | f, 44 | "SignRequest {{ host_chain_id: {}, host_block_number: {}, ru_chain_id: {}, gas_limit: {}, ru_reward_address: {}, contents: {} }}", 45 | self.host_chain_id, 46 | self.host_block_number, 47 | self.ru_chain_id, 48 | self.gas_limit, 49 | self.ru_reward_address, 50 | self.contents 51 | ) 52 | } 53 | } 54 | 55 | #[cfg(test)] 56 | mod test { 57 | use super::*; 58 | use alloy::primitives::b256; 59 | 60 | #[test] 61 | fn roundtrip() { 62 | let req = SignRequest { 63 | host_block_number: U256::from(0), 64 | host_chain_id: U256::from(1), 65 | ru_chain_id: U256::from(2), 66 | gas_limit: U256::from(5), 67 | ru_reward_address: Address::repeat_byte(6), 68 | contents: B256::repeat_byte(7), 69 | }; 70 | 71 | let ser = serde_json::to_string(&req).unwrap(); 72 | let de: SignRequest = serde_json::from_str(&ser).unwrap(); 73 | assert_eq!(req, de); 74 | 75 | assert_eq!( 76 | req.signing_hash(), 77 | b256!("74388c53a86cf15b3e8b11fa5f499dac87819fd00c20cfec4557b7d551b2c445") 78 | ); 79 | 80 | assert_eq!(de.signing_hash(), req.signing_hash()); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/resp.rs: -------------------------------------------------------------------------------- 1 | use crate::SignRequest; 2 | use alloy::primitives::{Address, PrimitiveSignature, SignatureError}; 3 | use serde::{Deserialize, Serialize}; 4 | 5 | /// A signature response from a [`RequestSigner`]. 6 | /// 7 | /// [`RequestSigner`]: crate::RequestSigner 8 | #[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)] 9 | #[serde(rename_all = "camelCase")] 10 | pub struct SignResponse { 11 | /// The request that was signed. 12 | pub req: SignRequest, 13 | /// The signature over that request. 14 | pub sig: PrimitiveSignature, 15 | } 16 | 17 | impl SignResponse { 18 | /// Get the signer of the request. 19 | /// 20 | /// # Panics 21 | /// 22 | /// - If recovery fails due to a k256 error. 23 | pub fn signer(&self) -> Result { 24 | self.sig.recover_address_from_prehash(&self.req.signing_hash()) 25 | } 26 | } 27 | 28 | #[cfg(test)] 29 | mod test { 30 | use super::*; 31 | use crate::{RequestSigner, SignRequest}; 32 | use alloy::primitives::U256; 33 | 34 | #[tokio::test] 35 | async fn test_sign_response() { 36 | let req = SignRequest { 37 | host_block_number: U256::from(0), 38 | host_chain_id: U256::from(1u64), 39 | ru_chain_id: U256::from(2u64), 40 | gas_limit: U256::from(5u64), 41 | ru_reward_address: Address::repeat_byte(6), 42 | contents: [7u8; 32].into(), 43 | }; 44 | let signer = alloy::signers::local::PrivateKeySigner::from_slice(&[8u8; 32]).unwrap(); 45 | let sig = signer.sign_request(&req).await.unwrap(); 46 | let resp = SignResponse { req, sig }; 47 | let addr = resp.signer().unwrap(); 48 | 49 | assert_eq!(addr, signer.address()); 50 | } 51 | 52 | #[tokio::test] 53 | async fn deser_roundtrip() { 54 | let req = SignRequest { 55 | host_block_number: U256::from(0), 56 | host_chain_id: U256::from(1u64), 57 | ru_chain_id: U256::from(2u64), 58 | gas_limit: U256::from(5u64), 59 | ru_reward_address: Address::repeat_byte(6), 60 | contents: [7u8; 32].into(), 61 | }; 62 | let signer = alloy::signers::local::PrivateKeySigner::from_slice(&[8u8; 32]).unwrap(); 63 | 64 | let sig = signer.sign_request(&req).await.unwrap(); 65 | 66 | let resp = SignResponse { req, sig }; 67 | 68 | let json = serde_json::to_string(&resp).unwrap(); 69 | let resp2: SignResponse = serde_json::from_str(&json).unwrap(); 70 | 71 | assert_eq!(resp, resp2); 72 | } 73 | } 74 | --------------------------------------------------------------------------------