From 3a4063deac25a49f539c6a5e3e6bcb18cdb047f2 Mon Sep 17 00:00:00 2001 From: Preston Ong Date: Thu, 26 Sep 2024 17:56:47 +0800 Subject: [PATCH] Holesky Deployment and Github Actions (#6) * holesky helper broadcasts, remapping, solc updates and untrack standard-json-inputs * updated README.md * network segregation * github actions and added slither * typo * update bot commenting script --- .github/scripts/slither-comment.js | 31 ++ .github/workflows/{test.yml => build.yml} | 15 +- .github/workflows/slither.yml | 41 +++ README.md | 57 +++- .../17000/updateStorageDao-latest.json | 52 +++ .../17000/deployAll-latest.json | 321 ++++++++++++++++++ .../deployEnclaveIdentityHelper-latest.json | 46 +++ .../17000/deployFmspcTcbHelper-latest.json | 46 +++ .../17000/deployPckHelper-latest.json | 46 +++ .../17000/deployX509CrlHelper-latest.json | 46 +++ foundry.toml | 9 +- remappings.txt | 4 +- slither.config.json | 3 + standard-json-input/enclaveidhelper.json | 1 - standard-json-input/pccsstorage.json | 1 - standard-json-input/pck.json | 1 - standard-json-input/pckhelper.json | 1 - standard-json-input/pcs.json | 1 - standard-json-input/qeiddao.json | 1 - standard-json-input/tcb.json | 1 - standard-json-input/tcbhelper.json | 1 - standard-json-input/x509crlhelper.json | 1 - 22 files changed, 692 insertions(+), 34 deletions(-) create mode 100644 .github/scripts/slither-comment.js rename .github/workflows/{test.yml => build.yml} (67%) create mode 100644 .github/workflows/slither.yml create mode 100644 broadcast/ConfigAutomataDao.s.sol/17000/updateStorageDao-latest.json create mode 100644 broadcast/DeployAutomataDao.s.sol/17000/deployAll-latest.json create mode 100644 broadcast/DeployHelpers.s.sol/17000/deployEnclaveIdentityHelper-latest.json create mode 100644 broadcast/DeployHelpers.s.sol/17000/deployFmspcTcbHelper-latest.json create mode 100644 broadcast/DeployHelpers.s.sol/17000/deployPckHelper-latest.json create mode 100644 broadcast/DeployHelpers.s.sol/17000/deployX509CrlHelper-latest.json create mode 100644 slither.config.json delete mode 100644 standard-json-input/enclaveidhelper.json delete mode 100644 standard-json-input/pccsstorage.json delete mode 100644 standard-json-input/pck.json delete mode 100644 standard-json-input/pckhelper.json delete mode 100644 standard-json-input/pcs.json delete mode 100644 standard-json-input/qeiddao.json delete mode 100644 standard-json-input/tcb.json delete mode 100644 standard-json-input/tcbhelper.json delete mode 100644 standard-json-input/x509crlhelper.json diff --git a/.github/scripts/slither-comment.js b/.github/scripts/slither-comment.js new file mode 100644 index 0000000..a1d35db --- /dev/null +++ b/.github/scripts/slither-comment.js @@ -0,0 +1,31 @@ +// Ref: https://github.com/marketplace/actions/slither-action#example-workflow-markdown-report + +module.exports = async ({ github, context, header, body }) => { + const collapse_details = '
Click me to view the full report '; + const close_details = '
'; + + const comment = [header, collapse_details, body, close_details].join("\n"); + + const { data: comments } = await github.rest.issues.listComments({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.payload.number, + }); + + const botComment = comments.find( + (comment) => + // github-actions bot user + comment.user.id === 41898282 && comment.body.startsWith(header) + ); + + const commentFn = botComment ? "updateComment" : "createComment"; + + await github.rest.issues[commentFn]({ + owner: context.repo.owner, + repo: context.repo.repo, + body: comment, + ...(botComment + ? { comment_id: botComment.id } + : { issue_number: context.payload.number }), + }); + }; \ No newline at end of file diff --git a/.github/workflows/test.yml b/.github/workflows/build.yml similarity index 67% rename from .github/workflows/test.yml rename to .github/workflows/build.yml index 9282e82..c00acd9 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/build.yml @@ -1,9 +1,11 @@ -name: test +name: Foundry Build CI/CD -on: workflow_dispatch - -env: - FOUNDRY_PROFILE: ci +# Controls when the workflow will run +on: + push: + branches: [main] + pull_request: + branches: [main] jobs: check: @@ -32,3 +34,6 @@ jobs: run: | forge test -vvv id: test + + - name: Run snapshot and Generate Summary + run: NO_COLOR=1 forge snapshot --gas-report >> $GITHUB_STEP_SUMMARY diff --git a/.github/workflows/slither.yml b/.github/workflows/slither.yml new file mode 100644 index 0000000..e07a7fd --- /dev/null +++ b/.github/workflows/slither.yml @@ -0,0 +1,41 @@ +name: Slither Static Analysis + +# Runs only on PR merging to main +on: + pull_request: + branches: [ main ] + types: [ opened, reopened, synchronize, ready_for_review ] + + workflow_dispatch: + +jobs: + analyze: + runs-on: ubuntu-latest + + # Do not run when PR is still a draft + if: ${{ !github.event.pull_request.draft }} + + env: + commit_url: ${{ github.server_url }}/${{ github.repository }}/blob/${{ github.event.pull_request.head.sha }}/ + + steps: + - uses: actions/checkout@v3 + + - name: Run Slither + uses: crytic/slither-action@v0.4.0 + id: slither + with: + fail-on: none + slither-args: --checklist --show-ignored-findings --markdown-root ${{ env.commit_url }} + + + - name: Create/update checklist as PR comment + uses: actions/github-script@v7 + env: + REPORT: ${{ steps.slither.outputs.stdout }} + with: + script: | + const script = require('.github/scripts/slither-comment') + const header = '# Slither report' + const body = process.env.REPORT + await script({ github, context, header, body }) \ No newline at end of file diff --git a/README.md b/README.md index 5a6f076..5e834da 100644 --- a/README.md +++ b/README.md @@ -29,20 +29,30 @@ The Helper contracts provide APIs for parsing collaterals and converting into So The Helper contracts have been deployed to testnet, and can be used by both on-chain and off-chain programs. +#### Testnet + +| | Network | Address | +| --- | --- | --- | +| `EnclaveIdentityHelper.sol` | Automata Testnet | [0xfd4a34b578B352FE1896CDafaEb0f45f993352Bf](https://explorer-testnet.ata.network/address/0xfd4a34b578B352FE1896CDafaEb0f45f993352Bf) | +| | Ethereum Holesky Testnet | [0xEea41Ae0cB09A478b80425Ae61c85e445E83c415](https://holesky.etherscan.io/address/0xEea41Ae0cB09A478b80425Ae61c85e445E83c415) | +| `FmspcTcbHelper.sol` | Automata Testnet | [0xC2A662e08A35513596E22D0aC236Ce72e59125EE](https://explorer-testnet.ata.network/address/0xC2A662e08A35513596E22D0aC236Ce72e59125EE) | +| | Ethereum Holesky Testnet | [0xc728DD0FcD76CD9166F66e1CD8002dE86d6525B8](https://holesky.etherscan.io/address/0xc728DD0FcD76CD9166F66e1CD8002dE86d6525B8) | +| `PCKHelper.sol` | Automata Testnet | [0x5213c0e3Ab478dbc83E8afFF8909717332E4f8E1](https://explorer-testnet.ata.network/address/0x5213c0e3Ab478dbc83E8afFF8909717332E4f8E1) | +| | Ethereum Holesky Testnet | [0xDe20629a87C371668bB371ef1d77D9D167E52021](https://holesky.etherscan.io/address/0xDe20629a87C371668bB371ef1d77D9D167E52021) | +| `X509CRLHelper.sol` | Automata Testnet | [0x12C1E13Aa2a238EAb15c2e2b6AC670266bc3C814](https://explorer-testnet.ata.network/address/0x12C1E13Aa2a238EAb15c2e2b6AC670266bc3C814) | +| | Ethereum Holesky Testnet | [0x3ACBfad7460e2fae32A31f863e1A38F7a002cEA8](https://holesky.etherscan.io/address/0x3ACBfad7460e2fae32A31f863e1A38F7a002cEA8) | + +#### Mainnet | | Network | Address | | --- | --- | --- | -| `EnclaveIdentityHelper.sol` | testnet | [0xfd4a34b578B352FE1896CDafaEb0f45f993352Bf](https://explorer-testnet.ata.network/address/0xfd4a34b578B352FE1896CDafaEb0f45f993352Bf) | -| | mainnet (preview) | [0x13BECaa512713Ac7C2d7a04ba221aD5E02D43DFE](https://explorer.ata.network/address/0x13BECaa512713Ac7C2d7a04ba221aD5E02D43DFE) | -| `FmspcTcbHelper.sol` | testnet | [0xC2A662e08A35513596E22D0aC236Ce72e59125EE](https://explorer-testnet.ata.network/address/0xC2A662e08A35513596E22D0aC236Ce72e59125EE) | -| | mainnet (preview) | [0xc99bf04c31bf3d026b5b47b2574fc19c1459b732](https://explorer.ata.network/address/0xc99bf04c31bf3d026b5b47b2574fc19c1459b732) | -| `PCKHelper.sol` | testnet | [0x5213c0e3Ab478dbc83E8afFF8909717332E4f8E1](https://explorer-testnet.ata.network/address/0x5213c0e3Ab478dbc83E8afFF8909717332E4f8E1) | -| | mainnet (preview) | [0x3e2fe733E444313A93Fa3f9AEd3bB203048dDE70](https://explorer.ata.network/address/0x3e2fe733E444313A93Fa3f9AEd3bB203048dDE70) | -| `X509CRLHelper.sol` | testnet | [0x12C1E13Aa2a238EAb15c2e2b6AC670266bc3C814](https://explorer-testnet.ata.network/address/0x12C1E13Aa2a238EAb15c2e2b6AC670266bc3C814) | -| | mainnet (preview) | [0x2567245dE6E349C8B7AA82fD6FF854b844A0aEF9](https://explorer.ata.network/address/0x2567245dE6E349C8B7AA82fD6FF854b844A0aEF9) | +| `EnclaveIdentityHelper.sol` | Automata Mainnet (preview) | [0x13BECaa512713Ac7C2d7a04ba221aD5E02D43DFE](https://explorer.ata.network/address/0x13BECaa512713Ac7C2d7a04ba221aD5E02D43DFE) | +| `FmspcTcbHelper.sol` | Automata Mainnet (preview) | [0xc99bf04c31bf3d026b5b47b2574fc19c1459b732](https://explorer.ata.network/address/0xc99bf04c31bf3d026b5b47b2574fc19c1459b732) | +| `PCKHelper.sol` | Automata Mainnet (preview) | [0x3e2fe733E444313A93Fa3f9AEd3bB203048dDE70](https://explorer.ata.network/address/0x3e2fe733E444313A93Fa3f9AEd3bB203048dDE70) | +| `X509CRLHelper.sol` | Automata Mainnet (preview) | [0x2567245dE6E349C8B7AA82fD6FF854b844A0aEF9](https://explorer.ata.network/address/0x2567245dE6E349C8B7AA82fD6FF854b844A0aEF9) | -### Base Contracts +### Base libraries and Automata DAO contracts -The Base contracts are libraries that provide the Data Access Object (DAO) APIs with similar designs inspired from the [Design Guide for Intel SGX PCCS](https://download.01.org/intel-sgx/sgx-dcap/1.21/linux/docs/SGX_DCAP_Caching_Service_Design_Guide.pdf). +The base contracts are libraries that provide the Data Access Object (DAO) APIs with similar designs inspired from the [Design Guide for Intel SGX PCCS](https://download.01.org/intel-sgx/sgx-dcap/1.21/linux/docs/SGX_DCAP_Caching_Service_Design_Guide.pdf). Base contracts are dependent on Helper contracts to parse collaterals, and contains implementation of basic collateral authenticity check functions for upserts. Smart contract developers are encouraged to extend the base contracts to build their own custom implementation of on-chain PCCS. @@ -50,16 +60,27 @@ Base contracts are dependent on Helper contracts to parse collaterals, and conta Our DAO implementation can be found in the [`automata_pccs`](./src/automata_pccs/) directory, and are deployed to testnet. +#### Testnet + +| | Network | Address | +| --- | --- | --- | +| `AutomataEnclaveIdentityDao.sol` | Automata Testnet | [0x413272890ab9F155a47A5F90a404Fb51aa259087](https://explorer-testnet.ata.network/address/0x413272890ab9F155a47A5F90a404Fb51aa259087) | +| | Ethereum Holesky Testnet | [0x9f4b0fB3A95072bD133082e9683A3536669EFE07](https://holesky.etherscan.io/address/0x9f4b0fB3A95072bD133082e9683A3536669EFE07) | +| `AutomataFmspcTcbDao.sol` | Automata Testnet | [0x7c04B466DebA13D48116b1339C62b35B9805E5A0](https://explorer-testnet.ata.network/address/0x7c04B466DebA13D48116b1339C62b35B9805E5A0) | +| | Ethereum Holesky Testnet | [0xaB5074445E5ae3C650553d5a7560B3A7121635B9](https://holesky.etherscan.io/address/0xaB5074445E5ae3C650553d5a7560B3A7121635B9) | +| `AutomataPckDao.sol` | Automata Testnet | [0x6D4cA6AE5315EBBcb4331c82531db0ad8853Eb31](https://explorer-testnet.ata.network/address/0x6D4cA6AE5315EBBcb4331c82531db0ad8853Eb31) | +| | Ethereum Holesky Testnet | [0x5B2d7781E3c44966769484daBCdc435EFD281c34](https://holesky.etherscan.io/address/0x5B2d7781E3c44966769484daBCdc435EFD281c34) | +| `AutomataPcsDao.sol` | Automata Testnet | [0xD0335cbC73CA2f8EDd98a2BE3909f55642F414D7](https://explorer-testnet.ata.network/address/0xD0335cbC73CA2f8EDd98a2BE3909f55642F414D7) | +| | Ethereum Holesky Testnet | [0x66FdB4E72d2F4a7e2081bf83F1FfACC9bbCb384b](https://holesky.etherscan.io/address/0x66FdB4E72d2F4a7e2081bf83F1FfACC9bbCb384b) | + +### Mainnet + | | Network | Address | | --- | --- | --- | -| `AutomataEnclaveIdentityDao.sol` | testnet | [0x413272890ab9F155a47A5F90a404Fb51aa259087](https://explorer-testnet.ata.network/address/0x413272890ab9F155a47A5F90a404Fb51aa259087) | -| | mainnet (preview) | [0x28111536292b34f37120861A46B39BF39187d73a](https://explorer.ata.network/address/0x28111536292b34f37120861A46B39BF39187d73a) | -| `AutomataFmspcTcbDao.sol` | testnet | [0x7c04B466DebA13D48116b1339C62b35B9805E5A0](https://explorer-testnet.ata.network/address/0x7c04B466DebA13D48116b1339C62b35B9805E5A0) | -| | mainnet (preview) | [0x868c18869f68E0E0b0b7B2B4439f7fDDd0421e6b](https://explorer.ata.network/address/0x868c18869f68E0E0b0b7B2B4439f7fDDd0421e6b) | -| `AutomataPckDao.sol` | testnet | [0x6D4cA6AE5315EBBcb4331c82531db0ad8853Eb31](https://explorer-testnet.ata.network/address/0x6D4cA6AE5315EBBcb4331c82531db0ad8853Eb31) | -| | mainnet (preview) | [0xeCc198936FcA3Ca1fDc97B8612B32185908917B0](https://explorer.ata.network/address/0xeCc198936FcA3Ca1fDc97B8612B32185908917B0) | -| `AutomataPcsDao.sol` | testnet | [0xD0335cbC73CA2f8EDd98a2BE3909f55642F414D7](https://explorer-testnet.ata.network/address/0xD0335cbC73CA2f8EDd98a2BE3909f55642F414D7) | -| | mainnet (preview) | [0x86f8865bce8be62cb8096b5b94fa3fb3a6ed330c](https://explorer.ata.network/address/0x86f8865bce8be62cb8096b5b94fa3fb3a6ed330c) | +| `AutomataEnclaveIdentityDao.sol` | Automata Mainnet (preview) | [0x28111536292b34f37120861A46B39BF39187d73a](https://explorer.ata.network/address/0x28111536292b34f37120861A46B39BF39187d73a) | +| `AutomataFmspcTcbDao.sol` | Automata Mainnet (preview) | [0x868c18869f68E0E0b0b7B2B4439f7fDDd0421e6b](https://explorer.ata.network/address/0x868c18869f68E0E0b0b7B2B4439f7fDDd0421e6b) | +| `AutomataPckDao.sol` | Automata Mainnet (preview) | [0xeCc198936FcA3Ca1fDc97B8612B32185908917B0](https://explorer.ata.network/address/0xeCc198936FcA3Ca1fDc97B8612B32185908917B0) | +| `AutomataPcsDao.sol` | Automata Mainnet (preview) | [0x86f8865bce8be62cb8096b5b94fa3fb3a6ed330c](https://explorer.ata.network/address/0x86f8865bce8be62cb8096b5b94fa3fb3a6ed330c) | --- diff --git a/broadcast/ConfigAutomataDao.s.sol/17000/updateStorageDao-latest.json b/broadcast/ConfigAutomataDao.s.sol/17000/updateStorageDao-latest.json new file mode 100644 index 0000000..00c3ac4 --- /dev/null +++ b/broadcast/ConfigAutomataDao.s.sol/17000/updateStorageDao-latest.json @@ -0,0 +1,52 @@ +{ + "transactions": [ + { + "hash": "0xf8dad7ed1df383b23241f479f1a2a09b0a99cbf242a984ca1345b65be38a5037", + "transactionType": "CALL", + "contractName": null, + "contractAddress": "0x528538ab97aa4f7d3397b3ca0a4a6f5d9cf52f97", + "function": "updateDao(address,address,address,address)", + "arguments": [ + "0x66FdB4E72d2F4a7e2081bf83F1FfACC9bbCb384b", + "0x5B2d7781E3c44966769484daBCdc435EFD281c34", + "0xaB5074445E5ae3C650553d5a7560B3A7121635B9", + "0x9f4b0fB3A95072bD133082e9683A3536669EFE07" + ], + "transaction": { + "from": "0xdc3bda6d40f0e33e0dfa4aef9604b66195e6c5dc", + "to": "0x528538ab97aa4f7d3397b3ca0a4a6f5d9cf52f97", + "gas": "0xb87b", + "value": "0x0", + "input": "0x40070f2d00000000000000000000000066fdb4e72d2f4a7e2081bf83f1ffacc9bbcb384b0000000000000000000000005b2d7781e3c44966769484dabcdc435efd281c34000000000000000000000000ab5074445e5ae3c650553d5a7560b3a7121635b90000000000000000000000009f4b0fb3a95072bd133082e9683a3536669efe07", + "nonce": "0x10", + "chainId": "0x4268" + }, + "additionalContracts": [], + "isFixedGasLimit": false + } + ], + "receipts": [ + { + "status": "0x1", + "cumulativeGasUsed": "0x286c78", + "logs": [], + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "type": "0x2", + "transactionHash": "0xf8dad7ed1df383b23241f479f1a2a09b0a99cbf242a984ca1345b65be38a5037", + "transactionIndex": "0x13", + "blockHash": "0xc3e11d60b12c88caa2a5070f3644768927443479537cf580bc3e6c6c77efb38b", + "blockNumber": "0x24b699", + "gasUsed": "0x8590", + "effectiveGasPrice": "0x5f5e109", + "from": "0xdc3bda6d40f0e33e0dfa4aef9604b66195e6c5dc", + "to": "0x528538ab97aa4f7d3397b3ca0a4a6f5d9cf52f97", + "contractAddress": null + } + ], + "libraries": [], + "pending": [], + "returns": {}, + "timestamp": 1727269520, + "chain": 17000, + "commit": "fbc73d7" +} \ No newline at end of file diff --git a/broadcast/DeployAutomataDao.s.sol/17000/deployAll-latest.json b/broadcast/DeployAutomataDao.s.sol/17000/deployAll-latest.json new file mode 100644 index 0000000..036bc23 --- /dev/null +++ b/broadcast/DeployAutomataDao.s.sol/17000/deployAll-latest.json @@ -0,0 +1,321 @@ +{ + "transactions": [ + { + "hash": "0x104c439ad134026dcfce4a9cbccade63125cde06471b3bb73ec83fc5cfe0cdfe", + "transactionType": "CREATE", + "contractName": "AutomataDaoStorage", + "contractAddress": "0x528538ab97aa4f7d3397b3ca0a4a6f5d9cf52f97", + "function": null, + "arguments": null, + "transaction": { + "from": "0xdc3bda6d40f0e33e0dfa4aef9604b66195e6c5dc", + "gas": "0xf167a", + "value": "0x0", + "input": "0x60806040523460465733638b78c6d819553360007f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08180a3604051610c62908161004c8239f35b600080fdfe608080604052600436101561001357600080fd5b60003560e01c90816303f2c7da146108d857508063256929621461086e5780633a91c2261461060557806340070f2d1461046d57806354d1f13d14610407578063715018a61461036757806386911cde146102c15780638da5cb5b14610250578063aad8a0e7146101c3578063f04e283e14610155578063f2fde38b146100f95763fee81cf4146100a357600080fd5b346100f45760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100f4576100da610a80565b63389a75e1600c52600052602080600c2054604051908152f35b600080fd5b60207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100f45761012b610a80565b610133610b72565b8060601b156101475761014590610baa565b005b637448fbae6000526004601cfd5b60207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100f457610187610a80565b61018f610b72565b63389a75e1600c52806000526020600c2090815442116101b55760006101459255610baa565b636f5e88186000526004601cfd5b346100f45760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100f45773ffffffffffffffffffffffffffffffffffffffff61020f610a80565b610217610b72565b16600052600060205260406000207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff008154169055600080f35b346100f45760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100f45760207fffffffffffffffffffffffffffffffffffffffffffffffffffffffff748739275473ffffffffffffffffffffffffffffffffffffffff60405191168152f35b346100f45760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100f45733600052600060205261030a60ff60406000205416610af6565b600435600052600160205260406000206103248154610aa3565b908161032c57005b81601f6000931160011461033e575055005b8183526020832061035a91601f0160051c810190600101610b5b565b8082528160208120915555005b60007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100f457610399610b72565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffff74873927547f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a360007fffffffffffffffffffffffffffffffffffffffffffffffffffffffff7487392755005b60007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100f45763389a75e1600c523360005260006020600c2055337ffa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92600080a2005b346100f45760807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100f4576104a4610a80565b60243573ffffffffffffffffffffffffffffffffffffffff81168091036100f4576044359073ffffffffffffffffffffffffffffffffffffffff82168092036100f4576064359273ffffffffffffffffffffffffffffffffffffffff84168094036100f45773ffffffffffffffffffffffffffffffffffffffff90610527610b72565b166000526000602052604060002060017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff008254161790556000526000602052604060002060017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff008254161790556000526000602052604060002060017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff008254161790556000526000602052604060002060017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00825416179055600080f35b346100f45760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100f45760243567ffffffffffffffff81116100f457366023820112156100f457806004013567ffffffffffffffff811161083f576040519161069b60207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8501160184610a3f565b81835236602483830101116100f4578160009260246020930183860137830101523360005260006020526106d660ff60406000205416610af6565b60043560005260016020526040600020815167ffffffffffffffff811161083f576107018254610aa3565b601f8111610802575b50602092601f8211600114610766579281929360009261075b575b50507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8260011b9260031b1c1916179055600080f35b015190508380610725565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe082169383600052806000209160005b8681106107ea57508360019596106107b3575b505050811b019055005b01517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f88460031b161c191690558380806107a9565b91926020600181928685015181550194019201610796565b61082f90836000526020600020601f840160051c81019160208510610835575b601f0160051c0190610b5b565b8361070a565b9091508190610822565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100f45763389a75e1600c52336000526202a30042016020600c2055337fdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d600080a2005b346100f45760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100f4576004356000526001602052604060002081600082549261092684610aa3565b80845293600181169081156109ff57506001146109b8575b5061094b92500382610a3f565b60405190602082528181519182602083015260005b8381106109a05750507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f836000604080968601015201168101030190f35b60208282018101516040878401015285935001610960565b90506000929192526020600020906000915b8183106109e357505090602061094b928201018461093e565b60209193508060019154838588010152019101909183926109ca565b6020935061094b9592507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0091501682840152151560051b8201018461093e565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761083f57604052565b6004359073ffffffffffffffffffffffffffffffffffffffff821682036100f457565b90600182811c92168015610aec575b6020831014610abd57565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b91607f1691610ab2565b15610afd57565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f556e617574686f72697a65642063616c6c6572000000000000000000000000006044820152fd5b818110610b66575050565b60008155600101610b5b565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffff74873927543303610b9c57565b6382b429006000526004601cfd5b73ffffffffffffffffffffffffffffffffffffffff16807fffffffffffffffffffffffffffffffffffffffffffffffffffffffff74873927547f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a37fffffffffffffffffffffffffffffffffffffffffffffffffffffffff748739275556fea26469706673582212201eeed37763cbf42dc736f6858134f10c584505bc6f89691676431bf36db288bf64736f6c634300081b0033", + "nonce": "0xa", + "chainId": "0x4268" + }, + "additionalContracts": [], + "isFixedGasLimit": false + }, + { + "hash": "0x0e3b682dcd48ffd100c72d1e457aa5095f2888ebd0754d2521cbd903a40dfd55", + "transactionType": "CREATE", + "contractName": "AutomataPcsDao", + "contractAddress": "0x66fdb4e72d2f4a7e2081bf83f1ffacc9bbcb384b", + "function": null, + "arguments": [ + "0x528538Ab97aA4f7D3397b3Ca0A4a6f5d9CF52F97", + "0xDe20629a87C371668bB371ef1d77D9D167E52021", + "0x3ACBfad7460e2fae32A31f863e1A38F7a002cEA8" + ], + "transaction": { + "from": "0xdc3bda6d40f0e33e0dfa4aef9604b66195e6c5dc", + "gas": "0x27f152", + "value": "0x0", + "input": "0x6080346100dd57601f6122fd38819003918201601f19168301916001600160401b038311848410176100e2578084926060946040528339810103126100dd57610047816100f8565b906100606040610059602084016100f8565b92016100f8565b600080546001600160a01b03199081166001600160a01b0395861617825560018054821694861694909417909355600280549093169390911692909217905533638b78c6d819819055907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08180a36040516121f0908161010d8239f35b600080fd5b634e487b7160e01b600052604160045260246000fd5b51906001600160a01b03821682036100dd5756fe6080604052600436101561001257600080fd5b60003560e01c806308854e0414610152578063189d97f71461014d57806325692962146101485780632bce0147146100fd57806337b8762d146101435780633b3954551461013e57806354d1f13d146101395780636b1c539914610134578063715018a61461012f578063722f13271461012a5780638da5cb5b14610125578063974ddd9514610120578063b414d0b21461011b578063bf721aaf14610116578063eae1c31d14610111578063ec950d331461010c578063f04e283e14610107578063f2fde38b14610102578063fb1c0125146100fd5763fee81cf4146100f857600080fd5b611562565b610697565b611508565b611498565b611446565b611377565b61133c565b61130f565b61129e565b61122d565b611163565b61105d565b610e16565b610db0565b610723565b6106d1565b61062d565b6105e3565b6101f4565b60043590600482101561016657565b600080fd5b9181601f840112156101665782359167ffffffffffffffff8311610166576020838186019501011161016657565b9060407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc83011261016657600435600481101561016657916024359067ffffffffffffffff8211610166576101f09160040161016b565b9091565b346101665761020236610199565b60048310156105785782158015610565575b61053057600061025561023c60025473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff1690565b906040517fa91105ce0000000000000000000000000000000000000000000000000000000081526020818061028e878960048401611790565b0381865afa908115610433578291610501575b50156104d9576040517f634fdad50000000000000000000000000000000000000000000000000000000081528181806102de878960048401611790565b0381865afa908115610433576103419161033d9184916104b7575b50610303886105a7565b600288148080156104a4575b156104955761031d896105a7565b156104885761032a6118be565b6020815191012090602081519101201490565b1590565b610460578060405180937ffcf0be24000000000000000000000000000000000000000000000000000000008252818061037e888a60048401611790565b03915afa80156104335781928291610438575b50602082604051806103a3818861197c565b039060025afa156104335761033d6103c7918351906103c189611cf3565b91611d6d565b61040b576104076103f6866103ee876103e98888602081519101209285611aa2565b611ab8565b9182916105b1565b556040519081529081906020820190565b0390f35b807fe7ef341f0000000000000000000000000000000000000000000000000000000060049252fd5b6116f7565b90506104579192503d8084833e61044f81836115fd565b810190611933565b91909138610391565b807f1e7ab5990000000000000000000000000000000000000000000000000000000060049252fd5b610490611883565b61032a565b5061049e61180d565b90610872565b506104ae896105a7565b6001891461030f565b6104d391503d8086833e6104cb81836115fd565b8101906117d6565b386102f9565b807fdba942a20000000000000000000000000000000000000000000000000000000060049252fd5b610523915060203d602011610529575b61051b81836115fd565b810190611778565b386102a1565b503d610511565b61055f6004847f9849e774000000000000000000000000000000000000000000000000000000006000526115c1565b60246000fd5b5061056f836105a7565b60038314610214565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b6004111561057857565b6004811015610578576000526004602052604060002090565b6004811015610578576000526003602052604060002090565b346101665760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261016657602061062461061f610157565b6105b1565b54604051908152f35b60007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101665763389a75e1600c52336000526202a30042016020600c2055337fdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d600080a2005b346101665760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261016657602060405160008152f35b346101665760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261016657602073ffffffffffffffffffffffffffffffffffffffff60025416604051908152f35b346101665761073136610199565b909161075861023c61023c60015473ffffffffffffffffffffffffffffffffffffffff1690565b916040517fed33378500000000000000000000000000000000000000000000000000000000815260208180610791858960048401611790565b0381875afa90811561043357600091610d91575b5015610d67576040517f634fdad5000000000000000000000000000000000000000000000000000000008152600081806107e3858960048401611790565b0381875afa90811561043357600091610d4c575b506040517f35c757bd00000000000000000000000000000000000000000000000000000000815260008180610830868a60048401611790565b0381885afa90811561043357600091610d31575b5061084d61180d565b6060610858866105a7565b60028603610cb0575061033d610886916108706118be565b945b906020815191012090602081519101201490565b610c86576108a69161033d91906020815191012090602081519101201490565b610c5c576000805260046020526108dd7f17ef568e3e12ab5b9c7254a8d58478811de00f9e6eb34345acd53bf8fd09d3ec54611703565b6108e6836105a7565b8215908115610b0457506040517f6d3537a000000000000000000000000000000000000000000000000000000000815260008180610928868a60048401611790565b0381885afa8015610433577f89f72d7c488e5b53a77c23ebcb36970ef7eb5bcf6658e9b8292cfbe4703a847391600091610ae1575b506020815191012003610ab7575b610973611ca9565b600060405180967ffcf0be2400000000000000000000000000000000000000000000000000000000825281806109ad888c60048401611790565b03915afa908115610433576000958692610a99575b5060206000604051806109d5818b61197c565b039060025afa1561043357600051926109ed866105a7565b15610a57575090610a03916103c1368589611993565b15610a2d57610407936103e9610a25928560206103f697519101209285611a37565b9182916105ca565b7fe7ef341f0000000000000000000000000000000000000000000000000000000060005260046000fd5b805190919015610a6f57610a6a92611d6d565b610a03565b7fcd69d3740000000000000000000000000000000000000000000000000000000060005260046000fd5b9095610aaf92503d8091833e61044f81836115fd565b9094386109c2565b7fe1406f790000000000000000000000000000000000000000000000000000000060005260046000fd5b610afe91503d806000833e610af681836115fd565b8101906116d1565b3861095d565b8051610b11575b5061096b565b604051907fb29b51cb00000000000000000000000000000000000000000000000000000000825260208280610b4a878b60048401611790565b0381895afa91821561043357600092610c26575b506020610bbc91610b8761023c60025473ffffffffffffffffffffffffffffffffffffffff1690565b60405180809581947fcedb97810000000000000000000000000000000000000000000000000000000083528860048401611908565b03915afa90811561043357600091610c07575b5015610b0b57610c0390847f291990cd0000000000000000000000000000000000000000000000000000000060005261191f565b6000fd5b610c20915060203d6020116105295761051b81836115fd565b38610bcf565b610bbc919250610c4d602091823d8411610c55575b610c4581836115fd565b8101906118f9565b929150610b5e565b503d610c3b565b7f92ec707e0000000000000000000000000000000000000000000000000000000060005260046000fd5b7f1e7ab5990000000000000000000000000000000000000000000000000000000060005260046000fd5b610cb9866105a7565b60018603610cd7575061033d61088691610cd1611883565b94610872565b610ce0866105a7565b60038603610cf8575061033d61088691610cd1611848565b92610d02866105a7565b8515610d16575b6108869161033d91610872565b925061033d61088691610d2761180d565b9491509150610d09565b610d4691503d806000833e6104cb81836115fd565b38610844565b610d6191503d806000833e6104cb81836115fd565b386107f7565b7fdba942a20000000000000000000000000000000000000000000000000000000060005260046000fd5b610daa915060203d6020116105295761051b81836115fd565b386107a5565b60007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101665763389a75e1600c523360005260006020600c2055337ffa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92600080a2005b3461016657600060207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261105a5760043567ffffffffffffffff811161105857610e6790369060040161016b565b90610e8a61023c60025473ffffffffffffffffffffffffffffffffffffffff1690565b926040517fa91105ce00000000000000000000000000000000000000000000000000000000815260208180610ec3878760048401611790565b0381885afa908115610433578291611039575b50156104d9576040517f634fdad5000000000000000000000000000000000000000000000000000000008152818180610f13878760048401611790565b0381885afa8015610433578261033d92610f49949261101d575b50610f37816105a7565b610f40816105a7565b5061032a61180d565b610c8657600060405180947ffcf0be240000000000000000000000000000000000000000000000000000000082528180610f87878760048401611790565b03915afa8015610433576000938491610ffe575b506020600060405180610fae818961197c565b039060025afa156104335761033d610fce91600051906103c16000611cf3565b610a2d576103e9610fea92846020809651910120926000611aa2565b80610ff560006105b1565b55604051908152f35b905061101491933d8091833e61044f81836115fd565b92909238610f9b565b6110329192503d8084833e6104cb81836115fd565b9038610f2d565b611052915060203d6020116105295761051b81836115fd565b38610ed6565b505b80fd5b60007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101665761108f611bef565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffff74873927547f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a360007fffffffffffffffffffffffffffffffffffffffffffffffffffffffff7487392755005b60005b8381106111105750506000910152565b8181015183820152602001611100565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f60209361115c815180928187528780880191016110fd565b0116010190565b346101665760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101665761119a610157565b6060906111a6816105ca565b548015611200576111e2926111c66111c061040793611703565b936105b1565b54806111f0575b50604051938493604085526040850190611120565b908382036020850152611120565b6111fa9150611703565b386111cd565b61055f827f33247a8a000000000000000000000000000000000000000000000000000000006000526115b3565b346101665760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101665760207fffffffffffffffffffffffffffffffffffffffffffffffffffffffff748739275473ffffffffffffffffffffffffffffffffffffffff60405191168152f35b346101665760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101665760206106246112da610157565b6105ca565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc60209101126101665760043590565b3461016657610407611328611323366112df565b611703565b604051918291602083526020830190611120565b3461016657602061134c366112df565b604051908152f35b6004359073ffffffffffffffffffffffffffffffffffffffff8216820361016657565b346101665760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610166576113ae611354565b6024359073ffffffffffffffffffffffffffffffffffffffff82168092036101665773ffffffffffffffffffffffffffffffffffffffff906113ee611bef565b167fffffffffffffffffffffffff000000000000000000000000000000000000000060015416176001557fffffffffffffffffffffffff00000000000000000000000000000000000000006002541617600255600080f35b346101665760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261016657602073ffffffffffffffffffffffffffffffffffffffff60015416604051908152f35b60207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610166576114ca611354565b6114d2611bef565b63389a75e1600c52806000526020600c2090815442116114fa5760006114f89255611c27565b005b636f5e88186000526004601cfd5b60207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101665761153a611354565b611542611bef565b8060601b15611554576114f890611c27565b637448fbae6000526004601cfd5b346101665760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261016657611599611354565b63389a75e1600c52600052602080600c2054604051908152f35b600481101561057857600452565b9060048210156105785752565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761163e57604052565b6115ce565b67ffffffffffffffff811161163e57601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b9092919261168a81611643565b9161169860405193846115fd565b8294828452828201116101665760206116b29301906110fd565b565b9080601f830112156101665781516116ce9260200161167d565b90565b9060208282031261016657815167ffffffffffffffff8111610166576116ce92016116b4565b6040513d6000823e3d90fd5b600073ffffffffffffffffffffffffffffffffffffffff815416916024604051809481937f03f2c7da00000000000000000000000000000000000000000000000000000000835260048301525afa90811561043357600091611763575090565b6116ce91503d806000833e610af681836115fd565b90816020910312610166575180151581036101665790565b90601f836040947fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe093602086528160208701528686013760008582860101520116010190565b6020818303126101665780519067ffffffffffffffff821161016657019080601f830112156101665781516116ce9260200161167d565b6040519061181c6040836115fd565b601182527f496e74656c2053475820526f6f742043410000000000000000000000000000006020830152565b604051906118576040836115fd565b601582527f496e74656c2053475820544342205369676e696e6700000000000000000000006020830152565b604051906118926040836115fd565b601a82527f496e74656c205347582050434b2050726f636573736f722043410000000000006020830152565b604051906118cd6040836115fd565b601982527f496e74656c205347582050434b20506c6174666f726d204341000000000000006020830152565b90816020910312610166575190565b6040906116ce939281528160208201520190611120565b919061192e60046044946115c1565b602452565b91909160408184031261016657805167ffffffffffffffff8111610166578361195d9183016116b4565b92602082015167ffffffffffffffff8111610166576116ce92016116b4565b9061198f602092828151948592016110fd565b0190565b92919261199f82611643565b916119ad60405193846115fd565b829481845281830111610166578281602093846000960137010152565b604051906040820182811067ffffffffffffffff82111761163e576040908152600083525160c081018367ffffffffffffffff82118383101761163e5760209160405260008352600082840152600060408401526000606084015260606080840152600060a08401520152565b90611a4d611a7b92611a476119ca565b506105ca565b549260405193611a5e60c0866115fd565b338552600060208601526001604086015260608501523691611993565b6080820152600060a082015260405190611a966040836115fd565b60008252602082015290565b90611a4d611a7b92611ab26119ca565b506105b1565b60200160608151015190600091611ace81611703565b51611b6e575b506080915051015160009073ffffffffffffffffffffffffffffffffffffffff82541690813b15611b6a57611b49839283926040519485809481937f3a91c2260000000000000000000000000000000000000000000000000000000083528a6004840152604060248401526044830190611120565b03925af1801561043357611b5c57505090565b81611b66916115fd565b5090565b8280fd5b73ffffffffffffffffffffffffffffffffffffffff83541690813b15611beb5783916024839260405196879384927f86911cde00000000000000000000000000000000000000000000000000000000845260048401525af192831561043357608093611bdb575b50611ad4565b81611be5916115fd565b38611bd5565b8380fd5b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffff74873927543303611c1957565b6382b429006000526004601cfd5b73ffffffffffffffffffffffffffffffffffffffff16807fffffffffffffffffffffffffffffffffffffffffffffffffffffffff74873927547f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a37fffffffffffffffffffffffffffffffffffffffffffffffffffffffff7487392755565b611cb360006105ca565b506000805260036020526116ce7f3617319a054d772f909f7c479a2cebe5066e836a939412e32403c99029b92eff546000611ced816105a7565b50611703565b611cfc816105ca565b54600080526003602052907f3617319a054d772f909f7c479a2cebe5066e836a939412e32403c99029b92eff549060048110156105785760028114908115611d59575b5015611d4f57506116ce90611703565b6116ce9150611703565b60019150611d66816105a7565b1438611d3f565b91906040815103611dfa576000611dd49273ffffffffffffffffffffffffffffffffffffffff80600154161660405180809681947f6d3537a00000000000000000000000000000000000000000000000000000000083526020600484018181520190611120565b03915afa91821561043357600092611e02575b506040825103611dfa576116ce92611e96565b505050600090565b611e189192503d806000833e610af681836115fd565b9038611de7565b602081519101519060208110611e33575090565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9060200360031b1b1690565b15611e6757565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052600160045260246000fd5b60008091611f5f93600195611ebe611eb3611eb8611eb385611f9d565b611e1f565b936120ac565b90611ed7611eb3611ed1611eb384611f9d565b926120ac565b91604051936020850195865260408501526060840152608083015260a082015260a08152611f0660c0826115fd565b519073c2b78104907f722dabac4c69f826a522b2754de45afa3d15611f6357611f503d91611f3383611643565b92611f4160405194856115fd565b83523d6000602085013e611e60565b602080825183010191016118f9565b1490565b611f50606091611e60565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b805160201161016657611fb06020611643565b90611fbe60405192836115fd565b60208252611fcc6020611643565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0602084019101368237602080920190915b602081101561205d578061203e57507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff905b518251821691191617905290565b61205261204d61205792612170565b6121ab565b61217e565b90612030565b909182518152602081018091116120a75791602081018091116120a757907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0810190811115611ffe575b611f6e565b8051604011610166576120bf6020611643565b906120cd60405192836115fd565b602082526120db6020611643565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe06020840191013682379060400160205b6020811015612122578061203e57509192915050565b909182518152602081018091116120a75791602081018091116120a757907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081019081111561210c57611f6e565b60200390602082116120a757565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82019182116120a757565b601f81116120a7576101000a9056fea2646970667358221220d9ae8c5e30c5a1064173af28a410fad081b790fc381e7d3f6e7ffe56048946c064736f6c634300081b0033000000000000000000000000528538ab97aa4f7d3397b3ca0a4a6f5d9cf52f97000000000000000000000000de20629a87c371668bb371ef1d77d9d167e520210000000000000000000000003acbfad7460e2fae32a31f863e1a38f7a002cea8", + "nonce": "0xb", + "chainId": "0x4268" + }, + "additionalContracts": [], + "isFixedGasLimit": false + }, + { + "hash": "0x7082f2d1151154d013e920428493b609386f8721cd951231472ae2a72e5c960d", + "transactionType": "CREATE", + "contractName": "AutomataPckDao", + "contractAddress": "0x5b2d7781e3c44966769484dabcdc435efd281c34", + "function": null, + "arguments": [ + "0x528538Ab97aA4f7D3397b3Ca0A4a6f5d9CF52F97", + "0x66FdB4E72d2F4a7e2081bf83F1FfACC9bbCb384b", + "0xDe20629a87C371668bB371ef1d77D9D167E52021", + "0x3ACBfad7460e2fae32A31f863e1A38F7a002cEA8" + ], + "transaction": { + "from": "0xdc3bda6d40f0e33e0dfa4aef9604b66195e6c5dc", + "gas": "0x3a14e0", + "value": "0x0", + "input": "0x60803461010257601f6132f638819003918201601f19168301916001600160401b0383118484101761010757808492608094604052833981010312610102576100478161011d565b906100546020820161011d565b9061006d60606100666040840161011d565b920161011d565b600080546001600160a01b03199081166001600160a01b0396871617825560018054821694871694851790556002805482169587169590951790945560038054851690931790925560048054909316931692909217905533638b78c6d819819055907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08180a36040516131c490816101328239f35b600080fd5b634e487b7160e01b600052604160045260246000fd5b51906001600160a01b03821682036101025756fe6080604052600436101561001257600080fd5b60003560e01c80632569296214610192578063286858391461018d57806336c33bb61461015157806337b8762d14610188578063382e8f6b146101835780633c7d71141461017e57806348ac80591461017957806354d1f13d1461017457806359a517ff1461016f5780635be0fa4b1461016a57806368ef0c5314610165578063715018a6146101605780637eb619c71461015b5780638da5cb5b14610156578063980e087814610151578063b414d0b21461014c578063bf721aaf14610147578063c925d17a14610142578063d88d1df61461013d578063e4a4171a14610138578063ec950d3314610133578063f04e283e1461012e578063f2fde38b146101295763fee81cf41461012457600080fd5b611048565b610fee565b610f7e565b610f2c565b610f06565b610eb4565b610dce565b610db6565b610d8e565b6105f3565b610d1d565b610c5b565b610bbb565b610b95565b610a88565b610a36565b6109d0565b610966565b610893565b6106c5565b61062d565b61038a565b60007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101fc5763389a75e1600c52336000526202a30042016020600c2055337fdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d600080a2005b600080fd5b9181601f840112156101fc5782359167ffffffffffffffff83116101fc57602083818601950101116101fc57565b60005b8381106102425750506000910152565b8181015183820152602001610232565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f60209361028e8151809281875287808801910161022f565b0116010190565b604081016040825282518091526060820190602060608260051b8501019401916000905b82821061034157505050506020818303910152815180825260208201916020808360051b8301019401926000915b8383106102f657505050505090565b9091929394602080610332837fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe086600196030187528951610252565b970193019301919392906102e7565b9091929460208061037c837fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa089600196030186528951610252565b9701920192019092916102b9565b346101fc5760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101fc5760043567ffffffffffffffff81116101fc576103d9903690600401610201565b9060243567ffffffffffffffff81116101fc57610419610400610438923690600401610201565b919061041160609586973691611192565b923691611192565b6104216111c9565b6104296111c9565b916104326111c9565b93611ed3565b50506040517fffffffffffffffffffffffffffffffff000000000000000000000000000000008416602082019081527fffff0000000000000000000000000000000000000000000000000000000000008416603083015292939291506104c981603281015b037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08101835282611105565b519020916104e1836000526005602052604060002090565b5490816104fc575b604051806104f8888883610295565b0390f35b91935091935061050b826111f6565b92610515836111f6565b9460005b84811061052657506104e9565b806105d76105d261057461054f60019561054a886000526005602052604060002090565b612ac7565b7fffffffffffffffffffffffffffffffffffff00000000000000000000000000001690565b6040517fffffffffffffffffffffffffffffffffffff0000000000000000000000000000821660208201526105b5906105b0816032810161049d565b612065565b6105bf858c61128c565b526105ca848b61128c565b5087876120cc565b611b60565b6105e1828a61128c565b526105ec818961128c565b5001610519565b346101fc5760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101fc57602060405160008152f35b346101fc5760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101fc57602073ffffffffffffffffffffffffffffffffffffffff60045416604051908152f35b6004359073ffffffffffffffffffffffffffffffffffffffff821682036101fc57565b6044359073ffffffffffffffffffffffffffffffffffffffff821682036101fc57565b346101fc5760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101fc576106fc61067f565b6024359073ffffffffffffffffffffffffffffffffffffffff821682036101fc5773ffffffffffffffffffffffffffffffffffffffff80928161073d6106a2565b9361074661216e565b167fffffffffffffffffffffffff00000000000000000000000000000000000000006002541617600255167fffffffffffffffffffffffff00000000000000000000000000000000000000006001541617600155167fffffffffffffffffffffffff00000000000000000000000000000000000000006004541617600455600080f35b60807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc8201126101fc5760043567ffffffffffffffff81116101fc578161081291600401610201565b9290929160243567ffffffffffffffff81116101fc578161083591600401610201565b9290929160443567ffffffffffffffff81116101fc578161085891600401610201565b929092916064359067ffffffffffffffff82116101fc5761087b91600401610201565b9091565b906020610890928181520190610252565b90565b346101fc576104116108d861092a6109076108fb6108e06108d06108b6366107c9565b989660009e95949d93969e989192985060609e3691611192565b973691611192565b993691611192565b90602096604051936108f28986611105565b60008552611ed3565b509082849593946121a6565b6000526006845261092461091f604060002054611b60565b6112a5565b916120cc565b80610945575b506104f8604051928392808452830190610252565b610950919250611b60565b9038610930565b6004359060048210156101fc57565b346101fc5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101fc576109c26104f86109ab6109a6610957565b611388565b604092919251938493604085526040850190610252565b908382036020850152610252565b60007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101fc5763389a75e1600c523360005260006020600c2055337ffa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92600080a2005b346101fc5760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101fc57602073ffffffffffffffffffffffffffffffffffffffff60035416604051908152f35b346101fc5760a07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101fc57610abf610957565b60243567ffffffffffffffff81116101fc57610adf903690600401610201565b919060443567ffffffffffffffff81116101fc57610b01903690600401610201565b60649391933567ffffffffffffffff81116101fc57610b24903690600401610201565b916084359567ffffffffffffffff87116101fc576104f897610b4d610b55983690600401610201565b97909661152d565b6040519081529081906020820190565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc60209101126101fc5760043590565b346101fc57610ba336610b65565b60005260076020526020604060002054604051908152f35b60007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101fc57610bed61216e565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffff74873927547f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a360007fffffffffffffffffffffffffffffffffffffffffffffffffffffffff7487392755005b346101fc57610411610c94610cb8610cae6108d0610c9c610c7b366107c9565b979660009d95929c93949991969d5060609d3691611192565b983691611192565b90602095604051936108f28886611105565b5092919091612540565b80610cd257506104f8604051928392808452830190610252565b61095091925061091f610ce491611b60565b7fffffffffffffffffffffffffffffffffffff0000000000000000000000000000604051911683820152601281526105b0603282611105565b346101fc5760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101fc5760207fffffffffffffffffffffffffffffffffffffffffffffffffffffffff748739275473ffffffffffffffffffffffffffffffffffffffff60405191168152f35b346101fc576104f8610da26105d236610b65565b604051918291602083526020830190610252565b346101fc576020610dc636610b65565b604051908152f35b346101fc5760a07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101fc5760043567ffffffffffffffff81116101fc57610e1d903690600401610201565b60243567ffffffffffffffff81116101fc57610e3d903690600401610201565b91909260443567ffffffffffffffff81116101fc57610e60903690600401610201565b9060643567ffffffffffffffff81116101fc57610e81903690600401610201565b9290916084359667ffffffffffffffff88116101fc576104f898610eac610b55993690600401610201565b989097611cec565b346101fc5760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101fc57602073ffffffffffffffffffffffffffffffffffffffff60025416604051908152f35b346101fc57610f1436610b65565b60005260066020526020604060002054604051908152f35b346101fc5760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101fc57602073ffffffffffffffffffffffffffffffffffffffff60015416604051908152f35b60207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101fc57610fb061067f565b610fb861216e565b63389a75e1600c52806000526020600c209081544211610fe0576000610fde92556124be565b005b636f5e88186000526004601cfd5b60207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101fc5761102061067f565b61102861216e565b8060601b1561103a57610fde906124be565b637448fbae6000526004601cfd5b346101fc5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101fc5761107f61067f565b63389a75e1600c52600052602080600c2054604051908152f35b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040810190811067ffffffffffffffff8211176110e457604052565b611099565b60c0810190811067ffffffffffffffff8211176110e457604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff8211176110e457604052565b6040519061115661012083611105565b565b67ffffffffffffffff81116110e457601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b92919261119e82611158565b916111ac6040519384611105565b8294818452818301116101fc578281602093846000960137010152565b604051906111d8602083611105565b60008252565b67ffffffffffffffff81116110e45760051b60200190565b90611200826111de565b61120d6040519182611105565b82815260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe061123d83956111de565b01910160005b82811061124f57505050565b606082820152602001611243565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b80518210156112a05760209160051b010190565b61125d565b90602082519201517fffffffffffffffffffffffffffffffffffff0000000000000000000000000000811692601281106112dd575050565b7fffffffffffffffffffffffffffffffffffff0000000000000000000000000000929350829060120360031b1b161690565b6004111561131957565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b90602491600481101561131957600452565b9190602083019260048210156113195752565b908160209103126101fc575190565b6040513d6000823e3d90fd5b906004821015611319578115801561151a575b6114e95761141360206113df6113c660025473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff1690565b93604051809381927f974ddd950000000000000000000000000000000000000000000000000000000083526004830161135a565b0381865afa9081156114c5576000916114ca575b50602060405180947f974ddd95000000000000000000000000000000000000000000000000000000008252818061146660048201906000602083019252565b03915afa9283156114c55760009361148c575b5061148661089091611b60565b92611b60565b6108909193506114b66114869160203d6020116114be575b6114ae8183611105565b81019061136d565b939150611479565b503d6114a4565b61137c565b6114e3915060203d6020116114be576114ae8183611105565b38611427565b611516827f9849e77400000000000000000000000000000000000000000000000000000000600052611348565b6000fd5b506115248261130f565b6003821461139b565b9392909695919660048510156113195784158015611ae5575b611ab8576115999392916115616115699260009a3691611192565b933691611192565b946020956104326040519261157e8985611105565b8a84526040519461158f8a87611105565b8b86523691611192565b98939692979150506115ac368484611192565b936115cf6113c660035473ffffffffffffffffffffffffffffffffffffffff1690565b6040517fed3337850000000000000000000000000000000000000000000000000000000081528781806116058a6004830161087f565b0381855afa9081156114c5578491611a9b575b5015611a73578260405180927f3b79a61e00000000000000000000000000000000000000000000000000000000825281806116568b6004830161087f565b03915afa9081156114c5578391611a51575b5061010081019182518881519101209660606116838361130f565b60028303611a1c57506116b66116b261169a612273565b8b860151906020815191012090602081519101201490565b1590565b6119f4576116e06116b260808501516116cd6122ae565b6020815191012090602081519101201490565b6119cc5760c08301516116f4918d8d6126f9565b6117166113c660025473ffffffffffffffffffffffffffffffffffffffff1690565b8860405180927f189d97f7000000000000000000000000000000000000000000000000000000008252818061174e876004830161135a565b03915afa9081156114c557899161176c9187916119af575b50611b60565b80516118d9575b505061177e90611388565b508051909290156118b1578361179c89925160405191828092612300565b039060025afa156114c5576117bc916116b29160e0855191015190612adb565b61188957506118839694926117dc6108909795936117e193888888612361565b6123c9565b6040517fffffffffffffffffffffffffffffffff0000000000000000000000000000000084169281019283527fffff000000000000000000000000000000000000000000000000000000000000851660108401527fffffffffffffffffffffffffffffffffffff000000000000000000000000000086166012840152909687929091611870816024840161049d565b5190206000526007602052604060002090565b556123e9565b807fe7ef341f0000000000000000000000000000000000000000000000000000000060049252fd5b6004847fcd69d374000000000000000000000000000000000000000000000000000000008152fd5b90611937916119006113c660045473ffffffffffffffffffffffffffffffffffffffff1690565b85516040518095819482937fcedb9781000000000000000000000000000000000000000000000000000000008452600484016122e9565b03915afa9081156114c5578591611982575b50611955578738611773565b81517f167c231a000000000000000000000000000000000000000000000000000000008552600452602484fd5b6119a29150893d8b116119a8575b61199a8183611105565b810190612220565b38611949565b503d611990565b6119c69150833d85116114be576114ae8183611105565b38611766565b6004857f92ec707e000000000000000000000000000000000000000000000000000000008152fd5b6004857f1e7ab599000000000000000000000000000000000000000000000000000000008152fd5b611a258361130f565b60018314611a3a575b6116b26116b69161169a565b506116b66116b2611a49612238565b915050611a2e565b611a6d91503d8085833e611a658183611105565b810190611bdd565b38611668565b6004837fdba942a2000000000000000000000000000000000000000000000000000000008152fd5b611ab29150883d8a116119a85761199a8183611105565b38611618565b611516857f9849e77400000000000000000000000000000000000000000000000000000000600052611348565b50611aef8561130f565b60038514611546565b81601f820112156101fc5760208151910190611b1381611158565b92611b216040519485611105565b818452818301116101fc5761089091602084019061022f565b906020828203126101fc57815167ffffffffffffffff81116101fc576108909201611af8565b600073ffffffffffffffffffffffffffffffffffffffff815416916024604051809481937f03f2c7da00000000000000000000000000000000000000000000000000000000835260048301525afa9081156114c557600091611bc0575090565b61089091503d806000833e611bd58183611105565b810190611b3a565b6020818303126101fc5780519067ffffffffffffffff82116101fc5701610120818303126101fc57611c0d611146565b9181518352602082015167ffffffffffffffff81116101fc5781611c32918401611af8565b60208401526040820151604084015260608201516060840152608082015167ffffffffffffffff81116101fc5781611c6b918401611af8565b608084015260a082015167ffffffffffffffff81116101fc5781611c90918401611af8565b60a084015260c082015160c084015260e082015167ffffffffffffffff81116101fc5781611cbf918401611af8565b60e084015261010082015167ffffffffffffffff81116101fc57611ce39201611af8565b61010082015290565b61043294611d17611d2794611d0f611d1f94611d2f9e9c9b969d989d3691611192565b9b3691611192565b963691611192565b953691611192565b943691611192565b6040517fffffffffffffffffffffffffffffffff000000000000000000000000000000008616602082019081527fffff000000000000000000000000000000000000000000000000000000000000861660308301527fffffffffffffffffffffffffffffffffffff0000000000000000000000000000831660328301529496959491939291611dc591611870816044810161049d565b548015611ea957611dd590611b60565b91611df86113c660035473ffffffffffffffffffffffffffffffffffffffff1690565b92600060405180957f3b79a61e0000000000000000000000000000000000000000000000000000000082528180611e32866004830161087f565b03915afa9384156114c557611e8b95611e65611e739360c0611e7b98611e6e95600091611e8e575b50015190838d6126f9565b84848b8a61292a565b6129b8565b9687956121a6565b6000526006602052604060002090565b55565b611ea391503d806000833e611a658183611105565b38611e5a565b7f82fba2950000000000000000000000000000000000000000000000000000000060005260046000fd5b9091929394600095600095600095600095600095602081511461201b575b506004815114612002575b506020815114611fb2575b506004815114611f70575b506024815114611f1f5750565b610890919250611f31611f48916129e0565b71ffffffffffffffffffffffffffffffffffff1690565b60701b7fffffffffffffffffffffffffffffffffffff00000000000000000000000000001690565b611fab919450611f8261ffff916129e0565b1660f01b7fffff0000000000000000000000000000000000000000000000000000000000001690565b9238611f12565b611ffb919650611fd26fffffffffffffffffffffffffffffffff916129e0565b1660801b7fffffffffffffffffffffffffffffffff000000000000000000000000000000001690565b9438611f07565b612014919850611f8261ffff916129e0565b9638611efc565b7fffffffffffffffffffffffffffffffff00000000000000000000000000000000919a506120596fffffffffffffffffffffffffffffffff916129e0565b1660801b169838611ef1565b90815191604051926022600285019482800186526f30313233343536373839616263646566600f5201908201915b8281036120a857506000815260200160405250565b60016002910191600f835116516001820153600f835160041c165181530190612093565b6040517fffffffffffffffffffffffffffffffff00000000000000000000000000000000909116602082019081527fffff00000000000000000000000000000000000000000000000000000000000090921660308201527fffffffffffffffffffffffffffffffffffff000000000000000000000000000090921660328301529061215a816044810161049d565b519020600052600760205260406000205490565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffff7487392754330361219857565b6382b429006000526004601cfd5b927fffffffffffffffffffffffffffffffff000000000000000000000000000000007fffff000000000000000000000000000000000000000000000000000000000000929383604051958360208801981688521660308601521660328401521660428201526024815261221a604482611105565b51902090565b908160209103126101fc575180151581036101fc5790565b60405190612247604083611105565b601a82527f496e74656c205347582050434b2050726f636573736f722043410000000000006020830152565b60405190612282604083611105565b601982527f496e74656c205347582050434b20506c6174666f726d204341000000000000006020830152565b604051906122bd604083611105565b601982527f496e74656c205347582050434b204365727469666963617465000000000000006020830152565b604090610890939281528160208201520190610252565b906123136020928281519485920161022f565b0190565b60405190612324826110c8565b60008252604051602083612337836110e9565b60008352600082840152600060408401526000606084015260606080840152600060a08401520152565b91612379916123a49493612373612317565b506120cc565b9260405193612387856110e9565b338552600060208601526001604086015260608501523691611192565b6080820152600060a0820152604051906123bd826110c8565b60008252602082015290565b6080602061089092016123e0606082510151612b8d565b51015182612c17565b6040517fffffffffffffffffffffffffffffffff00000000000000000000000000000000909116602082019081527fffff000000000000000000000000000000000000000000000000000000000000909216603082015261244d816032810161049d565b5190209081600052600560205261249c7fffffffffffffffffffffffffffffffffffff000000000000000000000000000060406000209216809260019160005201602052604060002054151590565b156124a5575050565b6124bb9160005260056020526040600020612f25565b50565b73ffffffffffffffffffffffffffffffffffffffff16807fffffffffffffffffffffffffffffffffffffffffffffffffffffffff74873927547f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a37fffffffffffffffffffffffffffffffffffffffffffffffffffffffff7487392755565b9061254c9392916121a6565b600052600660205260406000205490565b906080828203126101fc57815161ffff811681036101fc5792602083015167ffffffffffffffff81116101fc57830182601f820112156101fc5780516125a2816111de565b916125b06040519384611105565b81835260208084019260051b820101908582116101fc57602001915b8183106126165750505092604081015167ffffffffffffffff81116101fc57836125f7918301611af8565b92606082015167ffffffffffffffff81116101fc576108909201611af8565b825160ff811681036101fc578152602092830192016125cc565b929190612647602091604086526040860190610252565b930152565b90602082519201517fffff00000000000000000000000000000000000000000000000000000000000081169260028110612684575050565b7fffff000000000000000000000000000000000000000000000000000000000000929350829060020360031b1b161690565b60207fff00000000000000000000000000000000000000000000000000000000000000916126ed600195948281519485920161022f565b019160f81b1681520190565b9260009061275b9592936127256113c660035473ffffffffffffffffffffffffffffffffffffffff1690565b906040518098819482937fe81c707e00000000000000000000000000000000000000000000000000000000845260048401612630565b03915afa9283156114c5576000936000956000916128c6575b506127e86127ed927fffff000000000000000000000000000000000000000000000000000000000000806127aa61049d9561264c565b921691161495604051928391602083017fffff00000000000000000000000000000000000000000000000000000000000060029260f01b1681520190565b612d0a565b9260009260605b86518510156128325760019061282a612817612810888b61128c565b5160ff1690565b9161049d604051938492602084016126b6565b9401936127f4565b7fffffffffffffffffffffffffffffffffffff000000000000000000000000000093965061054f91945061091f61287b9161049d61288195986040519485936020850190612300565b90612300565b9116148115916128bd575b5061289357565b7f4a629e240000000000000000000000000000000000000000000000000000000060005260046000fd5b9050153861288c565b7fffff00000000000000000000000000000000000000000000000000000000000096506127aa955061049d91506127ed92876129176127e8933d806000833e61290f8183611105565b81019061255d565b9b92905099909a95505050509250612774565b9061293f939291612939612317565b50612540565b907fffffffffffffffffffffffffffffffffffff0000000000000000000000000000604051911660208201526020815261297a604082611105565b60405191612987836110e9565b338352600060208401526001604084015260608301526080820152600060a0820152604051906123bd604083611105565b6020608091016129cc606082510151612b8d565b510151610890815160208301209182612c17565b90600082517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6001821160208087015160f01c17613078141660011b915b7403010a071000000b0104040208000c05090d060e0f6d03e4088843e41bac00000000000060ff6001808701968a010151161c601f161a908460fc1c82151715029360041b01019082811015612a975790917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90612a1e565b50925015612aa157565b63101827966000526004601cfd5b80548210156112a05760005260206000200190600090565b90612ad191612aaf565b90549060031b1c90565b91906040815103612b68576000612b429273ffffffffffffffffffffffffffffffffffffffff80600154161660405180809681947f6d3537a00000000000000000000000000000000000000000000000000000000083526020600484018181520190610252565b03915afa9182156114c557600092612b70575b506040825103612b685761089092612e4d565b505050600090565b612b869192503d806000833e611bd58183611105565b9038612b55565b612b9681611b60565b9060009151612ba3575050565b73ffffffffffffffffffffffffffffffffffffffff82541690813b15612c135782916024839260405194859384927f86911cde00000000000000000000000000000000000000000000000000000000845260048401525af180156114c557612c09575050565b816124bb91611105565b8280fd5b9060009173ffffffffffffffffffffffffffffffffffffffff83541690813b15612c9757918391612c8593836040518096819582947f3a91c2260000000000000000000000000000000000000000000000000000000084526004840152604060248401526044830190610252565b03925af180156114c557612c09575050565b8380fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6020039060208211612cd857565b612c9b565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8201918211612cd857565b805160609291815b612d1a575050565b90927fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff840193808511612cd85782518510156112a057600160207fff00000000000000000000000000000000000000000000000000000000000000601f612dce948701015116936040519481612d99879351809286808701910161022f565b8201908382015203017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1810184520182611105565b929081612d12565b602081519101519060208110612dea575090565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9060200360031b1b1690565b15612e1e57565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052600160045260246000fd5b60008091612f1693600195612e75612e6a612e6f612e6a85612fad565b612dd6565b936130bb565b90612e8e612e6a612e88612e6a84612fad565b926130bb565b91604051936020850195865260408501526060840152608083015260a082015260a08152612ebd60c082611105565b519073c2b78104907f722dabac4c69f826a522b2754de45afa3d15612f1a57612f073d91612eea83611158565b92612ef86040519485611105565b83523d6000602085013e612e17565b6020808251830101910161136d565b1490565b612f07606091612e17565b6000828152600182016020526040902054612fa657805490680100000000000000008210156110e457612f5f826001809401835582612aaf565b81549060031b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff86831b921b191617905580549260005201602052604060002055600190565b5050600090565b80516020116101fc57612fc06020611158565b90612fce6040519283611105565b60208252612fdc6020611158565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0602084019101368237602080920190915b602081101561306d578061304e57507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff905b518251821691191617905290565b61306261305d61306792612cca565b61317f565b612cdd565b90613040565b90918251815260208101809111612cd8579160208101809111612cd857907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081019081111561300e57612c9b565b80516040116101fc576130ce6020611158565b906130dc6040519283611105565b602082526130ea6020611158565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe06020840191013682379060400160205b6020811015613131578061304e57509192915050565b90918251815260208101809111612cd8579160208101809111612cd857907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081019081111561311b57612c9b565b601f8111612cd8576101000a9056fea2646970667358221220bc29fe43299bc0b95bd40d8dff175ccf0c2942314871bd36be4b5ffc5686c9d164736f6c634300081b0033000000000000000000000000528538ab97aa4f7d3397b3ca0a4a6f5d9cf52f9700000000000000000000000066fdb4e72d2f4a7e2081bf83f1ffacc9bbcb384b000000000000000000000000de20629a87c371668bb371ef1d77d9d167e520210000000000000000000000003acbfad7460e2fae32a31f863e1a38f7a002cea8", + "nonce": "0xc", + "chainId": "0x4268" + }, + "additionalContracts": [], + "isFixedGasLimit": false + }, + { + "hash": "0xe2ffd95a1c67534b2ab7138bb090e761f7be6370a5f6908c0f1f54e8a58d6bdc", + "transactionType": "CREATE", + "contractName": "AutomataEnclaveIdentityDao", + "contractAddress": "0x9f4b0fb3a95072bd133082e9683a3536669efe07", + "function": null, + "arguments": [ + "0x528538Ab97aA4f7D3397b3Ca0A4a6f5d9CF52F97", + "0x66FdB4E72d2F4a7e2081bf83F1FfACC9bbCb384b", + "0xEea41Ae0cB09A478b80425Ae61c85e445E83c415", + "0xDe20629a87C371668bB371ef1d77D9D167E52021" + ], + "transaction": { + "from": "0xdc3bda6d40f0e33e0dfa4aef9604b66195e6c5dc", + "gas": "0x22bf5d", + "value": "0x0", + "input": "0x6080346100fa57601f611ddb38819003918201601f19168301916001600160401b038311848410176100ff578084926080946040528339810103126100fa5761004781610115565b61005360208301610115565b9061006c606061006560408601610115565b9401610115565b600080546001600160a01b03199081166001600160a01b03948516178255600180548216938516939093179092556002805483169484169490941790935560038054909116939091169290921790915533638b78c6d819819055907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08180a3604051611cb1908161012a8239f35b600080fd5b634e487b7160e01b600052604160045260246000fd5b51906001600160a01b03821682036100fa5756fe6080604052600436101561001257600080fd5b60003560e01c80630cac637814610137578063256929621461013257806330f704ea1461012d5780633e2b584c1461012857806354d1f13d146101235780635e3d47111461011e57806361d20bea14610119578063715018a6146101145780637ecda5f01461010f5780638da5cb5b1461010a578063b414d0b214610105578063bf721aaf14610100578063d88d1df6146100fb578063ec950d33146100f6578063f04e283e146100f1578063f0f074f7146100ec578063f2fde38b146100e75763fee81cf4146100e257600080fd5b610bd2565b610ba1565b610a90565b6109f6565b6109a4565b610952565b61093a565b61090d565b61089c565b610749565b61061b565b6105c9565b6105a3565b61050d565b6104b3565b6101e0565b610176565b346101715760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261017157602060405160008152f35b600080fd5b60007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101715763389a75e1600c52336000526202a30042016020600c2055337fdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d600080a2005b346101715760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101715760443560043560243567ffffffffffffffff831161017157826004019260407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc82360301126101715761029361027a60025473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff1690565b602060405180927f974ddd9500000000000000000000000000000000000000000000000000000000825281806102d160048201906003602083019252565b03915afa8015610408576102ed91600091610437575b50610e2f565b90602060006102fc8780610bfa565b9061030c60405180938193610c4b565b039060025afa15610408576103439161033f916103396103326024600051930189610bfa565b36916110e6565b9061182e565b1590565b61040d57602060006103606103598685876113d3565b9580610bfa565b9061037060405180938193610c4b565b039060025afa15610408576103f36103e0926103b46103956104049660005190611609565b9485946040519283916020830195869091604092825260208201520190565b037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08101835282610cd1565b5190206000526004602052604060002090565b556040519081529081906020820190565b0390f35b610c59565b7f8de7233f0000000000000000000000000000000000000000000000000000000060005260046000fd5b610459915060203d60201161045f575b6104518183610cd1565b810190610d12565b386102e7565b503d610447565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc60209101126101715760043573ffffffffffffffffffffffffffffffffffffffff811681036101715790565b346101715773ffffffffffffffffffffffffffffffffffffffff6104d636610466565b6104de611740565b167fffffffffffffffffffffffff00000000000000000000000000000000000000006002541617600255600080f35b60007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101715763389a75e1600c523360005260006020600c2055337ffa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92600080a2005b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc60209101126101715760043590565b34610171576105b136610573565b60005260046020526020604060002054604051908152f35b346101715760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261017157602073ffffffffffffffffffffffffffffffffffffffff60035416604051908152f35b60007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101715761064d611740565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffff74873927547f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a360007fffffffffffffffffffffffffffffffffffffffffffffffffffffffff7487392755005b60005b8381106106ce5750506000910152565b81810151838201526020016106be565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f60209361071a815180928187528780880191016106bb565b0116010190565b9091610738610746936040845260408401906106de565b9160208184039101526106de565b90565b346101715760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101715761079a61027a60025473ffffffffffffffffffffffffffffffffffffffff1690565b6040517f974ddd9500000000000000000000000000000000000000000000000000000000815260036004820152602081602481855afa9081156104085760009161087d575b50602060405180937f974ddd95000000000000000000000000000000000000000000000000000000008252818061081e60048201906000602083019252565b03915afa91821561040857600092610854575b5061083e61084491610e2f565b91610e2f565b9061040460405192839283610721565b61084491925061087561083e9160203d60201161045f576104518183610cd1565b929150610831565b610896915060203d60201161045f576104518183610cd1565b386107df565b346101715760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101715760207fffffffffffffffffffffffffffffffffffffffffffffffffffffffff748739275473ffffffffffffffffffffffffffffffffffffffff60405191168152f35b346101715761040461092661092136610573565b610e2f565b6040519182916020835260208301906106de565b3461017157602061094a36610573565b604051908152f35b346101715760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261017157602073ffffffffffffffffffffffffffffffffffffffff60025416604051908152f35b346101715760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261017157602073ffffffffffffffffffffffffffffffffffffffff60015416604051908152f35b6109ff36610466565b610a07611740565b63389a75e1600c52806000526020600c209081544211610a2f576000610a2d9255611778565b005b636f5e88186000526004601cfd5b9061074691602081526020610a5d835160408385015260608401906106de565b9201519060407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0828503019101526106de565b346101715760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261017157600435602435610ae760405191610ad583610c94565b606083526020830193606085526117fa565b80610afb575b604051806104048482610a3d565b610b0490610e2f565b9182518301906060846020840193031261017157602084015167ffffffffffffffff811161017157826020610b3b92870101610ff4565b50604084015167ffffffffffffffff811161017157840182603f8201121561017157828160406020610b709401519101610dba565b9160608501519067ffffffffffffffff8211610171576020610b9792610404970101610def565b9052815238610aed565b610baa36610466565b610bb2611740565b8060601b15610bc457610a2d90611778565b637448fbae6000526004601cfd5b3461017157610be036610466565b63389a75e1600c52600052602080600c2054604051908152f35b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe181360301821215610171570180359067ffffffffffffffff82116101715760200191813603831361017157565b908092918237016000815290565b6040513d6000823e3d90fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040810190811067ffffffffffffffff821117610cb057604052565b610c65565b6060810190811067ffffffffffffffff821117610cb057604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff821117610cb057604052565b90816020910312610171575190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b60405190610d6061018083610cd1565b565b60405190610d6060c083610cd1565b60405190610d60604083610cd1565b67ffffffffffffffff8111610cb057601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b90929192610dc781610d80565b91610dd56040519384610cd1565b829482845282820111610171576020610d609301906106bb565b9080601f8301121561017157815161074692602001610dba565b9060208282031261017157815167ffffffffffffffff8111610171576107469201610def565b600073ffffffffffffffffffffffffffffffffffffffff815416916024604051809481937f03f2c7da00000000000000000000000000000000000000000000000000000000835260048301525afa90811561040857600091610e8f575090565b61074691503d806000833e610ea48183610cd1565b810190610e09565b5190600382101561017157565b519063ffffffff8216820361017157565b519067ffffffffffffffff8216820361017157565b51907fffffffff000000000000000000000000000000000000000000000000000000008216820361017157565b51907fffffffffffffffffffffffffffffffff000000000000000000000000000000008216820361017157565b519061ffff8216820361017157565b81601f820112156101715780519067ffffffffffffffff8211610cb05760405192610f7960208460051b0185610cd1565b8284526020606081860194028301019181831161017157602001925b828410610fa3575050505090565b6060848303126101715760405190610fba82610cb5565b610fc385610f39565b8252602085015160208301526040850151906004821015610171578260209260406060950152815201930192610f95565b919091610180818403126101715761100a610d50565b9261101482610eac565b845261102260208301610eb9565b602085015261103360408301610eca565b604085015261104460608301610eca565b606085015261105560808301610eb9565b608085015261106660a08301610edf565b60a085015261107760c08301610edf565b60c085015261108860e08301610f0c565b60e085015261109a6101008301610f0c565b6101008501526101208201516101208501526110b96101408301610f39565b61014085015261016082015167ffffffffffffffff8111610171576110de9201610f48565b610160830152565b9291926110f282610d80565b916111006040519384610cd1565b829481845281830111610171578281602093846000960137010152565b6040519061112a82610c94565b6000825260405160c081018367ffffffffffffffff821183831017610cb05760209160405260008352600082840152600060408401526000606084015260606080840152600060a08401520152565b9060208282031261017157815167ffffffffffffffff8111610171576107469201610ff4565b601f82602094937fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0938186528686013760008582860101520116010190565b91602061074693818152019161119f565b600311156111f957565b610d21565b9060038210156111f95752565b90602080835192838152019201906000905b80821061122a5750505090565b9091926040845161ffff81511683526020810151602084015201519060048210156111f9576060816001936040602094015201940192019061121d565b939161074695936113b76101606113c5946060895261128a60608a0182516111fe565b602081015163ffffffff1660808a0152604081015167ffffffffffffffff1660a08a0152606081015167ffffffffffffffff1660c08a0152608081015163ffffffff1660e08a015260a08101517fffffffff00000000000000000000000000000000000000000000000000000000166101008a015260c08101517fffffffff00000000000000000000000000000000000000000000000000000000166101208a015260e08101517fffffffffffffffffffffffffffffffff00000000000000000000000000000000166101408a01526101008101517fffffffffffffffffffffffffffffffff0000000000000000000000000000000016898301526101208101516101808a015261014081015161ffff166101a08a015201516101806101c08901526101e088019061120b565b91868303602088015261119f565b92604081850391015261119f565b91906113eb611451916113e461111d565b50846117fa565b92600061141061027a60035473ffffffffffffffffffffffffffffffffffffffff1690565b61141a8580610bfa565b91906040518096819482937fc0ed9773000000000000000000000000000000000000000000000000000000008452600484016111de565b03915afa918215610408576000926115e6575b50815190611471826111ef565b61147a826111ef565b036115bc576114a4611497604083015167ffffffffffffffff1690565b67ffffffffffffffff1690565b4210801561159c575b61157257606061152491611515846114e96114cb826114d898610bfa565b9790926020810190610bfa565b906040519889948760208701611267565b037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08101865285610cd1565b015167ffffffffffffffff1690565b91611545611530610d62565b3381529367ffffffffffffffff166020850152565b6001604084015260608301526080820152600060a0820152611565610d71565b9060008252602082015290565b7f9ac044990000000000000000000000000000000000000000000000000000000060005260046000fd5b506115b5611497606083015167ffffffffffffffff1690565b42116114ad565b7f289fa0cb0000000000000000000000000000000000000000000000000000000060005260046000fd5b61160291923d8091833e6115fa8183610cd1565b810190611179565b9038611464565b6020016060815101519060009161161f81610e2f565b516116bf575b506080915051015160009073ffffffffffffffffffffffffffffffffffffffff82541690813b156116bb5761169a839283926040519485809481937f3a91c2260000000000000000000000000000000000000000000000000000000083528a60048401526040602484015260448301906106de565b03925af18015610408576116ad57505090565b816116b791610cd1565b5090565b8280fd5b73ffffffffffffffffffffffffffffffffffffffff83541690813b1561173c5783916024839260405196879384927f86911cde00000000000000000000000000000000000000000000000000000000845260048401525af19283156104085760809361172c575b50611625565b8161173691610cd1565b38611726565b8380fd5b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffff7487392754330361176a57565b6382b429006000526004601cfd5b73ffffffffffffffffffffffffffffffffffffffff16807fffffffffffffffffffffffffffffffffffffffffffffffffffffffff74873927547f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a37fffffffffffffffffffffffffffffffffffffffffffffffffffffffff7487392755565b6040805160208101928352908101929092529061181a81606081016103b4565b519020600052600460205260406000205490565b919060408151036118bb5760006118959273ffffffffffffffffffffffffffffffffffffffff80600154161660405180809681947f6d3537a000000000000000000000000000000000000000000000000000000000835260206004840181815201906106de565b03915afa918215610408576000926118c3575b5060408251036118bb5761074692611957565b505050600090565b6118d99192503d806000833e610ea48183610cd1565b90386118a8565b6020815191015190602081106118f4575090565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9060200360031b1b1690565b1561192857565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052600160045260246000fd5b60008091611a209360019561197f61197461197961197485611a5e565b6118e0565b93611b6d565b9061199861197461199261197484611a5e565b92611b6d565b91604051936020850195865260408501526060840152608083015260a082015260a081526119c760c082610cd1565b519073c2b78104907f722dabac4c69f826a522b2754de45afa3d15611a2457611a113d916119f483610d80565b92611a026040519485610cd1565b83523d6000602085013e611921565b60208082518301019101610d12565b1490565b611a11606091611921565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b805160201161017157611a716020610d80565b90611a7f6040519283610cd1565b60208252611a8d6020610d80565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0602084019101368237602080920190915b6020811015611b1e5780611aff57507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff905b518251821691191617905290565b611b13611b0e611b1892611c31565b611c6c565b611c3f565b90611af1565b90918251815260208101809111611b68579160208101809111611b6857907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0810190811115611abf575b611a2f565b805160401161017157611b806020610d80565b90611b8e6040519283610cd1565b60208252611b9c6020610d80565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe06020840191013682379060400160205b6020811015611be35780611aff57509192915050565b90918251815260208101809111611b68579160208101809111611b6857907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0810190811115611bcd57611a2f565b6020039060208211611b6857565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8201918211611b6857565b601f8111611b68576101000a9056fea2646970667358221220a1fdabc0793e74f4e3ce438f5ed622068a62a9c13e618209c97ba0a1ba90d46464736f6c634300081b0033000000000000000000000000528538ab97aa4f7d3397b3ca0a4a6f5d9cf52f9700000000000000000000000066fdb4e72d2f4a7e2081bf83f1ffacc9bbcb384b000000000000000000000000eea41ae0cb09a478b80425ae61c85e445e83c415000000000000000000000000de20629a87c371668bb371ef1d77d9d167e52021", + "nonce": "0xd", + "chainId": "0x4268" + }, + "additionalContracts": [], + "isFixedGasLimit": false + }, + { + "hash": "0xa3c4cf799eb00b233a77a50813894f60fd98e6a7ed9713e7d13f406568ba61c1", + "transactionType": "CREATE", + "contractName": "AutomataFmspcTcbDao", + "contractAddress": "0xab5074445e5ae3c650553d5a7560b3a7121635b9", + "function": null, + "arguments": [ + "0x528538Ab97aA4f7D3397b3Ca0A4a6f5d9CF52F97", + "0x66FdB4E72d2F4a7e2081bf83F1FfACC9bbCb384b", + "0xc728DD0FcD76CD9166F66e1CD8002dE86d6525B8", + "0xDe20629a87C371668bB371ef1d77D9D167E52021" + ], + "transaction": { + "from": "0xdc3bda6d40f0e33e0dfa4aef9604b66195e6c5dc", + "gas": "0x2f48f9", + "value": "0x0", + "input": "0x6080346100fa57601f61295b38819003918201601f19168301916001600160401b038311848410176100ff578084926080946040528339810103126100fa5761004781610115565b61005360208301610115565b9061006c606061006560408601610115565b9401610115565b600080546001600160a01b03199081166001600160a01b03948516178255600180548216938516939093179092556002805483169484169490941790935560038054909116939091169290921790915533638b78c6d819819055907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08180a3604051612831908161012a8239f35b600080fd5b634e487b7160e01b600052604160045260246000fd5b51906001600160a01b03821682036100fa5756fe6080604052600436101561001257600080fd5b60003560e01c806321c7a3211461014757806325692962146101425780633e2b584c1461013d5780634ba52fa51461013857806354d1f13d14610133578063715018a61461012e5780638da5cb5b14610129578063a53e727514610124578063a8349fb71461011f578063b414d0b21461011a578063bf721aaf14610115578063c9d55de414610110578063cb7a96661461010b578063cfbc42fb14610106578063d88d1df614610101578063ec950d33146100fc578063f04e283e146100f7578063f2fde38b146100f25763fee81cf4146100ed57600080fd5b610c37565b610c06565b610bbf565b610b6d565b610b1b565b610a8a565b610147565b610a11565b6109f9565b6109cc565b610673565b6104ee565b6103ef565b61034f565b6102e9565b610297565b61023d565b610186565b346101815760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261018157602060405160008152f35b600080fd5b60007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101815763389a75e1600c52336000526202a30042016020600c2055337fdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d600080a2005b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc60209101126101815760043573ffffffffffffffffffffffffffffffffffffffff811681036101815790565b346101815773ffffffffffffffffffffffffffffffffffffffff610260366101f0565b6102686117b4565b167fffffffffffffffffffffffff00000000000000000000000000000000000000006002541617600255600080f35b346101815760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261018157602073ffffffffffffffffffffffffffffffffffffffff60035416604051908152f35b60007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101815763389a75e1600c523360005260006020600c2055337ffa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92600080a2005b60007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610181576103816117b4565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffff74873927547f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a360007fffffffffffffffffffffffffffffffffffffffffffffffffffffffff7487392755005b346101815760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101815760207fffffffffffffffffffffffffffffffffffffffffffffffffffffffff748739275473ffffffffffffffffffffffffffffffffffffffff60405191168152f35b60005b8381106104735750506000910152565b8181015183820152602001610463565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f6020936104bf81518092818752878088019101610460565b0116010190565b90916104dd6104eb93604084526040840190610483565b916020818403910152610483565b90565b346101815760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101815761055861053f60025473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff1690565b6040517f974ddd9500000000000000000000000000000000000000000000000000000000815260036004820152602081602481855afa90811561064f57600091610654575b50602060405180937f974ddd9500000000000000000000000000000000000000000000000000000000825281806105dc60048201906000602083019252565b03915afa91821561064f57600092610616575b506105fc61060291610ece565b91610ece565b90610612604051928392836104c6565b0390f35b6106029192506106406105fc9160203d602011610648575b6106388183610d04565b810190610d45565b9291506105ef565b503d61062e565b610d83565b61066d915060203d602011610648576106388183610d04565b3861059d565b346101815760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101815760043567ffffffffffffffff811161018157806004019060407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc82360301126101815761070761053f60025473ffffffffffffffffffffffffffffffffffffffff1690565b602060405180927f974ddd95000000000000000000000000000000000000000000000000000000008252818061074560048201906003602083019252565b03915afa801561064f576107619160009161096c575b50610ece565b90602060006107708580610d8f565b9061078060405180938193610de0565b039060025afa1561064f576107b7916107b3916107ad6107a66024600051930187610d8f565b3691610f4b565b90611ca4565b1590565b6109425780602060006107cc6107d594611907565b94909380610d8f565b906107e560405180938193610de0565b039060025afa1561064f57610612916108046109319260005190611ac3565b91816020849301519061081682610dee565b61081f82610dee565b61091e610860604061085460c08501517fffffffffffff00000000000000000000000000000000000000000000000000001690565b93015163ffffffff1690565b916108f260405193849260208401968791927fffffffffffff0000000000000000000000000000000000000000000000000000600b947fff000000000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000009460f81b16855216600184015260e01b1660078201520190565b037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08101835282610d04565b5190206000526004602052604060002090565b556040519081529081906020820190565b7f8de7233f0000000000000000000000000000000000000000000000000000000060005260046000fd5b610985915060203d602011610648576106388183610d04565b3861075b565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc60209101126101815760043590565b9060206104eb928181520190610483565b34610181576106126109e56109e03661098b565b610ece565b604051918291602083526020830190610483565b34610181576020610a093661098b565b604051908152f35b3461018157610a1f3661098b565b60005260046020526020604060002054604051908152f35b906104eb91602081526020610a5783516040838501526060840190610483565b9201519060407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe082850301910152610483565b346101815760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101815760243560043567ffffffffffffffff821161018157366023830112156101815781600401359167ffffffffffffffff83116101815736602484830101116101815761061292610b0f926024604435930190611617565b60405191829182610a37565b346101815760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261018157602073ffffffffffffffffffffffffffffffffffffffff60025416604051908152f35b346101815760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261018157602073ffffffffffffffffffffffffffffffffffffffff60015416604051908152f35b610bc8366101f0565b610bd06117b4565b63389a75e1600c52806000526020600c209081544211610bf8576000610bf692556117ec565b005b636f5e88186000526004601cfd5b610c0f366101f0565b610c176117b4565b8060601b15610c2957610bf6906117ec565b637448fbae6000526004601cfd5b3461018157610c45366101f0565b63389a75e1600c52600052602080600c2054604051908152f35b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b610100810190811067ffffffffffffffff821117610cab57604052565b610c5f565b6060810190811067ffffffffffffffff821117610cab57604052565b60a0810190811067ffffffffffffffff821117610cab57604052565b6040810190811067ffffffffffffffff821117610cab57604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff821117610cab57604052565b90816020910312610181575190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b6040513d6000823e3d90fd5b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe181360301821215610181570180359067ffffffffffffffff82116101815760200191813603831361018157565b908092918237016000815290565b60021115610df857565b610d54565b60405190610e0c60a083610d04565b565b60405190610e0c60c083610d04565b60405190610e0c604083610d04565b67ffffffffffffffff8111610cab57601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b81601f820112156101815760208151910190610e8181610e2c565b92610e8f6040519485610d04565b81845281830111610181576104eb916020840190610460565b9060208282031261018157815167ffffffffffffffff8111610181576104eb9201610e66565b600073ffffffffffffffffffffffffffffffffffffffff815416916024604051809481937f03f2c7da00000000000000000000000000000000000000000000000000000000835260048301525afa90811561064f57600091610f2e575090565b6104eb91503d806000833e610f438183610d04565b810190610ea8565b929192610f5782610e2c565b91610f656040519384610d04565b829481845281830111610181578281602093846000960137010152565b519060ff8216820361018157565b5190600282101561018157565b519063ffffffff8216820361018157565b519067ffffffffffffffff8216820361018157565b51907fffffffffffff00000000000000000000000000000000000000000000000000008216820361018157565b51907fffff0000000000000000000000000000000000000000000000000000000000008216820361018157565b9190826101009103126101815760405161103681610c8e565b60e06110b581839561104781610f82565b855261105560208201610f90565b602086015261106660408201610f9d565b604086015261107760608201610fae565b606086015261108860808201610fae565b608086015261109960a08201610f9d565b60a08601526110aa60c08201610fc3565b60c086015201610ff0565b910152565b51907fffffffffffffffff0000000000000000000000000000000000000000000000008216820361018157565b919091606081840312610181576040519061110182610cb0565b819381519067ffffffffffffffff82116101815782611129604094926110b594869401610e66565b8552611137602082016110ba565b6020860152016110ba565b67ffffffffffffffff8111610cab5760051b60200190565b5190600882101561018157565b81601f820112156101815780519061117e82611142565b9261118c6040519485610d04565b8284526020606081860194028301019181831161018157602001925b8284106111b6575050505090565b6060848303126101815760206060916040516111d181610cb0565b6111da87610f82565b81526111e7838801610fae565b838201526111f76040880161115a565b60408201528152019301926111a8565b9080601f830112156101815781519161121f83611142565b9261122d6040519485610d04565b80845260208085019160051b830101918383116101815760208101915b83831061125957505050505090565b825167ffffffffffffffff81116101815782019060a07fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08388030112610181576112a1610dfd565b90602083015167ffffffffffffffff8111610181578760206112c592860101610e66565b82526112d3604084016110ba565b60208301526112e4606084016110ba565b6040830152608083015167ffffffffffffffff81116101815787602061130c92860101610e66565b606083015260a08301519167ffffffffffffffff83116101815761133888602080969581960101611167565b608082015281520192019161124a565b9080601f8301121561018157815161135f81611142565b9261136d6040519485610d04565b81845260208085019260051b82010192831161018157602001905b8282106113955750505090565b602080916113a284610f82565b815201910190611388565b9080601f83011215610181578151916113c583611142565b926113d36040519485610d04565b80845260208085019160051b830101918383116101815760208101915b8383106113ff57505050505090565b825167ffffffffffffffff81116101815782019060a07fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08388030112610181576040519061144c82610ccc565b602083015161ffff81168103610181578252604083015167ffffffffffffffff81116101815787602061148192860101611348565b602083015260608301519167ffffffffffffffff8311610181576114cf60a0856114b38b602080999881990101611348565b60408501526114c460808201610fae565b60608501520161115a565b60808201528152019201916113f0565b916101a083830312610181576114f5828461101d565b9261010081015167ffffffffffffffff811161018157836115179183016110e7565b9261012082015167ffffffffffffffff81116101815781611539918401611207565b9261014083015167ffffffffffffffff8111610181578261155b9185016113ad565b9261016081015167ffffffffffffffff8111610181578361157d918301610e66565b9261018082015167ffffffffffffffff8111610181576104eb9201610e66565b9061016082820312610181576115b3818361101d565b9261010083015167ffffffffffffffff811161018157826115d59185016113ad565b9261012081015167ffffffffffffffff811161018157836115f7918301610e66565b9261014082015167ffffffffffffffff8111610181576104eb9201610e66565b909193926040519161162883610ce8565b6060835261164160208401946060865284973691610f4b565b600081517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6001821160208086015160f01c17613078141660011b915b7403010a071000000b0104040208000c05090d060e0f6d03e4088843e41bac00000000000060ff60018087019689010151161c601f161a908460fc1c82151715029360041b010190828110156116f75790917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9061167e565b509150156117a6576117449161173e611716859365ffffffffffff1690565b60d01b7fffffffffffff00000000000000000000000000000000000000000000000000001690565b90611bfa565b80611750575b50505050565b61175b600391610ece565b91101561178557806020806117759351830101910161159d565b9094525050525b3880808061174a565b80602080611798935183010191016114df565b94509592505050525261177c565b63101827966000526004601cfd5b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffff748739275433036117de57565b6382b429006000526004601cfd5b73ffffffffffffffffffffffffffffffffffffffff16807fffffffffffffffffffffffffffffffffffffffffffffffffffffffff74873927547f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a37fffffffffffffffffffffffffffffffffffffffffffffffffffffffff7487392755565b6040519061187b82610ce8565b6000825260405160c081018367ffffffffffffffff821183831017610cab5760209160405260008352600082840152600060408401526000606084015260606080840152600060a08401520152565b604051906118d782610c8e565b600060e0838281528260208201528260408201528260608201528260808201528260a08201528260c08201520152565b6119559061191361186e565b5061191c6118ca565b5061194f61193d6119476119308480610d8f565b9290946020810190610d8f565b9490923691610f4b565b923691610f4b565b9061222f565b809160208201519061196682610dee565b61196f82610dee565b6119c261199f60c08501517fffffffffffff00000000000000000000000000000000000000000000000000001690565b92604085019360ff63ffffffff6119ba875163ffffffff1690565b169216611bfa565b926119e86119db606083015167ffffffffffffffff1690565b67ffffffffffffffff1690565b42108015611aa3575b611a79576080015160039263ffffffff92611a539267ffffffffffffffff1695611a31611a1c610e0e565b3381529767ffffffffffffffff166020890152565b6001604088015260608701526080860152600060a08601525163ffffffff1690565b161015611a71576000905b611a66610e1d565b918252602082015291565b600090611a5e565b7fbae576490000000000000000000000000000000000000000000000000000000060005260046000fd5b50611abc6119db608083015167ffffffffffffffff1690565b42116119f1565b60200160608151015190600091611ad981610ece565b51611b79575b506080915051015160009073ffffffffffffffffffffffffffffffffffffffff82541690813b15611b7557611b54839283926040519485809481937f3a91c2260000000000000000000000000000000000000000000000000000000083528a6004840152604060248401526044830190610483565b03925af1801561064f57611b6757505090565b81611b7191610d04565b5090565b8280fd5b73ffffffffffffffffffffffffffffffffffffffff83541690813b15611bf65783916024839260405196879384927f86911cde00000000000000000000000000000000000000000000000000000000845260048401525af192831561064f57608093611be6575b50611adf565b81611bf091610d04565b38611be0565b8380fd5b60405160f89190911b7fff0000000000000000000000000000000000000000000000000000000000000016602082019081527fffffffffffff0000000000000000000000000000000000000000000000000000909216602182015260e09290921b7fffffffff0000000000000000000000000000000000000000000000000000000016602783015290611c9081602b81016108f2565b519020600052600460205260406000205490565b91906040815103611d31576000611d0b9273ffffffffffffffffffffffffffffffffffffffff80600154161660405180809681947f6d3537a00000000000000000000000000000000000000000000000000000000083526020600484018181520190610483565b03915afa91821561064f57600092611d39575b506040825103611d31576104eb926124d7565b505050600090565b611d4f9192503d806000833e610f438183610d04565b9038611d1e565b91909160408184031261018157805192602082015167ffffffffffffffff8111610181576104eb92016113ad565b9061010082820312610181576104eb9161101d565b60405190611da682610cb0565b6000604083606081528260208201520152565b91909160408184031261018157805167ffffffffffffffff81116101815783611de39183016110e7565b92602082015167ffffffffffffffff8111610181576104eb9201611207565b9060ff82511681526020820151916002831015610df85760e08091610e0c946020850152611e3d6040820151604086019063ffffffff169052565b60608181015167ffffffffffffffff169085015260808181015167ffffffffffffffff169085015260a08181015163ffffffff169085015260c0818101517fffffffffffff0000000000000000000000000000000000000000000000000000169085015201517fffff00000000000000000000000000000000000000000000000000000000000016910152565b906008821015610df85752565b9080602083519182815201906020808260051b8501019401916000905b828210611f0357505050505090565b9091929395947fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe087820301825284516080611fb4611f4a835160a0865260a0860190610483565b7fffffffffffffffff00000000000000000000000000000000000000000000000060208501511660208601527fffffffffffffffff000000000000000000000000000000000000000000000000604085015116604086015260608401518582036060870152610483565b910151916080818303910152602080835192838152019201906000905b808210611ff35750505060208060019296019201920190929195939495611ef4565b909192602060606001926120296040885160ff815116845267ffffffffffffffff86820151168685015201516040830190611eca565b019401920190611fd1565b906020808351928381520192019060005b8181106120525750505090565b825160ff16845260209384019390920191600101612045565b9080602083519182815201916020808360051b8301019401926000915b83831061209757505050505090565b9091929394602080827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0856001950301865261212589519161ffff83511681526080806121066120f48787015160a08987015260a0860190612034565b60408701518582036040870152612034565b9467ffffffffffffffff60608201511660608501520151910190611eca565b97019301930191939290612088565b9491936121ba6104eb97956121d895612150896121c996611e02565b6101a06101008a01527fffffffffffffffff00000000000000000000000000000000000000000000000060406121958b60606101a087519201526102008d0190610483565b93826020820151166101c08d01520151166101e08a01528882036101208a0152611ed7565b9086820361014088015261206b565b90848203610160860152610483565b91610180818403910152610483565b926122116104eb95936121fd8661222095611e02565b61016061010087015261016086019061206b565b90848203610120860152610483565b91610140818403910152610483565b906122386118ca565b5061225b61053f60035473ffffffffffffffffffffffffffffffffffffffff1690565b91604051917f0985653c0000000000000000000000000000000000000000000000000000000083526000838061229485600483016109bb565b0381875afa92831561064f57600093612439575b50604051907f0f1dbf4500000000000000000000000000000000000000000000000000000000825261010082806122e286600483016109bb565b0381885afa91821561064f57600092612408575b508194600363ffffffff612311604086015163ffffffff1690565b16101561233357506108f29061233093604051958694602086016121e7565b91565b9261233c611d99565b936060906001602086015161235081610dee565b61235981610dee565b1461237a575b509061233094956108f2939260405197889660208801612134565b945050600060405180957f2793d5d200000000000000000000000000000000000000000000000000000000825281806123b686600483016109bb565b03915afa801561064f576000946000916123d4575b506108f261235f565b61233095506108f2939291506123fc903d806000833e6123f48183610d04565b810190611db9565b909592935090916123cb565b61242b9192506101003d8111612432575b6124238183610d04565b810190611d84565b90386122f6565b503d612419565b6124579193503d806000833e61244f8183610d04565b810190611d56565b905091386122a8565b602081519101519060208110612474575090565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9060200360031b1b1690565b156124a857565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052600160045260246000fd5b600080916125a0936001956124ff6124f46124f96124f4856125de565b612460565b936126ed565b906125186124f46125126124f4846125de565b926126ed565b91604051936020850195865260408501526060840152608083015260a082015260a0815261254760c082610d04565b519073c2b78104907f722dabac4c69f826a522b2754de45afa3d156125a4576125913d9161257483610e2c565b926125826040519485610d04565b83523d6000602085013e6124a1565b60208082518301019101610d45565b1490565b6125916060916124a1565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b8051602011610181576125f16020610e2c565b906125ff6040519283610d04565b6020825261260d6020610e2c565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0602084019101368237602080920190915b602081101561269e578061267f57507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff905b518251821691191617905290565b61269361268e612698926127b1565b6127ec565b6127bf565b90612671565b909182518152602081018091116126e85791602081018091116126e857907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081019081111561263f575b6125af565b8051604011610181576127006020610e2c565b9061270e6040519283610d04565b6020825261271c6020610e2c565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe06020840191013682379060400160205b6020811015612763578061267f57509192915050565b909182518152602081018091116126e85791602081018091116126e857907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081019081111561274d576125af565b60200390602082116126e857565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82019182116126e857565b601f81116126e8576101000a9056fea2646970667358221220f5e2b0251ca2f51ed9232af62a9c106c033a093291d80fb98653aa9c5b06e18864736f6c634300081b0033000000000000000000000000528538ab97aa4f7d3397b3ca0a4a6f5d9cf52f9700000000000000000000000066fdb4e72d2f4a7e2081bf83f1ffacc9bbcb384b000000000000000000000000c728dd0fcd76cd9166f66e1cd8002de86d6525b8000000000000000000000000de20629a87c371668bb371ef1d77d9d167e52021", + "nonce": "0xe", + "chainId": "0x4268" + }, + "additionalContracts": [], + "isFixedGasLimit": false + }, + { + "hash": "0xa6c4df4d9dc825879c0bd25186b86188f2e81dc08fc58f9097483cf39d665014", + "transactionType": "CALL", + "contractName": "AutomataDaoStorage", + "contractAddress": "0x528538ab97aa4f7d3397b3ca0a4a6f5d9cf52f97", + "function": "updateDao(address,address,address,address)", + "arguments": [ + "0x66FdB4E72d2F4a7e2081bf83F1FfACC9bbCb384b", + "0x5B2d7781E3c44966769484daBCdc435EFD281c34", + "0xaB5074445E5ae3C650553d5a7560B3A7121635B9", + "0x9f4b0fB3A95072bD133082e9683A3536669EFE07" + ], + "transaction": { + "from": "0xdc3bda6d40f0e33e0dfa4aef9604b66195e6c5dc", + "to": "0x528538ab97aa4f7d3397b3ca0a4a6f5d9cf52f97", + "gas": "0x265f7", + "value": "0x0", + "input": "0x40070f2d00000000000000000000000066fdb4e72d2f4a7e2081bf83f1ffacc9bbcb384b0000000000000000000000005b2d7781e3c44966769484dabcdc435efd281c34000000000000000000000000ab5074445e5ae3c650553d5a7560b3a7121635b90000000000000000000000009f4b0fb3a95072bd133082e9683a3536669efe07", + "nonce": "0xf", + "chainId": "0x4268" + }, + "additionalContracts": [], + "isFixedGasLimit": false + } + ], + "receipts": [ + { + "status": "0x1", + "cumulativeGasUsed": "0x332963", + "logs": [ + { + "address": "0x528538ab97aa4f7d3397b3ca0a4a6f5d9cf52f97", + "topics": [ + "0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x000000000000000000000000dc3bda6d40f0e33e0dfa4aef9604b66195e6c5dc" + ], + "data": "0x", + "blockHash": "0x03e1d14c0173f5b4ffc8ab6c22908be23b9283c5db69794562aa9425e5e661a4", + "blockNumber": "0x24b688", + "transactionHash": "0x104c439ad134026dcfce4a9cbccade63125cde06471b3bb73ec83fc5cfe0cdfe", + "transactionIndex": "0x1e", + "logIndex": "0x2b", + "removed": false + } + ], + "logsBloom": "0x00004000800000000000000000000000000000000000000000800000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000011000000000000000000000000000000000000020000000000000000000800200000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000", + "type": "0x2", + "transactionHash": "0x104c439ad134026dcfce4a9cbccade63125cde06471b3bb73ec83fc5cfe0cdfe", + "transactionIndex": "0x1e", + "blockHash": "0x03e1d14c0173f5b4ffc8ab6c22908be23b9283c5db69794562aa9425e5e661a4", + "blockNumber": "0x24b688", + "gasUsed": "0xb9bef", + "effectiveGasPrice": "0x622cf8f", + "from": "0xdc3bda6d40f0e33e0dfa4aef9604b66195e6c5dc", + "to": null, + "contractAddress": "0x528538ab97aa4f7d3397b3ca0a4a6f5d9cf52f97" + }, + { + "status": "0x1", + "cumulativeGasUsed": "0x51e53b", + "logs": [ + { + "address": "0x66fdb4e72d2f4a7e2081bf83f1ffacc9bbcb384b", + "topics": [ + "0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x000000000000000000000000dc3bda6d40f0e33e0dfa4aef9604b66195e6c5dc" + ], + "data": "0x", + "blockHash": "0x03e1d14c0173f5b4ffc8ab6c22908be23b9283c5db69794562aa9425e5e661a4", + "blockNumber": "0x24b688", + "transactionHash": "0x0e3b682dcd48ffd100c72d1e457aa5095f2888ebd0754d2521cbd903a40dfd55", + "transactionIndex": "0x1f", + "logIndex": "0x2c", + "removed": false + } + ], + "logsBloom": "0x00004000800000000000000000000000000000004000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011000000000000000000000000000000000000020000000000000000000800000000000000000000002000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000800000000000000000000000000000000", + "type": "0x2", + "transactionHash": "0x0e3b682dcd48ffd100c72d1e457aa5095f2888ebd0754d2521cbd903a40dfd55", + "transactionIndex": "0x1f", + "blockHash": "0x03e1d14c0173f5b4ffc8ab6c22908be23b9283c5db69794562aa9425e5e661a4", + "blockNumber": "0x24b688", + "gasUsed": "0x1ebbd8", + "effectiveGasPrice": "0x622cf8f", + "from": "0xdc3bda6d40f0e33e0dfa4aef9604b66195e6c5dc", + "to": null, + "contractAddress": "0x66fdb4e72d2f4a7e2081bf83f1ffacc9bbcb384b" + }, + { + "status": "0x1", + "cumulativeGasUsed": "0x7e960c", + "logs": [ + { + "address": "0x5b2d7781e3c44966769484dabcdc435efd281c34", + "topics": [ + "0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x000000000000000000000000dc3bda6d40f0e33e0dfa4aef9604b66195e6c5dc" + ], + "data": "0x", + "blockHash": "0x03e1d14c0173f5b4ffc8ab6c22908be23b9283c5db69794562aa9425e5e661a4", + "blockNumber": "0x24b688", + "transactionHash": "0x7082f2d1151154d013e920428493b609386f8721cd951231472ae2a72e5c960d", + "transactionIndex": "0x20", + "logIndex": "0x2d", + "removed": false + } + ], + "logsBloom": "0x00084000800000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000011000000000000000000000000000000000000020000000000000000000800000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000008000000", + "type": "0x2", + "transactionHash": "0x7082f2d1151154d013e920428493b609386f8721cd951231472ae2a72e5c960d", + "transactionIndex": "0x20", + "blockHash": "0x03e1d14c0173f5b4ffc8ab6c22908be23b9283c5db69794562aa9425e5e661a4", + "blockNumber": "0x24b688", + "gasUsed": "0x2cb0d1", + "effectiveGasPrice": "0x622cf8f", + "from": "0xdc3bda6d40f0e33e0dfa4aef9604b66195e6c5dc", + "to": null, + "contractAddress": "0x5b2d7781e3c44966769484dabcdc435efd281c34" + }, + { + "status": "0x1", + "cumulativeGasUsed": "0x995289", + "logs": [ + { + "address": "0x9f4b0fb3a95072bd133082e9683a3536669efe07", + "topics": [ + "0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x000000000000000000000000dc3bda6d40f0e33e0dfa4aef9604b66195e6c5dc" + ], + "data": "0x", + "blockHash": "0x03e1d14c0173f5b4ffc8ab6c22908be23b9283c5db69794562aa9425e5e661a4", + "blockNumber": "0x24b688", + "transactionHash": "0xe2ffd95a1c67534b2ab7138bb090e761f7be6370a5f6908c0f1f54e8a58d6bdc", + "transactionIndex": "0x21", + "logIndex": "0x2e", + "removed": false + } + ], + "logsBloom": "0x00004000800000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011000000000000000000000000000000000000020000000000000000000800000000000000000000000000000200400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000020000000000000000000000000000000000004000000000000000000000000000000", + "type": "0x2", + "transactionHash": "0xe2ffd95a1c67534b2ab7138bb090e761f7be6370a5f6908c0f1f54e8a58d6bdc", + "transactionIndex": "0x21", + "blockHash": "0x03e1d14c0173f5b4ffc8ab6c22908be23b9283c5db69794562aa9425e5e661a4", + "blockNumber": "0x24b688", + "gasUsed": "0x1abc7d", + "effectiveGasPrice": "0x622cf8f", + "from": "0xdc3bda6d40f0e33e0dfa4aef9604b66195e6c5dc", + "to": null, + "contractAddress": "0x9f4b0fb3a95072bd133082e9683a3536669efe07" + }, + { + "status": "0x1", + "cumulativeGasUsed": "0x5c5ff3", + "logs": [ + { + "address": "0xab5074445e5ae3c650553d5a7560b3a7121635b9", + "topics": [ + "0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x000000000000000000000000dc3bda6d40f0e33e0dfa4aef9604b66195e6c5dc" + ], + "data": "0x", + "blockHash": "0x5da77311eb8ceb1d847d731a6a77fa69eef11574565c8af9a65e6d98c30ce557", + "blockNumber": "0x24b689", + "transactionHash": "0xa3c4cf799eb00b233a77a50813894f60fd98e6a7ed9713e7d13f406568ba61c1", + "transactionIndex": "0x1b", + "logIndex": "0x55", + "removed": false + } + ], + "logsBloom": "0x00004000800000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000011000000000000000000000000800000000000020000000000000000000800000000000000000000000000000000400000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000", + "type": "0x2", + "transactionHash": "0xa3c4cf799eb00b233a77a50813894f60fd98e6a7ed9713e7d13f406568ba61c1", + "transactionIndex": "0x1b", + "blockHash": "0x5da77311eb8ceb1d847d731a6a77fa69eef11574565c8af9a65e6d98c30ce557", + "blockNumber": "0x24b689", + "gasUsed": "0x246223", + "effectiveGasPrice": "0x622cf90", + "from": "0xdc3bda6d40f0e33e0dfa4aef9604b66195e6c5dc", + "to": null, + "contractAddress": "0xab5074445e5ae3c650553d5a7560b3a7121635b9" + }, + { + "status": "0x1", + "cumulativeGasUsed": "0x5e1c73", + "logs": [], + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "type": "0x2", + "transactionHash": "0xa6c4df4d9dc825879c0bd25186b86188f2e81dc08fc58f9097483cf39d665014", + "transactionIndex": "0x1c", + "blockHash": "0x5da77311eb8ceb1d847d731a6a77fa69eef11574565c8af9a65e6d98c30ce557", + "blockNumber": "0x24b689", + "gasUsed": "0x1bc80", + "effectiveGasPrice": "0x622cf90", + "from": "0xdc3bda6d40f0e33e0dfa4aef9604b66195e6c5dc", + "to": "0x528538ab97aa4f7d3397b3ca0a4a6f5d9cf52f97", + "contractAddress": null + } + ], + "libraries": [], + "pending": [], + "returns": {}, + "timestamp": 1727269401, + "chain": 17000, + "commit": "fbc73d7" +} \ No newline at end of file diff --git a/broadcast/DeployHelpers.s.sol/17000/deployEnclaveIdentityHelper-latest.json b/broadcast/DeployHelpers.s.sol/17000/deployEnclaveIdentityHelper-latest.json new file mode 100644 index 0000000..b8c07ab --- /dev/null +++ b/broadcast/DeployHelpers.s.sol/17000/deployEnclaveIdentityHelper-latest.json @@ -0,0 +1,46 @@ +{ + "transactions": [ + { + "hash": "0xf62602dacfbf574f29cfc6be5bc93215d684d3e0a894ad58c4ba726c6c77b5b2", + "transactionType": "CREATE", + "contractName": "EnclaveIdentityHelper", + "contractAddress": "0xeea41ae0cb09a478b80425ae61c85e445e83c415", + "function": null, + "arguments": null, + "transaction": { + "from": "0xdc3bda6d40f0e33e0dfa4aef9604b66195e6c5dc", + "gas": "0x31cff1", + "value": "0x0", + "input": "0x60808060405234601557612caa908161001b8239f35b600080fdfe6080604052600436101561001257600080fd5b60003560e01c63c0ed97731461002757600080fd5b346110875760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126110875760043567ffffffffffffffff811161108757366023820112156110875780600401359067ffffffffffffffff8211611087573660248383010111611087576101046024926100a261111f565b506100ab61111f565b9260206100de817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f860116016110db565b928084528060009788940183860137830101526100f961108c565b848152604052612912565b9061010e8261298e565b604090845b61011c8561298e565b51811015610eb85761012e81836111d0565b5161014061013b826111e4565b611344565b6101a261014c866110db565b600981527f69737375654461746500000000000000000000000000000000000000000000006020918201528251908301207f6076d2bbe55ad3c4b6ee7953ef9ae2e5b181f22d3eba84d569dbda4b99713e821490565b156101d9575061011c9167ffffffffffffffff6101cb6101c661013b60019561272f565b611658565b16858701525b019050610113565b61023b6101e5866110db565b600a81527f6e657874557064617465000000000000000000000000000000000000000000006020918201528251908301207f763eaaeea7af2e6dd24f817d656a3a2f55700b655f70efd88231fddddb3c82281490565b1561026a575061011c9167ffffffffffffffff61025f6101c661013b60019561272f565b1660608701526101d1565b6102cc610276866110db565b600281527f69640000000000000000000000000000000000000000000000000000000000006020918201528251908301207fa709fd3aa96d9faf770e44a5aef2f4808a6fe3a5ddf546568f36ad3a3873f31d1490565b15610468575061013b6102de9161272f565b6103406102ea856110db565b600281527f51450000000000000000000000000000000000000000000000000000000000006020918201528251908301207f92d0cd3eba06fc871a8ce75aa3a118281eb4fae949bdb178e3efe8d9fd6445251490565b156103545750600161011c918786526101d1565b6103b6610360856110db565b600381527f51564500000000000000000000000000000000000000000000000000000000006020918201528251908301207f2fb474852ebc3fe034f7e1ae5a7b9bef7ecc6ec335a26e41ba919461c688a9771490565b156103ca5750600161011c918186526101d1565b61042c906103d7856110db565b600581527f54445f514500000000000000000000000000000000000000000000000000000060209182015281519101207f764c56994ed21d522232e8d6957d7041137c17a68c129db5c6ac324d92d33c861490565b1561044057600161011c91600286526101d1565b6004867f1423e927000000000000000000000000000000000000000000000000000000008152fd5b6104ca610474866110db565b600781527f76657273696f6e000000000000000000000000000000000000000000000000006020918201528251908301207fba1b4dd49a85c82b73f138b112d5135149203ed36c1ec80c46f8c572daa7c5ec1490565b156104f7575061011c9163ffffffff6104ec6104e760019461272f565b61150c565b1660208701526101d1565b610559610503866110db565b601781527f7463624576616c756174696f6e446174614e756d6265720000000000000000006020918201528251908301207f4e781c4bf4f96634340ceb53dcdb4730bdc24be94fdc96d345fdaa60d3d6d7b91490565b15610581575061011c9163ffffffff6105766104e760019461272f565b1660808701526101d1565b6105e361058d866110db565b600a81527f6d69736373656c656374000000000000000000000000000000000000000000006020918201528251908301207f355e097d35df62dadf6ff6c8a506f838c49019cee633e5834f0c8328a79243be1490565b15610632575061011c917fffffffff0000000000000000000000000000000000000000000000000000000061062461061f61013b60019561272f565b611597565b60e01b1660a08701526101d1565b61069461063e866110db565b600e81527f6d69736373656c6563744d61736b0000000000000000000000000000000000006020918201528251908301207f52b593a1791fdc596f47be468717d77cb65fb4ca41a49cc2251c93a07ee007e31490565b156106de575061011c917fffffffff000000000000000000000000000000000000000000000000000000006106d061061f61013b60019561272f565b60e01b1660c08701526101d1565b6107406106ea866110db565b600a81527f61747472696275746573000000000000000000000000000000000000000000006020918201528251908301207fa400e15477493b58388b48f7b160ed2fb67c4a5ddc41d82fa2a63f904d2e622b1490565b1561078a575061011c917fffffffffffffffffffffffffffffffff0000000000000000000000000000000061077c61061f61013b60019561272f565b60801b1660e08701526101d1565b6107ec610796866110db565b600e81527f617474726962757465734d61736b0000000000000000000000000000000000006020918201528251908301207f17c4cfec1552be72636a92eab6688a6f9dc4a883e1e7d7f4bbacb37d473e14b51490565b15610837575061011c917fffffffffffffffffffffffffffffffff0000000000000000000000000000000061082861061f61013b60019561272f565b60801b166101008701526101d1565b610899610843866110db565b600881527f6d727369676e65720000000000000000000000000000000000000000000000006020918201528251908301207fc6289de500ffb547b5e0b7a91f7777325c606b45d58329945f4f303ea7a9424a1490565b156108bf575061011c916108b461061f61013b60019461272f565b6101208701526101d1565b6109216108cb866110db565b600981527f69737670726f64696400000000000000000000000000000000000000000000006020918201528251908301207f930cd84e6ba04258f5ad32400f8aaf42263bb2b57105dab5bedd61b2bf8ceb751490565b15610948575061011c9161ffff61093c6104e760019461272f565b166101408701526101d1565b6109aa90610955866110db565b600981527f7463624c6576656c73000000000000000000000000000000000000000000000060209182015281519101207fd6d591efa424a0827fb062970a57c851088716066a090ed22b00fe6502c114351490565b6109bb575b50600161011c916101d1565b6109c76109d99161272f565b6109cf61108c565b8881528552612912565b6109eb6109e58261298e565b9161298e565b516109fd6109f8826114f4565b6110db565b918183527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0610a2b836114f4565b01895b818110610e52575050885b828110610a505750505061016085015260016109af565b610a63610a5d82846111d0565b5161298e565b51610a71610a5d83856111d0565b8b5b828110610a8557505050600101610a39565b808d610a9f61013b610a99600195876111d0565b516111e4565b8c610b02610aac826110db565b600381527f74636200000000000000000000000000000000000000000000000000000000006020918201528351908401207f4030b14cdfc8fe1ef57f74f5c81fbdb05eb1ddd8761b287757c0be02f5788ab91490565b15610bc4575050610b16610a5d83866111d0565b90610b888d610b33610b2d61013b610a9987611194565b916110db565b600681527f69737673766e000000000000000000000000000000000000000000000000000060209182015281519101207f22402e2860cb074cd5b23d7be21f9d4928c0842771359c97717295e5cb1af67c1490565b610b96575b50505b01610a73565b610bae6104e7610ba861ffff94611194565b5161272f565b905016610bbb868a6111d0565b5152388e610b8d565b610bd0610c26916110db565b600781527f74636244617465000000000000000000000000000000000000000000000000006020918201528251908301207f5add4344b96eb4a412e6c34fcb56b6f1f9be776dbb6917001ce8245b90d0419b1490565b15610c5c57506101c661013b610c4892610c4085886111d0565b51905061272f565b6020610c54878b6111d0565b510152610b90565b610cbf9150610c6a8d6110db565b600981527f746362537461747573000000000000000000000000000000000000000000000060209182015281519101207f8ccaeed3ed0825b5048ee5eaf2994841e462373ff830e62b0e66e06e56cd20d61490565b15610b90578d8b610d38610ce2610cdc61013b610ba8878a6111d0565b926110db565b600881527f5570546f446174650000000000000000000000000000000000000000000000006020918201528251908301207f8dd11fcce5839087111530125177803569655488ae47b1f980ef0b1a711bf07c1490565b15610d55575082908c610d4b888c6111d0565b5101905052610b90565b8c610db8610d62826110db565b600781527f5265766f6b6564000000000000000000000000000000000000000000000000006020918201528351908401207f78b54e92f33559320da4a0c1454741ea1f5c5fa31eb1dd482882c6df0231e4781490565b15610dcc576002929150610d4b888c6111d0565b90610dd9610e2e926110db565b600981527f4f75744f6644617465000000000000000000000000000000000000000000000060209182015281519101207fe1dbc1592712cb224ab58df345a7bffafe7d5092dc9eff532076ee2c5e6870991490565b610e39575b50610b90565b6003908c610e47888c6111d0565b51019050528d610e33565b87516060810181811067ffffffffffffffff821117610e8b5790602092918a528c81528c838201528c8a82015282828801015201610a2e565b60248d7f4e487b710000000000000000000000000000000000000000000000000000000081526041600452fd5b828487825191602083526101a08301908051600381101561105a579061016091602086959495015263ffffffff6020820151168686015267ffffffffffffffff8682015116606086015267ffffffffffffffff606082015116608086015263ffffffff60808201511660a08601527fffffffff0000000000000000000000000000000000000000000000000000000060a08201511660c08601527fffffffff0000000000000000000000000000000000000000000000000000000060c08201511660e08601527fffffffffffffffffffffffffffffffff0000000000000000000000000000000060e0820151166101008601527fffffffffffffffffffffffffffffffff000000000000000000000000000000006101008201511661012086015261012081015161014086015261ffff610140820151168286015201519161018080850152825180915260206101c0850193019180955b82871061101c5785850386f35b9091929381855161ffff8151168352602081015160208401520151600481101561105a5760608260209285600195015201950196019592919061100f565b6024847f4e487b710000000000000000000000000000000000000000000000000000000081526021600452fd5b600080fd5b604051906020820182811067ffffffffffffffff8211176110ac57604052565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f604051930116820182811067ffffffffffffffff8211176110ac57604052565b60405190610180820182811067ffffffffffffffff8211176110ac5760405260606101608360008152600060208201526000604082015260008382015260006080820152600060a0820152600060c0820152600060e08201526000610100820152600061012082015260006101408201520152565b8051156111a15760200190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b80518210156111a15760209160051b010190565b9060609160808151166111f45750565b6111ff919250612823565b90565b63101827966000526004601cfd5b91600092600481019283111590516000915b6004830361122f57505050565b90919482861a907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd082019083667e0000007e03ff831c161561120257600192826007601060307fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd0961160051b9211020190039060041b010195019190611222565b90607f81111561133a5780601f538060061c601e536107ff8111156113225780600c1c601d5361ffff8111156113085780621100009160121c601c5360005163073f3f3f1663f08080801760e01b83521060021b0190565b50600390600051620f3f3f1662e080801760e81b81520190565b50600290600051611f3f1661c0801760f01b81520190565b9060019181530190565b908151601f81840101906001825160001a600286015161ff001617612222149111161561120257602160206040510193015b8181036113b6575050600082526020604051927fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe084820301845201604052565b6001819492945160001a910190605c81036114d5575b50908382036113e35763101827966000526004601cfd5b6001825160001a92019160016b100000000000800400000000821c166114c857846075821461145f57507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffab72080000000c000000000000000a0000000d000991011a90816114515750611202565b600191815301925b90611376565b61146e91506114899293611210565b818161dbff821161d8008310171561148f575b5050926112b0565b92611459565b620ffc0093506114af9192506002615c75825160f01c1489029101611210565b92906103ff1691600a1b16016201000001903880611481565b9060019181530192611459565b602281036114e657505082386113cc565b916001919281530192611459565b67ffffffffffffffff81116110ac5760051b60200190565b9060009180516000905b60ff6001808401938501015116947f1999999999999999999999999999999999999999999999999999999999999999811190600a029060097fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd0808985010198011191871017171502908181101561158d5790611516565b5090501561120257565b90600082517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6001821160208087015160f01c17613078141660011b915b7403010a071000000b0104040208000c05090d060e0f6d03e4088843e41bac00000000000060ff6001808701968a010151161c601f161a908460fc1c82151715029360041b0101908281101561164e5790917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff906115d5565b5092501561120257565b6014815103611c92576000600460609083516004811115611c8a575b8015611c82575b50808310611bd5575b5061168f9150612b68565b906005600760609083516007811115611bcd575b6005811115611bc5575b50808310611b18575b506116c19150612b68565b906008600a6060908351600a811115611b10575b6008811115611b08575b50808310611a5b575b506116f39150612b68565b90600b600d6060908351600d811115611a53575b600b811115611a4b575b5080831061199e575b506117259150612b68565b92600e601060609084516010811115611996575b600e81111561198e575b508083106118e1575b506117579150612b68565b91601190601390606092815160138111156118d9575b60118111156118d1575b508281106117ff575b5050509162023ab16101907ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff50593936117ba6201518096612b68565b9860038210900393610e10603c8487069202990297610301600c60096064850495010661f4ff0201600b1c019061016d8160021c910201010392040201010201010190565b6040519281900380845292959350017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f830181165b80830151818801520190811561186e57907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090611836565b5050600081850160200152603f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01683016040529162023ab16101907ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff50593611780565b905038611777565b92508261176d565b90508160405191039182825284017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe080601f8501165b80830151818501520190811561194f57907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090611917565b50507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0603f61175793600060208286010152011681016040523861174c565b925038611743565b905080611739565b90508160405191039182825283017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe080601f8501165b808301518185015201908115611a0c57907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0906119d4565b50507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0603f61172593600060208286010152011681016040523861171a565b925038611711565b905080611707565b90508160405191039182825283017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe080601f8501165b808301518185015201908115611ac957907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090611a91565b50507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0603f6116f39360006020828601015201168101604052386116e8565b9250386116df565b9050806116d5565b90508160405191039182825283017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe080601f8501165b808301518185015201908115611b8657907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090611b4e565b50507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0603f6116c19360006020828601015201168101604052386116b6565b9250386116ad565b9050806116a3565b90508160405191039182825283017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe080601f8501165b808301518185015201908115611c4357907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090611c0b565b50507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0603f61168f936000602082860101520116810160405238611684565b92503861167b565b905080611674565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f696e76616c69642069736f20737472696e67206c656e677468000000000000006044820152fd5b5b6001640100002600825160001a1c16156111ff57600101611cf1565b9063ffffffff81113d3d3e60181b907fffffffffffffffffffffffffffffffffffffffffffffffffff00000000ffffff161790565b9063ffffffff81113d3d3e60981b907fffffffffffffffffff00000000ffffffffffffffffffffffffffffffffffffff161790565b9063ffffffff81113d3d3e60381b907fffffffffffffffffffffffffffffffffffffffffff00000000ffffffffffffff161790565b9063ffffffff81113d3d3e60b81b907fffffffffff00000000ffffffffffffffffffffffffffffffffffffffffffffff161790565b907fffffffffffffffffffffffffff0000000000000000ffffffffffffffffffffff7fffffffffffffffffffffffffff00000000ffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0600495949796978560405199039503019363ffffffff85113d3d3e63ffffffff81113d3d3e60781b9360581b169116171717825260208201604052565b907fffffffffffffffffffffffffff0000000000000000ffffffffffffffffffffff7fffffffffffffffffffffffffff00000000ffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0600595949796978560405199039503019363ffffffff85113d3d3e63ffffffff81113d3d3e60781b9360581b169116171717825260208201604052565b907fffffffffffffffffffffffffff0000000000000000ffffffffffffffffffffff7fffffffffffffffffffffffffff00000000ffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0600695949796978560405199039503019363ffffffff85113d3d3e63ffffffff81113d3d3e60781b9360581b169116171717825260208201604052565b907fffffffffffffffffffffffffff0000000000000000ffffffffffffffffffffff7fffffffffffffffffffffffffff00000000ffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0600195949796978560405199039503019363ffffffff85113d3d3e63ffffffff81113d3d3e60781b9360581b169116171717825260208201604052565b907fffffffffffffffffffffffffff0000000000000000ffffffffffffffffffffff7fffffffffffffffffffffffffff00000000ffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0600295949796978560405199039503019363ffffffff85113d3d3e63ffffffff81113d3d3e60781b9360581b169116171717825260208201604052565b939261212061211a60009493600051611d0d565b92611cf0565b92818410156122385750825160001a6022811490605b811460016703ff200000000000607b8414931c16926122195761220b576121f2576121db576004830181811115612193575b5060058301908111611202576466616c7365835160d81c03611202576111ff9261211a928296611e82565b8390845160e01c90637472756582146121c75750636e756c6c036121685761211a9295915060046111ff9401958692611f23565b6111ff955080935061211a94915096611e82565b90916121ea926111ff95612579565b929092611cf0565b5091906121ff9394612316565b9091906111ff90611cf0565b505091906121ff939461223f565b505050938261223061211a93966111ff9592612491565b958692611de1565b9450505090565b939060009081600185015b8681101561231157869082156122db575b6122659289612106565b80828061227a575b5050506001865b0161224a565b948561228e82600194959851604017611d42565b905201935160001a605d81146122b457602c146122ac57808061226d565b600190612274565b506122d29196506122d8959350600190979294975b01958694611d77565b90611fc4565b91565b6122e59150611cf0565b90605d825160001a146122fa5790869061225b565b95506122d894925060016122d291979294976122c9565b611202565b939091600092600182015b85811015612311578415612412575b61233990611cf0565b6123438682612491565b908661234e83611cf0565b603a815160001a14612369575b509050600191505b01612321565b96600161237a93949801908a612106565b9080928197839461238f575b5088915061235b565b6123c7827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08d6123cc95030192038451608017611dac565b611d42565b90525160001a607d81146123f257602c146123ea5780808080612386565b600190612363565b506122d8949550600161240c919792949701958694611d77565b90612065565b61241b90611cf0565b607d815160001a03612330576122d8949550600161240c91979294976122c9565b6001667e0000007e03ff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd08484015160001a011c16156112025760058203612482575050565b600161248f92019061243c565b565b81811015611202576001015b805160001a6022811461252c57605c0361252457600181015160001a60016a05101104000000000020017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffde83011c1661251b576075146125075750805b8110156112025760010190565b80612515600260069361243c565b0161249d565b5060020161249d565b60010161249d565b50906124fa565b90815b600a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd0825160001a01101561256d57600101612536565b80921460011661120257565b919092508092602d825160001a14612724575b600a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd0855160001a0110156112025760306001855160001a950194036126e1575b602e845160001a146126ce575b835160658160001a6020171461268b575b50906003917fffffffffffffffffffffffffff0000000000000000ffffffffffffffffffffff7fffffffffffffffffffffffffff00000000ffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0604051968589039503019363ffffffff85113d3d3e63ffffffff81113d3d3e60781b9360581b16911617171781526020810160405291565b60016003939295620100017ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff26126c694841a011a0101612533565b9390916125eb565b9260016126db9101612533565b926125da565b9190925b600a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd0825160001a01101561271c576001016126e5565b9290916125cd565b60018201935061258c565b805191906060811584151761281e57508260581c63ffffffff16928060101615612757575050565b8060781c63ffffffff16604051948260d81c63ffffffff16017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe080601f8401165b8083015181890152019081156127d057907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090612798565b5050604090808652850160006020820152016040528363ffffffff81113d3d3e60581b907fffffffffffffffffffffffffffffffffff00000000ffffffffffffffffffffff16176010179052565b925050565b805191906060811584151761281e57508260981c63ffffffff1692806008161561284b575050565b8060b81c63ffffffff16604051948260d81c63ffffffff16017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe080601f8401165b8083015181890152019081156128c457907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09061288c565b5050604090808652850160006020820152016040528363ffffffff81113d3d3e60981b907fffffffffffffffffff00000000ffffffffffffffffffffffffffffffffffffff16176008179052565b9060009160208101906020815182010190818303612937575b50831591101761120257565b91819450906129829160008251811a946022845363ffffffff81113d3d3e8060d81b7f2200000000000000000000000000000000000000000000000000000000000000178252612106565b9390939181533861292b565b606090805190600282600716119060208316928060381c63ffffffff169180158215179363ffffffff8211923d3d908460181b983d983d947fffffffffffffffffffffffffffffffffffffffffff00000000ffffffffffffff1695156129fd575b505050505050505050505090565b9a909192939495969798999a612b615750612b48576040519860208a019796889695949392885b612af857505050506040945087840396877fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe001948560051c8a52865263ffffffff8911913e8660381b1760201790521015612a88575b8080808080808080806129ef565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe080602092938501015b83518151855281520191019080821015612aef577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0602091612ab2565b50503880612a7a565b88602099899596979899528051908383883e84827fffffffffffffffffffffffffffffffffffffffffffffffffff00000000ffffff1617905260181c63ffffffff16988994019796959493612a24565b50505050505091505038808080808080808080806129ef565b99806129ef565b906000805b835182101561281e5760006020838601015160f81c603081101580612c69575b612b9e575b50509060010190612b6d565b600a830292808404600a1490151715612c3c57807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd0810111612c3c577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd090830101809211612c0f5750600138612b92565b807f4e487b7100000000000000000000000000000000000000000000000000000000602492526011600452fd5b6024827f4e487b710000000000000000000000000000000000000000000000000000000081526011600452fd5b506039811115612b8d56fea2646970667358221220d10d5e13b01e57defcbaf0b8acc64e901c107f7eca6f9c63d4a37fc28253d94f64736f6c634300081b0033", + "nonce": "0x7", + "chainId": "0x4268" + }, + "additionalContracts": [], + "isFixedGasLimit": false + } + ], + "receipts": [ + { + "status": "0x1", + "cumulativeGasUsed": "0x445a6b", + "logs": [], + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "type": "0x2", + "transactionHash": "0xf62602dacfbf574f29cfc6be5bc93215d684d3e0a894ad58c4ba726c6c77b5b2", + "transactionIndex": "0x11", + "blockHash": "0x4e6e706ff95c7d0590dcb7c60ab262c453b8a749a09b0444586efb1156168407", + "blockNumber": "0x24b38f", + "gasUsed": "0x2653fe", + "effectiveGasPrice": "0xd23cd91", + "from": "0xdc3bda6d40f0e33e0dfa4aef9604b66195e6c5dc", + "to": null, + "contractAddress": "0xeea41ae0cb09a478b80425ae61c85e445e83c415" + } + ], + "libraries": [], + "pending": [], + "returns": {}, + "timestamp": 1727259265, + "chain": 17000, + "commit": "a6abae3" +} \ No newline at end of file diff --git a/broadcast/DeployHelpers.s.sol/17000/deployFmspcTcbHelper-latest.json b/broadcast/DeployHelpers.s.sol/17000/deployFmspcTcbHelper-latest.json new file mode 100644 index 0000000..8c32cbd --- /dev/null +++ b/broadcast/DeployHelpers.s.sol/17000/deployFmspcTcbHelper-latest.json @@ -0,0 +1,46 @@ +{ + "transactions": [ + { + "hash": "0x5e81ff52f1e9c5cd9562940e5c7348b932415d7251c76837873fac5a31ac97a0", + "transactionType": "CREATE", + "contractName": "FmspcTcbHelper", + "contractAddress": "0xc728dd0fcd76cd9166f66e1cd8002de86d6525b8", + "function": null, + "arguments": null, + "transaction": { + "from": "0xdc3bda6d40f0e33e0dfa4aef9604b66195e6c5dc", + "gas": "0x47c7ca", + "value": "0x0", + "input": "0x608080604052346015576140c9908161001b8239f35b600080fdfe610360604052600436101561001357600080fd5b60003560e01c80630985653c1461131a5780630f1dbf4514610b6a57632793d5d21461003e57600080fd5b34610b655761004c36611b04565b61006a61006561005a611e30565b936060933691611c8f565b611e50565b906100748261378c565b604060e05260009081908190819081805b61008e8961378c565b51821015610b5757506100a18183611d30565b516100b36100ae82611efe565b612050565b6100d36100be611d44565b82906020815191012090602081519101201490565b610b0f575b6100e36100be611dba565b610abe575b61015160e051516100fb60e05182611c4e565b600981527f7464784d6f64756c6500000000000000000000000000000000000000000000006020918201528251908301207f6f1c9533f85324bc119f3689d641a35996bdbfce949a5b36ae5ef040c09e2c091490565b61099c575b6101c09060e051519061016b60e05183611c4e565b601382527f7464784d6f64756c654964656e7469746965730000000000000000000000000060209283015280519101207f6d3b4de4f5b6531723dfb0e5d8257d4dbfe34b65370323640e41f5cf63ee9a4f1490565b6103f2575b508590816103ea575b816103e2575b816103da575b816101e85760010190610085565b5095965050505050505b156103b05760e051519060e05182527fffffffffffffffff0000000000000000000000000000000000000000000000006102398451606060e05186015260a0850190611bb7565b9381602082015116606085015260e05101511660808301528183036020830152805180845260208401906020808260051b8701019301916000905b8282106102815785850386f35b909192937fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0878203018252845160806103326102c6835160a0865260a0860190611bb7565b7fffffffffffffffff00000000000000000000000000000000000000000000000060208501511660208601527fffffffffffffffff00000000000000000000000000000000000000000000000060e0518501511660e05186015260608401518582036060870152611bb7565b910151916080818303910152602080835192838152019201906000905b80821061036d57505050602080600192960192019201909291610274565b909192602060606001926103a5875160ff815116835267ffffffffffffffff85820151168584015260e051015160e051830190611baa565b01940192019061034f565b7f5aeb6bce0000000000000000000000000000000000000000000000000000000060005260046000fd5b8591506101da565b8491506101d4565b8391506101ce565b90945061040091965061378c565b80519060006104226104118461228b565b60e051516101a0526101a051611c4e565b826101a051527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe06104528461228b565b0190805b82811061096057505060006101205250905b8061012051106104825750506101a05194600193856101c5565b90966104a161049b6101209a989a97959697518a611d30565b5161378c565b9860006080525b6104b861049b610120518b611d30565b516080511015610947576105506104de6100ae6104d88d60805190611d30565b51611efe565b8b6104ff6104ea611dba565b83906020815191012090602081519101201490565b61091c575b61050f6104ea612b2f565b6108e6575b61051f6104ea612ab9565b610890575b61052f6104ea612af4565b610834575b5061053d611d7f565b6020815191012090602081519101201490565b610563575b6001608051016080526104a8565b61057261049b6080518c611d30565b60c05260c0515160a05261058760a05161228b565b61059560e051519182611c4e565b60a05181527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe06105c660a05161228b565b0160005b81811061080657505060806105e5610120516101a051611d30565b5101526000610140525b60a051610140511015610555579061061961049b6101409b9a9b999897969594995160c051611d30565b6101005260005b61010051518110156107ea5761063f6100ae6104d88361010051611d30565b61064a6100be6122a3565b610715575b9061066e6001926106616100be6122de565b6106cd575b61053d612319565b610679575b01610620565b6106c861069d6106986100ae6106928561010051611d30565b516135a9565b613966565b60e0516106c16101405160806106b9610120516101a051611d30565b510151611d30565b5101612354565b610673565b67ffffffffffffffff6106f16106ec6100ae6106928761010051611d30565b612421565b16602061070d6101405160806106b9610120516101a051611d30565b510152610666565b5061072661049b8261010051611d30565b906107366100ae6104d884611cf4565b9160e051506107a460e0515161074e60e05182611c4e565b600681527f69737673766e00000000000000000000000000000000000000000000000000006020918201528451908501207f22402e2860cb074cd5b23d7be21f9d4928c0842771359c97717295e5cb1af67c1490565b156103b05760019260ff6107c56107c061069261066e95611cf4565b612200565b166107df6101405160806106b9610120516101a051611d30565b51529192505061064f565b50909897989691929394959660016101405101610140526105ef565b60209060e0515161081681611c16565b60008152600083820152600060e051820152828286010152016105ca565b61087061086b6100ae6106927fffffffffffffffff0000000000000000000000000000000000000000000000009460805190611d30565b612360565b60c01b1660e051610887610120516101a051611d30565b5101528b610534565b6108c761086b6100ae6106927fffffffffffffffff0000000000000000000000000000000000000000000000009460805190611d30565b60c01b1660206108dd610120516101a051611d30565b5101528b610524565b6108fc6100ae6106926109019360805190611d30565b613e75565b6060610913610120516101a051611d30565b5101528b610514565b6100ae61069261092f9260805190611d30565b61093f610120516101a051611d30565b51528b610504565b9698509694939294600161012051016101205290610468565b60209060e0515161097081611c32565b6060815283838201528360e0518201526060808201526060608082015282826101a05101015201610456565b91959296939897945098506109b08961378c565b956109b9611e30565b9760005b8851811015610aa6578089610a166109ed6100ae610692856109e76100ae6104d860019a89611d30565b95611d30565b916109f96100be612ab9565b610a6d575b610a096100be612af4565b610a33575b61053d612b2f565b610a22575b50016109bd565b610a2b90613e75565b8b5238610a1b565b8d7fffffffffffffffff000000000000000000000000000000000000000000000000610a5e85612360565b60c01b169060e0510152610a0e565b8d60207fffffffffffffffff000000000000000000000000000000000000000000000000610a9a86612360565b60c01b169101526109fe565b50989396509194909396986101c06001959050610156565b93506000610ad9610ad16100ae846135a9565b61053d611df5565b15610ae757506001936100e8565b807f31ab78b50000000000000000000000000000000000000000000000000000000060049252fd5b965060006003610b216107c0846135a9565b10610b2f57506001966100d8565b807f8dcbb5950000000000000000000000000000000000000000000000000000000060049252fd5b9697505050505050506101f2565b600080fd5b34610b6557610b7836611b04565b6040519161010083019083821067ffffffffffffffff8311176112eb57610bdc92610065926040526000855260006020860152600060408601526000606086015260006080860152600060a0860152600060c0860152600060e08601523691611c8f565b80610be68161378c565b6000906000906000916000806000926000946000976000908c600091610c0e60409d5b61378c565b518310156112d857908c8392610c278796958e98611d30565b5191610c44610c386100ae85611efe565b600061034052936135a9565b926103405150610caf8251610c598482611c4e565b600781527f74636254797065000000000000000000000000000000000000000000000000006020918201528251908301207ff1ef84607333b53a2004a6165e8ab88f55e83518116e21fd0b464b25722b2c3f1490565b15610e6957505050610cc260ff91612200565b1690526001938e905b610ce3575b506001610c0e91019192938e918e61378c565b91508380610e62575b80610e5b575b80610e54575b80610e4d575b80610e46575b600363ffffffff6040839501516103405150161015610e2f575b5081610d2a578d610cd0565b50999a505050505050505050505b156103b05780519060ff835116825260208301516002811015610e0057610100937fffff0000000000000000000000000000000000000000000000000000000000009260e092602086015263ffffffff6040830151169085015267ffffffffffffffff606082015116606085015267ffffffffffffffff608082015116608085015263ffffffff60a08201511660a08501527fffffffffffff000000000000000000000000000000000000000000000000000060c08201511660c085015201511660e0820152f35b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b90915080610e3f575b908e610d1e565b5089610e38565b5088610d04565b5087610cfe565b5086610cf8565b5085610cf2565b5084610cec565b90919750610e7b6100be949394611dba565b15610f5d575050508a9b50610e8f90612050565b8c51610ef590610e9f8f82611c4e565b600381527f53475800000000000000000000000000000000000000000000000000000000006020918201528251908301207f8ec2409669ebaea0d83544d244ace12e63a771ee426935bcc157d252eb5f30461490565b15610f0e57506103405160208f01525b8d60019b610ccb565b610f1c919a5061053d611df5565b15610f2f57600160208e01528998610f05565b7f5aeb6bce000000000000000000000000000000000000000000000000000000006103405152600461034051fd5b81519798969795969495929492610fce90610f788482611c4e565b600581527f666d7370630000000000000000000000000000000000000000000000000000006020918201528251908301207f32d406b8d9e731fc9123600c61c66a13cbea488c34cc3d265954e87695b412101490565b1561101e575050509060c07fffffffffffff00000000000000000000000000000000000000000000000000008061100c61086b8f9a99989796612050565b60d01b16169101526001948e90610ccb565b6110316100be9e9399989796959e611d44565b156110565750505061104763ffffffff91612200565b1660408b01526001998a610ccb565b8151929d8e9a909493909291906110c7906110718482611c4e565b600981527f69737375654461746500000000000000000000000000000000000000000000006020918201528251908301207f6076d2bbe55ad3c4b6ee7953ef9ae2e5b181f22d3eba84d569dbda4b99713e821490565b156110f45750505067ffffffffffffffff6110e66106ec606093612050565b169101526001958e90610ccb565b81519a9b929a61115e906111088482611c4e565b600a81527f6e657874557064617465000000000000000000000000000000000000000000006020918201528251908301207f763eaaeea7af2e6dd24f817d656a3a2f55700b655f70efd88231fddddb3c82281490565b1561118b5750505067ffffffffffffffff61117d6106ec608093612050565b169101526001968e90610ccb565b81519b9c929b6111f59061119f8482611c4e565b600581527f70636549640000000000000000000000000000000000000000000000000000006020918201528251908301207f1f5653bc1bc6e041fc665b046bb7155c6612899340379c5caa4d030682426aea1490565b1561123f575050507fffff0000000000000000000000000000000000000000000000000000000000008061122d61086b60e094612050565b60f01b16169101526001978e90610ccb565b8151929c93926112ab929091906112569083611c4e565b601782527f7463624576616c756174696f6e446174614e756d62657200000000000000000060209283015280519101207f4e781c4bf4f96634340ceb53dcdb4730bdc24be94fdc96d345fdaa60d3d6d7b91490565b6112b6575b50610ccb565b60a0919b506112c963ffffffff91612200565b169101526001988e90386112b0565b5050509899505050505050505050610d38565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b34610b655761006561133a61132e36611b04565b92906000933691611c8f565b6113438161378c565b60008060609260005b6113558661378c565b51811015611af9576113678183611d30565b516113746100ae82611efe565b61137f6100be611d44565b611ae0575b6113909061053d611d7f565b611ac8575b508380611ac1575b6113a95760010161134c565b5050909192505b81611ab9575b50156103b05780516000916113ca8261228b565b926113d86040519485611c4e565b8284527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe06114058461228b565b019060005b828110611a82575050506000915b8083106114ff5750505090604051918291604083019083526040602084015281518091526060830190602060608260051b8601019301916000905b82821061146257505050500390f35b91936020817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa060019496988394030185526114ef88519161ffff83511681526080806114d06114be8787015160a08987015260a0860190611b73565b60408701518582036040870152611b73565b9467ffffffffffffffff60608201511660608501520151910190611baa565b9601920192018594939192611453565b61150f61049b8484979597611d30565b936000915b61152161049b8886611d30565b51831015611a72576115396100ae6104d88589611d30565b6115446100be6122a3565b156119f05750611569610c0961006561155d868a611d30565b5160006102a0526135a9565b6102005260028503611673576102a05193611582613c91565b956102a051956102a051905b6102005151821015611606576115ad6100ae6104d88461020051611d30565b906115cd6115c46107c06106928661020051611d30565b9261053d613cd2565b156115e3575061ffff60019116915b019061158e565b918861160060ff6115f76001959c93613d0d565b9b16918c611d30565b526115dc565b93959297919660109150036116455760019260206116248b87611d30565b51019061ffff6116348c88611d30565b5191169052525b0191939293611514565b7f5aeb6bce000000000000000000000000000000000000000000000000000000006102a0515260046102a051fd5b600385939692979514600014611645576102a0516101c052611693613c91565b6102805261169f613c91565b6102e0526102a051610320525b61020051516103205110156119a5576116d16100ae6104d86103205161020051611d30565b610240526116f86116e0613cd2565b61024051906020815191012090602081519101201490565b1561172c5761ffff6117166107c06106926103205161020051611d30565b166101c0525b60016103205101610320526116ac565b611751611743610320979593975161020051611d30565b516102a0516103005261378c565b610300526102a0516102c0819052610260526040610220525b61177d61049b6103205161020051611d30565b51610260511015611967576117a961179b6102605161030051611d30565b516102a0516101e05261378c565b6101e0526102a0515b6117c561049b6102605161030051611d30565b51811015611956578061184d6117e66100ae6104d86001956101e051611d30565b61022051805191906117f89083611c4e565b600382527f73766e000000000000000000000000000000000000000000000000000000000060209283015280519101207f4294eaea1f2439036f92859f5d2aee946a31a48b4d3ebceca5872241d2d718091490565b611858575b016117b2565b6102205180516118c59161186c9082611c4e565b601081527f746478746362636f6d706f6e656e7473000000000000000000000000000000006020918201526102405180519101207fd039dfd6fdf9579033b5870aaca39cde10fbba50b5b2d55d2fa2454f62ea34541490565b156119135760ff6118df6107c0610692846101e051611d30565b6102a05150166102c051610180526118f96102c051613d0d565b6102c05261190d610180516102e051611d30565b52611852565b60ff6119286107c0610692846101e051611d30565b6102a05150166102c051610160526119426102c051613d0d565b6102c05261190d6101605161028051611d30565b50600161026051016102605261176a565b9490929460106102c051031561171c577f5aeb6bce000000000000000000000000000000000000000000000000000000006102a0515260046102a051fd5b91929395909460019060206119ba8985611d30565b510160406119c88a86611d30565b51016119d48a86611d30565b5161ffff6101c0511690526102e051905261028051905261163b565b9260019193611a036100be9796976122de565b15611a3a575067ffffffffffffffff611a256106ec6100ae610692858c611d30565b166060611a328a86611d30565b51015261163b565b611a469061053d612319565b1561163b57611a6d611a616106986100ae610692858c611d30565b60806106c18b87611d30565b61163b565b9395909450600191500191611418565b602090604051611a9181611c32565b838152606083820152606060408201528360608201528360808201528282890101520161140a565b9050836113b6565b508261139d565b909250611ad691945061378c565b9260019187611395565b94509650611af06107c0886135a9565b96600194611384565b5050909192506113b0565b9060207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc830112610b655760043567ffffffffffffffff8111610b655782602382011215610b655780600401359267ffffffffffffffff8411610b655760248483010111610b65576024019190565b906020808351928381520192019060005b818110611b915750505090565b825160ff16845260209384019390920191600101611b84565b906008821015610e005752565b919082519283825260005b848110611c015750507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8460006020809697860101520116010190565b80602080928401015182828601015201611bc2565b6060810190811067ffffffffffffffff8211176112eb57604052565b60a0810190811067ffffffffffffffff8211176112eb57604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff8211176112eb57604052565b92919267ffffffffffffffff82116112eb5760405191611cd7601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200184611c4e565b829481845281830111610b65578281602093846000960137010152565b805115611d015760200190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b8051821015611d015760209160051b010190565b60405190611d53604083611c4e565b600782527f76657273696f6e000000000000000000000000000000000000000000000000006020830152565b60405190611d8e604083611c4e565b600982527f7463624c6576656c7300000000000000000000000000000000000000000000006020830152565b60405190611dc9604083611c4e565b600282527f69640000000000000000000000000000000000000000000000000000000000006020830152565b60405190611e04604083611c4e565b600382527f54445800000000000000000000000000000000000000000000000000000000006020830152565b60405190611e3d82611c16565b6000604083606081528260208201520152565b6040516020810181811067ffffffffffffffff8211176112eb576040526000815260405260009060208101906020815182010190818303611ea7575b508215911017611e995790565b63101827966000526004601cfd5b9181935090611ef29160008251811a946022845363ffffffff81113d3d3e8060d81b7f2200000000000000000000000000000000000000000000000000000000000000178252612f80565b92909291815338611e8c565b906060916080815116611f0e5750565b611f1991925061369d565b90565b91600092600481019283111590516000915b60048303611f3b57505050565b90919482861a907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd082019083667e0000007e03ff831c1615611e9957600192826007601060307fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd0961160051b9211020190039060041b010195019190611f2e565b90607f8111156120465780601f538060061c601e536107ff81111561202e5780600c1c601d5361ffff8111156120145780621100009160121c601c5360005163073f3f3f1663f08080801760e01b83521060021b0190565b50600390600051620f3f3f1662e080801760e81b81520190565b50600290600051611f3f1661c0801760f01b81520190565b9060019181530190565b908151601f81840101906001825160001a600286015161ff0016176122221491111615611e9957602160206040510193015b8181036120c2575050600082526020604051927fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe084820301845201604052565b6001819492945160001a910190605c81036121e1575b50908382036120ef5763101827966000526004601cfd5b6001825160001a92019160016b100000000000800400000000821c166121d457846075821461216b57507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffab72080000000c000000000000000a0000000d000991011a908161215d5750611e99565b600191815301925b90612082565b61217a91506121959293611f1c565b818161dbff821161d8008310171561219b575b505092611fbc565b92612165565b620ffc0093506121bb9192506002615c75825160f01c1489029101611f1c565b92906103ff1691600a1b1601620100000190388061218d565b9060019181530192612165565b602281036121f257505082386120d8565b916001919281530192612165565b9060009180516000905b60ff6001808401938501015116947f1999999999999999999999999999999999999999999999999999999999999999811190600a029060097fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd08089850101980111918710171715029081811015612281579061220a565b50905015611e9957565b67ffffffffffffffff81116112eb5760051b60200190565b604051906122b2604083611c4e565b600382527f74636200000000000000000000000000000000000000000000000000000000006020830152565b604051906122ed604083611c4e565b600782527f74636244617465000000000000000000000000000000000000000000000000006020830152565b60405190612328604083611c4e565b600982527f74636253746174757300000000000000000000000000000000000000000000006020830152565b6008821015610e005752565b90600082517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6001821160208087015160f01c17613078141660011b915b7403010a071000000b0104040208000c05090d060e0f6d03e4088843e41bac00000000000060ff6001808701968a010151161c601f161a908460fc1c82151715029360041b010190828110156124175790917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9061239e565b50925015611e9957565b6014815103612a5b576000600460609083516004811115612a53575b8015612a4b575b5080831061299e575b506124589150613d69565b906005600760609083516007811115612996575b600581111561298e575b508083106128e1575b5061248a9150613d69565b906008600a6060908351600a8111156128d9575b60088111156128d1575b50808310612824575b506124bc9150613d69565b90600b600d6060908351600d81111561281c575b600b811115612814575b50808310612767575b506124ee9150613d69565b92600e60106060908451601081111561275f575b600e811115612757575b508083106126aa575b506125209150613d69565b91601190601390606092815160138111156126a2575b601181111561269a575b508281106125c8575b5050509162023ab16101907ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff50593936125836201518096613d69565b9860038210900393610e10603c8487069202990297610301600c60096064850495010661f4ff0201600b1c019061016d8160021c910201010392040201010201010190565b6040519281900380845292959350017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f830181165b80830151818801520190811561263757907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0906125ff565b5050600081850160200152603f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01683016040529162023ab16101907ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff50593612549565b905038612540565b925082612536565b90508160405191039182825284017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe080601f8501165b80830151818501520190811561271857907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0906126e0565b50507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0603f612520936000602082860101520116810160405238612515565b92503861250c565b905080612502565b90508160405191039182825283017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe080601f8501165b8083015181850152019081156127d557907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09061279d565b50507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0603f6124ee9360006020828601015201168101604052386124e3565b9250386124da565b9050806124d0565b90508160405191039182825283017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe080601f8501165b80830151818501520190811561289257907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09061285a565b50507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0603f6124bc9360006020828601015201168101604052386124b1565b9250386124a8565b90508061249e565b90508160405191039182825283017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe080601f8501165b80830151818501520190811561294f57907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090612917565b50507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0603f61248a93600060208286010152011681016040523861247f565b925038612476565b90508061246c565b90508160405191039182825283017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe080601f8501165b808301518185015201908115612a0c57907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0906129d4565b50507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0603f61245893600060208286010152011681016040523861244d565b925038612444565b90508061243d565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f696e76616c69642069736f20737472696e67206c656e677468000000000000006044820152fd5b60405190612ac8604083611c4e565b600a82527f61747472696275746573000000000000000000000000000000000000000000006020830152565b60405190612b03604083611c4e565b600e82527f617474726962757465734d61736b0000000000000000000000000000000000006020830152565b60405190612b3e604083611c4e565b600882527f6d727369676e65720000000000000000000000000000000000000000000000006020830152565b5b6001640100002600825160001a1c1615611f1957600101612b6b565b9063ffffffff81113d3d3e60181b907fffffffffffffffffffffffffffffffffffffffffffffffffff00000000ffffff161790565b9063ffffffff81113d3d3e60981b907fffffffffffffffffff00000000ffffffffffffffffffffffffffffffffffffff161790565b9063ffffffff81113d3d3e60381b907fffffffffffffffffffffffffffffffffffffffffff00000000ffffffffffffff161790565b9063ffffffff81113d3d3e60b81b907fffffffffff00000000ffffffffffffffffffffffffffffffffffffffffffffff161790565b907fffffffffffffffffffffffffff0000000000000000ffffffffffffffffffffff7fffffffffffffffffffffffffff00000000ffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0600495949796978560405199039503019363ffffffff85113d3d3e63ffffffff81113d3d3e60781b9360581b169116171717825260208201604052565b907fffffffffffffffffffffffffff0000000000000000ffffffffffffffffffffff7fffffffffffffffffffffffffff00000000ffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0600595949796978560405199039503019363ffffffff85113d3d3e63ffffffff81113d3d3e60781b9360581b169116171717825260208201604052565b907fffffffffffffffffffffffffff0000000000000000ffffffffffffffffffffff7fffffffffffffffffffffffffff00000000ffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0600695949796978560405199039503019363ffffffff85113d3d3e63ffffffff81113d3d3e60781b9360581b169116171717825260208201604052565b907fffffffffffffffffffffffffff0000000000000000ffffffffffffffffffffff7fffffffffffffffffffffffffff00000000ffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0600195949796978560405199039503019363ffffffff85113d3d3e63ffffffff81113d3d3e60781b9360581b169116171717825260208201604052565b907fffffffffffffffffffffffffff0000000000000000ffffffffffffffffffffff7fffffffffffffffffffffffffff00000000ffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0600295949796978560405199039503019363ffffffff85113d3d3e63ffffffff81113d3d3e60781b9360581b169116171717825260208201604052565b9392612f9a612f9460009493600051612b87565b92612b6a565b92818410156130b25750825160001a6022811490605b811460016703ff200000000000607b8414931c1692613093576130855761306c5761305557600483018181111561300d575b5060058301908111611e99576466616c7365835160d81c03611e9957611f1992612f94928296612cfc565b8390845160e01c90637472756582146130415750636e756c6c03612fe257612f94929591506004611f199401958692612d9d565b611f199550809350612f9494915096612cfc565b909161306492611f19956133f3565b929092612b6a565b5091906130799394613190565b909190611f1990612b6a565b5050919061307993946130b9565b50505093826130aa612f949396611f19959261330b565b958692612c5b565b9450505090565b939060009081600185015b8681101561318b5786908215613155575b6130df9289612f80565b8082806130f4575b5050506001865b016130c4565b948561310882600194959851604017612bbc565b905201935160001a605d811461312e57602c146131265780806130e7565b6001906130ee565b5061314c919650613152959350600190979294975b01958694612bf1565b90612e3e565b91565b61315f9150612b6a565b90605d825160001a14613174579086906130d5565b9550613152949250600161314c9197929497613143565b611e99565b939091600092600182015b8581101561318b57841561328c575b6131b390612b6a565b6131bd868261330b565b90866131c883612b6a565b603a815160001a146131e3575b509050600191505b0161319b565b9660016131f493949801908a612f80565b90809281978394613209575b508891506131d5565b613241827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08d61324695030192038451608017612c26565b612bbc565b90525160001a607d811461326c57602c146132645780808080613200565b6001906131dd565b506131529495506001613286919792949701958694612bf1565b90612edf565b61329590612b6a565b607d815160001a036131aa5761315294955060016132869197929497613143565b6001667e0000007e03ff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd08484015160001a011c1615611e9957600582036132fc575050565b60016133099201906132b6565b565b81811015611e99576001015b805160001a602281146133a657605c0361339e57600181015160001a60016a05101104000000000020017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffde83011c16613395576075146133815750805b811015611e995760010190565b8061338f60026006936132b6565b01613317565b50600201613317565b600101613317565b5090613374565b90815b600a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd0825160001a0110156133e7576001016133b0565b809214600116611e9957565b919092508092602d825160001a1461359e575b600a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd0855160001a011015611e995760306001855160001a9501940361355b575b602e845160001a14613548575b835160658160001a60201714613505575b50906003917fffffffffffffffffffffffffff0000000000000000ffffffffffffffffffffff7fffffffffffffffffffffffffff00000000ffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0604051968589039503019363ffffffff85113d3d3e63ffffffff81113d3d3e60781b9360581b16911617171781526020810160405291565b60016003939295620100017ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff261354094841a011a01016133ad565b939091613465565b92600161355591016133ad565b92613454565b9190925b600a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd0825160001a0110156135965760010161355f565b929091613447565b600182019350613406565b805191906060811584151761369857508260581c63ffffffff169280601016156135d1575050565b8060781c63ffffffff16604051948260d81c63ffffffff16017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe080601f8401165b80830151818901520190811561364a57907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090613612565b5050604090808652850160006020820152016040528363ffffffff81113d3d3e60581b907fffffffffffffffffffffffffffffffffff00000000ffffffffffffffffffffff16176010179052565b925050565b805191906060811584151761369857508260981c63ffffffff169280600816156136c5575050565b8060b81c63ffffffff16604051948260d81c63ffffffff16017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe080601f8401165b80830151818901520190811561373e57907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090613706565b5050604090808652850160006020820152016040528363ffffffff81113d3d3e60981b907fffffffffffffffffff00000000ffffffffffffffffffffffffffffffffffffff16176008179052565b606090805190600282600716119060208316928060381c63ffffffff169180158215179363ffffffff8211923d3d908460181b983d983d947fffffffffffffffffffffffffffffffffffffffffff00000000ffffffffffffff1695156137fb575b505050505050505050505090565b9a909192939495969798999a61395f5750613946576040519860208a019796889695949392885b6138f657505050506040945087840396877fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe001948560051c8a52865263ffffffff8911913e8660381b1760201790521015613886575b8080808080808080806137ed565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe080602092938501015b835181518552815201910190808210156138ed577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe06020916138b0565b50503880613878565b88602099899596979899528051908383883e84827fffffffffffffffffffffffffffffffffffffffffffffffffff00000000ffffff1617905260181c63ffffffff16988994019796959493613822565b50505050505091505038808080808080808080806137ed565b99806137ed565b60406139cd81516139778382611c4e565b600881527f5570546f446174650000000000000000000000000000000000000000000000006020918201528351908401207f8dd11fcce5839087111530125177803569655488ae47b1f980ef0b1a711bf07c1490565b156139d9575050600090565b613a3e81516139e88382611c4e565b600981527f4f75744f664461746500000000000000000000000000000000000000000000006020918201528351908401207fe1dbc1592712cb224ab58df345a7bffafe7d5092dc9eff532076ee2c5e6870991490565b15613a4a575050600490565b613aaf8151613a598382611c4e565b601c81527f4f75744f6644617465436f6e66696775726174696f6e4e6565646564000000006020918201528351908401207f605a88f0014fcefe2d398f17b974556e2ae7481bfdc313f7e0af48c270400c491490565b15613abb575050600590565b613b208151613aca8382611c4e565b601381527f436f6e66696775726174696f6e4e6565646564000000000000000000000000006020918201528351908401207f820e8e2a851961706b111f73b809da9ccf7c4d4181cd135f0696ca2daa5059421490565b15613b2c575050600390565b613ba08151613b3c606082611c4e565b602181527f436f6e66696775726174696f6e416e64535748617264656e696e674e6565646560208201527f64000000000000000000000000000000000000000000000000000000000000008382015283906020815191012090602081519101201490565b15613bac575050600290565b613c118151613bbb8382611c4e565b601181527f535748617264656e696e674e65656465640000000000000000000000000000006020918201528351908401207f7ad9f5b3c5180451fdebfb145dcb7bcd2dc9f95342d3659e0dcc8d07b31182a11490565b15613c1d575050600190565b613c8291613c2d82519283611c4e565b600782527f5265766f6b65640000000000000000000000000000000000000000000000000060209283015280519101207f78b54e92f33559320da4a0c1454741ea1f5c5fa31eb1dd482882c6df0231e4781490565b15613c8c57600690565b600790565b6040516102209190613ca38382611c4e565b60108152917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe001366020840137565b60405190613ce1604083611c4e565b600682527f70636573766e00000000000000000000000000000000000000000000000000006020830152565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8114613d3a5760010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b906000805b83518210156136985760006020838601015160f81c603081101580613e6a575b613d9f575b50509060010190613d6e565b600a830292808404600a1490151715613e3d57807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd0810111613e3d577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd090830101809211613e105750600138613d93565b807f4e487b7100000000000000000000000000000000000000000000000000000000602492526011600452fd5b6024827f4e487b710000000000000000000000000000000000000000000000000000000081526011600452fd5b506039811115613d8e565b600060106060918351601081111561408b575b8015614083575b50818110613fd8575b505060109160309060609381516030811115613fd0575b6010811115613fc8575b50828110613f19575b505050613ed1613ed791612360565b91612360565b7fffffffffffffffffffffffffffffffff000000000000000000000000000000006040519260801b166020830152603082015260308152611f19605082611c4e565b60405192819003808452929450017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f830181165b808301518187015201908115613f8757907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090613f4f565b5050600083820160200152603f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201604052613ed1613ed738613ec2565b905038613eb9565b925082613eaf565b8091925060405192039081835283017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe080601f8401165b80830151818601520190811561404757907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09061400f565b5050600082820160200152603f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01681016040523880613e98565b905038613e8f565b915081613e8856fea2646970667358221220bac0c2e810699f47893ffed7970c934aa77968cb8337d8a159e5a970c40ac7b264736f6c634300081b0033", + "nonce": "0x6", + "chainId": "0x4268" + }, + "additionalContracts": [], + "isFixedGasLimit": false + } + ], + "receipts": [ + { + "status": "0x1", + "cumulativeGasUsed": "0x4cc68a", + "logs": [], + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "type": "0x2", + "transactionHash": "0x5e81ff52f1e9c5cd9562940e5c7348b932415d7251c76837873fac5a31ac97a0", + "transactionIndex": "0x10", + "blockHash": "0xa7aa8b9a7f2f0cdb47ec67c0c240cd9715b02bee5274074b720dbae0c1c7a25a", + "blockNumber": "0x24b382", + "gasUsed": "0x373b49", + "effectiveGasPrice": "0xd8bd68f", + "from": "0xdc3bda6d40f0e33e0dfa4aef9604b66195e6c5dc", + "to": null, + "contractAddress": "0xc728dd0fcd76cd9166f66e1cd8002de86d6525b8" + } + ], + "libraries": [], + "pending": [], + "returns": {}, + "timestamp": 1727259080, + "chain": 17000, + "commit": "a6abae3" +} \ No newline at end of file diff --git a/broadcast/DeployHelpers.s.sol/17000/deployPckHelper-latest.json b/broadcast/DeployHelpers.s.sol/17000/deployPckHelper-latest.json new file mode 100644 index 0000000..f3d8b03 --- /dev/null +++ b/broadcast/DeployHelpers.s.sol/17000/deployPckHelper-latest.json @@ -0,0 +1,46 @@ +{ + "transactions": [ + { + "hash": "0x1b5c566f880131bcfc74451077f000c149606b7c21428718554a0cd4656f2306", + "transactionType": "CREATE", + "contractName": "PCKHelper", + "contractAddress": "0xde20629a87c371668bb371ef1d77d9d167e52021", + "function": null, + "arguments": null, + "transaction": { + "from": "0xdc3bda6d40f0e33e0dfa4aef9604b66195e6c5dc", + "gas": "0x28a820", + "value": "0x0", + "input": "0x6080806040523460155761245d908161001b8239f35b600080fdfe6080604052600436101561001257600080fd5b60003560e01c806335c757bd146100975780633b79a61e14610092578063634fdad51461008d5780636d3537a014610088578063b29b51cb14610083578063e81c707e1461007e578063ed333785146100795763fcf0be241461007457600080fd5b610920565b6108c8565b61076a565b6105b1565b610558565b6104ef565b61035f565b3461014d5761014961013d6100ab36610152565b61013761012d61012261011761010c6101016100f16100e66100d66100d1368b8d6106c3565b611545565b6100e1368b8d6106c3565b610acb565b6100e1368a8c6106c3565b6100fc36898b6106c3565b610bde565b6100fc36888a6106c3565b6100fc3687896106c3565b6100fc3686886106c3565b6100fc3685876106c3565b6100e183856109a0565b91610c03565b60405191829182610227565b0390f35b600080fd5b9060207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc83011261014d5760043567ffffffffffffffff811161014d578260238201121561014d5780600401359267ffffffffffffffff841161014d576024848301011161014d576024019190565b60005b8381106101d45750506000910152565b81810151838201526020016101c4565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f602093610220815180928187528780880191016101c1565b0116010190565b9060206102389281815201906101e4565b90565b9061023891602081528151602082015261010061032b6102ee6102ba610272602087015161012060408801526101408701906101e4565b604087015160608701526060870151608087015260808701517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08783030160a08801526101e4565b60a08601517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08683030160c08701526101e4565b60c085015160e085015260e08501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe085830301848601526101e4565b920151906101207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0828503019101526101e4565b3461014d576101496104de61037336610152565b919061037d6109ab565b926104d86104cd6103eb6104bd61049461046b61044b6104226104178a8d61040a6104058d6104006103f66103c46103b96100d136868b6106c3565b6100e136868b6106c3565b9d8e6103da816103d536888d6106c3565b610c67565b6101008901526100e136868b6106c3565b6100fc36858a6106c3565b95869236916106c3565b610c9e565b610cd8565b90526100fc368c8e6106c3565b6100fc368b8d6106c3565b61043b610434826100e1368d8f6106c3565b8a8c610c03565b60208d01526100fc368a8c6106c3565b61045681898b610d59565b60608d015260408c01526100fc36898b6106c3565b61048461047d826100e1368b8d6106c3565b888a610c03565b60808b01526100fc36888a6106c3565b6104ad6104a6826100e1368a8c6106c3565b8789610da3565b60a08a01526100fc3687896106c3565b60c08801526100fc3685876106c3565b6100fc3684866106c3565b91610f8c565b60e08201526040519182918261023b565b3461014d5761014961054461050336610152565b61013761053961012261011761010c61052e6105236100d136898b6106c3565b6100e136898b6106c3565b6100e136888a6106c3565b6100e13684866106c3565b6040519182916020835260208301906101e4565b3461014d5761014961054461056c36610152565b6105ab61053961012261011761010c6101016100f16105a06100d66105956100d1368c8e6106c3565b6100e1368c8e6106c3565b6100fc368a8c6106c3565b91610da3565b3461014d57602061060c6104056104006105ca36610152565b91906106046105f96105ee6105e36100d13688876106c3565b6100e13688876106c3565b6100e13687866106c3565b6100fc3686856106c3565b9236916106c3565b604051908152f35b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761068457604052565b610614565b67ffffffffffffffff811161068457601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b9291926106cf82610689565b916106dd6040519384610643565b82948184528183011161014d578281602093846000960137010152565b92909493919461ffff6080850191168452608060208501528551809152602060a0850196019060005b8181106107515750505084610743918461023896970360408601526101e4565b9160608184039101526101e4565b825160ff16885260209788019790920191600101610723565b3461014d5760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261014d5760043567ffffffffffffffff811161014d573660238201121561014d576107ca9036906024816004013591016106c3565b602435907fa3000000000000000000000000000000000000000000000000000000000000007fff0000000000000000000000000000000000000000000000000000000000000061082669ffffffffffffffffffff851684610a55565b51160361086a576108528161084161085e9461014994610acb565b9061084c8282610acb565b906111bd565b92949395909195610a66565b604051948594856106fa565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f4e6f7420616e20657874656e73696f6e000000000000000000000000000000006044820152fd5b3461014d5760206109016108db36610152565b6108fb6104cd61012261011761010c61052e6105236100d136898b6106c3565b91610d59565b9042119081610916575b506040519015158152f35b905042103861090b565b3461014d5761099261097e61014961093736610152565b61095661094b6100d19593953684886106c3565b6100e13684886106c3565b9061097861096c6103eb846100fc36868b6106c3565b926103d53684896106c3565b94610f8c565b6040519384936040855260408501906101e4565b9083820360208501526101e4565b6102389136916106c3565b60405190610120820182811067ffffffffffffffff8211176106845760405260606101008360008152826020820152600060408201526000838201528260808201528260a0820152600060c08201528260e08201520152565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b805115610a405760200190565b610a04565b805160011015610a405760210190565b908151811015610a40570160200190565b15610a6d57565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f696e76616c69642053475820657874656e73696f6e00000000000000000000006044820152fd5b7f200000000000000000000000000000000000000000000000000000000000000080610b0369ffffffffffffffffffff851684610a55565b511603610b23576102389160501c69ffffffffffffffffffff16906116be565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4e6f74206120636f6e73747275637465642074797065000000000000000000006044820152fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b9060018201809211610bbe57565b610b81565b9060028201809211610bbe57565b91908201809211610bbe57565b9069ffffffffffffffffffff9060a01c1660018101809111610bbe57610238916116be565b610400906106046105f96105ee610238966100e13688876106c3565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8201918211610bbe57565b6020039060208211610bbe57565b91908203918211610bbe57565b9069ffffffffffffffffffff8082169160a01c16906001820191828111610bbe57816001910301918211610bbe576102389261183c565b9069ffffffffffffffffffff808260501c169160a01c16906001820191828111610bbe57816001910301918211610bbe576102389261183c565b80516020039060208211610bbe577f1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82168203610bbe57602081519101519060208110610d29575b509060031b1c90565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9060200360031b1b1638610d20565b610d9690610400610d746102389496956100e13689866106c3565b91610d9b610d96610d8a856100fc368c876106c3565b94610400368b866106c3565b611a5c565b9636916106c3565b610ed8610ed2610ecc610eb394610ec7610ec2610eb3610dda610e8098610dd2610ede9c6100fc3689856106c3565b9536916106c3565b9884610e407f03000000000000000000000000000000000000000000000000000000000000007fff00000000000000000000000000000000000000000000000000000000000000610e3869ffffffffffffffffffff8f9a168a610a55565b511614611ea0565b610ead7fff00000000000000000000000000000000000000000000000000000000000000610ea669ffffffffffffffffffff8460501c169c8d809a610a55565b517fff000000000000000000000000000000000000000000000000000000000000001690565b16156117e6565b60a01c90565b69ffffffffffffffffffff1690565b610bb0565b610c5a565b92610bb0565b91610c1f565b9161183c565b6041815103610ef05761023890611f05565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602360248201527f636f6d70726573736564207075626c6963206b6579206e6f7420737570706f7260448201527f74656400000000000000000000000000000000000000000000000000000000006064820152608490fd5b90610f88602092828151948592016101c1565b0190565b69ffffffffffffffffffff610fa23684846106c3565b93610ff47f03000000000000000000000000000000000000000000000000000000000000007fff00000000000000000000000000000000000000000000000000000000000000610e3885851689610a55565b60501c169160018301809311610bbe576110556110446110759261040061102e6110236102389860209a6116be565b6100e136858a6106c3565b95610604611049611044896104003688876106c3565b611fae565b976100fc3686856106c3565b60405194859361106e82860191828151948592016101c1565b0190610f75565b037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08101835282610643565b604051906110b0604083610643565b600982527f2a864886f84d010d0100000000000000000000000000000000000000000000006020830152565b604051906060820182811067ffffffffffffffff8211176106845760405260006040838281528260208201520152565b6040519061111b604083610643565b600a82527f2a864886f84d010d0102000000000000000000000000000000000000000000006020830152565b60405190611156604083610643565b600a82527f2a864886f84d010d0103000000000000000000000000000000000000000000006020830152565b60405190611191604083610643565b600a82527f2a864886f84d010d0104000000000000000000000000000000000000000000006020830152565b90929192600093600093606093849385938160a0925b6111de575b50505050565b6111e88185610acb565b7f060000000000000000000000000000000000000000000000000000000000000061124a611225610e8069ffffffffffffffffffff851689610a55565b7fff000000000000000000000000000000000000000000000000000000000000001690565b036114b95761126a61125c8287610c9e565b6112646110a1565b9061204c565b6112b3575061127f610eb360ff851683901c81565b61128f610eb360ff861685901c81565b11156112a95761129f9084610bde565b91825b90926111d3565b50600091826112a2565b90506112d29298969499506112cc915083979593610bde565b866120e0565b6112dc8187610acb565b6112f7610eb3610eb38a6112ee6110dc565b959060ff161c90565b905b82511515806114ac575b8061149f575b611491576113178189610acb565b7f0600000000000000000000000000000000000000000000000000000000000000611354611225610e8069ffffffffffffffffffff85168d610a55565b0361147d5761136e611366828b610c9e565b61126461110c565b61145a575b611388611380828b610c9e565b611264611147565b611438575b6113a261139a828b610c9e565b611264611182565b611417575b50816113b9610eb360ff8c1684901c81565b10156113ce576113c99088610bde565b6112f9565b5050919395509193505b805115159081611402575b816113f4575b5094388080806111d8565b6040015115159050386113e9565b90506114116020820151151590565b906113e3565b61142d9195506114279089610bde565b88610c9e565b6001835293386113a7565b935061144d611447858a610bde565b89610c9e565b600160208501529361138d565b9650611478955061146b87896122b0565b9115156040860152979096565b611373565b505050509295919450925060009493929190565b5050919395509193506113d8565b5060408301511515611309565b5060208301511515611303565b506000995050505050565b60ff166020039060ff8211610bbe57565b60ff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd09116019060ff8211610bbe57565b60031b906107f860f8831692168203610bbe57565b60ff60649116029060ff8216918203610bbe57565b60ff600a9116029060ff8216918203610bbe57565b6102389060007f8000000000000000000000000000000000000000000000000000000000000000611581610e8061157b84610bb0565b85610a55565b166115ed57506115aa6115a4610e806115b09361159e6000610bb0565b90610a55565b60f81c90565b60ff1690565b69ffffffffffffffffffff806115e0610eb36115db6115d2610eb36000610bc3565b95848716610bd1565b610c1f565b1660a01b911660501b1790565b9069ffffffffffffffffffff61165c610eb36115db6115d2610eb38596607f611621610e8061161b8c610bb0565b84610a55565b60f81c168099600182146000146116615750506115aa61164b916116456000610bc3565b90612407565b975b6116576000610bc3565b610bd1565b6115e0565b6002820361168f5750506116826116899161167c6000610bc3565b906123e9565b61ffff1690565b9761164d565b90611689926116a06116a693610bc3565b9061239a565b6116b76116b28b6114c4565b611506565b60ff161c90565b90610238917f80000000000000000000000000000000000000000000000000000000000000006116f3610e8061161b85610bb0565b16611749576115aa6115a4610e8061170e9361159e86610bb0565b9069ffffffffffffffffffff80611739610eb36115db611730610eb387610bc3565b96848816610bd1565b1692169160a01b9160501b171790565b9069ffffffffffffffffffff806117a4610eb36115db611730610eb3607f61177c610e806117768b610bb0565b8c610a55565b60f81c169860018a036117a9576115aa611799916116458b610bc3565b985b61165789610bc3565b611739565b89600281036117c957506116826117c39161167c8b610bc3565b9861179b565b6117da906117c3926116a08c610bc3565b6116b76116b28c6114c4565b1561014d57565b906117f782610689565b6118046040519182610643565b8281527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe06118328294610689565b0190602036910137565b90828101808211610bbe5782511061014d5760209061185a846117ed565b9392010160208301915b60208110156118be57806118a457507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff905b518251821691191617905290565b6115db6118b36118b892610c4c565b612418565b90611896565b90918251815260208101809111610bbe579160208101809111610bbe57907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081019081111561186457610b81565b61ffff6103e89116029061ffff8216918203610bbe57565b61ffff169061ffff8211610bbe57565b61ffff61076c9116019061ffff8211610bbe57565b61ffff6107d09116019061ffff8211610bbe57565b9061ffff8091169116019061ffff8211610bbe57565b60ff169060ff8211610bbe57565b60ff60019116019060ff8211610bbe57565b60ff60029116019060ff8211610bbe57565b60ff60039116019060ff8211610bbe57565b60ff60049116019060ff8211610bbe57565b60ff60059116019060ff8211610bbe57565b60ff60069116019060ff8211610bbe57565b60ff60079116019060ff8211610bbe57565b60ff60089116019060ff8211610bbe57565b60ff60099116019060ff8211610bbe57565b60ff600a9116019060ff8211610bbe57565b60ff600b9116019060ff8211610bbe57565b9060ff8091169116019060ff8211610bbe57565b600090600091600d825114600014611e4957600560ff611a89611a846115a4610e8087610a33565b6114d5565b161015611e3b57611a9990611949565b611aa283611974565b60ff16611aaf9083610a55565b51611abc9060f81c6114d5565b611ac590611530565b611ace84611982565b60ff16611adb9084610a55565b517fff000000000000000000000000000000000000000000000000000000000000001660f81c611b0a91611a48565b611b13906114d5565b60ff16611b1f9161195e565b91611b2981611994565b60ff16611b369083610a55565b51611b439060f81c6114d5565b611b4c90611530565b611b55826119a6565b60ff16611b629084610a55565b517fff000000000000000000000000000000000000000000000000000000000000001660f81c611b9191611a48565b611b9a906114d5565b611ba3826119b8565b60ff16611bb09084610a55565b51611bbd9060f81c6114d5565b611bc690611530565b611bcf836119ca565b60ff16611bdc9085610a55565b517fff000000000000000000000000000000000000000000000000000000000000001660f81c611c0b91611a48565b611c14906114d5565b611c1d90611974565b90611c27836119dc565b60ff16611c349085610a55565b51611c419060f81c6114d5565b611c4a90611530565b611c53846119ee565b60ff16611c609086610a55565b517fff000000000000000000000000000000000000000000000000000000000000001660f81c611c8f91611a48565b611c98906114d5565b611ca190611974565b92611cab81611a00565b60ff16611cb89086610a55565b51611cc59060f81c6114d5565b611cce90611530565b611cd782611a12565b60ff16611ce49087610a55565b517fff000000000000000000000000000000000000000000000000000000000000001660f81c611d1391611a48565b611d1c906114d5565b611d2590611974565b94611d2f82611a24565b60ff16611d3c9082610a55565b51611d499060f81c6114d5565b611d5290611530565b91611d5c90611a36565b60ff16611d6891610a55565b517fff000000000000000000000000000000000000000000000000000000000000001660f81c611d9791611a48565b611da0906114d5565b611da990611974565b60ff169360ff169260ff169160ff169060ff169461ffff1694610238959362023ab16101907ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff50593936201518095969760038210900393610e10603c8487069202990297610301600c60096064850495010661f4ff0201600b1c019061016d8160021c910201010392040201010201010190565b611e4490611934565b611a99565b509050611e97611e92611e6f60ff611e69611a846115a4610e8088610a33565b1661190c565b611e8c6115aa611e87611a846115a4610e8089610a45565b61151b565b9061195e565b611924565b90600291611a99565b15611ea757565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f4e6f7420747970652042495420535452494e47000000000000000000000000006044820152fd5b90815160408114600014611f165750565b6040811015611f7657611f2960406117ed565b92816040039060408211610bbe5760005b838110611f475750505050565b80611f57610e8060019385610a55565b611f6f611f648387610bd1565b9160001a9189610a55565b5301611f3a565b90917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc08201918211610bbe576102389160409161183c565b90815160208114600014611fbf5750565b602081101561201457611fd260206117ed565b92816020039060208211610bbe5760005b838110611ff05750505050565b80612000610e8060019385610a55565b61200d611f648387610bd1565b5301611fe3565b90917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08201918211610bbe576102389160209161183c565b9081518151036120d95760005b82518110156120d1577fff0000000000000000000000000000000000000000000000000000000000000061208d8285610a55565b51167fff000000000000000000000000000000000000000000000000000000000000006120ba8385610a55565b5116036120c957600101612059565b505050600090565b505050600190565b5050600090565b7f04000000000000000000000000000000000000000000000000000000000000007fff0000000000000000000000000000000000000000000000000000000000000061213869ffffffffffffffffffff851684610a55565b511603612158576102389160501c69ffffffffffffffffffff16906116be565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4e6f742074797065204f4354455420535452494e4700000000000000000000006044820152fd5b60405161022091906121c88382610643565b60108152917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe001366020840137565b90602082519201517fffff0000000000000000000000000000000000000000000000000000000000008116926002811061222f575050565b7fffff000000000000000000000000000000000000000000000000000000000000929350829060020360031b1b161690565b60405190612270604083610643565b600b82527f2a864886f84d010d0102110000000000000000000000000000000000000000006020830152565b8051821015610a405760209160051b010190565b6122c56122bf60009383610bde565b82610acb565b6122cd6121b6565b9160006011915b8282106122e45750505050600192565b909192946122f28683610acb565b9061234661233e61230c6123068587610bde565b86610c9e565b60028151106000146123885761232d612327612336926121f7565b60f01c90565b60081c60ff1690565b935b85610c9e565b611264612261565b15612363575061235a600191965b83610bde565b939201906122d4565b9561235a9061238360ff6001941661237b878a61229c565b9060ff169052565b612354565b612327612394916121f7565b93612338565b91906020821161014d57818101808211610bbe5783511061014d577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60209283036101000a0119920101511690565b60028201808311610bbe5781511061014d57016002015161ffff1690565b9061241191610a55565b5160f81c90565b601f8111610bbe576101000a9056fea2646970667358221220b552a3b4a62bfd76c64f25285888e63bef87233fcc1b637b99836503b9ce228b64736f6c634300081b0033", + "nonce": "0x8", + "chainId": "0x4268" + }, + "additionalContracts": [], + "isFixedGasLimit": false + } + ], + "receipts": [ + { + "status": "0x1", + "cumulativeGasUsed": "0x3efa04", + "logs": [], + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "type": "0x2", + "transactionHash": "0x1b5c566f880131bcfc74451077f000c149606b7c21428718554a0cd4656f2306", + "transactionIndex": "0x13", + "blockHash": "0x89991a5be656098fc72f0fff218a19df9d7d7d3faa1db8965ca879a3d88962a7", + "blockNumber": "0x24b397", + "gasUsed": "0x1f4888", + "effectiveGasPrice": "0xd23cd91", + "from": "0xdc3bda6d40f0e33e0dfa4aef9604b66195e6c5dc", + "to": null, + "contractAddress": "0xde20629a87c371668bb371ef1d77d9d167e52021" + } + ], + "libraries": [], + "pending": [], + "returns": {}, + "timestamp": 1727259383, + "chain": 17000, + "commit": "a6abae3" +} \ No newline at end of file diff --git a/broadcast/DeployHelpers.s.sol/17000/deployX509CrlHelper-latest.json b/broadcast/DeployHelpers.s.sol/17000/deployX509CrlHelper-latest.json new file mode 100644 index 0000000..eabc8ce --- /dev/null +++ b/broadcast/DeployHelpers.s.sol/17000/deployX509CrlHelper-latest.json @@ -0,0 +1,46 @@ +{ + "transactions": [ + { + "hash": "0x52616604663a8c80c8959214545d3b8da39febee42b3723f4c914dfd62d7ad21", + "transactionType": "CREATE", + "contractName": "X509CRLHelper", + "contractAddress": "0x3acbfad7460e2fae32a31f863e1a38f7a002cea8", + "function": null, + "arguments": null, + "transaction": { + "from": "0xdc3bda6d40f0e33e0dfa4aef9604b66195e6c5dc", + "gas": "0x2138c9", + "value": "0x0", + "input": "0x60808060405234601557611d7e908161001b8239f35b600080fdfe6080604052600436101561001257600080fd5b60003560e01c806301cd975b14610077578063634fdad514610072578063a91105ce1461006d578063b29b51cb14610068578063cedb9781146100635763fcf0be241461005e57600080fd5b610593565b6104d7565b61047d565b610428565b6103a6565b346101b1576101ad61019c61008b366101e4565b91906100956106c1565b9261019661018b6100c26100b26100ad368688610745565b61120e565b6100bd368688610745565b61081a565b61017b6101746101696101496101206101158a8d6101036100fe8d6100f96100ef8d6100bd36858a610745565b9586923691610745565b610963565b61077c565b9052610110368c8e610745565b61099d565b610110368b8d610745565b610139610132826100bd368d8f610745565b8a8c6109c2565b60208d0152610110368a8c610745565b61015481898b6109e9565b60608d015260408c015261011036898b610745565b61011036888a610745565b8587610c26565b6080880152610110368587610745565b610110368486610745565b91611002565b60a082015260405191829182610297565b0390f35b600080fd5b9181601f840112156101b15782359167ffffffffffffffff83116101b157602083818601950101116101b157565b60207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc8201126101b1576004359067ffffffffffffffff82116101b15761022d916004016101b6565b9091565b60005b8381106102445750506000910152565b8181015183820152602001610234565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f60209361029081518092818752878088019101610231565b0116010190565b919060208352805160208401526102be602082015160e06040860152610100850190610254565b9260408201516060820152606082015160808201526080820151937fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08282030160a08301526020808651928381520195019060005b8181106103905750505060c061035a61038d949560a08501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08583030184860152610254565b9201519060e07fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe082850301910152610254565b90565b8251875260209687019690920191600101610313565b346101b1576101ad6104146103ba366101e4565b61040e6104036103f86103ed6103e26103d76100ad36888a610745565b6100bd36888a610745565b6100bd368789610745565b610110368688610745565b610110368587610745565b6100bd368486610745565b916109c2565b604051918291602083526020830190610254565b346101b157602061045e61043b366101e4565b61045861018b6103f86103ed6103e26103d76100ad36888a610745565b916109e9565b9042119081610473575b506040519015158152f35b9050421038610468565b346101b15760206104cf6104ca6100f9610496366101e4565b91906104c26104b76104ac6100ad368786610745565b6100bd368786610745565b6100bd368685610745565b923691610745565b611145565b604051908152f35b346101b15760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101b15760043560243567ffffffffffffffff81116101b1578161052d6105779236906004016101b6565b61057161018b6103f86103ed61056661016961055b6105506100ad368a8c610745565b6100bd368a8c610745565b6100bd36898b610745565b610110368789610745565b91610e8d565b9081511561058e5760209182015160405191148152f35b6107bd565b346101b1576105a1366101e4565b906105be6105b36100ad368585610745565b6100bd368585610745565b916105d161018b84610110368587610745565b926105dd368385610745565b9369ffffffffffffffffffff8083169260a01c16926001840193848111610647578360019103019384116106475761061f6101ad94610625946106399861151a565b94611002565b604051938493604085526040850190610254565b908382036020850152610254565b6108d0565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff8211176106bc57604052565b61064c565b6040519060e0820182811067ffffffffffffffff8211176106bc57604052606060c08360008152826020820152600060408201526000838201528260808201528260a08201520152565b67ffffffffffffffff81116106bc57601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b9291926107518261070b565b9161075f604051938461067b565b8294818452818301116101b1578281602093846000960137010152565b602081519101519060208110610790575090565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9060200360031b1b1690565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b80511561058e5760200190565b80516001101561058e5760210190565b90815181101561058e570160200190565b7f20000000000000000000000000000000000000000000000000000000000000008061085269ffffffffffffffffffff851684610809565b5116036108725761038d9160501c69ffffffffffffffffffff16906113ad565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4e6f74206120636f6e73747275637465642074797065000000000000000000006044820152fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b906001820180921161064757565b906002820180921161064757565b9190820180921161064757565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820191821161064757565b602003906020821161064757565b9069ffffffffffffffffffff808260501c169160a01c16906001820191828111610647578160019103019182116106475761038d9261151a565b9069ffffffffffffffffffff9060a01c16600181018091116106475761038d916113ad565b6100f9906104c26109de6104ac61038d966100bd368887610745565b610110368685610745565b61038d9193926100f9610a1792610a1c610a17610a0b85610110368c87610745565b946100f9368b86610745565b61173a565b963691610745565b9082101561058e570190565b90610a4360209282815194859201610231565b0190565b6020929190610a5d849282815194859201610231565b019081520190565b67ffffffffffffffff81116106bc5760051b60200190565b60408051909190610a8e838261067b565b60018152917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe001366020840137565b90610ac782610a65565b610ad4604051918261067b565b8281527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0610b028294610a65565b0190602036910137565b6020818303126101b15780519067ffffffffffffffff82116101b157019080601f830112156101b1578151610b4081610a65565b92610b4e604051948561067b565b81845260208085019260051b8201019283116101b157602001905b828210610b765750505090565b8151815260209182019101610b69565b60405190610b9560408361067b565b600382527f551d1400000000000000000000000000000000000000000000000000000000006020830152565b15610bc857565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600b60248201527f696e76616c69642043524c0000000000000000000000000000000000000000006044820152fd5b60609392610c39816100bd368686610745565b907fa0000000000000000000000000000000000000000000000000000000000000007fff00000000000000000000000000000000000000000000000000000000000000610ccf610ca9610ca269ffffffffffffffffffff87165b69ffffffffffffffffffff1690565b8888610a24565b357fff000000000000000000000000000000000000000000000000000000000000001690565b1603610d085750610d06926100f9610d01936104c2610cf3946100bd368685610745565b610cfb610b86565b90611b7e565b610bc1565b565b91945060a093909160605b610d23610c9360ff881686901c81565b610d33610c9360ff891685901c81565b10610db757610c93610c93610dad610d3393610da1610d626104ca8b6100f98f6104c28e6100bd368685610745565b91610d7560405193849260208401610a47565b037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0810183528261067b565b96610110368a8d610745565b9592505050610d13565b93505093505061038d91506020610e77610e82610dd5845160051c90565b610e7d60405195610e1c87610df08882019060208083019252565b037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0810189528861067b565b610e3860405191610e6483610e38878b83019190602083019252565b037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0810185528461067b565b604051968793610e778a8601809c610a30565b90610a30565b610abd565b508051010190610b0c565b9091939293606094610ea4826100bd368787610745565b917fa0000000000000000000000000000000000000000000000000000000000000007fff00000000000000000000000000000000000000000000000000000000000000610f09610ca9610f0269ffffffffffffffffffff8816610c93565b8989610a24565b1603610f2e575050610d06926100f9610d01936104c2610cf3946100bd368685610745565b92955060a09492909160605b610f4a610c9360ff891684901c81565b610f5a610c9360ff8a1686901c81565b10610fe357610f9a929190610f856104ca610f7a846100bd368c8f610745565b6100f9368b8e610745565b938491610d7560405193849260208401610a47565b928414610fc557610c93610c93610fba610f5a936101108c8b3691610745565b939492505050610f3a565b5050509250925050610fd5610a7d565b90610fdf826107ec565b5290565b9450505050925061038d91506020610e77610e82610dd5845160051c90565b61100d368383610745565b917f03000000000000000000000000000000000000000000000000000000000000007fff0000000000000000000000000000000000000000000000000000000000000061106669ffffffffffffffffffff871686610809565b5116036110e7576110d76110c6610d75926100f96110b06110a561038d9861109f61109a610c93610c93610e779e60501c90565b6108ff565b906113ad565b6100bd36858a610745565b956104c26110cb6110c6896100f9368887610745565b611c12565b97610110368685610745565b6040519485936020850190610a30565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f4e6f7420747970652042495420535452494e47000000000000000000000000006044820152fd5b80516020039060208211610647577f1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82168203610647576111859061077c565b9060031b1c90565b60ff166020039060ff821161064757565b60ff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd09116019060ff821161064757565b60031b906107f860f883169216820361064757565b60ff60649116029060ff821691820361064757565b60ff600a9116029060ff821691820361064757565b61038d9060007f800000000000000000000000000000000000000000000000000000000000000061127061124a611244846108ff565b85610809565b517fff000000000000000000000000000000000000000000000000000000000000001690565b166112dc575061129961129361124a61129f9361128d60006108ff565b90610809565b60f81c90565b60ff1690565b69ffffffffffffffffffff806112cf610c936112ca6112c1610c93600061090d565b9584871661091b565b610928565b1660a01b911660501b1790565b9069ffffffffffffffffffff61134b610c936112ca6112c1610c938596607f61131061124a61130a8c6108ff565b84610809565b60f81c1680996001821460001461135057505061129961133a91611334600061090d565b90611d28565b975b611346600061090d565b61091b565b6112cf565b6002820361137e5750506113716113789161136b600061090d565b90611d0a565b61ffff1690565b9761133c565b906113789261138f6113959361090d565b90611cbb565b6113a66113a18b61118d565b6111cf565b60ff161c90565b9061038d917f80000000000000000000000000000000000000000000000000000000000000006113e261124a61130a856108ff565b166114385761129961129361124a6113fd9361128d866108ff565b9069ffffffffffffffffffff80611428610c936112ca61141f610c938761090d565b9684881661091b565b1692169160a01b9160501b171790565b9069ffffffffffffffffffff80611493610c936112ca61141f610c93607f61146b61124a6114658b6108ff565b8c610809565b60f81c169860018a0361149857611299611488916113348b61090d565b985b6113468961090d565b611428565b89600281036114b857506113716114b29161136b8b61090d565b9861148a565b6114c9906114b29261138f8c61090d565b6113a66113a18c61118d565b906114df8261070b565b6114ec604051918261067b565b8281527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0610b02829461070b565b90828101808211610647578251106101b157602090611538846114d5565b9392010160208301915b602081101561159c578061158257507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff905b518251821691191617905290565b6112ca61159161159692610955565b611d39565b90611574565b9091825181526020810180911161064757916020810180911161064757907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0810190811115611542576108d0565b61ffff6103e89116029061ffff821691820361064757565b61ffff169061ffff821161064757565b61ffff61076c9116019061ffff821161064757565b61ffff6107d09116019061ffff821161064757565b9061ffff8091169116019061ffff821161064757565b60ff169060ff821161064757565b60ff60019116019060ff821161064757565b60ff60029116019060ff821161064757565b60ff60039116019060ff821161064757565b60ff60049116019060ff821161064757565b60ff60059116019060ff821161064757565b60ff60069116019060ff821161064757565b60ff60079116019060ff821161064757565b60ff60089116019060ff821161064757565b60ff60099116019060ff821161064757565b60ff600a9116019060ff821161064757565b60ff600b9116019060ff821161064757565b9060ff8091169116019060ff821161064757565b600090600091600d825114600014611b2757600560ff61176761176261129361124a876107ec565b61119e565b161015611b195761177790611627565b61178083611652565b60ff1661178d9083610809565b5161179a9060f81c61119e565b6117a3906111f9565b6117ac84611660565b60ff166117b99084610809565b517fff000000000000000000000000000000000000000000000000000000000000001660f81c6117e891611726565b6117f19061119e565b60ff166117fd9161163c565b9161180781611672565b60ff166118149083610809565b516118219060f81c61119e565b61182a906111f9565b61183382611684565b60ff166118409084610809565b517fff000000000000000000000000000000000000000000000000000000000000001660f81c61186f91611726565b6118789061119e565b61188182611696565b60ff1661188e9084610809565b5161189b9060f81c61119e565b6118a4906111f9565b6118ad836116a8565b60ff166118ba9085610809565b517fff000000000000000000000000000000000000000000000000000000000000001660f81c6118e991611726565b6118f29061119e565b6118fb90611652565b90611905836116ba565b60ff166119129085610809565b5161191f9060f81c61119e565b611928906111f9565b611931846116cc565b60ff1661193e9086610809565b517fff000000000000000000000000000000000000000000000000000000000000001660f81c61196d91611726565b6119769061119e565b61197f90611652565b92611989816116de565b60ff166119969086610809565b516119a39060f81c61119e565b6119ac906111f9565b6119b5826116f0565b60ff166119c29087610809565b517fff000000000000000000000000000000000000000000000000000000000000001660f81c6119f191611726565b6119fa9061119e565b611a0390611652565b94611a0d82611702565b60ff16611a1a9082610809565b51611a279060f81c61119e565b611a30906111f9565b91611a3a90611714565b60ff16611a4691610809565b517fff000000000000000000000000000000000000000000000000000000000000001660f81c611a7591611726565b611a7e9061119e565b611a8790611652565b60ff169360ff169260ff169160ff169060ff169461ffff169461038d959362023ab16101907ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff50593936201518095969760038210900393610e10603c8487069202990297610301600c60096064850495010661f4ff0201600b1c019061016d8160021c910201010392040201010201010190565b611b2290611612565b611777565b509050611b75611b70611b4d60ff611b4761176261129361124a886107ec565b166115ea565b611b6a611299611b6561176261129361124a896107f9565b6111e4565b9061163c565b611602565b90600291611777565b908151815103611c0b5760005b8251811015611c03577fff00000000000000000000000000000000000000000000000000000000000000611bbf8285610809565b51167fff00000000000000000000000000000000000000000000000000000000000000611bec8385610809565b511603611bfb57600101611b8b565b505050600090565b505050600190565b5050600090565b90815160208114600014611c235750565b6020811015611c8357611c3660206114d5565b928160200390602082116106475760005b838110611c545750505050565b80611c6461124a60019385610809565b611c7c611c71838761091b565b9160001a9189610809565b5301611c47565b90917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe082019182116106475761038d9160209161151a565b9190602082116101b157818101808211610647578351106101b1577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60209283036101000a0119920101511690565b60028201808311610647578151106101b157016002015161ffff1690565b90611d3291610809565b5160f81c90565b601f8111610647576101000a9056fea26469706673582212200186cb2ca83bfa8ecce26807f2c748c6569068dcc6a660e6ee083b1b9d1eebd864736f6c634300081b0033", + "nonce": "0x9", + "chainId": "0x4268" + }, + "additionalContracts": [], + "isFixedGasLimit": false + } + ], + "receipts": [ + { + "status": "0x1", + "cumulativeGasUsed": "0x36e346", + "logs": [], + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "type": "0x2", + "transactionHash": "0x52616604663a8c80c8959214545d3b8da39febee42b3723f4c914dfd62d7ad21", + "transactionIndex": "0xf", + "blockHash": "0x01a3eea843152dcb817da9466c65526121d392fbff3a98a3c655195259da5ac2", + "blockNumber": "0x24b39f", + "gasUsed": "0x198fff", + "effectiveGasPrice": "0xd23cd91", + "from": "0xdc3bda6d40f0e33e0dfa4aef9604b66195e6c5dc", + "to": null, + "contractAddress": "0x3acbfad7460e2fae32a31f863e1a38f7a002cea8" + } + ], + "libraries": [], + "pending": [], + "returns": {}, + "timestamp": 1727259474, + "chain": 17000, + "commit": "a6abae3" +} \ No newline at end of file diff --git a/foundry.toml b/foundry.toml index 20bdaf6..4a3943c 100644 --- a/foundry.toml +++ b/foundry.toml @@ -5,9 +5,16 @@ libs = ["lib"] fs_permissions = [{ access = "read", path = "./"}] -solc = "0.8.24" +solc = "0.8.27" optimizer = true optimizer_runs = 999999 +# https://github.com/foundry-rs/foundry/issues/6780 via_ir = true +# https://github.com/foundry-rs/foundry/issues/6780#issuecomment-1962319449 +# auto_detect_remappings = false +# bytecode_hash = "none" +# cbor_metadata = false +# sparse_mode = false + # See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options diff --git a/remappings.txt b/remappings.txt index 2e2ddcc..96e83c7 100644 --- a/remappings.txt +++ b/remappings.txt @@ -1,2 +1,4 @@ solady/=lib/solady/src/ -@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/ \ No newline at end of file +@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/ +ds-test/=lib/forge-std/lib/ds-test/src/ +forge-std/=lib/forge-std/src/ \ No newline at end of file diff --git a/slither.config.json b/slither.config.json new file mode 100644 index 0000000..81b734e --- /dev/null +++ b/slither.config.json @@ -0,0 +1,3 @@ +{ + "filter_paths": "(lib/|scripts/|test/)" +} \ No newline at end of file diff --git a/standard-json-input/enclaveidhelper.json b/standard-json-input/enclaveidhelper.json deleted file mode 100644 index 15492af..0000000 --- a/standard-json-input/enclaveidhelper.json +++ /dev/null @@ -1 +0,0 @@ -{"language":"Solidity","sources":{"src/helpers/EnclaveIdentityHelper.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport {JSONParserLib} from \"solady/utils/JSONParserLib.sol\";\nimport {LibString} from \"solady/utils/LibString.sol\";\nimport {DateTimeUtils} from \"../utils/DateTimeUtils.sol\";\n\nenum EnclaveId {\n QE,\n QVE,\n TD_QE\n}\n\n/**\n * @title Solidity Structure representing the EnclaveIdentity JSON\n * @param identityStr Identity string object body\n * @param signature The signature to be passed as bytes array\n */\nstruct EnclaveIdentityJsonObj {\n string identityStr;\n bytes signature;\n}\n\n/// @dev Parsed IdentityStr to an object, except for TCBLevels\nstruct IdentityObj {\n EnclaveId id;\n uint32 version;\n uint64 issueDateTimestamp; // UNIX Epoch Timestamp in seconds\n uint64 nextUpdateTimestamp; // UNIX Epoch Timestamp in seconds\n uint32 tcbEvaluationDataNumber;\n bytes4 miscselect;\n bytes4 miscselectMask;\n bytes16 attributes;\n bytes16 attributesMask;\n bytes32 mrsigner;\n uint16 isvprodid;\n Tcb[] tcb;\n}\n\nenum EnclaveIdTcbStatus {\n SGX_ENCLAVE_REPORT_ISVSVN_NOT_SUPPORTED,\n OK,\n SGX_ENCLAVE_REPORT_ISVSVN_REVOKED,\n SGX_ENCLAVE_REPORT_ISVSVN_OUT_OF_DATE\n}\n\nstruct Tcb {\n uint16 isvsvn;\n uint256 dateTimestamp;\n EnclaveIdTcbStatus status;\n}\n\n/**\n * @title Enclave Identity Helper Contract\n * @notice This is a standalone contract that can be used by off-chain applications and smart contracts\n * to parse QEIdentity data\n */\ncontract EnclaveIdentityHelper {\n using JSONParserLib for JSONParserLib.Item;\n using LibString for string;\n\n error Invalid_ID();\n\n function parseIdentityString(string calldata identityStr) external pure returns (IdentityObj memory identity) {\n identity = _parseIdentity(identityStr);\n }\n\n function _parseIdentity(string calldata identityStr) private pure returns (IdentityObj memory identity) {\n JSONParserLib.Item memory root = JSONParserLib.parse(identityStr);\n JSONParserLib.Item[] memory identityObj = root.children();\n\n for (uint256 i = 0; i < root.size(); i++) {\n JSONParserLib.Item memory current = identityObj[i];\n string memory decodedKey = JSONParserLib.decodeString(current.key());\n\n if (decodedKey.eq(\"issueDate\")) {\n identity.issueDateTimestamp =\n uint64(DateTimeUtils.fromISOToTimestamp(JSONParserLib.decodeString(current.value())));\n } else if (decodedKey.eq(\"nextUpdate\")) {\n identity.nextUpdateTimestamp =\n uint64(DateTimeUtils.fromISOToTimestamp(JSONParserLib.decodeString(current.value())));\n } else if (decodedKey.eq(\"id\")) {\n string memory idStr = JSONParserLib.decodeString(current.value());\n if (LibString.eq(idStr, \"QE\")) {\n identity.id = EnclaveId.QE;\n } else if (LibString.eq(idStr, \"QVE\")) {\n identity.id = EnclaveId.QVE;\n } else if (LibString.eq(idStr, \"TD_QE\")) {\n identity.id = EnclaveId.TD_QE;\n } else {\n revert Invalid_ID();\n }\n } else if (decodedKey.eq(\"version\")) {\n identity.version = uint32(JSONParserLib.parseUint(current.value()));\n } else if (decodedKey.eq(\"tcbEvaluationDataNumber\")) {\n identity.tcbEvaluationDataNumber = uint32(JSONParserLib.parseUint(current.value()));\n } else if (decodedKey.eq(\"miscselect\")) {\n uint256 val = JSONParserLib.parseUintFromHex(JSONParserLib.decodeString(current.value()));\n identity.miscselect = bytes4(uint32(val));\n } else if (decodedKey.eq(\"miscselectMask\")) {\n uint256 val = JSONParserLib.parseUintFromHex(JSONParserLib.decodeString(current.value()));\n identity.miscselectMask = bytes4(uint32(val));\n } else if (decodedKey.eq(\"attributes\")) {\n uint256 val = JSONParserLib.parseUintFromHex(JSONParserLib.decodeString(current.value()));\n identity.attributes = bytes16(uint128(val));\n } else if (decodedKey.eq(\"attributesMask\")) {\n uint256 val = JSONParserLib.parseUintFromHex(JSONParserLib.decodeString(current.value()));\n identity.attributesMask = bytes16(uint128(val));\n } else if (decodedKey.eq(\"mrsigner\")) {\n uint256 val = JSONParserLib.parseUintFromHex(JSONParserLib.decodeString(current.value()));\n identity.mrsigner = bytes32(val);\n } else if (decodedKey.eq(\"isvprodid\")) {\n identity.isvprodid = uint16(JSONParserLib.parseUint(current.value()));\n } else if (decodedKey.eq(\"tcbLevels\")) {\n identity.tcb = _parseTcb(current.value());\n }\n }\n }\n\n function _parseTcb(string memory tcbLevelsStr) internal pure returns (Tcb[] memory tcb) {\n JSONParserLib.Item memory tcbLevelsParent = JSONParserLib.parse(tcbLevelsStr);\n JSONParserLib.Item[] memory tcbLevels = tcbLevelsParent.children();\n uint256 tcbLevelsSize = tcbLevelsParent.size();\n tcb = new Tcb[](tcbLevelsSize);\n for (uint256 i = 0; i < tcbLevelsSize; i++) {\n uint256 tcbLevelsChildSize = tcbLevels[i].size();\n JSONParserLib.Item[] memory tcbObj = tcbLevels[i].children();\n for (uint256 j = 0; j < tcbLevelsChildSize; j++) {\n string memory tcbKey = JSONParserLib.decodeString(tcbObj[j].key());\n if (tcbKey.eq(\"tcb\")) {\n JSONParserLib.Item[] memory tcbChild = tcbObj[j].children();\n string memory childKey = JSONParserLib.decodeString(tcbChild[0].key());\n if (childKey.eq(\"isvsvn\")) {\n tcb[i].isvsvn = uint16(JSONParserLib.parseUint(tcbChild[0].value()));\n }\n } else if (tcbKey.eq(\"tcbDate\")) {\n tcb[i].dateTimestamp =\n DateTimeUtils.fromISOToTimestamp(JSONParserLib.decodeString(tcbObj[j].value()));\n } else if (tcbKey.eq(\"tcbStatus\")) {\n string memory decodedValue = JSONParserLib.decodeString(tcbObj[j].value());\n if (decodedValue.eq(\"UpToDate\")) {\n tcb[i].status = EnclaveIdTcbStatus.OK;\n } else if (decodedValue.eq(\"Revoked\")) {\n tcb[i].status = EnclaveIdTcbStatus.SGX_ENCLAVE_REPORT_ISVSVN_REVOKED;\n } else if (decodedValue.eq(\"OutOfDate\")) {\n tcb[i].status = EnclaveIdTcbStatus.SGX_ENCLAVE_REPORT_ISVSVN_OUT_OF_DATE;\n }\n }\n }\n }\n }\n}\n"},"lib/solady/src/utils/JSONParserLib.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\n/// @notice Library for parsing JSONs.\n/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/JSONParserLib.sol)\nlibrary JSONParserLib {\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* CUSTOM ERRORS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev The input is invalid.\n error ParsingFailed();\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* CONSTANTS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n // There are 6 types of variables in JSON (excluding undefined).\n\n /// @dev For denoting that an item has not been initialized.\n /// A item returned from `parse` will never be of an undefined type.\n /// Parsing a invalid JSON string will simply revert.\n uint8 internal constant TYPE_UNDEFINED = 0;\n\n /// @dev Type representing an array (e.g. `[1,2,3]`).\n uint8 internal constant TYPE_ARRAY = 1;\n\n /// @dev Type representing an object (e.g. `{\"a\":\"A\",\"b\":\"B\"}`).\n uint8 internal constant TYPE_OBJECT = 2;\n\n /// @dev Type representing a number (e.g. `-1.23e+21`).\n uint8 internal constant TYPE_NUMBER = 3;\n\n /// @dev Type representing a string (e.g. `\"hello\"`).\n uint8 internal constant TYPE_STRING = 4;\n\n /// @dev Type representing a boolean (i.e. `true` or `false`).\n uint8 internal constant TYPE_BOOLEAN = 5;\n\n /// @dev Type representing null (i.e. `null`).\n uint8 internal constant TYPE_NULL = 6;\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* STRUCTS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev A pointer to a parsed JSON node.\n struct Item {\n // Do NOT modify the `_data` directly.\n uint256 _data;\n }\n\n // Private constants for packing `_data`.\n\n uint256 private constant _BITPOS_STRING = 32 * 7 - 8;\n uint256 private constant _BITPOS_KEY_LENGTH = 32 * 6 - 8;\n uint256 private constant _BITPOS_KEY = 32 * 5 - 8;\n uint256 private constant _BITPOS_VALUE_LENGTH = 32 * 4 - 8;\n uint256 private constant _BITPOS_VALUE = 32 * 3 - 8;\n uint256 private constant _BITPOS_CHILD = 32 * 2 - 8;\n uint256 private constant _BITPOS_SIBLING_OR_PARENT = 32 * 1 - 8;\n uint256 private constant _BITMASK_POINTER = 0xffffffff;\n uint256 private constant _BITMASK_TYPE = 7;\n uint256 private constant _KEY_INITED = 1 << 3;\n uint256 private constant _VALUE_INITED = 1 << 4;\n uint256 private constant _CHILDREN_INITED = 1 << 5;\n uint256 private constant _PARENT_IS_ARRAY = 1 << 6;\n uint256 private constant _PARENT_IS_OBJECT = 1 << 7;\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* JSON PARSING OPERATION */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev Parses the JSON string `s`, and returns the root.\n /// Reverts if `s` is not a valid JSON as specified in RFC 8259.\n /// Object items WILL simply contain all their children, inclusive of repeated keys,\n /// in the same order which they appear in the JSON string.\n ///\n /// Note: For efficiency, this function WILL NOT make a copy of `s`.\n /// The parsed tree WILL contain offsets to `s`.\n /// Do NOT pass in a string that WILL be modified later on.\n function parse(string memory s) internal pure returns (Item memory result) {\n /// @solidity memory-safe-assembly\n assembly {\n mstore(0x40, result) // We will use our own allocation instead.\n }\n bytes32 r = _query(_toInput(s), 255);\n /// @solidity memory-safe-assembly\n assembly {\n result := r\n }\n }\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* JSON ITEM OPERATIONS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n // Note:\n // - An item is a node in the JSON tree.\n // - The value of a string item WILL be double-quoted, JSON encoded.\n // - We make a distinction between `index` and `key`.\n // - Items in arrays are located by `index` (uint256).\n // - Items in objects are located by `key` (string).\n // - Keys are always strings, double-quoted, JSON encoded.\n //\n // These design choices are made to balance between efficiency and ease-of-use.\n\n /// @dev Returns the string value of the item.\n /// This is its exact string representation in the original JSON string.\n /// The returned string WILL have leading and trailing whitespace trimmed.\n /// All inner whitespace WILL be preserved, exactly as it is in the original JSON string.\n /// If the item's type is string, the returned string WILL be double-quoted, JSON encoded.\n ///\n /// Note: This function lazily instantiates and caches the returned string.\n /// Do NOT modify the returned string.\n function value(Item memory item) internal pure returns (string memory result) {\n bytes32 r = _query(_toInput(item), 0);\n /// @solidity memory-safe-assembly\n assembly {\n result := r\n }\n }\n\n /// @dev Returns the index of the item in the array.\n /// It the item's parent is not an array, returns 0.\n function index(Item memory item) internal pure returns (uint256 result) {\n /// @solidity memory-safe-assembly\n assembly {\n if and(mload(item), _PARENT_IS_ARRAY) {\n result := and(_BITMASK_POINTER, shr(_BITPOS_KEY, mload(item)))\n }\n }\n }\n\n /// @dev Returns the key of the item in the object.\n /// It the item's parent is not an object, returns an empty string.\n /// The returned string WILL be double-quoted, JSON encoded.\n ///\n /// Note: This function lazily instantiates and caches the returned string.\n /// Do NOT modify the returned string.\n function key(Item memory item) internal pure returns (string memory result) {\n if (item._data & _PARENT_IS_OBJECT != 0) {\n bytes32 r = _query(_toInput(item), 1);\n /// @solidity memory-safe-assembly\n assembly {\n result := r\n }\n }\n }\n\n /// @dev Returns the key of the item in the object.\n /// It the item is neither an array nor object, returns an empty array.\n ///\n /// Note: This function lazily instantiates and caches the returned array.\n /// Do NOT modify the returned array.\n function children(Item memory item) internal pure returns (Item[] memory result) {\n bytes32 r = _query(_toInput(item), 3);\n /// @solidity memory-safe-assembly\n assembly {\n result := r\n }\n }\n\n /// @dev Returns the number of children.\n /// It the item is neither an array nor object, returns zero.\n function size(Item memory item) internal pure returns (uint256 result) {\n bytes32 r = _query(_toInput(item), 3);\n /// @solidity memory-safe-assembly\n assembly {\n result := mload(r)\n }\n }\n\n /// @dev Returns the item at index `i` for (array).\n /// If `item` is not an array, the result's type WILL be undefined.\n /// If there is no item with the index, the result's type WILL be undefined.\n function at(Item memory item, uint256 i) internal pure returns (Item memory result) {\n /// @solidity memory-safe-assembly\n assembly {\n mstore(0x40, result) // Free the default allocation. We'll allocate manually.\n }\n bytes32 r = _query(_toInput(item), 3);\n /// @solidity memory-safe-assembly\n assembly {\n result := mload(add(add(r, 0x20), shl(5, i)))\n if iszero(and(lt(i, mload(r)), eq(and(mload(item), _BITMASK_TYPE), TYPE_ARRAY))) {\n result := 0x60 // Reset to the zero pointer.\n }\n }\n }\n\n /// @dev Returns the item at key `k` for (object).\n /// If `item` is not an object, the result's type WILL be undefined.\n /// The key MUST be double-quoted, JSON encoded. This is for efficiency reasons.\n /// - Correct : `item.at('\"k\"')`.\n /// - Wrong : `item.at(\"k\")`.\n /// For duplicated keys, the last item with the key WILL be returned.\n /// If there is no item with the key, the result's type WILL be undefined.\n function at(Item memory item, string memory k) internal pure returns (Item memory result) {\n /// @solidity memory-safe-assembly\n assembly {\n mstore(0x40, result) // Free the default allocation. We'll allocate manually.\n result := 0x60 // Initialize to the zero pointer.\n }\n if (isObject(item)) {\n bytes32 kHash = keccak256(bytes(k));\n Item[] memory r = children(item);\n // We'll just do a linear search. The alternatives are very bloated.\n for (uint256 i = r.length << 5; i != 0;) {\n /// @solidity memory-safe-assembly\n assembly {\n item := mload(add(r, i))\n i := sub(i, 0x20)\n }\n if (keccak256(bytes(key(item))) != kHash) continue;\n result = item;\n break;\n }\n }\n }\n\n /// @dev Returns the item's type.\n function getType(Item memory item) internal pure returns (uint8 result) {\n result = uint8(item._data & _BITMASK_TYPE);\n }\n\n /// Note: All types are mutually exclusive.\n\n /// @dev Returns whether the item is of type undefined.\n function isUndefined(Item memory item) internal pure returns (bool result) {\n result = item._data & _BITMASK_TYPE == TYPE_UNDEFINED;\n }\n\n /// @dev Returns whether the item is of type array.\n function isArray(Item memory item) internal pure returns (bool result) {\n result = item._data & _BITMASK_TYPE == TYPE_ARRAY;\n }\n\n /// @dev Returns whether the item is of type object.\n function isObject(Item memory item) internal pure returns (bool result) {\n result = item._data & _BITMASK_TYPE == TYPE_OBJECT;\n }\n\n /// @dev Returns whether the item is of type number.\n function isNumber(Item memory item) internal pure returns (bool result) {\n result = item._data & _BITMASK_TYPE == TYPE_NUMBER;\n }\n\n /// @dev Returns whether the item is of type string.\n function isString(Item memory item) internal pure returns (bool result) {\n result = item._data & _BITMASK_TYPE == TYPE_STRING;\n }\n\n /// @dev Returns whether the item is of type boolean.\n function isBoolean(Item memory item) internal pure returns (bool result) {\n result = item._data & _BITMASK_TYPE == TYPE_BOOLEAN;\n }\n\n /// @dev Returns whether the item is of type null.\n function isNull(Item memory item) internal pure returns (bool result) {\n result = item._data & _BITMASK_TYPE == TYPE_NULL;\n }\n\n /// @dev Returns the item's parent.\n /// If the item does not have a parent, the result's type will be undefined.\n function parent(Item memory item) internal pure returns (Item memory result) {\n /// @solidity memory-safe-assembly\n assembly {\n mstore(0x40, result) // Free the default allocation. We've already allocated.\n result := and(shr(_BITPOS_SIBLING_OR_PARENT, mload(item)), _BITMASK_POINTER)\n if iszero(result) { result := 0x60 } // Reset to the zero pointer.\n }\n }\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* UTILITY FUNCTIONS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev Parses an unsigned integer from a string (in decimal, i.e. base 10).\n /// Reverts if `s` is not a valid uint256 string matching the RegEx `^[0-9]+$`,\n /// or if the parsed number is too big for a uint256.\n function parseUint(string memory s) internal pure returns (uint256 result) {\n /// @solidity memory-safe-assembly\n assembly {\n let n := mload(s)\n let preMulOverflowThres := div(not(0), 10)\n for { let i := 0 } 1 {} {\n i := add(i, 1)\n let digit := sub(and(mload(add(s, i)), 0xff), 48)\n let mulOverflowed := gt(result, preMulOverflowThres)\n let product := mul(10, result)\n result := add(product, digit)\n n := mul(n, iszero(or(or(mulOverflowed, lt(result, product)), gt(digit, 9))))\n if iszero(lt(i, n)) { break }\n }\n if iszero(n) {\n mstore(0x00, 0x10182796) // `ParsingFailed()`.\n revert(0x1c, 0x04)\n }\n }\n }\n\n /// @dev Parses a signed integer from a string (in decimal, i.e. base 10).\n /// Reverts if `s` is not a valid int256 string matching the RegEx `^[+-]?[0-9]+$`,\n /// or if the parsed number is too big for a int256.\n function parseInt(string memory s) internal pure returns (int256 result) {\n uint256 n = bytes(s).length;\n uint256 sign;\n uint256 isNegative;\n /// @solidity memory-safe-assembly\n assembly {\n if n {\n let c := and(mload(add(s, 1)), 0xff)\n isNegative := eq(c, 45)\n if or(eq(c, 43), isNegative) {\n sign := c\n s := add(s, 1)\n mstore(s, sub(n, 1))\n }\n if iszero(or(sign, lt(sub(c, 48), 10))) { s := 0x60 }\n }\n }\n uint256 x = parseUint(s);\n /// @solidity memory-safe-assembly\n assembly {\n if shr(255, x) {\n mstore(0x00, 0x10182796) // `ParsingFailed()`.\n revert(0x1c, 0x04)\n }\n if sign {\n mstore(s, sign)\n s := sub(s, 1)\n mstore(s, n)\n }\n result := xor(x, mul(xor(x, add(not(x), 1)), isNegative))\n }\n }\n\n /// @dev Parses an unsigned integer from a string (in hexadecimal, i.e. base 16).\n /// Reverts if `s` is not a valid uint256 hex string matching the RegEx\n /// `^(0[xX])?[0-9a-fA-F]+$`, or if the parsed number is too big for a uint256.\n function parseUintFromHex(string memory s) internal pure returns (uint256 result) {\n /// @solidity memory-safe-assembly\n assembly {\n let n := mload(s)\n // Skip two if starts with '0x' or '0X'.\n let i := shl(1, and(eq(0x3078, or(shr(240, mload(add(s, 0x20))), 0x20)), gt(n, 1)))\n for {} 1 {} {\n i := add(i, 1)\n let c :=\n byte(\n and(0x1f, shr(and(mload(add(s, i)), 0xff), 0x3e4088843e41bac000000000000)),\n 0x3010a071000000b0104040208000c05090d060e0f\n )\n n := mul(n, iszero(or(iszero(c), shr(252, result))))\n result := add(shl(4, result), sub(c, 1))\n if iszero(lt(i, n)) { break }\n }\n if iszero(n) {\n mstore(0x00, 0x10182796) // `ParsingFailed()`.\n revert(0x1c, 0x04)\n }\n }\n }\n\n /// @dev Decodes a JSON encoded string.\n /// The string MUST be double-quoted, JSON encoded.\n /// Reverts if the string is invalid.\n /// As you can see, it's pretty complex for a deceptively simple looking task.\n function decodeString(string memory s) internal pure returns (string memory result) {\n /// @solidity memory-safe-assembly\n assembly {\n function fail() {\n mstore(0x00, 0x10182796) // `ParsingFailed()`.\n revert(0x1c, 0x04)\n }\n\n function decodeUnicodeEscapeSequence(pIn_, end_) -> _unicode, _pOut {\n _pOut := add(pIn_, 4)\n let b_ := iszero(gt(_pOut, end_))\n let t_ := mload(pIn_) // Load the whole word.\n for { let i_ := 0 } iszero(eq(i_, 4)) { i_ := add(i_, 1) } {\n let c_ := sub(byte(i_, t_), 48)\n if iszero(and(shr(c_, 0x7e0000007e03ff), b_)) { fail() } // Not hexadecimal.\n c_ := sub(c_, add(mul(gt(c_, 16), 7), shl(5, gt(c_, 48))))\n _unicode := add(shl(4, _unicode), c_)\n }\n }\n\n function decodeUnicodeCodePoint(pIn_, end_) -> _unicode, _pOut {\n _unicode, _pOut := decodeUnicodeEscapeSequence(pIn_, end_)\n if iszero(or(lt(_unicode, 0xd800), gt(_unicode, 0xdbff))) {\n let t_ := mload(_pOut) // Load the whole word.\n end_ := mul(end_, eq(shr(240, t_), 0x5c75)) // Fail if not starting with '\\\\u'.\n t_, _pOut := decodeUnicodeEscapeSequence(add(_pOut, 2), end_)\n _unicode := add(0x10000, add(shl(10, and(0x3ff, _unicode)), and(0x3ff, t_)))\n }\n }\n\n function appendCodePointAsUTF8(pIn_, c_) -> _pOut {\n if iszero(gt(c_, 0x7f)) {\n mstore8(pIn_, c_)\n _pOut := add(pIn_, 1)\n leave\n }\n mstore8(0x1f, c_)\n mstore8(0x1e, shr(6, c_))\n if iszero(gt(c_, 0x7ff)) {\n mstore(pIn_, shl(240, or(0xc080, and(0x1f3f, mload(0x00)))))\n _pOut := add(pIn_, 2)\n leave\n }\n mstore8(0x1d, shr(12, c_))\n if iszero(gt(c_, 0xffff)) {\n mstore(pIn_, shl(232, or(0xe08080, and(0x0f3f3f, mload(0x00)))))\n _pOut := add(pIn_, 3)\n leave\n }\n mstore8(0x1c, shr(18, c_))\n mstore(pIn_, shl(224, or(0xf0808080, and(0x073f3f3f, mload(0x00)))))\n _pOut := add(pIn_, shl(2, lt(c_, 0x110000)))\n }\n\n function chr(p_) -> _c {\n _c := byte(0, mload(p_))\n }\n\n let n := mload(s)\n let end := add(add(s, n), 0x1f)\n if iszero(and(gt(n, 1), eq(0x2222, or(and(0xff00, mload(add(s, 2))), chr(end))))) {\n fail() // Fail if not double-quoted.\n }\n let out := add(mload(0x40), 0x20)\n for { let curr := add(s, 0x21) } iszero(eq(curr, end)) {} {\n let c := chr(curr)\n curr := add(curr, 1)\n // Not '\\\\'.\n if iszero(eq(c, 92)) {\n // Not '\"'.\n if iszero(eq(c, 34)) {\n mstore8(out, c)\n out := add(out, 1)\n continue\n }\n curr := end\n }\n if iszero(eq(curr, end)) {\n let escape := chr(curr)\n curr := add(curr, 1)\n // '\"', '/', '\\\\'.\n if and(shr(escape, 0x100000000000800400000000), 1) {\n mstore8(out, escape)\n out := add(out, 1)\n continue\n }\n // 'u'.\n if eq(escape, 117) {\n escape, curr := decodeUnicodeCodePoint(curr, end)\n out := appendCodePointAsUTF8(out, escape)\n continue\n }\n // `{'b':'\\b', 'f':'\\f', 'n':'\\n', 'r':'\\r', 't':'\\t'}`.\n escape := byte(sub(escape, 85), 0x080000000c000000000000000a0000000d0009)\n if escape {\n mstore8(out, escape)\n out := add(out, 1)\n continue\n }\n }\n fail()\n break\n }\n mstore(out, 0) // Zeroize the last slot.\n result := mload(0x40)\n mstore(result, sub(out, add(result, 0x20))) // Store the length.\n mstore(0x40, add(out, 0x20)) // Allocate the memory.\n }\n }\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* PRIVATE HELPERS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev Performs a query on the input with the given mode.\n function _query(bytes32 input, uint256 mode) private pure returns (bytes32 result) {\n /// @solidity memory-safe-assembly\n assembly {\n function fail() {\n mstore(0x00, 0x10182796) // `ParsingFailed()`.\n revert(0x1c, 0x04)\n }\n\n function chr(p_) -> _c {\n _c := byte(0, mload(p_))\n }\n\n function skipWhitespace(pIn_, end_) -> _pOut {\n for { _pOut := pIn_ } 1 { _pOut := add(_pOut, 1) } {\n if iszero(and(shr(chr(_pOut), 0x100002600), 1)) { leave } // Not in ' \\n\\r\\t'.\n }\n }\n\n function setP(packed_, bitpos_, p_) -> _packed {\n // Perform an out-of-gas revert if `p_` exceeds `_BITMASK_POINTER`.\n returndatacopy(returndatasize(), returndatasize(), gt(p_, _BITMASK_POINTER))\n _packed := or(and(not(shl(bitpos_, _BITMASK_POINTER)), packed_), shl(bitpos_, p_))\n }\n\n function getP(packed_, bitpos_) -> _p {\n _p := and(_BITMASK_POINTER, shr(bitpos_, packed_))\n }\n\n function mallocItem(s_, packed_, pStart_, pCurr_, type_) -> _item {\n _item := mload(0x40)\n // forgefmt: disable-next-item\n packed_ := setP(setP(packed_, _BITPOS_VALUE, sub(pStart_, add(s_, 0x20))),\n _BITPOS_VALUE_LENGTH, sub(pCurr_, pStart_))\n mstore(_item, or(packed_, type_))\n mstore(0x40, add(_item, 0x20)) // Allocate memory.\n }\n\n function parseValue(s_, sibling_, pIn_, end_) -> _item, _pOut {\n let packed_ := setP(mload(0x00), _BITPOS_SIBLING_OR_PARENT, sibling_)\n _pOut := skipWhitespace(pIn_, end_)\n if iszero(lt(_pOut, end_)) { leave }\n for { let c_ := chr(_pOut) } 1 {} {\n // If starts with '\"'.\n if eq(c_, 34) {\n let pStart_ := _pOut\n _pOut := parseStringSub(s_, packed_, _pOut, end_)\n _item := mallocItem(s_, packed_, pStart_, _pOut, TYPE_STRING)\n break\n }\n // If starts with '['.\n if eq(c_, 91) {\n _item, _pOut := parseArray(s_, packed_, _pOut, end_)\n break\n }\n // If starts with '{'.\n if eq(c_, 123) {\n _item, _pOut := parseObject(s_, packed_, _pOut, end_)\n break\n }\n // If starts with any in '0123456789-'.\n if and(shr(c_, shl(45, 0x1ff9)), 1) {\n _item, _pOut := parseNumber(s_, packed_, _pOut, end_)\n break\n }\n if iszero(gt(add(_pOut, 4), end_)) {\n let pStart_ := _pOut\n let w_ := shr(224, mload(_pOut))\n // 'true' in hex format.\n if eq(w_, 0x74727565) {\n _pOut := add(_pOut, 4)\n _item := mallocItem(s_, packed_, pStart_, _pOut, TYPE_BOOLEAN)\n break\n }\n // 'null' in hex format.\n if eq(w_, 0x6e756c6c) {\n _pOut := add(_pOut, 4)\n _item := mallocItem(s_, packed_, pStart_, _pOut, TYPE_NULL)\n break\n }\n }\n if iszero(gt(add(_pOut, 5), end_)) {\n let pStart_ := _pOut\n let w_ := shr(216, mload(_pOut))\n // 'false' in hex format.\n if eq(w_, 0x66616c7365) {\n _pOut := add(_pOut, 5)\n _item := mallocItem(s_, packed_, pStart_, _pOut, TYPE_BOOLEAN)\n break\n }\n }\n fail()\n break\n }\n _pOut := skipWhitespace(_pOut, end_)\n }\n\n function parseArray(s_, packed_, pIn_, end_) -> _item, _pOut {\n let j_ := 0\n for { _pOut := add(pIn_, 1) } 1 { _pOut := add(_pOut, 1) } {\n if iszero(lt(_pOut, end_)) { fail() }\n if iszero(_item) {\n _pOut := skipWhitespace(_pOut, end_)\n if eq(chr(_pOut), 93) { break } // ']'.\n }\n _item, _pOut := parseValue(s_, _item, _pOut, end_)\n if _item {\n // forgefmt: disable-next-item\n mstore(_item, setP(or(_PARENT_IS_ARRAY, mload(_item)),\n _BITPOS_KEY, j_))\n j_ := add(j_, 1)\n let c_ := chr(_pOut)\n if eq(c_, 93) { break } // ']'.\n if eq(c_, 44) { continue } // ','.\n }\n _pOut := end_\n }\n _pOut := add(_pOut, 1)\n packed_ := setP(packed_, _BITPOS_CHILD, _item)\n _item := mallocItem(s_, packed_, pIn_, _pOut, TYPE_ARRAY)\n }\n\n function parseObject(s_, packed_, pIn_, end_) -> _item, _pOut {\n for { _pOut := add(pIn_, 1) } 1 { _pOut := add(_pOut, 1) } {\n if iszero(lt(_pOut, end_)) { fail() }\n if iszero(_item) {\n _pOut := skipWhitespace(_pOut, end_)\n if eq(chr(_pOut), 125) { break } // '}'.\n }\n _pOut := skipWhitespace(_pOut, end_)\n let pKeyStart_ := _pOut\n let pKeyEnd_ := parseStringSub(s_, _item, _pOut, end_)\n _pOut := skipWhitespace(pKeyEnd_, end_)\n // If ':'.\n if eq(chr(_pOut), 58) {\n _item, _pOut := parseValue(s_, _item, add(_pOut, 1), end_)\n if _item {\n // forgefmt: disable-next-item\n mstore(_item, setP(setP(or(_PARENT_IS_OBJECT, mload(_item)),\n _BITPOS_KEY_LENGTH, sub(pKeyEnd_, pKeyStart_)),\n _BITPOS_KEY, sub(pKeyStart_, add(s_, 0x20))))\n let c_ := chr(_pOut)\n if eq(c_, 125) { break } // '}'.\n if eq(c_, 44) { continue } // ','.\n }\n }\n _pOut := end_\n }\n _pOut := add(_pOut, 1)\n packed_ := setP(packed_, _BITPOS_CHILD, _item)\n _item := mallocItem(s_, packed_, pIn_, _pOut, TYPE_OBJECT)\n }\n\n function checkStringU(p_, o_) {\n // If not in '0123456789abcdefABCDEF', revert.\n if iszero(and(shr(sub(chr(add(p_, o_)), 48), 0x7e0000007e03ff), 1)) { fail() }\n if iszero(eq(o_, 5)) { checkStringU(p_, add(o_, 1)) }\n }\n\n function parseStringSub(s_, packed_, pIn_, end_) -> _pOut {\n if iszero(lt(pIn_, end_)) { fail() }\n for { _pOut := add(pIn_, 1) } 1 {} {\n let c_ := chr(_pOut)\n if eq(c_, 34) { break } // '\"'.\n // Not '\\'.\n if iszero(eq(c_, 92)) {\n _pOut := add(_pOut, 1)\n continue\n }\n c_ := chr(add(_pOut, 1))\n // '\"', '\\', '//', 'b', 'f', 'n', 'r', 't'.\n if and(shr(sub(c_, 34), 0x510110400000000002001), 1) {\n _pOut := add(_pOut, 2)\n continue\n }\n // 'u'.\n if eq(c_, 117) {\n checkStringU(_pOut, 2)\n _pOut := add(_pOut, 6)\n continue\n }\n _pOut := end_\n break\n }\n if iszero(lt(_pOut, end_)) { fail() }\n _pOut := add(_pOut, 1)\n }\n\n function skip0To9s(pIn_, end_, atLeastOne_) -> _pOut {\n for { _pOut := pIn_ } 1 { _pOut := add(_pOut, 1) } {\n if iszero(lt(sub(chr(_pOut), 48), 10)) { break } // Not '0'..'9'.\n }\n if and(atLeastOne_, eq(pIn_, _pOut)) { fail() }\n }\n\n function parseNumber(s_, packed_, pIn_, end_) -> _item, _pOut {\n _pOut := pIn_\n if eq(chr(_pOut), 45) { _pOut := add(_pOut, 1) } // '-'.\n if iszero(lt(sub(chr(_pOut), 48), 10)) { fail() } // Not '0'..'9'.\n let c_ := chr(_pOut)\n _pOut := add(_pOut, 1)\n if iszero(eq(c_, 48)) { _pOut := skip0To9s(_pOut, end_, 0) } // Not '0'.\n if eq(chr(_pOut), 46) { _pOut := skip0To9s(add(_pOut, 1), end_, 1) } // '.'.\n let t_ := mload(_pOut)\n // 'E', 'e'.\n if eq(or(0x20, byte(0, t_)), 101) {\n // forgefmt: disable-next-item\n _pOut := skip0To9s(add(byte(sub(byte(1, t_), 14), 0x010001), // '+', '-'.\n add(_pOut, 1)), end_, 1)\n }\n _item := mallocItem(s_, packed_, pIn_, _pOut, TYPE_NUMBER)\n }\n\n function copyStr(s_, offset_, len_) -> _sCopy {\n _sCopy := mload(0x40)\n s_ := add(s_, offset_)\n let w_ := not(0x1f)\n for { let i_ := and(add(len_, 0x1f), w_) } 1 {} {\n mstore(add(_sCopy, i_), mload(add(s_, i_)))\n i_ := add(i_, w_) // `sub(i_, 0x20)`.\n if iszero(i_) { break }\n }\n mstore(_sCopy, len_) // Copy the length.\n mstore(add(add(_sCopy, 0x20), len_), 0) // Zeroize the last slot.\n mstore(0x40, add(add(_sCopy, 0x40), len_)) // Allocate memory.\n }\n\n function value(item_) -> _value {\n let packed_ := mload(item_)\n _value := getP(packed_, _BITPOS_VALUE) // The offset in the string.\n if iszero(and(_VALUE_INITED, packed_)) {\n let s_ := getP(packed_, _BITPOS_STRING)\n _value := copyStr(s_, _value, getP(packed_, _BITPOS_VALUE_LENGTH))\n packed_ := setP(packed_, _BITPOS_VALUE, _value)\n mstore(s_, or(_VALUE_INITED, packed_))\n }\n }\n\n function children(item_) -> _arr {\n _arr := 0x60 // Initialize to the zero pointer.\n let packed_ := mload(item_)\n for {} iszero(gt(and(_BITMASK_TYPE, packed_), TYPE_OBJECT)) {} {\n if or(iszero(packed_), iszero(item_)) { break }\n if and(packed_, _CHILDREN_INITED) {\n _arr := getP(packed_, _BITPOS_CHILD)\n break\n }\n _arr := mload(0x40)\n let o_ := add(_arr, 0x20)\n for { let h_ := getP(packed_, _BITPOS_CHILD) } h_ {} {\n mstore(o_, h_)\n let q_ := mload(h_)\n let y_ := getP(q_, _BITPOS_SIBLING_OR_PARENT)\n mstore(h_, setP(q_, _BITPOS_SIBLING_OR_PARENT, item_))\n h_ := y_\n o_ := add(o_, 0x20)\n }\n let w_ := not(0x1f)\n let n_ := add(w_, sub(o_, _arr))\n mstore(_arr, shr(5, n_))\n mstore(0x40, o_) // Allocate memory.\n packed_ := setP(packed_, _BITPOS_CHILD, _arr)\n mstore(item_, or(_CHILDREN_INITED, packed_))\n // Reverse the array.\n if iszero(lt(n_, 0x40)) {\n let lo_ := add(_arr, 0x20)\n let hi_ := add(_arr, n_)\n for {} 1 {} {\n let temp_ := mload(lo_)\n mstore(lo_, mload(hi_))\n mstore(hi_, temp_)\n hi_ := add(hi_, w_)\n lo_ := add(lo_, 0x20)\n if iszero(lt(lo_, hi_)) { break }\n }\n }\n break\n }\n }\n\n function getStr(item_, bitpos_, bitposLength_, bitmaskInited_) -> _result {\n _result := 0x60 // Initialize to the zero pointer.\n let packed_ := mload(item_)\n if or(iszero(item_), iszero(packed_)) { leave }\n _result := getP(packed_, bitpos_)\n if iszero(and(bitmaskInited_, packed_)) {\n let s_ := getP(packed_, _BITPOS_STRING)\n _result := copyStr(s_, _result, getP(packed_, bitposLength_))\n mstore(item_, or(bitmaskInited_, setP(packed_, bitpos_, _result)))\n }\n }\n\n switch mode\n // Get value.\n case 0 { result := getStr(input, _BITPOS_VALUE, _BITPOS_VALUE_LENGTH, _VALUE_INITED) }\n // Get key.\n case 1 { result := getStr(input, _BITPOS_KEY, _BITPOS_KEY_LENGTH, _KEY_INITED) }\n // Get children.\n case 3 { result := children(input) }\n // Parse.\n default {\n let p := add(input, 0x20)\n let e := add(p, mload(input))\n if iszero(eq(p, e)) {\n let c := chr(e)\n mstore8(e, 34) // Place a '\"' at the end to speed up parsing.\n // The `34 << 248` makes `mallocItem` preserve '\"' at the end.\n mstore(0x00, setP(shl(248, 34), _BITPOS_STRING, input))\n result, p := parseValue(input, 0, p, e)\n mstore8(e, c) // Restore the original char at the end.\n }\n if or(lt(p, e), iszero(result)) { fail() }\n }\n }\n }\n\n /// @dev Casts the input to a bytes32.\n function _toInput(string memory input) private pure returns (bytes32 result) {\n /// @solidity memory-safe-assembly\n assembly {\n result := input\n }\n }\n\n /// @dev Casts the input to a bytes32.\n function _toInput(Item memory input) private pure returns (bytes32 result) {\n /// @solidity memory-safe-assembly\n assembly {\n result := input\n }\n }\n}\n"},"lib/solady/src/utils/LibString.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\n/// @notice Library for converting numbers into strings and other string operations.\n/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibString.sol)\n/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/LibString.sol)\n///\n/// Note:\n/// For performance and bytecode compactness, most of the string operations are restricted to\n/// byte strings (7-bit ASCII), except where otherwise specified.\n/// Usage of byte string operations on charsets with runes spanning two or more bytes\n/// can lead to undefined behavior.\nlibrary LibString {\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* CUSTOM ERRORS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev The length of the output is too small to contain all the hex digits.\n error HexLengthInsufficient();\n\n /// @dev The length of the string is more than 32 bytes.\n error TooBigForSmallString();\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* CONSTANTS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev The constant returned when the `search` is not found in the string.\n uint256 internal constant NOT_FOUND = type(uint256).max;\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* DECIMAL OPERATIONS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev Returns the base 10 decimal representation of `value`.\n function toString(uint256 value) internal pure returns (string memory str) {\n /// @solidity memory-safe-assembly\n assembly {\n // The maximum value of a uint256 contains 78 digits (1 byte per digit), but\n // we allocate 0xa0 bytes to keep the free memory pointer 32-byte word aligned.\n // We will need 1 word for the trailing zeros padding, 1 word for the length,\n // and 3 words for a maximum of 78 digits.\n str := add(mload(0x40), 0x80)\n // Update the free memory pointer to allocate.\n mstore(0x40, add(str, 0x20))\n // Zeroize the slot after the string.\n mstore(str, 0)\n\n // Cache the end of the memory to calculate the length later.\n let end := str\n\n let w := not(0) // Tsk.\n // We write the string from rightmost digit to leftmost digit.\n // The following is essentially a do-while loop that also handles the zero case.\n for { let temp := value } 1 {} {\n str := add(str, w) // `sub(str, 1)`.\n // Write the character to the pointer.\n // The ASCII index of the '0' character is 48.\n mstore8(str, add(48, mod(temp, 10)))\n // Keep dividing `temp` until zero.\n temp := div(temp, 10)\n if iszero(temp) { break }\n }\n\n let length := sub(end, str)\n // Move the pointer 32 bytes leftwards to make room for the length.\n str := sub(str, 0x20)\n // Store the length.\n mstore(str, length)\n }\n }\n\n /// @dev Returns the base 10 decimal representation of `value`.\n function toString(int256 value) internal pure returns (string memory str) {\n if (value >= 0) {\n return toString(uint256(value));\n }\n unchecked {\n str = toString(uint256(-value));\n }\n /// @solidity memory-safe-assembly\n assembly {\n // We still have some spare memory space on the left,\n // as we have allocated 3 words (96 bytes) for up to 78 digits.\n let length := mload(str) // Load the string length.\n mstore(str, 0x2d) // Store the '-' character.\n str := sub(str, 1) // Move back the string pointer by a byte.\n mstore(str, add(length, 1)) // Update the string length.\n }\n }\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* HEXADECIMAL OPERATIONS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev Returns the hexadecimal representation of `value`,\n /// left-padded to an input length of `length` bytes.\n /// The output is prefixed with \"0x\" encoded using 2 hexadecimal digits per byte,\n /// giving a total length of `length * 2 + 2` bytes.\n /// Reverts if `length` is too small for the output to contain all the digits.\n function toHexString(uint256 value, uint256 length) internal pure returns (string memory str) {\n str = toHexStringNoPrefix(value, length);\n /// @solidity memory-safe-assembly\n assembly {\n let strLength := add(mload(str), 2) // Compute the length.\n mstore(str, 0x3078) // Write the \"0x\" prefix.\n str := sub(str, 2) // Move the pointer.\n mstore(str, strLength) // Write the length.\n }\n }\n\n /// @dev Returns the hexadecimal representation of `value`,\n /// left-padded to an input length of `length` bytes.\n /// The output is prefixed with \"0x\" encoded using 2 hexadecimal digits per byte,\n /// giving a total length of `length * 2` bytes.\n /// Reverts if `length` is too small for the output to contain all the digits.\n function toHexStringNoPrefix(uint256 value, uint256 length)\n internal\n pure\n returns (string memory str)\n {\n /// @solidity memory-safe-assembly\n assembly {\n // We need 0x20 bytes for the trailing zeros padding, `length * 2` bytes\n // for the digits, 0x02 bytes for the prefix, and 0x20 bytes for the length.\n // We add 0x20 to the total and round down to a multiple of 0x20.\n // (0x20 + 0x20 + 0x02 + 0x20) = 0x62.\n str := add(mload(0x40), and(add(shl(1, length), 0x42), not(0x1f)))\n // Allocate the memory.\n mstore(0x40, add(str, 0x20))\n // Zeroize the slot after the string.\n mstore(str, 0)\n\n // Cache the end to calculate the length later.\n let end := str\n // Store \"0123456789abcdef\" in scratch space.\n mstore(0x0f, 0x30313233343536373839616263646566)\n\n let start := sub(str, add(length, length))\n let w := not(1) // Tsk.\n let temp := value\n // We write the string from rightmost digit to leftmost digit.\n // The following is essentially a do-while loop that also handles the zero case.\n for {} 1 {} {\n str := add(str, w) // `sub(str, 2)`.\n mstore8(add(str, 1), mload(and(temp, 15)))\n mstore8(str, mload(and(shr(4, temp), 15)))\n temp := shr(8, temp)\n if iszero(xor(str, start)) { break }\n }\n\n if temp {\n mstore(0x00, 0x2194895a) // `HexLengthInsufficient()`.\n revert(0x1c, 0x04)\n }\n\n // Compute the string's length.\n let strLength := sub(end, str)\n // Move the pointer and write the length.\n str := sub(str, 0x20)\n mstore(str, strLength)\n }\n }\n\n /// @dev Returns the hexadecimal representation of `value`.\n /// The output is prefixed with \"0x\" and encoded using 2 hexadecimal digits per byte.\n /// As address are 20 bytes long, the output will left-padded to have\n /// a length of `20 * 2 + 2` bytes.\n function toHexString(uint256 value) internal pure returns (string memory str) {\n str = toHexStringNoPrefix(value);\n /// @solidity memory-safe-assembly\n assembly {\n let strLength := add(mload(str), 2) // Compute the length.\n mstore(str, 0x3078) // Write the \"0x\" prefix.\n str := sub(str, 2) // Move the pointer.\n mstore(str, strLength) // Write the length.\n }\n }\n\n /// @dev Returns the hexadecimal representation of `value`.\n /// The output is prefixed with \"0x\".\n /// The output excludes leading \"0\" from the `toHexString` output.\n /// `0x00: \"0x0\", 0x01: \"0x1\", 0x12: \"0x12\", 0x123: \"0x123\"`.\n function toMinimalHexString(uint256 value) internal pure returns (string memory str) {\n str = toHexStringNoPrefix(value);\n /// @solidity memory-safe-assembly\n assembly {\n let o := eq(byte(0, mload(add(str, 0x20))), 0x30) // Whether leading zero is present.\n let strLength := add(mload(str), 2) // Compute the length.\n mstore(add(str, o), 0x3078) // Write the \"0x\" prefix, accounting for leading zero.\n str := sub(add(str, o), 2) // Move the pointer, accounting for leading zero.\n mstore(str, sub(strLength, o)) // Write the length, accounting for leading zero.\n }\n }\n\n /// @dev Returns the hexadecimal representation of `value`.\n /// The output excludes leading \"0\" from the `toHexStringNoPrefix` output.\n /// `0x00: \"0\", 0x01: \"1\", 0x12: \"12\", 0x123: \"123\"`.\n function toMinimalHexStringNoPrefix(uint256 value) internal pure returns (string memory str) {\n str = toHexStringNoPrefix(value);\n /// @solidity memory-safe-assembly\n assembly {\n let o := eq(byte(0, mload(add(str, 0x20))), 0x30) // Whether leading zero is present.\n let strLength := mload(str) // Get the length.\n str := add(str, o) // Move the pointer, accounting for leading zero.\n mstore(str, sub(strLength, o)) // Write the length, accounting for leading zero.\n }\n }\n\n /// @dev Returns the hexadecimal representation of `value`.\n /// The output is encoded using 2 hexadecimal digits per byte.\n /// As address are 20 bytes long, the output will left-padded to have\n /// a length of `20 * 2` bytes.\n function toHexStringNoPrefix(uint256 value) internal pure returns (string memory str) {\n /// @solidity memory-safe-assembly\n assembly {\n // We need 0x20 bytes for the trailing zeros padding, 0x20 bytes for the length,\n // 0x02 bytes for the prefix, and 0x40 bytes for the digits.\n // The next multiple of 0x20 above (0x20 + 0x20 + 0x02 + 0x40) is 0xa0.\n str := add(mload(0x40), 0x80)\n // Allocate the memory.\n mstore(0x40, add(str, 0x20))\n // Zeroize the slot after the string.\n mstore(str, 0)\n\n // Cache the end to calculate the length later.\n let end := str\n // Store \"0123456789abcdef\" in scratch space.\n mstore(0x0f, 0x30313233343536373839616263646566)\n\n let w := not(1) // Tsk.\n // We write the string from rightmost digit to leftmost digit.\n // The following is essentially a do-while loop that also handles the zero case.\n for { let temp := value } 1 {} {\n str := add(str, w) // `sub(str, 2)`.\n mstore8(add(str, 1), mload(and(temp, 15)))\n mstore8(str, mload(and(shr(4, temp), 15)))\n temp := shr(8, temp)\n if iszero(temp) { break }\n }\n\n // Compute the string's length.\n let strLength := sub(end, str)\n // Move the pointer and write the length.\n str := sub(str, 0x20)\n mstore(str, strLength)\n }\n }\n\n /// @dev Returns the hexadecimal representation of `value`.\n /// The output is prefixed with \"0x\", encoded using 2 hexadecimal digits per byte,\n /// and the alphabets are capitalized conditionally according to\n /// https://eips.ethereum.org/EIPS/eip-55\n function toHexStringChecksummed(address value) internal pure returns (string memory str) {\n str = toHexString(value);\n /// @solidity memory-safe-assembly\n assembly {\n let mask := shl(6, div(not(0), 255)) // `0b010000000100000000 ...`\n let o := add(str, 0x22)\n let hashed := and(keccak256(o, 40), mul(34, mask)) // `0b10001000 ... `\n let t := shl(240, 136) // `0b10001000 << 240`\n for { let i := 0 } 1 {} {\n mstore(add(i, i), mul(t, byte(i, hashed)))\n i := add(i, 1)\n if eq(i, 20) { break }\n }\n mstore(o, xor(mload(o), shr(1, and(mload(0x00), and(mload(o), mask)))))\n o := add(o, 0x20)\n mstore(o, xor(mload(o), shr(1, and(mload(0x20), and(mload(o), mask)))))\n }\n }\n\n /// @dev Returns the hexadecimal representation of `value`.\n /// The output is prefixed with \"0x\" and encoded using 2 hexadecimal digits per byte.\n function toHexString(address value) internal pure returns (string memory str) {\n str = toHexStringNoPrefix(value);\n /// @solidity memory-safe-assembly\n assembly {\n let strLength := add(mload(str), 2) // Compute the length.\n mstore(str, 0x3078) // Write the \"0x\" prefix.\n str := sub(str, 2) // Move the pointer.\n mstore(str, strLength) // Write the length.\n }\n }\n\n /// @dev Returns the hexadecimal representation of `value`.\n /// The output is encoded using 2 hexadecimal digits per byte.\n function toHexStringNoPrefix(address value) internal pure returns (string memory str) {\n /// @solidity memory-safe-assembly\n assembly {\n str := mload(0x40)\n\n // Allocate the memory.\n // We need 0x20 bytes for the trailing zeros padding, 0x20 bytes for the length,\n // 0x02 bytes for the prefix, and 0x28 bytes for the digits.\n // The next multiple of 0x20 above (0x20 + 0x20 + 0x02 + 0x28) is 0x80.\n mstore(0x40, add(str, 0x80))\n\n // Store \"0123456789abcdef\" in scratch space.\n mstore(0x0f, 0x30313233343536373839616263646566)\n\n str := add(str, 2)\n mstore(str, 40)\n\n let o := add(str, 0x20)\n mstore(add(o, 40), 0)\n\n value := shl(96, value)\n\n // We write the string from rightmost digit to leftmost digit.\n // The following is essentially a do-while loop that also handles the zero case.\n for { let i := 0 } 1 {} {\n let p := add(o, add(i, i))\n let temp := byte(i, value)\n mstore8(add(p, 1), mload(and(temp, 15)))\n mstore8(p, mload(shr(4, temp)))\n i := add(i, 1)\n if eq(i, 20) { break }\n }\n }\n }\n\n /// @dev Returns the hex encoded string from the raw bytes.\n /// The output is encoded using 2 hexadecimal digits per byte.\n function toHexString(bytes memory raw) internal pure returns (string memory str) {\n str = toHexStringNoPrefix(raw);\n /// @solidity memory-safe-assembly\n assembly {\n let strLength := add(mload(str), 2) // Compute the length.\n mstore(str, 0x3078) // Write the \"0x\" prefix.\n str := sub(str, 2) // Move the pointer.\n mstore(str, strLength) // Write the length.\n }\n }\n\n /// @dev Returns the hex encoded string from the raw bytes.\n /// The output is encoded using 2 hexadecimal digits per byte.\n function toHexStringNoPrefix(bytes memory raw) internal pure returns (string memory str) {\n /// @solidity memory-safe-assembly\n assembly {\n let length := mload(raw)\n str := add(mload(0x40), 2) // Skip 2 bytes for the optional prefix.\n mstore(str, add(length, length)) // Store the length of the output.\n\n // Store \"0123456789abcdef\" in scratch space.\n mstore(0x0f, 0x30313233343536373839616263646566)\n\n let o := add(str, 0x20)\n let end := add(raw, length)\n\n for {} iszero(eq(raw, end)) {} {\n raw := add(raw, 1)\n mstore8(add(o, 1), mload(and(mload(raw), 15)))\n mstore8(o, mload(and(shr(4, mload(raw)), 15)))\n o := add(o, 2)\n }\n mstore(o, 0) // Zeroize the slot after the string.\n mstore(0x40, add(o, 0x20)) // Allocate the memory.\n }\n }\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* RUNE STRING OPERATIONS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev Returns the number of UTF characters in the string.\n function runeCount(string memory s) internal pure returns (uint256 result) {\n /// @solidity memory-safe-assembly\n assembly {\n if mload(s) {\n mstore(0x00, div(not(0), 255))\n mstore(0x20, 0x0202020202020202020202020202020202020202020202020303030304040506)\n let o := add(s, 0x20)\n let end := add(o, mload(s))\n for { result := 1 } 1 { result := add(result, 1) } {\n o := add(o, byte(0, mload(shr(250, mload(o)))))\n if iszero(lt(o, end)) { break }\n }\n }\n }\n }\n\n /// @dev Returns if this string is a 7-bit ASCII string.\n /// (i.e. all characters codes are in [0..127])\n function is7BitASCII(string memory s) internal pure returns (bool result) {\n /// @solidity memory-safe-assembly\n assembly {\n let mask := shl(7, div(not(0), 255))\n result := 1\n let n := mload(s)\n if n {\n let o := add(s, 0x20)\n let end := add(o, n)\n let last := mload(end)\n mstore(end, 0)\n for {} 1 {} {\n if and(mask, mload(o)) {\n result := 0\n break\n }\n o := add(o, 0x20)\n if iszero(lt(o, end)) { break }\n }\n mstore(end, last)\n }\n }\n }\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* BYTE STRING OPERATIONS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n // For performance and bytecode compactness, byte string operations are restricted\n // to 7-bit ASCII strings. All offsets are byte offsets, not UTF character offsets.\n // Usage of byte string operations on charsets with runes spanning two or more bytes\n // can lead to undefined behavior.\n\n /// @dev Returns `subject` all occurrences of `search` replaced with `replacement`.\n function replace(string memory subject, string memory search, string memory replacement)\n internal\n pure\n returns (string memory result)\n {\n /// @solidity memory-safe-assembly\n assembly {\n let subjectLength := mload(subject)\n let searchLength := mload(search)\n let replacementLength := mload(replacement)\n\n subject := add(subject, 0x20)\n search := add(search, 0x20)\n replacement := add(replacement, 0x20)\n result := add(mload(0x40), 0x20)\n\n let subjectEnd := add(subject, subjectLength)\n if iszero(gt(searchLength, subjectLength)) {\n let subjectSearchEnd := add(sub(subjectEnd, searchLength), 1)\n let h := 0\n if iszero(lt(searchLength, 0x20)) { h := keccak256(search, searchLength) }\n let m := shl(3, sub(0x20, and(searchLength, 0x1f)))\n let s := mload(search)\n for {} 1 {} {\n let t := mload(subject)\n // Whether the first `searchLength % 32` bytes of\n // `subject` and `search` matches.\n if iszero(shr(m, xor(t, s))) {\n if h {\n if iszero(eq(keccak256(subject, searchLength), h)) {\n mstore(result, t)\n result := add(result, 1)\n subject := add(subject, 1)\n if iszero(lt(subject, subjectSearchEnd)) { break }\n continue\n }\n }\n // Copy the `replacement` one word at a time.\n for { let o := 0 } 1 {} {\n mstore(add(result, o), mload(add(replacement, o)))\n o := add(o, 0x20)\n if iszero(lt(o, replacementLength)) { break }\n }\n result := add(result, replacementLength)\n subject := add(subject, searchLength)\n if searchLength {\n if iszero(lt(subject, subjectSearchEnd)) { break }\n continue\n }\n }\n mstore(result, t)\n result := add(result, 1)\n subject := add(subject, 1)\n if iszero(lt(subject, subjectSearchEnd)) { break }\n }\n }\n\n let resultRemainder := result\n result := add(mload(0x40), 0x20)\n let k := add(sub(resultRemainder, result), sub(subjectEnd, subject))\n // Copy the rest of the string one word at a time.\n for {} lt(subject, subjectEnd) {} {\n mstore(resultRemainder, mload(subject))\n resultRemainder := add(resultRemainder, 0x20)\n subject := add(subject, 0x20)\n }\n result := sub(result, 0x20)\n let last := add(add(result, 0x20), k) // Zeroize the slot after the string.\n mstore(last, 0)\n mstore(0x40, add(last, 0x20)) // Allocate the memory.\n mstore(result, k) // Store the length.\n }\n }\n\n /// @dev Returns the byte index of the first location of `search` in `subject`,\n /// searching from left to right, starting from `from`.\n /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.\n function indexOf(string memory subject, string memory search, uint256 from)\n internal\n pure\n returns (uint256 result)\n {\n /// @solidity memory-safe-assembly\n assembly {\n for { let subjectLength := mload(subject) } 1 {} {\n if iszero(mload(search)) {\n if iszero(gt(from, subjectLength)) {\n result := from\n break\n }\n result := subjectLength\n break\n }\n let searchLength := mload(search)\n let subjectStart := add(subject, 0x20)\n\n result := not(0) // Initialize to `NOT_FOUND`.\n\n subject := add(subjectStart, from)\n let end := add(sub(add(subjectStart, subjectLength), searchLength), 1)\n\n let m := shl(3, sub(0x20, and(searchLength, 0x1f)))\n let s := mload(add(search, 0x20))\n\n if iszero(and(lt(subject, end), lt(from, subjectLength))) { break }\n\n if iszero(lt(searchLength, 0x20)) {\n for { let h := keccak256(add(search, 0x20), searchLength) } 1 {} {\n if iszero(shr(m, xor(mload(subject), s))) {\n if eq(keccak256(subject, searchLength), h) {\n result := sub(subject, subjectStart)\n break\n }\n }\n subject := add(subject, 1)\n if iszero(lt(subject, end)) { break }\n }\n break\n }\n for {} 1 {} {\n if iszero(shr(m, xor(mload(subject), s))) {\n result := sub(subject, subjectStart)\n break\n }\n subject := add(subject, 1)\n if iszero(lt(subject, end)) { break }\n }\n break\n }\n }\n }\n\n /// @dev Returns the byte index of the first location of `search` in `subject`,\n /// searching from left to right.\n /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.\n function indexOf(string memory subject, string memory search)\n internal\n pure\n returns (uint256 result)\n {\n result = indexOf(subject, search, 0);\n }\n\n /// @dev Returns the byte index of the first location of `search` in `subject`,\n /// searching from right to left, starting from `from`.\n /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.\n function lastIndexOf(string memory subject, string memory search, uint256 from)\n internal\n pure\n returns (uint256 result)\n {\n /// @solidity memory-safe-assembly\n assembly {\n for {} 1 {} {\n result := not(0) // Initialize to `NOT_FOUND`.\n let searchLength := mload(search)\n if gt(searchLength, mload(subject)) { break }\n let w := result\n\n let fromMax := sub(mload(subject), searchLength)\n if iszero(gt(fromMax, from)) { from := fromMax }\n\n let end := add(add(subject, 0x20), w)\n subject := add(add(subject, 0x20), from)\n if iszero(gt(subject, end)) { break }\n // As this function is not too often used,\n // we shall simply use keccak256 for smaller bytecode size.\n for { let h := keccak256(add(search, 0x20), searchLength) } 1 {} {\n if eq(keccak256(subject, searchLength), h) {\n result := sub(subject, add(end, 1))\n break\n }\n subject := add(subject, w) // `sub(subject, 1)`.\n if iszero(gt(subject, end)) { break }\n }\n break\n }\n }\n }\n\n /// @dev Returns the byte index of the first location of `search` in `subject`,\n /// searching from right to left.\n /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.\n function lastIndexOf(string memory subject, string memory search)\n internal\n pure\n returns (uint256 result)\n {\n result = lastIndexOf(subject, search, uint256(int256(-1)));\n }\n\n /// @dev Returns true if `search` is found in `subject`, false otherwise.\n function contains(string memory subject, string memory search) internal pure returns (bool) {\n return indexOf(subject, search) != NOT_FOUND;\n }\n\n /// @dev Returns whether `subject` starts with `search`.\n function startsWith(string memory subject, string memory search)\n internal\n pure\n returns (bool result)\n {\n /// @solidity memory-safe-assembly\n assembly {\n let searchLength := mload(search)\n // Just using keccak256 directly is actually cheaper.\n // forgefmt: disable-next-item\n result := and(\n iszero(gt(searchLength, mload(subject))),\n eq(\n keccak256(add(subject, 0x20), searchLength),\n keccak256(add(search, 0x20), searchLength)\n )\n )\n }\n }\n\n /// @dev Returns whether `subject` ends with `search`.\n function endsWith(string memory subject, string memory search)\n internal\n pure\n returns (bool result)\n {\n /// @solidity memory-safe-assembly\n assembly {\n let searchLength := mload(search)\n let subjectLength := mload(subject)\n // Whether `search` is not longer than `subject`.\n let withinRange := iszero(gt(searchLength, subjectLength))\n // Just using keccak256 directly is actually cheaper.\n // forgefmt: disable-next-item\n result := and(\n withinRange,\n eq(\n keccak256(\n // `subject + 0x20 + max(subjectLength - searchLength, 0)`.\n add(add(subject, 0x20), mul(withinRange, sub(subjectLength, searchLength))),\n searchLength\n ),\n keccak256(add(search, 0x20), searchLength)\n )\n )\n }\n }\n\n /// @dev Returns `subject` repeated `times`.\n function repeat(string memory subject, uint256 times)\n internal\n pure\n returns (string memory result)\n {\n /// @solidity memory-safe-assembly\n assembly {\n let subjectLength := mload(subject)\n if iszero(or(iszero(times), iszero(subjectLength))) {\n subject := add(subject, 0x20)\n result := mload(0x40)\n let output := add(result, 0x20)\n for {} 1 {} {\n // Copy the `subject` one word at a time.\n for { let o := 0 } 1 {} {\n mstore(add(output, o), mload(add(subject, o)))\n o := add(o, 0x20)\n if iszero(lt(o, subjectLength)) { break }\n }\n output := add(output, subjectLength)\n times := sub(times, 1)\n if iszero(times) { break }\n }\n mstore(output, 0) // Zeroize the slot after the string.\n let resultLength := sub(output, add(result, 0x20))\n mstore(result, resultLength) // Store the length.\n // Allocate the memory.\n mstore(0x40, add(result, add(resultLength, 0x20)))\n }\n }\n }\n\n /// @dev Returns a copy of `subject` sliced from `start` to `end` (exclusive).\n /// `start` and `end` are byte offsets.\n function slice(string memory subject, uint256 start, uint256 end)\n internal\n pure\n returns (string memory result)\n {\n /// @solidity memory-safe-assembly\n assembly {\n let subjectLength := mload(subject)\n if iszero(gt(subjectLength, end)) { end := subjectLength }\n if iszero(gt(subjectLength, start)) { start := subjectLength }\n if lt(start, end) {\n result := mload(0x40)\n let resultLength := sub(end, start)\n mstore(result, resultLength)\n subject := add(subject, start)\n let w := not(0x1f)\n // Copy the `subject` one word at a time, backwards.\n for { let o := and(add(resultLength, 0x1f), w) } 1 {} {\n mstore(add(result, o), mload(add(subject, o)))\n o := add(o, w) // `sub(o, 0x20)`.\n if iszero(o) { break }\n }\n // Zeroize the slot after the string.\n mstore(add(add(result, 0x20), resultLength), 0)\n // Allocate memory for the length and the bytes,\n // rounded up to a multiple of 32.\n mstore(0x40, add(result, and(add(resultLength, 0x3f), w)))\n }\n }\n }\n\n /// @dev Returns a copy of `subject` sliced from `start` to the end of the string.\n /// `start` is a byte offset.\n function slice(string memory subject, uint256 start)\n internal\n pure\n returns (string memory result)\n {\n result = slice(subject, start, uint256(int256(-1)));\n }\n\n /// @dev Returns all the indices of `search` in `subject`.\n /// The indices are byte offsets.\n function indicesOf(string memory subject, string memory search)\n internal\n pure\n returns (uint256[] memory result)\n {\n /// @solidity memory-safe-assembly\n assembly {\n let subjectLength := mload(subject)\n let searchLength := mload(search)\n\n if iszero(gt(searchLength, subjectLength)) {\n subject := add(subject, 0x20)\n search := add(search, 0x20)\n result := add(mload(0x40), 0x20)\n\n let subjectStart := subject\n let subjectSearchEnd := add(sub(add(subject, subjectLength), searchLength), 1)\n let h := 0\n if iszero(lt(searchLength, 0x20)) { h := keccak256(search, searchLength) }\n let m := shl(3, sub(0x20, and(searchLength, 0x1f)))\n let s := mload(search)\n for {} 1 {} {\n let t := mload(subject)\n // Whether the first `searchLength % 32` bytes of\n // `subject` and `search` matches.\n if iszero(shr(m, xor(t, s))) {\n if h {\n if iszero(eq(keccak256(subject, searchLength), h)) {\n subject := add(subject, 1)\n if iszero(lt(subject, subjectSearchEnd)) { break }\n continue\n }\n }\n // Append to `result`.\n mstore(result, sub(subject, subjectStart))\n result := add(result, 0x20)\n // Advance `subject` by `searchLength`.\n subject := add(subject, searchLength)\n if searchLength {\n if iszero(lt(subject, subjectSearchEnd)) { break }\n continue\n }\n }\n subject := add(subject, 1)\n if iszero(lt(subject, subjectSearchEnd)) { break }\n }\n let resultEnd := result\n // Assign `result` to the free memory pointer.\n result := mload(0x40)\n // Store the length of `result`.\n mstore(result, shr(5, sub(resultEnd, add(result, 0x20))))\n // Allocate memory for result.\n // We allocate one more word, so this array can be recycled for {split}.\n mstore(0x40, add(resultEnd, 0x20))\n }\n }\n }\n\n /// @dev Returns a arrays of strings based on the `delimiter` inside of the `subject` string.\n function split(string memory subject, string memory delimiter)\n internal\n pure\n returns (string[] memory result)\n {\n uint256[] memory indices = indicesOf(subject, delimiter);\n /// @solidity memory-safe-assembly\n assembly {\n let w := not(0x1f)\n let indexPtr := add(indices, 0x20)\n let indicesEnd := add(indexPtr, shl(5, add(mload(indices), 1)))\n mstore(add(indicesEnd, w), mload(subject))\n mstore(indices, add(mload(indices), 1))\n let prevIndex := 0\n for {} 1 {} {\n let index := mload(indexPtr)\n mstore(indexPtr, 0x60)\n if iszero(eq(index, prevIndex)) {\n let element := mload(0x40)\n let elementLength := sub(index, prevIndex)\n mstore(element, elementLength)\n // Copy the `subject` one word at a time, backwards.\n for { let o := and(add(elementLength, 0x1f), w) } 1 {} {\n mstore(add(element, o), mload(add(add(subject, prevIndex), o)))\n o := add(o, w) // `sub(o, 0x20)`.\n if iszero(o) { break }\n }\n // Zeroize the slot after the string.\n mstore(add(add(element, 0x20), elementLength), 0)\n // Allocate memory for the length and the bytes,\n // rounded up to a multiple of 32.\n mstore(0x40, add(element, and(add(elementLength, 0x3f), w)))\n // Store the `element` into the array.\n mstore(indexPtr, element)\n }\n prevIndex := add(index, mload(delimiter))\n indexPtr := add(indexPtr, 0x20)\n if iszero(lt(indexPtr, indicesEnd)) { break }\n }\n result := indices\n if iszero(mload(delimiter)) {\n result := add(indices, 0x20)\n mstore(result, sub(mload(indices), 2))\n }\n }\n }\n\n /// @dev Returns a concatenated string of `a` and `b`.\n /// Cheaper than `string.concat()` and does not de-align the free memory pointer.\n function concat(string memory a, string memory b)\n internal\n pure\n returns (string memory result)\n {\n /// @solidity memory-safe-assembly\n assembly {\n let w := not(0x1f)\n result := mload(0x40)\n let aLength := mload(a)\n // Copy `a` one word at a time, backwards.\n for { let o := and(add(aLength, 0x20), w) } 1 {} {\n mstore(add(result, o), mload(add(a, o)))\n o := add(o, w) // `sub(o, 0x20)`.\n if iszero(o) { break }\n }\n let bLength := mload(b)\n let output := add(result, aLength)\n // Copy `b` one word at a time, backwards.\n for { let o := and(add(bLength, 0x20), w) } 1 {} {\n mstore(add(output, o), mload(add(b, o)))\n o := add(o, w) // `sub(o, 0x20)`.\n if iszero(o) { break }\n }\n let totalLength := add(aLength, bLength)\n let last := add(add(result, 0x20), totalLength)\n // Zeroize the slot after the string.\n mstore(last, 0)\n // Stores the length.\n mstore(result, totalLength)\n // Allocate memory for the length and the bytes,\n // rounded up to a multiple of 32.\n mstore(0x40, and(add(last, 0x1f), w))\n }\n }\n\n /// @dev Returns a copy of the string in either lowercase or UPPERCASE.\n /// WARNING! This function is only compatible with 7-bit ASCII strings.\n function toCase(string memory subject, bool toUpper)\n internal\n pure\n returns (string memory result)\n {\n /// @solidity memory-safe-assembly\n assembly {\n let length := mload(subject)\n if length {\n result := add(mload(0x40), 0x20)\n subject := add(subject, 1)\n let flags := shl(add(70, shl(5, toUpper)), 0x3ffffff)\n let w := not(0)\n for { let o := length } 1 {} {\n o := add(o, w)\n let b := and(0xff, mload(add(subject, o)))\n mstore8(add(result, o), xor(b, and(shr(b, flags), 0x20)))\n if iszero(o) { break }\n }\n result := mload(0x40)\n mstore(result, length) // Store the length.\n let last := add(add(result, 0x20), length)\n mstore(last, 0) // Zeroize the slot after the string.\n mstore(0x40, add(last, 0x20)) // Allocate the memory.\n }\n }\n }\n\n /// @dev Returns a string from a small bytes32 string.\n /// `s` must be null-terminated, or behavior will be undefined.\n function fromSmallString(bytes32 s) internal pure returns (string memory result) {\n /// @solidity memory-safe-assembly\n assembly {\n result := mload(0x40)\n let n := 0\n for {} byte(n, s) { n := add(n, 1) } {} // Scan for '\\0'.\n mstore(result, n)\n let o := add(result, 0x20)\n mstore(o, s)\n mstore(add(o, n), 0)\n mstore(0x40, add(result, 0x40))\n }\n }\n\n /// @dev Returns the small string, with all bytes after the first null byte zeroized.\n function normalizeSmallString(bytes32 s) internal pure returns (bytes32 result) {\n /// @solidity memory-safe-assembly\n assembly {\n for {} byte(result, s) { result := add(result, 1) } {} // Scan for '\\0'.\n mstore(0x00, s)\n mstore(result, 0x00)\n result := mload(0x00)\n }\n }\n\n /// @dev Returns the string as a normalized null-terminated small string.\n function toSmallString(string memory s) internal pure returns (bytes32 result) {\n /// @solidity memory-safe-assembly\n assembly {\n result := mload(s)\n if iszero(lt(result, 33)) {\n mstore(0x00, 0xec92f9a3) // `TooBigForSmallString()`.\n revert(0x1c, 0x04)\n }\n result := shl(shl(3, sub(32, result)), mload(add(s, result)))\n }\n }\n\n /// @dev Returns a lowercased copy of the string.\n /// WARNING! This function is only compatible with 7-bit ASCII strings.\n function lower(string memory subject) internal pure returns (string memory result) {\n result = toCase(subject, false);\n }\n\n /// @dev Returns an UPPERCASED copy of the string.\n /// WARNING! This function is only compatible with 7-bit ASCII strings.\n function upper(string memory subject) internal pure returns (string memory result) {\n result = toCase(subject, true);\n }\n\n /// @dev Escapes the string to be used within HTML tags.\n function escapeHTML(string memory s) internal pure returns (string memory result) {\n /// @solidity memory-safe-assembly\n assembly {\n let end := add(s, mload(s))\n result := add(mload(0x40), 0x20)\n // Store the bytes of the packed offsets and strides into the scratch space.\n // `packed = (stride << 5) | offset`. Max offset is 20. Max stride is 6.\n mstore(0x1f, 0x900094)\n mstore(0x08, 0xc0000000a6ab)\n // Store \""&'<>\" into the scratch space.\n mstore(0x00, shl(64, 0x2671756f743b26616d703b262333393b266c743b2667743b))\n for {} iszero(eq(s, end)) {} {\n s := add(s, 1)\n let c := and(mload(s), 0xff)\n // Not in `[\"\\\"\",\"'\",\"&\",\"<\",\">\"]`.\n if iszero(and(shl(c, 1), 0x500000c400000000)) {\n mstore8(result, c)\n result := add(result, 1)\n continue\n }\n let t := shr(248, mload(c))\n mstore(result, mload(and(t, 0x1f)))\n result := add(result, shr(5, t))\n }\n let last := result\n mstore(last, 0) // Zeroize the slot after the string.\n result := mload(0x40)\n mstore(result, sub(last, add(result, 0x20))) // Store the length.\n mstore(0x40, add(last, 0x20)) // Allocate the memory.\n }\n }\n\n /// @dev Escapes the string to be used within double-quotes in a JSON.\n /// If `addDoubleQuotes` is true, the result will be enclosed in double-quotes.\n function escapeJSON(string memory s, bool addDoubleQuotes)\n internal\n pure\n returns (string memory result)\n {\n /// @solidity memory-safe-assembly\n assembly {\n let end := add(s, mload(s))\n result := add(mload(0x40), 0x20)\n if addDoubleQuotes {\n mstore8(result, 34)\n result := add(1, result)\n }\n // Store \"\\\\u0000\" in scratch space.\n // Store \"0123456789abcdef\" in scratch space.\n // Also, store `{0x08:\"b\", 0x09:\"t\", 0x0a:\"n\", 0x0c:\"f\", 0x0d:\"r\"}`.\n // into the scratch space.\n mstore(0x15, 0x5c75303030303031323334353637383961626364656662746e006672)\n // Bitmask for detecting `[\"\\\"\",\"\\\\\"]`.\n let e := or(shl(0x22, 1), shl(0x5c, 1))\n for {} iszero(eq(s, end)) {} {\n s := add(s, 1)\n let c := and(mload(s), 0xff)\n if iszero(lt(c, 0x20)) {\n if iszero(and(shl(c, 1), e)) {\n // Not in `[\"\\\"\",\"\\\\\"]`.\n mstore8(result, c)\n result := add(result, 1)\n continue\n }\n mstore8(result, 0x5c) // \"\\\\\".\n mstore8(add(result, 1), c)\n result := add(result, 2)\n continue\n }\n if iszero(and(shl(c, 1), 0x3700)) {\n // Not in `[\"\\b\",\"\\t\",\"\\n\",\"\\f\",\"\\d\"]`.\n mstore8(0x1d, mload(shr(4, c))) // Hex value.\n mstore8(0x1e, mload(and(c, 15))) // Hex value.\n mstore(result, mload(0x19)) // \"\\\\u00XX\".\n result := add(result, 6)\n continue\n }\n mstore8(result, 0x5c) // \"\\\\\".\n mstore8(add(result, 1), mload(add(c, 8)))\n result := add(result, 2)\n }\n if addDoubleQuotes {\n mstore8(result, 34)\n result := add(1, result)\n }\n let last := result\n mstore(last, 0) // Zeroize the slot after the string.\n result := mload(0x40)\n mstore(result, sub(last, add(result, 0x20))) // Store the length.\n mstore(0x40, add(last, 0x20)) // Allocate the memory.\n }\n }\n\n /// @dev Escapes the string to be used within double-quotes in a JSON.\n function escapeJSON(string memory s) internal pure returns (string memory result) {\n result = escapeJSON(s, false);\n }\n\n /// @dev Returns whether `a` equals `b`.\n function eq(string memory a, string memory b) internal pure returns (bool result) {\n /// @solidity memory-safe-assembly\n assembly {\n result := eq(keccak256(add(a, 0x20), mload(a)), keccak256(add(b, 0x20), mload(b)))\n }\n }\n\n /// @dev Returns whether `a` equals `b`, where `b` is a null-terminated small string.\n function eqs(string memory a, bytes32 b) internal pure returns (bool result) {\n /// @solidity memory-safe-assembly\n assembly {\n // These should be evaluated on compile time, as far as possible.\n let m := not(shl(7, div(not(iszero(b)), 255))) // `0x7f7f ...`.\n let x := not(or(m, or(b, add(m, and(b, m)))))\n let r := shl(7, iszero(iszero(shr(128, x))))\n r := or(r, shl(6, iszero(iszero(shr(64, shr(r, x))))))\n r := or(r, shl(5, lt(0xffffffff, shr(r, x))))\n r := or(r, shl(4, lt(0xffff, shr(r, x))))\n r := or(r, shl(3, lt(0xff, shr(r, x))))\n // forgefmt: disable-next-item\n result := gt(eq(mload(a), add(iszero(x), xor(31, shr(3, r)))),\n xor(shr(add(8, r), b), shr(add(8, r), mload(add(a, 0x20)))))\n }\n }\n\n /// @dev Packs a single string with its length into a single word.\n /// Returns `bytes32(0)` if the length is zero or greater than 31.\n function packOne(string memory a) internal pure returns (bytes32 result) {\n /// @solidity memory-safe-assembly\n assembly {\n // We don't need to zero right pad the string,\n // since this is our own custom non-standard packing scheme.\n result :=\n mul(\n // Load the length and the bytes.\n mload(add(a, 0x1f)),\n // `length != 0 && length < 32`. Abuses underflow.\n // Assumes that the length is valid and within the block gas limit.\n lt(sub(mload(a), 1), 0x1f)\n )\n }\n }\n\n /// @dev Unpacks a string packed using {packOne}.\n /// Returns the empty string if `packed` is `bytes32(0)`.\n /// If `packed` is not an output of {packOne}, the output behavior is undefined.\n function unpackOne(bytes32 packed) internal pure returns (string memory result) {\n /// @solidity memory-safe-assembly\n assembly {\n // Grab the free memory pointer.\n result := mload(0x40)\n // Allocate 2 words (1 for the length, 1 for the bytes).\n mstore(0x40, add(result, 0x40))\n // Zeroize the length slot.\n mstore(result, 0)\n // Store the length and bytes.\n mstore(add(result, 0x1f), packed)\n // Right pad with zeroes.\n mstore(add(add(result, 0x20), mload(result)), 0)\n }\n }\n\n /// @dev Packs two strings with their lengths into a single word.\n /// Returns `bytes32(0)` if combined length is zero or greater than 30.\n function packTwo(string memory a, string memory b) internal pure returns (bytes32 result) {\n /// @solidity memory-safe-assembly\n assembly {\n let aLength := mload(a)\n // We don't need to zero right pad the strings,\n // since this is our own custom non-standard packing scheme.\n result :=\n mul(\n // Load the length and the bytes of `a` and `b`.\n or(\n shl(shl(3, sub(0x1f, aLength)), mload(add(a, aLength))),\n mload(sub(add(b, 0x1e), aLength))\n ),\n // `totalLength != 0 && totalLength < 31`. Abuses underflow.\n // Assumes that the lengths are valid and within the block gas limit.\n lt(sub(add(aLength, mload(b)), 1), 0x1e)\n )\n }\n }\n\n /// @dev Unpacks strings packed using {packTwo}.\n /// Returns the empty strings if `packed` is `bytes32(0)`.\n /// If `packed` is not an output of {packTwo}, the output behavior is undefined.\n function unpackTwo(bytes32 packed)\n internal\n pure\n returns (string memory resultA, string memory resultB)\n {\n /// @solidity memory-safe-assembly\n assembly {\n // Grab the free memory pointer.\n resultA := mload(0x40)\n resultB := add(resultA, 0x40)\n // Allocate 2 words for each string (1 for the length, 1 for the byte). Total 4 words.\n mstore(0x40, add(resultB, 0x40))\n // Zeroize the length slots.\n mstore(resultA, 0)\n mstore(resultB, 0)\n // Store the lengths and bytes.\n mstore(add(resultA, 0x1f), packed)\n mstore(add(resultB, 0x1f), mload(add(add(resultA, 0x20), mload(resultA))))\n // Right pad with zeroes.\n mstore(add(add(resultA, 0x20), mload(resultA)), 0)\n mstore(add(add(resultB, 0x20), mload(resultB)), 0)\n }\n }\n\n /// @dev Directly returns `a` without copying.\n function directReturn(string memory a) internal pure {\n assembly {\n // Assumes that the string does not start from the scratch space.\n let retStart := sub(a, 0x20)\n let retSize := add(mload(a), 0x40)\n // Right pad with zeroes. Just in case the string is produced\n // by a method that doesn't zero right pad.\n mstore(add(retStart, retSize), 0)\n // Store the return offset.\n mstore(retStart, 0x20)\n // End the transaction, returning the string.\n return(retStart, retSize)\n }\n }\n}\n"},"src/utils/DateTimeUtils.sol":{"content":"// SPDX-License-Identifier: GPL-3.0\npragma solidity ^0.8.0;\n\nimport {DateTimeLib} from \"solady/utils/DateTimeLib.sol\";\nimport {LibString} from \"solady/utils/LibString.sol\";\n\nlibrary DateTimeUtils {\n using LibString for string;\n\n /*\n * @dev Convert a DER-encoded time to a unix timestamp\n * @param x509Time The DER-encoded time\n * @return The unix timestamp\n */\n function fromDERToTimestamp(bytes memory x509Time) internal pure returns (uint256) {\n uint16 yrs;\n uint8 mnths;\n uint8 dys;\n uint8 hrs;\n uint8 mins;\n uint8 secs;\n uint8 offset;\n\n if (x509Time.length == 13) {\n if (uint8(x509Time[0]) - 48 < 5) yrs += 2000;\n else yrs += 1900;\n } else {\n yrs += (uint8(x509Time[0]) - 48) * 1000 + (uint8(x509Time[1]) - 48) * 100;\n offset = 2;\n }\n yrs += (uint8(x509Time[offset + 0]) - 48) * 10 + uint8(x509Time[offset + 1]) - 48;\n mnths = (uint8(x509Time[offset + 2]) - 48) * 10 + uint8(x509Time[offset + 3]) - 48;\n dys += (uint8(x509Time[offset + 4]) - 48) * 10 + uint8(x509Time[offset + 5]) - 48;\n hrs += (uint8(x509Time[offset + 6]) - 48) * 10 + uint8(x509Time[offset + 7]) - 48;\n mins += (uint8(x509Time[offset + 8]) - 48) * 10 + uint8(x509Time[offset + 9]) - 48;\n secs += (uint8(x509Time[offset + 10]) - 48) * 10 + uint8(x509Time[offset + 11]) - 48;\n\n return DateTimeLib.dateTimeToTimestamp(yrs, mnths, dys, hrs, mins, secs);\n }\n\n /// @dev iso follows pattern: \"YYYY-MM-DDTHH:mm:ssZ\"\n function fromISOToTimestamp(string memory iso) internal pure returns (uint256) {\n require(bytes(iso).length == 20, \"invalid iso string length\");\n uint256 y = stringToUint(iso.slice(0, 4));\n uint256 m = stringToUint(iso.slice(5, 7));\n uint256 d = stringToUint(iso.slice(8, 10));\n uint256 h = stringToUint(iso.slice(11, 13));\n uint256 min = stringToUint(iso.slice(14, 16));\n uint256 s = stringToUint(iso.slice(17, 19));\n\n return DateTimeLib.dateTimeToTimestamp(y, m, d, h, min, s);\n }\n\n // https://ethereum.stackexchange.com/questions/10932/how-to-convert-string-to-int\n function stringToUint(string memory s) private pure returns (uint256 result) {\n bytes memory b = bytes(s);\n result = 0;\n for (uint256 i = 0; i < b.length; i++) {\n uint256 c = uint256(uint8(b[i]));\n if (c >= 48 && c <= 57) {\n result = result * 10 + (c - 48);\n }\n }\n }\n}\n"},"lib/solady/src/utils/DateTimeLib.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\n/// @notice Library for date time operations.\n/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/DateTimeLib.sol)\n///\n/// Conventions:\n/// --------------------------------------------------------------------+\n/// Unit | Range | Notes |\n/// --------------------------------------------------------------------|\n/// timestamp | 0..0x1e18549868c76ff | Unix timestamp. |\n/// epochDay | 0..0x16d3e098039 | Days since 1970-01-01. |\n/// year | 1970..0xffffffff | Gregorian calendar year. |\n/// month | 1..12 | Gregorian calendar month. |\n/// day | 1..31 | Gregorian calendar day of month. |\n/// weekday | 1..7 | The day of the week (1-indexed). |\n/// --------------------------------------------------------------------+\n/// All timestamps of days are rounded down to 00:00:00 UTC.\nlibrary DateTimeLib {\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* CONSTANTS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n // Weekdays are 1-indexed for a traditional rustic feel.\n\n // \"And on the seventh day God finished his work that he had done,\n // and he rested on the seventh day from all his work that he had done.\"\n // -- Genesis 2:2\n\n uint256 internal constant MON = 1;\n uint256 internal constant TUE = 2;\n uint256 internal constant WED = 3;\n uint256 internal constant THU = 4;\n uint256 internal constant FRI = 5;\n uint256 internal constant SAT = 6;\n uint256 internal constant SUN = 7;\n\n // Months and days of months are 1-indexed for ease of use.\n\n uint256 internal constant JAN = 1;\n uint256 internal constant FEB = 2;\n uint256 internal constant MAR = 3;\n uint256 internal constant APR = 4;\n uint256 internal constant MAY = 5;\n uint256 internal constant JUN = 6;\n uint256 internal constant JUL = 7;\n uint256 internal constant AUG = 8;\n uint256 internal constant SEP = 9;\n uint256 internal constant OCT = 10;\n uint256 internal constant NOV = 11;\n uint256 internal constant DEC = 12;\n\n // These limits are large enough for most practical purposes.\n // Inputs that exceed these limits result in undefined behavior.\n\n uint256 internal constant MAX_SUPPORTED_YEAR = 0xffffffff;\n uint256 internal constant MAX_SUPPORTED_EPOCH_DAY = 0x16d3e098039;\n uint256 internal constant MAX_SUPPORTED_TIMESTAMP = 0x1e18549868c76ff;\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* DATE TIME OPERATIONS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev Returns the number of days since 1970-01-01 from (`year`,`month`,`day`).\n /// See: https://howardhinnant.github.io/date_algorithms.html\n /// Note: Inputs outside the supported ranges result in undefined behavior.\n /// Use {isSupportedDate} to check if the inputs are supported.\n function dateToEpochDay(uint256 year, uint256 month, uint256 day)\n internal\n pure\n returns (uint256 epochDay)\n {\n /// @solidity memory-safe-assembly\n assembly {\n year := sub(year, lt(month, 3))\n let doy := add(shr(11, add(mul(62719, mod(add(month, 9), 12)), 769)), day)\n let yoe := mod(year, 400)\n let doe := sub(add(add(mul(yoe, 365), shr(2, yoe)), doy), div(yoe, 100))\n epochDay := sub(add(mul(div(year, 400), 146097), doe), 719469)\n }\n }\n\n /// @dev Returns (`year`,`month`,`day`) from the number of days since 1970-01-01.\n /// Note: Inputs outside the supported ranges result in undefined behavior.\n /// Use {isSupportedDays} to check if the inputs is supported.\n function epochDayToDate(uint256 epochDay)\n internal\n pure\n returns (uint256 year, uint256 month, uint256 day)\n {\n /// @solidity memory-safe-assembly\n assembly {\n epochDay := add(epochDay, 719468)\n let doe := mod(epochDay, 146097)\n let yoe :=\n div(sub(sub(add(doe, div(doe, 36524)), div(doe, 1460)), eq(doe, 146096)), 365)\n let doy := sub(doe, sub(add(mul(365, yoe), shr(2, yoe)), div(yoe, 100)))\n let mp := div(add(mul(5, doy), 2), 153)\n day := add(sub(doy, shr(11, add(mul(mp, 62719), 769))), 1)\n month := byte(mp, shl(160, 0x030405060708090a0b0c0102))\n year := add(add(yoe, mul(div(epochDay, 146097), 400)), lt(month, 3))\n }\n }\n\n /// @dev Returns the unix timestamp from (`year`,`month`,`day`).\n /// Note: Inputs outside the supported ranges result in undefined behavior.\n /// Use {isSupportedDate} to check if the inputs are supported.\n function dateToTimestamp(uint256 year, uint256 month, uint256 day)\n internal\n pure\n returns (uint256 result)\n {\n unchecked {\n result = dateToEpochDay(year, month, day) * 86400;\n }\n }\n\n /// @dev Returns (`year`,`month`,`day`) from the given unix timestamp.\n /// Note: Inputs outside the supported ranges result in undefined behavior.\n /// Use {isSupportedTimestamp} to check if the inputs are supported.\n function timestampToDate(uint256 timestamp)\n internal\n pure\n returns (uint256 year, uint256 month, uint256 day)\n {\n (year, month, day) = epochDayToDate(timestamp / 86400);\n }\n\n /// @dev Returns the unix timestamp from\n /// (`year`,`month`,`day`,`hour`,`minute`,`second`).\n /// Note: Inputs outside the supported ranges result in undefined behavior.\n /// Use {isSupportedDateTime} to check if the inputs are supported.\n function dateTimeToTimestamp(\n uint256 year,\n uint256 month,\n uint256 day,\n uint256 hour,\n uint256 minute,\n uint256 second\n ) internal pure returns (uint256 result) {\n unchecked {\n result = dateToEpochDay(year, month, day) * 86400 + hour * 3600 + minute * 60 + second;\n }\n }\n\n /// @dev Returns (`year`,`month`,`day`,`hour`,`minute`,`second`)\n /// from the given unix timestamp.\n /// Note: Inputs outside the supported ranges result in undefined behavior.\n /// Use {isSupportedTimestamp} to check if the inputs are supported.\n function timestampToDateTime(uint256 timestamp)\n internal\n pure\n returns (\n uint256 year,\n uint256 month,\n uint256 day,\n uint256 hour,\n uint256 minute,\n uint256 second\n )\n {\n unchecked {\n (year, month, day) = epochDayToDate(timestamp / 86400);\n uint256 secs = timestamp % 86400;\n hour = secs / 3600;\n secs = secs % 3600;\n minute = secs / 60;\n second = secs % 60;\n }\n }\n\n /// @dev Returns if the `year` is leap.\n function isLeapYear(uint256 year) internal pure returns (bool leap) {\n /// @solidity memory-safe-assembly\n assembly {\n leap := iszero(and(add(mul(iszero(mod(year, 25)), 12), 3), year))\n }\n }\n\n /// @dev Returns number of days in given `month` of `year`.\n function daysInMonth(uint256 year, uint256 month) internal pure returns (uint256 result) {\n bool flag = isLeapYear(year);\n /// @solidity memory-safe-assembly\n assembly {\n // `daysInMonths = [31,28,31,30,31,30,31,31,30,31,30,31]`.\n // `result = daysInMonths[month - 1] + isLeapYear(year)`.\n result :=\n add(byte(month, shl(152, 0x1F1C1F1E1F1E1F1F1E1F1E1F)), and(eq(month, 2), flag))\n }\n }\n\n /// @dev Returns the weekday from the unix timestamp.\n /// Monday: 1, Tuesday: 2, ....., Sunday: 7.\n function weekday(uint256 timestamp) internal pure returns (uint256 result) {\n unchecked {\n result = ((timestamp / 86400 + 3) % 7) + 1;\n }\n }\n\n /// @dev Returns if (`year`,`month`,`day`) is a supported date.\n /// - `1970 <= year <= MAX_SUPPORTED_YEAR`.\n /// - `1 <= month <= 12`.\n /// - `1 <= day <= daysInMonth(year, month)`.\n function isSupportedDate(uint256 year, uint256 month, uint256 day)\n internal\n pure\n returns (bool result)\n {\n uint256 md = daysInMonth(year, month);\n /// @solidity memory-safe-assembly\n assembly {\n let w := not(0)\n result :=\n and(\n lt(sub(year, 1970), sub(MAX_SUPPORTED_YEAR, 1969)),\n and(lt(add(month, w), 12), lt(add(day, w), md))\n )\n }\n }\n\n /// @dev Returns if (`year`,`month`,`day`,`hour`,`minute`,`second`) is a supported date time.\n /// - `1970 <= year <= MAX_SUPPORTED_YEAR`.\n /// - `1 <= month <= 12`.\n /// - `1 <= day <= daysInMonth(year, month)`.\n /// - `hour < 24`.\n /// - `minute < 60`.\n /// - `second < 60`.\n function isSupportedDateTime(\n uint256 year,\n uint256 month,\n uint256 day,\n uint256 hour,\n uint256 minute,\n uint256 second\n ) internal pure returns (bool result) {\n if (isSupportedDate(year, month, day)) {\n /// @solidity memory-safe-assembly\n assembly {\n result := and(lt(hour, 24), and(lt(minute, 60), lt(second, 60)))\n }\n }\n }\n\n /// @dev Returns if `epochDay` is a supported unix epoch day.\n function isSupportedEpochDay(uint256 epochDay) internal pure returns (bool result) {\n unchecked {\n result = epochDay < MAX_SUPPORTED_EPOCH_DAY + 1;\n }\n }\n\n /// @dev Returns if `timestamp` is a supported unix timestamp.\n function isSupportedTimestamp(uint256 timestamp) internal pure returns (bool result) {\n unchecked {\n result = timestamp < MAX_SUPPORTED_TIMESTAMP + 1;\n }\n }\n\n /// @dev Returns the unix timestamp of the given `n`th weekday `wd`, in `month` of `year`.\n /// Example: 3rd Friday of Feb 2022 is `nthWeekdayInMonthOfYearTimestamp(2022, 2, 3, 5)`\n /// Note: `n` is 1-indexed for traditional consistency.\n /// Invalid weekdays (i.e. `wd == 0 || wd > 7`) result in undefined behavior.\n function nthWeekdayInMonthOfYearTimestamp(uint256 year, uint256 month, uint256 n, uint256 wd)\n internal\n pure\n returns (uint256 result)\n {\n uint256 d = dateToEpochDay(year, month, 1);\n uint256 md = daysInMonth(year, month);\n /// @solidity memory-safe-assembly\n assembly {\n let diff := sub(wd, add(mod(add(d, 3), 7), 1))\n let date := add(mul(sub(n, 1), 7), add(mul(gt(diff, 6), 7), diff))\n result := mul(mul(86400, add(date, d)), and(lt(date, md), iszero(iszero(n))))\n }\n }\n\n /// @dev Returns the unix timestamp of the most recent Monday.\n function mondayTimestamp(uint256 timestamp) internal pure returns (uint256 result) {\n uint256 t = timestamp;\n /// @solidity memory-safe-assembly\n assembly {\n let day := div(t, 86400)\n result := mul(mul(sub(day, mod(add(day, 3), 7)), 86400), gt(t, 345599))\n }\n }\n\n /// @dev Returns whether the unix timestamp falls on a Saturday or Sunday.\n /// To check whether it is a week day, just take the negation of the result.\n function isWeekEnd(uint256 timestamp) internal pure returns (bool result) {\n result = weekday(timestamp) > FRI;\n }\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* DATE TIME ARITHMETIC OPERATIONS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev Adds `numYears` to the unix timestamp, and returns the result.\n /// Note: The result will share the same Gregorian calendar month,\n /// but different Gregorian calendar years for non-zero `numYears`.\n /// If the Gregorian calendar month of the result has less days\n /// than the Gregorian calendar month day of the `timestamp`,\n /// the result's month day will be the maximum possible value for the month.\n /// (e.g. from 29th Feb to 28th Feb)\n function addYears(uint256 timestamp, uint256 numYears) internal pure returns (uint256 result) {\n (uint256 year, uint256 month, uint256 day) = epochDayToDate(timestamp / 86400);\n result = _offsetted(year + numYears, month, day, timestamp);\n }\n\n /// @dev Adds `numMonths` to the unix timestamp, and returns the result.\n /// Note: If the Gregorian calendar month of the result has less days\n /// than the Gregorian calendar month day of the `timestamp`,\n /// the result's month day will be the maximum possible value for the month.\n /// (e.g. from 29th Feb to 28th Feb)\n function addMonths(uint256 timestamp, uint256 numMonths)\n internal\n pure\n returns (uint256 result)\n {\n (uint256 year, uint256 month, uint256 day) = epochDayToDate(timestamp / 86400);\n month = _sub(month + numMonths, 1);\n result = _offsetted(year + month / 12, _add(month % 12, 1), day, timestamp);\n }\n\n /// @dev Adds `numDays` to the unix timestamp, and returns the result.\n function addDays(uint256 timestamp, uint256 numDays) internal pure returns (uint256 result) {\n result = timestamp + numDays * 86400;\n }\n\n /// @dev Adds `numHours` to the unix timestamp, and returns the result.\n function addHours(uint256 timestamp, uint256 numHours) internal pure returns (uint256 result) {\n result = timestamp + numHours * 3600;\n }\n\n /// @dev Adds `numMinutes` to the unix timestamp, and returns the result.\n function addMinutes(uint256 timestamp, uint256 numMinutes)\n internal\n pure\n returns (uint256 result)\n {\n result = timestamp + numMinutes * 60;\n }\n\n /// @dev Adds `numSeconds` to the unix timestamp, and returns the result.\n function addSeconds(uint256 timestamp, uint256 numSeconds)\n internal\n pure\n returns (uint256 result)\n {\n result = timestamp + numSeconds;\n }\n\n /// @dev Subtracts `numYears` from the unix timestamp, and returns the result.\n /// Note: The result will share the same Gregorian calendar month,\n /// but different Gregorian calendar years for non-zero `numYears`.\n /// If the Gregorian calendar month of the result has less days\n /// than the Gregorian calendar month day of the `timestamp`,\n /// the result's month day will be the maximum possible value for the month.\n /// (e.g. from 29th Feb to 28th Feb)\n function subYears(uint256 timestamp, uint256 numYears) internal pure returns (uint256 result) {\n (uint256 year, uint256 month, uint256 day) = epochDayToDate(timestamp / 86400);\n result = _offsetted(year - numYears, month, day, timestamp);\n }\n\n /// @dev Subtracts `numYears` from the unix timestamp, and returns the result.\n /// Note: If the Gregorian calendar month of the result has less days\n /// than the Gregorian calendar month day of the `timestamp`,\n /// the result's month day will be the maximum possible value for the month.\n /// (e.g. from 29th Feb to 28th Feb)\n function subMonths(uint256 timestamp, uint256 numMonths)\n internal\n pure\n returns (uint256 result)\n {\n (uint256 year, uint256 month, uint256 day) = epochDayToDate(timestamp / 86400);\n uint256 yearMonth = _totalMonths(year, month) - _add(numMonths, 1);\n result = _offsetted(yearMonth / 12, _add(yearMonth % 12, 1), day, timestamp);\n }\n\n /// @dev Subtracts `numDays` from the unix timestamp, and returns the result.\n function subDays(uint256 timestamp, uint256 numDays) internal pure returns (uint256 result) {\n result = timestamp - numDays * 86400;\n }\n\n /// @dev Subtracts `numHours` from the unix timestamp, and returns the result.\n function subHours(uint256 timestamp, uint256 numHours) internal pure returns (uint256 result) {\n result = timestamp - numHours * 3600;\n }\n\n /// @dev Subtracts `numMinutes` from the unix timestamp, and returns the result.\n function subMinutes(uint256 timestamp, uint256 numMinutes)\n internal\n pure\n returns (uint256 result)\n {\n result = timestamp - numMinutes * 60;\n }\n\n /// @dev Subtracts `numSeconds` from the unix timestamp, and returns the result.\n function subSeconds(uint256 timestamp, uint256 numSeconds)\n internal\n pure\n returns (uint256 result)\n {\n result = timestamp - numSeconds;\n }\n\n /// @dev Returns the difference in Gregorian calendar years\n /// between `fromTimestamp` and `toTimestamp`.\n /// Note: Even if the true time difference is less than a year,\n /// the difference can be non-zero is the timestamps are\n /// from different Gregorian calendar years\n function diffYears(uint256 fromTimestamp, uint256 toTimestamp)\n internal\n pure\n returns (uint256 result)\n {\n toTimestamp - fromTimestamp;\n (uint256 fromYear,,) = epochDayToDate(fromTimestamp / 86400);\n (uint256 toYear,,) = epochDayToDate(toTimestamp / 86400);\n result = _sub(toYear, fromYear);\n }\n\n /// @dev Returns the difference in Gregorian calendar months\n /// between `fromTimestamp` and `toTimestamp`.\n /// Note: Even if the true time difference is less than a month,\n /// the difference can be non-zero is the timestamps are\n /// from different Gregorian calendar months.\n function diffMonths(uint256 fromTimestamp, uint256 toTimestamp)\n internal\n pure\n returns (uint256 result)\n {\n toTimestamp - fromTimestamp;\n (uint256 fromYear, uint256 fromMonth,) = epochDayToDate(fromTimestamp / 86400);\n (uint256 toYear, uint256 toMonth,) = epochDayToDate(toTimestamp / 86400);\n result = _sub(_totalMonths(toYear, toMonth), _totalMonths(fromYear, fromMonth));\n }\n\n /// @dev Returns the difference in days between `fromTimestamp` and `toTimestamp`.\n function diffDays(uint256 fromTimestamp, uint256 toTimestamp)\n internal\n pure\n returns (uint256 result)\n {\n result = (toTimestamp - fromTimestamp) / 86400;\n }\n\n /// @dev Returns the difference in hours between `fromTimestamp` and `toTimestamp`.\n function diffHours(uint256 fromTimestamp, uint256 toTimestamp)\n internal\n pure\n returns (uint256 result)\n {\n result = (toTimestamp - fromTimestamp) / 3600;\n }\n\n /// @dev Returns the difference in minutes between `fromTimestamp` and `toTimestamp`.\n function diffMinutes(uint256 fromTimestamp, uint256 toTimestamp)\n internal\n pure\n returns (uint256 result)\n {\n result = (toTimestamp - fromTimestamp) / 60;\n }\n\n /// @dev Returns the difference in seconds between `fromTimestamp` and `toTimestamp`.\n function diffSeconds(uint256 fromTimestamp, uint256 toTimestamp)\n internal\n pure\n returns (uint256 result)\n {\n result = toTimestamp - fromTimestamp;\n }\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* PRIVATE HELPERS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev Unchecked arithmetic for computing the total number of months.\n function _totalMonths(uint256 numYears, uint256 numMonths)\n private\n pure\n returns (uint256 total)\n {\n unchecked {\n total = numYears * 12 + numMonths;\n }\n }\n\n /// @dev Unchecked arithmetic for adding two numbers.\n function _add(uint256 a, uint256 b) private pure returns (uint256 c) {\n unchecked {\n c = a + b;\n }\n }\n\n /// @dev Unchecked arithmetic for subtracting two numbers.\n function _sub(uint256 a, uint256 b) private pure returns (uint256 c) {\n unchecked {\n c = a - b;\n }\n }\n\n /// @dev Returns the offsetted timestamp.\n function _offsetted(uint256 year, uint256 month, uint256 day, uint256 timestamp)\n private\n pure\n returns (uint256 result)\n {\n uint256 dm = daysInMonth(year, month);\n if (day >= dm) {\n day = dm;\n }\n result = dateToEpochDay(year, month, day) * 86400 + (timestamp % 86400);\n }\n}\n"}},"settings":{"remappings":["solady/=lib/solady/src/","@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/","ds-test/=lib/forge-std/lib/ds-test/src/","erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/","forge-std/=lib/forge-std/src/","halmos-cheatcodes/=lib/openzeppelin-contracts/lib/halmos-cheatcodes/src/","openzeppelin-contracts/=lib/openzeppelin-contracts/"],"optimizer":{"enabled":true,"runs":999999},"metadata":{"useLiteralContent":false,"bytecodeHash":"ipfs","appendCBOR":true},"outputSelection":{"*":{"*":["abi","evm.bytecode","evm.deployedBytecode","evm.methodIdentifiers","metadata"]}},"evmVersion":"paris","viaIR":true,"libraries":{}}} diff --git a/standard-json-input/pccsstorage.json b/standard-json-input/pccsstorage.json deleted file mode 100644 index 4326a39..0000000 --- a/standard-json-input/pccsstorage.json +++ /dev/null @@ -1 +0,0 @@ -{"language":"Solidity","sources":{"src/automata_pccs/shared/AutomataDaoStorage.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport \"solady/auth/Ownable.sol\";\n\ncontract AutomataDaoStorage is Ownable {\n mapping(address => bool) _authorized;\n\n mapping(bytes32 attId => bytes attData) _pccsData;\n\n modifier onlyAuthorized(address dao) {\n require(_authorized[dao], \"Unauthorized caller\");\n _;\n }\n\n constructor() {\n _initializeOwner(msg.sender);\n }\n\n function updateDao(address _pcsDao, address _pckDao, address _fmspcTcbDao, address _enclaveIdDao)\n external\n onlyOwner\n {\n _updateDao(_pcsDao, _pckDao, _fmspcTcbDao, _enclaveIdDao);\n }\n\n function revokeDao(address revoked) external onlyOwner {\n _authorized[revoked] = false;\n }\n\n function writeToPccs(bytes32 attId, bytes memory attData) external onlyAuthorized(msg.sender) {\n _pccsData[attId] = attData;\n }\n\n function deleteData(bytes32 attId) external onlyAuthorized(msg.sender) {\n delete _pccsData[attId];\n }\n\n function readPccs(bytes32 attId) external view returns (bytes memory attData) {\n attData = _pccsData[attId];\n }\n\n function _updateDao(address _pcsDao, address _pckDao, address _fmspcTcbDao, address _enclaveIdDao) private {\n _authorized[_pcsDao] = true;\n _authorized[_pckDao] = true;\n _authorized[_fmspcTcbDao] = true;\n _authorized[_enclaveIdDao] = true;\n }\n}\n"},"lib/solady/src/auth/Ownable.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\n/// @notice Simple single owner authorization mixin.\n/// @author Solady (https://github.com/vectorized/solady/blob/main/src/auth/Ownable.sol)\n///\n/// @dev Note:\n/// This implementation does NOT auto-initialize the owner to `msg.sender`.\n/// You MUST call the `_initializeOwner` in the constructor / initializer.\n///\n/// While the ownable portion follows\n/// [EIP-173](https://eips.ethereum.org/EIPS/eip-173) for compatibility,\n/// the nomenclature for the 2-step ownership handover may be unique to this codebase.\nabstract contract Ownable {\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* CUSTOM ERRORS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev The caller is not authorized to call the function.\n error Unauthorized();\n\n /// @dev The `newOwner` cannot be the zero address.\n error NewOwnerIsZeroAddress();\n\n /// @dev The `pendingOwner` does not have a valid handover request.\n error NoHandoverRequest();\n\n /// @dev Cannot double-initialize.\n error AlreadyInitialized();\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* EVENTS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev The ownership is transferred from `oldOwner` to `newOwner`.\n /// This event is intentionally kept the same as OpenZeppelin's Ownable to be\n /// compatible with indexers and [EIP-173](https://eips.ethereum.org/EIPS/eip-173),\n /// despite it not being as lightweight as a single argument event.\n event OwnershipTransferred(address indexed oldOwner, address indexed newOwner);\n\n /// @dev An ownership handover to `pendingOwner` has been requested.\n event OwnershipHandoverRequested(address indexed pendingOwner);\n\n /// @dev The ownership handover to `pendingOwner` has been canceled.\n event OwnershipHandoverCanceled(address indexed pendingOwner);\n\n /// @dev `keccak256(bytes(\"OwnershipTransferred(address,address)\"))`.\n uint256 private constant _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE =\n 0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0;\n\n /// @dev `keccak256(bytes(\"OwnershipHandoverRequested(address)\"))`.\n uint256 private constant _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE =\n 0xdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d;\n\n /// @dev `keccak256(bytes(\"OwnershipHandoverCanceled(address)\"))`.\n uint256 private constant _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE =\n 0xfa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92;\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* STORAGE */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev The owner slot is given by:\n /// `bytes32(~uint256(uint32(bytes4(keccak256(\"_OWNER_SLOT_NOT\")))))`.\n /// It is intentionally chosen to be a high value\n /// to avoid collision with lower slots.\n /// The choice of manual storage layout is to enable compatibility\n /// with both regular and upgradeable contracts.\n bytes32 internal constant _OWNER_SLOT =\n 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff74873927;\n\n /// The ownership handover slot of `newOwner` is given by:\n /// ```\n /// mstore(0x00, or(shl(96, user), _HANDOVER_SLOT_SEED))\n /// let handoverSlot := keccak256(0x00, 0x20)\n /// ```\n /// It stores the expiry timestamp of the two-step ownership handover.\n uint256 private constant _HANDOVER_SLOT_SEED = 0x389a75e1;\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* INTERNAL FUNCTIONS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev Override to return true to make `_initializeOwner` prevent double-initialization.\n function _guardInitializeOwner() internal pure virtual returns (bool guard) {}\n\n /// @dev Initializes the owner directly without authorization guard.\n /// This function must be called upon initialization,\n /// regardless of whether the contract is upgradeable or not.\n /// This is to enable generalization to both regular and upgradeable contracts,\n /// and to save gas in case the initial owner is not the caller.\n /// For performance reasons, this function will not check if there\n /// is an existing owner.\n function _initializeOwner(address newOwner) internal virtual {\n if (_guardInitializeOwner()) {\n /// @solidity memory-safe-assembly\n assembly {\n let ownerSlot := _OWNER_SLOT\n if sload(ownerSlot) {\n mstore(0x00, 0x0dc149f0) // `AlreadyInitialized()`.\n revert(0x1c, 0x04)\n }\n // Clean the upper 96 bits.\n newOwner := shr(96, shl(96, newOwner))\n // Store the new value.\n sstore(ownerSlot, or(newOwner, shl(255, iszero(newOwner))))\n // Emit the {OwnershipTransferred} event.\n log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner)\n }\n } else {\n /// @solidity memory-safe-assembly\n assembly {\n // Clean the upper 96 bits.\n newOwner := shr(96, shl(96, newOwner))\n // Store the new value.\n sstore(_OWNER_SLOT, newOwner)\n // Emit the {OwnershipTransferred} event.\n log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner)\n }\n }\n }\n\n /// @dev Sets the owner directly without authorization guard.\n function _setOwner(address newOwner) internal virtual {\n if (_guardInitializeOwner()) {\n /// @solidity memory-safe-assembly\n assembly {\n let ownerSlot := _OWNER_SLOT\n // Clean the upper 96 bits.\n newOwner := shr(96, shl(96, newOwner))\n // Emit the {OwnershipTransferred} event.\n log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner)\n // Store the new value.\n sstore(ownerSlot, or(newOwner, shl(255, iszero(newOwner))))\n }\n } else {\n /// @solidity memory-safe-assembly\n assembly {\n let ownerSlot := _OWNER_SLOT\n // Clean the upper 96 bits.\n newOwner := shr(96, shl(96, newOwner))\n // Emit the {OwnershipTransferred} event.\n log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner)\n // Store the new value.\n sstore(ownerSlot, newOwner)\n }\n }\n }\n\n /// @dev Throws if the sender is not the owner.\n function _checkOwner() internal view virtual {\n /// @solidity memory-safe-assembly\n assembly {\n // If the caller is not the stored owner, revert.\n if iszero(eq(caller(), sload(_OWNER_SLOT))) {\n mstore(0x00, 0x82b42900) // `Unauthorized()`.\n revert(0x1c, 0x04)\n }\n }\n }\n\n /// @dev Returns how long a two-step ownership handover is valid for in seconds.\n /// Override to return a different value if needed.\n /// Made internal to conserve bytecode. Wrap it in a public function if needed.\n function _ownershipHandoverValidFor() internal view virtual returns (uint64) {\n return 48 * 3600;\n }\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* PUBLIC UPDATE FUNCTIONS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev Allows the owner to transfer the ownership to `newOwner`.\n function transferOwnership(address newOwner) public payable virtual onlyOwner {\n /// @solidity memory-safe-assembly\n assembly {\n if iszero(shl(96, newOwner)) {\n mstore(0x00, 0x7448fbae) // `NewOwnerIsZeroAddress()`.\n revert(0x1c, 0x04)\n }\n }\n _setOwner(newOwner);\n }\n\n /// @dev Allows the owner to renounce their ownership.\n function renounceOwnership() public payable virtual onlyOwner {\n _setOwner(address(0));\n }\n\n /// @dev Request a two-step ownership handover to the caller.\n /// The request will automatically expire in 48 hours (172800 seconds) by default.\n function requestOwnershipHandover() public payable virtual {\n unchecked {\n uint256 expires = block.timestamp + _ownershipHandoverValidFor();\n /// @solidity memory-safe-assembly\n assembly {\n // Compute and set the handover slot to `expires`.\n mstore(0x0c, _HANDOVER_SLOT_SEED)\n mstore(0x00, caller())\n sstore(keccak256(0x0c, 0x20), expires)\n // Emit the {OwnershipHandoverRequested} event.\n log2(0, 0, _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE, caller())\n }\n }\n }\n\n /// @dev Cancels the two-step ownership handover to the caller, if any.\n function cancelOwnershipHandover() public payable virtual {\n /// @solidity memory-safe-assembly\n assembly {\n // Compute and set the handover slot to 0.\n mstore(0x0c, _HANDOVER_SLOT_SEED)\n mstore(0x00, caller())\n sstore(keccak256(0x0c, 0x20), 0)\n // Emit the {OwnershipHandoverCanceled} event.\n log2(0, 0, _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE, caller())\n }\n }\n\n /// @dev Allows the owner to complete the two-step ownership handover to `pendingOwner`.\n /// Reverts if there is no existing ownership handover requested by `pendingOwner`.\n function completeOwnershipHandover(address pendingOwner) public payable virtual onlyOwner {\n /// @solidity memory-safe-assembly\n assembly {\n // Compute and set the handover slot to 0.\n mstore(0x0c, _HANDOVER_SLOT_SEED)\n mstore(0x00, pendingOwner)\n let handoverSlot := keccak256(0x0c, 0x20)\n // If the handover does not exist, or has expired.\n if gt(timestamp(), sload(handoverSlot)) {\n mstore(0x00, 0x6f5e8818) // `NoHandoverRequest()`.\n revert(0x1c, 0x04)\n }\n // Set the handover slot to 0.\n sstore(handoverSlot, 0)\n }\n _setOwner(pendingOwner);\n }\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* PUBLIC READ FUNCTIONS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev Returns the owner of the contract.\n function owner() public view virtual returns (address result) {\n /// @solidity memory-safe-assembly\n assembly {\n result := sload(_OWNER_SLOT)\n }\n }\n\n /// @dev Returns the expiry timestamp for the two-step ownership handover to `pendingOwner`.\n function ownershipHandoverExpiresAt(address pendingOwner)\n public\n view\n virtual\n returns (uint256 result)\n {\n /// @solidity memory-safe-assembly\n assembly {\n // Compute the handover slot.\n mstore(0x0c, _HANDOVER_SLOT_SEED)\n mstore(0x00, pendingOwner)\n // Load the handover slot.\n result := sload(keccak256(0x0c, 0x20))\n }\n }\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* MODIFIERS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev Marks a function as only callable by the owner.\n modifier onlyOwner() virtual {\n _checkOwner();\n _;\n }\n}\n"}},"settings":{"remappings":["solady/=lib/solady/src/","@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/","ds-test/=lib/forge-std/lib/ds-test/src/","erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/","forge-std/=lib/forge-std/src/","halmos-cheatcodes/=lib/openzeppelin-contracts/lib/halmos-cheatcodes/src/","openzeppelin-contracts/=lib/openzeppelin-contracts/"],"optimizer":{"enabled":true,"runs":999999},"metadata":{"useLiteralContent":false,"bytecodeHash":"ipfs","appendCBOR":true},"outputSelection":{"*":{"*":["abi","evm.bytecode","evm.deployedBytecode","evm.methodIdentifiers","metadata"]}},"evmVersion":"paris","viaIR":true,"libraries":{}}} diff --git a/standard-json-input/pck.json b/standard-json-input/pck.json deleted file mode 100644 index b9d8b8a..0000000 --- a/standard-json-input/pck.json +++ /dev/null @@ -1 +0,0 @@ -{"language":"Solidity","sources":{"src/automata_pccs/AutomataPckDao.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport {AutomataDaoBase} from \"./shared/AutomataDaoBase.sol\";\nimport {PckDao, AttestationRequest, PcsDao, X509CRLHelper} from \"../bases/PckDao.sol\";\n\nimport {Ownable} from \"solady/auth/Ownable.sol\";\n\ncontract AutomataPckDao is Ownable, AutomataDaoBase, PckDao {\n constructor(address _storage, address _pcs, address _x509, address _crl)\n AutomataDaoBase(_storage)\n PckDao(_pcs, _x509, _crl)\n {\n _initializeOwner(msg.sender);\n }\n\n function updateDeps(address _pcs, address _x509, address _crl) external onlyOwner {\n Pcs = PcsDao(_pcs);\n x509 = _x509;\n crlLib = X509CRLHelper(_crl);\n }\n\n function pckSchemaID() public pure override returns (bytes32) {\n // NOT-APPLICABLE FOR OUR USE CASE\n // but this is required by most attestation services, such as EAS, Verax etc\n return bytes32(0);\n }\n\n function tcbmSchemaID() public pure override returns (bytes32) {\n // NOT-APPLICABLE FOR OUR USE CASE\n // but this is required by most attestation services, such as EAS, Verax etc\n return bytes32(0);\n }\n\n function _attestPck(AttestationRequest memory req, bytes32 hash)\n internal\n override\n returns (bytes32 attestationId)\n {\n // delete the predecessor if replacing\n _deletePredecessor(req.data.refUID);\n _attestCollateral(hash, req.data.data);\n attestationId = hash;\n }\n\n function _attestTcbm(AttestationRequest memory req) internal override returns (bytes32 attestationId) {\n // delete the predecessor if replacing\n _deletePredecessor(req.data.refUID);\n\n bytes32 hash = keccak256(req.data.data);\n _attestCollateral(hash, req.data.data);\n attestationId = hash;\n }\n}\n"},"src/automata_pccs/shared/AutomataDaoBase.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport {DaoBase} from \"../../bases/DaoBase.sol\";\nimport {CA} from \"../../Common.sol\";\n\ninterface IAutomataDaoStorage {\n function writeToPccs(bytes32 attId, bytes memory attData) external;\n\n function readPccs(bytes32 attId) external view returns (bytes memory attData);\n\n function deleteData(bytes32 attId) external;\n}\n\nabstract contract AutomataDaoBase is DaoBase {\n IAutomataDaoStorage pccsStorage;\n\n constructor(address _storage) {\n pccsStorage = IAutomataDaoStorage(_storage);\n }\n\n function getAttestedData(bytes32 attestationId) public view override returns (bytes memory attestationData) {\n attestationData = pccsStorage.readPccs(attestationId);\n }\n\n /// @dev we simply map the collateral hash to the data itself in our use case\n /// @dev however, this may not be the case when the dao integrates an attestation service, such as EAS\n /// @dev it is recommended to store the hash of the collateral as a separate attestation from the collateral\n /// to reduce the size of data read\n function getCollateralHash(bytes32 attestationId) public pure override returns (bytes32) {\n return attestationId;\n }\n\n function _attestCollateral(bytes32 collateralHash, bytes memory data) internal {\n pccsStorage.writeToPccs(collateralHash, data);\n }\n\n function _deletePredecessor(bytes32 predecessor) internal {\n if (getAttestedData(predecessor).length > 0) {\n pccsStorage.deleteData(predecessor);\n }\n }\n}\n"},"src/bases/PckDao.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport {CA, AttestationRequestData, AttestationRequest} from \"../Common.sol\";\nimport {PCKHelper, X509CertObj} from \"../helpers/PCKHelper.sol\";\nimport {X509CRLHelper, X509CRLObj} from \"../helpers/X509CRLHelper.sol\";\n\nimport {EnumerableSet} from \"@openzeppelin/contracts/utils/structs/EnumerableSet.sol\";\nimport {LibString} from \"solady/utils/LibString.sol\";\n\nimport {PcsDao} from \"./PcsDao.sol\";\nimport {DaoBase} from \"./DaoBase.sol\";\nimport {SigVerifyBase} from \"./SigVerifyBase.sol\";\n\n/**\n * @title Intel PCK Certificate Data Access Object\n * @notice This contract is heavily inspired by Sections 4.2.2, 4.2.4 and 4.2.8 in the Intel SGX PCCS Design Guideline\n * https://download.01.org/intel-sgx/sgx-dcap/1.19/linux/docs/SGX_DCAP_Caching_Service_Design_Guide.pdf\n * @notice This contract is the combination of both PckDao and PlatformTcbsDao as described in section 4.2\n */\nabstract contract PckDao is DaoBase, SigVerifyBase {\n using EnumerableSet for EnumerableSet.Bytes32Set;\n\n error Certificate_Revoked(uint256 serialNum);\n error Certificate_Expired();\n error Invalid_Issuer_Name();\n error Invalid_Subject_Name();\n error Expired_Certificates();\n error TCB_Mismatch();\n error Missing_Issuer();\n error Invalid_Signature();\n\n string constant PCK_PLATFORM_CA_COMMON_NAME = \"Intel SGX PCK Platform CA\";\n string constant PCK_PROCESSOR_CA_COMMON_NAME = \"Intel SGX PCK Processor CA\";\n string constant PCK_COMMON_NAME = \"Intel SGX PCK Certificate\";\n\n PcsDao public Pcs;\n PCKHelper public pckLib;\n X509CRLHelper public crlLib;\n\n /// mapping (keccak256(qeid ++ pceid) => Enumerable tcbm Set)\n /// tcbm is a 18-byte data which is a concatenation of PCK cpusvn (16 bytes) and pcesvn (2 bytes)\n mapping(bytes32 => EnumerableSet.Bytes32Set) private _tcbmHSets;\n\n /// @notice retrieves the attested TCBm from the registry\n /// key: keccak256(qeid ++ pceid ++ platformCpuSvn ++ platformPceSvn)\n ///\n /// @notice the schema of the attested data is the following:\n /// - bytes18 tcbm\n mapping(bytes32 => bytes32) public tcbmAttestations;\n\n /// @notice retrieves the attested PCK Cert from the registry\n /// key: keccak256(qeid ++ pceid ++ tcbm)\n ///\n /// @notice the schema of the attested data is the following:\n /// - bytes pckCert\n mapping(bytes32 => bytes32) public pckCertAttestations;\n\n /// @notice the input CA parameter can only be either PROCESSOR or PLATFORM\n error Invalid_PCK_CA(CA ca);\n /// @notice The corresponding PCK Certificate cannot be found for the given platform\n error Pck_Not_Found();\n\n modifier pckCACheck(CA ca) {\n if (ca == CA.ROOT || ca == CA.SIGNING) {\n revert Invalid_PCK_CA(ca);\n }\n _;\n }\n\n constructor(address _pcs, address _x509, address _crl) SigVerifyBase(_x509) {\n Pcs = PcsDao(_pcs);\n pckLib = PCKHelper(_x509);\n crlLib = X509CRLHelper(_crl);\n }\n\n /**\n * @notice Section 4.2.2 (getCert(qe_id, cpu_svn, pce_svn, pce_id))\n */\n function getCert(\n string calldata qeid,\n string calldata platformCpuSvn,\n string calldata platformPceSvn,\n string calldata pceid\n ) external view returns (bytes memory pckCert) {\n (bytes16 qeidBytes, bytes2 pceidBytes, bytes16 platformCpuSvnBytes, bytes2 platformPceSvnBytes,) =\n _parseStringInputs(qeid, pceid, platformCpuSvn, platformPceSvn, \"\");\n\n bytes32 tcbmAttestationId =\n tcbmAttestations[_getTcbmKey(qeidBytes, pceidBytes, platformCpuSvnBytes, platformPceSvnBytes)];\n bytes18 tcbmBytes = bytes18(getAttestedData(tcbmAttestationId));\n bytes32 attestationId = _getPckAttestationId(qeidBytes, pceidBytes, tcbmBytes);\n if (attestationId != bytes32(0)) {\n pckCert = getAttestedData(attestationId);\n }\n }\n\n function getCerts(string calldata qeid, string calldata pceid)\n external\n view\n returns (string[] memory tcbms, bytes[] memory pckCerts)\n {\n (bytes16 qeidBytes, bytes2 pceidBytes,,,) = _parseStringInputs(qeid, pceid, \"\", \"\", \"\");\n\n bytes32 k = keccak256(abi.encodePacked(qeidBytes, pceidBytes));\n uint256 n = _tcbmHSets[k].length();\n if (n > 0) {\n tcbms = new string[](n);\n pckCerts = new bytes[](n);\n for (uint256 i = 0; i < n; i++) {\n bytes18 tcbmBytes = bytes18(_tcbmHSets[k].at(i));\n tcbms[i] = LibString.toHexStringNoPrefix(abi.encodePacked(tcbmBytes));\n bytes32 attestationId = _getPckAttestationId(qeidBytes, pceidBytes, tcbmBytes);\n pckCerts[i] = getAttestedData(attestationId);\n }\n }\n }\n\n /**\n * @notice Modified from Section 4.2.8 (getPlatformTcbsById)\n * @dev For simplicity's sake, the contract currently requires all the necessary parameters\n * to return a single tcbm.\n */\n function getPlatformTcbByIdAndSvns(\n string calldata qeid,\n string calldata pceid,\n string calldata platformCpuSvn,\n string calldata platformPceSvn\n ) external view returns (string memory tcbm) {\n (bytes16 qeidBytes, bytes2 pceidBytes, bytes16 platformCpuSvnBytes, bytes2 platformPceSvnBytes,) =\n _parseStringInputs(qeid, pceid, platformCpuSvn, platformPceSvn, \"\");\n\n bytes32 attestationId = _getTcbmAttestationId(qeidBytes, pceidBytes, platformCpuSvnBytes, platformPceSvnBytes);\n if (attestationId != bytes32(0)) {\n tcbm = LibString.toHexStringNoPrefix(abi.encodePacked(bytes18(getAttestedData(attestationId))));\n }\n }\n\n /**\n * @notice Modified from Section 4.2.2 (upsertPckCert)\n * @notice This method requires an additional CA parameter, because the on-chain PCCS does not\n * store any data that is contained in the PLATFORMS table.\n * @notice Therefore, there is no way to form a mapping between (qeid, pceid) to its corresponding CA.\n * @notice Hence, it is explicitly required to be stated here.\n * @param cert DER-encoded PCK Leaf Certificate\n * @dev Attestation Registry Entrypoint Contracts, such as Portals on Verax are responsible\n * @dev for performing ECDSA verification on the provided PCK Certs prior to attestations\n */\n function upsertPckCert(\n CA ca,\n string calldata qeid,\n string calldata pceid,\n string calldata tcbm,\n bytes calldata cert\n ) external pckCACheck(ca) returns (bytes32 attestationId) {\n (bytes16 qeidBytes, bytes2 pceidBytes,,, bytes18 tcbmBytes) = _parseStringInputs(qeid, pceid, \"\", \"\", tcbm);\n bytes32 hash = _validatePck(ca, cert, tcbmBytes, pceidBytes);\n AttestationRequest memory req = _buildPckCertAttestationRequest(qeidBytes, pceidBytes, tcbmBytes, cert);\n attestationId = _attestPck(req, hash);\n pckCertAttestations[keccak256(abi.encodePacked(qeidBytes, pceidBytes, tcbmBytes))] = attestationId;\n _upsertTcbm(qeidBytes, pceidBytes, tcbmBytes);\n }\n\n /// @dev currently missing strict TCB check on platformCpuSvn and platformPceSvn\n /// @dev is recommended to overwrite this method to implement TCB check\n function upsertPlatformTcbs(\n string calldata qeid,\n string calldata pceid,\n string calldata platformCpuSvn,\n string calldata platformPceSvn,\n string calldata tcbm\n ) external virtual returns (bytes32 attestationId) {\n (\n bytes16 qeidBytes,\n bytes2 pceidBytes,\n bytes16 platformCpuSvnBytes,\n bytes2 platformPceSvnBytes,\n bytes18 tcbmBytes\n ) = _parseStringInputs(qeid, pceid, platformCpuSvn, platformPceSvn, tcbm);\n\n bytes32 pckKey = keccak256(abi.encodePacked(qeidBytes, pceidBytes, tcbmBytes));\n bytes32 pckAttestationId = pckCertAttestations[pckKey];\n\n if (pckAttestationId == bytes32(0)) {\n revert Pck_Not_Found();\n }\n\n // parse PCK to check PCEID and tcbm\n bytes memory der = getAttestedData(pckAttestationId);\n X509CertObj memory pck = pckLib.parseX509DER(der);\n _validatePckTcb(pceidBytes, tcbmBytes, der, pck.extensionPtr);\n\n AttestationRequest memory req =\n _buildTcbmAttestationRequest(qeidBytes, pceidBytes, platformCpuSvnBytes, platformPceSvnBytes, tcbmBytes);\n attestationId = _attestTcbm(req);\n bytes32 tcbmKey = _getTcbmKey(qeidBytes, pceidBytes, platformCpuSvnBytes, platformPceSvnBytes);\n tcbmAttestations[tcbmKey] = attestationId;\n }\n\n /**\n * Queries PCK Certificate issuer chain for the input ca.\n * @param ca is either CA.PROCESSOR (uint8(1)) or CA.PLATFORM ((uint8(2)))\n * @return intermediateCert - the corresponding intermediate PCK CA (DER-encoded)\n * @return rootCert - Intel SGX Root CA (DER-encoded)\n */\n function getPckCertChain(CA ca)\n public\n view\n pckCACheck(ca)\n returns (bytes memory intermediateCert, bytes memory rootCert)\n {\n bytes32 intermediateCertAttestationId = Pcs.pcsCertAttestations(ca);\n bytes32 rootCertAttestationId = Pcs.pcsCertAttestations(CA.ROOT);\n intermediateCert = getAttestedData(intermediateCertAttestationId);\n rootCert = getAttestedData(rootCertAttestationId);\n }\n\n /**\n * @dev call this method to check whether the provided pck certificate has been revoked\n */\n function _checkPckIsRevocable(CA ca, bytes memory pck) internal view pckCACheck(ca) returns (bool revocable) {\n uint256 serialNumber = pckLib.getSerialNumber(pck);\n bytes memory crlData = getAttestedData(Pcs.pcsCrlAttestations(ca));\n revocable = crlLib.serialNumberIsRevoked(serialNumber, crlData);\n }\n\n /**\n * @dev overwrite this method to define the schemaID for the attestation of PCK Certificates\n */\n function pckSchemaID() public view virtual returns (bytes32 PCK_SCHEMA_ID);\n\n function tcbmSchemaID() public view virtual returns (bytes32 TCBM_SCHEMA_ID);\n\n /**\n * @dev implement logic to validate and attest PCK Certificates\n * @param req structure as defined by EAS\n * https://github.com/ethereum-attestation-service/eas-contracts/blob/52af661748bde9b40ae782907702f885852bc149/contracts/IEAS.sol#L9C1-L23C2\n * @return attestationId\n */\n function _attestPck(AttestationRequest memory req, bytes32 hash) internal virtual returns (bytes32 attestationId);\n\n /**\n * @dev implement logic to validate and attest TCBm\n * @param req structure as defined by EAS\n * https://github.com/ethereum-attestation-service/eas-contracts/blob/52af661748bde9b40ae782907702f885852bc149/contracts/IEAS.sol#L9C1-L23C2\n * @return attestationId\n */\n function _attestTcbm(AttestationRequest memory req) internal virtual returns (bytes32 attestationId);\n\n /**\n * @notice computes the key that maps to the corresponding attestation ID\n */\n function _getPckAttestationId(bytes16 qeid, bytes2 pceid, bytes18 tcbm)\n private\n view\n returns (bytes32 attestationId)\n {\n attestationId = pckCertAttestations[keccak256(abi.encodePacked(qeid, pceid, tcbm))];\n }\n\n /**\n * @notice computes the key that maps to the corresponding attestation ID\n */\n function _getTcbmAttestationId(bytes16 qeid, bytes2 pceid, bytes16 platformCpuSvn, bytes2 platformPceSvn)\n private\n view\n returns (bytes32 attestationId)\n {\n attestationId = tcbmAttestations[_getTcbmKey(qeid, pceid, platformCpuSvn, platformPceSvn)];\n }\n\n /**\n * @notice builds an EAS compliant attestation request\n */\n function _buildPckCertAttestationRequest(bytes16 qeid, bytes2 pceid, bytes18 tcbm, bytes calldata cert)\n private\n view\n returns (AttestationRequest memory req)\n {\n bytes32 predecessorAttestationId = _getPckAttestationId(qeid, pceid, tcbm);\n AttestationRequestData memory reqData = AttestationRequestData({\n recipient: msg.sender,\n expirationTime: 0,\n revocable: true,\n refUID: predecessorAttestationId,\n data: cert,\n value: 0\n });\n req = AttestationRequest({schema: pckSchemaID(), data: reqData});\n }\n\n function _buildTcbmAttestationRequest(\n bytes16 qeid,\n bytes2 pceid,\n bytes16 platformCpuSvn,\n bytes2 platformPceSvn,\n bytes18 tcbm\n ) private view returns (AttestationRequest memory req) {\n bytes32 predecessorAttestationId = _getTcbmAttestationId(qeid, pceid, platformCpuSvn, platformPceSvn);\n AttestationRequestData memory reqData = AttestationRequestData({\n recipient: msg.sender,\n expirationTime: 0, // assign zero here because this has already been checked\n revocable: true,\n refUID: predecessorAttestationId,\n data: abi.encode(tcbm),\n value: 0\n });\n req = AttestationRequest({schema: tcbmSchemaID(), data: reqData});\n }\n\n function _getTcbmKey(bytes16 qeid, bytes2 pceid, bytes16 platformCpuSvn, bytes2 platformPceSvn)\n private\n pure\n returns (bytes32 key)\n {\n key = keccak256(abi.encodePacked(qeid, pceid, platformCpuSvn, platformPceSvn));\n }\n\n function _upsertTcbm(bytes16 qeid, bytes2 pceid, bytes18 tcbm) private {\n bytes32 k = keccak256(abi.encodePacked(qeid, pceid));\n if (!_tcbmHSets[k].contains(bytes32(tcbm))) {\n _tcbmHSets[k].add(bytes32(tcbm));\n }\n }\n\n function _validatePck(CA ca, bytes memory der, bytes18 tcbm, bytes2 pceid) private view returns (bytes32 hash) {\n // Step 1: Check whether the pck has expired\n bool notExpired = pckLib.certIsNotExpired(der);\n if (!notExpired) {\n revert Certificate_Expired();\n }\n\n X509CertObj memory pck = pckLib.parseX509DER(der);\n hash = keccak256(pck.tbs);\n\n // Step 2: Check Issuer and Subject names\n string memory expectedIssuer;\n if (ca == CA.PLATFORM) {\n expectedIssuer = PCK_PLATFORM_CA_COMMON_NAME;\n } else if (ca == CA.PROCESSOR) {\n expectedIssuer = PCK_PROCESSOR_CA_COMMON_NAME;\n }\n if (!LibString.eq(pck.issuerCommonName, expectedIssuer)) {\n revert Invalid_Issuer_Name();\n }\n if (!LibString.eq(pck.subjectCommonName, PCK_COMMON_NAME)) {\n revert Invalid_Subject_Name();\n }\n\n // Step 3: validate PCEID and TCBm\n _validatePckTcb(pceid, tcbm, der, pck.extensionPtr);\n\n // Step 4: Check whether the pck has been revoked\n bytes memory crlData = getAttestedData(Pcs.pcsCrlAttestations(ca));\n if (crlData.length > 0) {\n bool revocable = crlLib.serialNumberIsRevoked(pck.serialNumber, crlData);\n if (revocable) {\n revert Certificate_Revoked(pck.serialNumber);\n }\n }\n\n // Step 5: Check signature\n (bytes memory issuerCert,) = getPckCertChain(ca);\n if (issuerCert.length > 0) {\n bytes32 digest = sha256(pck.tbs);\n bool sigVerified = verifySignature(digest, pck.signature, issuerCert);\n if (!sigVerified) {\n revert Invalid_Signature();\n }\n } else {\n revert Missing_Issuer();\n }\n }\n\n function _validatePckTcb(bytes2 pceid, bytes18 tcbm, bytes memory der, uint256 pckExtensionPtr) private view {\n (uint16 pcesvn, uint8[] memory cpusvns,, bytes memory pceidBytes) =\n pckLib.parsePckExtension(der, pckExtensionPtr);\n bool pceidMatched = bytes2(pceidBytes) == pceid;\n bytes memory encodedPceSvn = _littleEndianEncode(abi.encodePacked(pcesvn));\n bytes memory encodedCpuSvn;\n for (uint256 i = 0; i < cpusvns.length; i++) {\n encodedCpuSvn = abi.encodePacked(encodedCpuSvn, cpusvns[i]);\n }\n bytes memory encodedTcbmBytes = abi.encodePacked(encodedCpuSvn, encodedPceSvn);\n bool tcbIsValid = tcbm == bytes18(encodedTcbmBytes);\n if (!pceidMatched || !tcbIsValid) {\n revert TCB_Mismatch();\n }\n }\n\n function _littleEndianEncode(bytes memory input) private pure returns (bytes memory encoded) {\n uint256 n = input.length;\n for (uint256 i = n; i > 0;) {\n encoded = abi.encodePacked(encoded, input[i - 1]);\n unchecked {\n i--;\n }\n }\n }\n\n function _parseStringInputs(\n string memory qeid,\n string memory pceid,\n string memory platformCpuSvn,\n string memory platformPceSvn,\n string memory tcbm\n )\n private\n pure\n returns (\n bytes16 qeidBytes,\n bytes2 pceidBytes,\n bytes16 platformCpuSvnBytes,\n bytes2 platformPceSvnBytes,\n bytes18 tcbmBytes\n )\n {\n if (bytes(qeid).length == 32) {\n qeidBytes = bytes16(uint128(_parseUintFromHex(qeid)));\n }\n if (bytes(pceid).length == 4) {\n pceidBytes = bytes2(uint16(_parseUintFromHex(pceid)));\n }\n if (bytes(platformCpuSvn).length == 32) {\n platformCpuSvnBytes = bytes16(uint128(_parseUintFromHex(platformCpuSvn)));\n }\n if (bytes(platformPceSvn).length == 4) {\n platformPceSvnBytes = bytes2(uint16(_parseUintFromHex(platformPceSvn)));\n }\n if (bytes(tcbm).length == 36) {\n tcbmBytes = bytes18(uint144(_parseUintFromHex(tcbm)));\n }\n }\n}\n"},"lib/solady/src/auth/Ownable.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\n/// @notice Simple single owner authorization mixin.\n/// @author Solady (https://github.com/vectorized/solady/blob/main/src/auth/Ownable.sol)\n///\n/// @dev Note:\n/// This implementation does NOT auto-initialize the owner to `msg.sender`.\n/// You MUST call the `_initializeOwner` in the constructor / initializer.\n///\n/// While the ownable portion follows\n/// [EIP-173](https://eips.ethereum.org/EIPS/eip-173) for compatibility,\n/// the nomenclature for the 2-step ownership handover may be unique to this codebase.\nabstract contract Ownable {\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* CUSTOM ERRORS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev The caller is not authorized to call the function.\n error Unauthorized();\n\n /// @dev The `newOwner` cannot be the zero address.\n error NewOwnerIsZeroAddress();\n\n /// @dev The `pendingOwner` does not have a valid handover request.\n error NoHandoverRequest();\n\n /// @dev Cannot double-initialize.\n error AlreadyInitialized();\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* EVENTS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev The ownership is transferred from `oldOwner` to `newOwner`.\n /// This event is intentionally kept the same as OpenZeppelin's Ownable to be\n /// compatible with indexers and [EIP-173](https://eips.ethereum.org/EIPS/eip-173),\n /// despite it not being as lightweight as a single argument event.\n event OwnershipTransferred(address indexed oldOwner, address indexed newOwner);\n\n /// @dev An ownership handover to `pendingOwner` has been requested.\n event OwnershipHandoverRequested(address indexed pendingOwner);\n\n /// @dev The ownership handover to `pendingOwner` has been canceled.\n event OwnershipHandoverCanceled(address indexed pendingOwner);\n\n /// @dev `keccak256(bytes(\"OwnershipTransferred(address,address)\"))`.\n uint256 private constant _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE =\n 0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0;\n\n /// @dev `keccak256(bytes(\"OwnershipHandoverRequested(address)\"))`.\n uint256 private constant _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE =\n 0xdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d;\n\n /// @dev `keccak256(bytes(\"OwnershipHandoverCanceled(address)\"))`.\n uint256 private constant _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE =\n 0xfa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92;\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* STORAGE */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev The owner slot is given by:\n /// `bytes32(~uint256(uint32(bytes4(keccak256(\"_OWNER_SLOT_NOT\")))))`.\n /// It is intentionally chosen to be a high value\n /// to avoid collision with lower slots.\n /// The choice of manual storage layout is to enable compatibility\n /// with both regular and upgradeable contracts.\n bytes32 internal constant _OWNER_SLOT =\n 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff74873927;\n\n /// The ownership handover slot of `newOwner` is given by:\n /// ```\n /// mstore(0x00, or(shl(96, user), _HANDOVER_SLOT_SEED))\n /// let handoverSlot := keccak256(0x00, 0x20)\n /// ```\n /// It stores the expiry timestamp of the two-step ownership handover.\n uint256 private constant _HANDOVER_SLOT_SEED = 0x389a75e1;\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* INTERNAL FUNCTIONS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev Override to return true to make `_initializeOwner` prevent double-initialization.\n function _guardInitializeOwner() internal pure virtual returns (bool guard) {}\n\n /// @dev Initializes the owner directly without authorization guard.\n /// This function must be called upon initialization,\n /// regardless of whether the contract is upgradeable or not.\n /// This is to enable generalization to both regular and upgradeable contracts,\n /// and to save gas in case the initial owner is not the caller.\n /// For performance reasons, this function will not check if there\n /// is an existing owner.\n function _initializeOwner(address newOwner) internal virtual {\n if (_guardInitializeOwner()) {\n /// @solidity memory-safe-assembly\n assembly {\n let ownerSlot := _OWNER_SLOT\n if sload(ownerSlot) {\n mstore(0x00, 0x0dc149f0) // `AlreadyInitialized()`.\n revert(0x1c, 0x04)\n }\n // Clean the upper 96 bits.\n newOwner := shr(96, shl(96, newOwner))\n // Store the new value.\n sstore(ownerSlot, or(newOwner, shl(255, iszero(newOwner))))\n // Emit the {OwnershipTransferred} event.\n log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner)\n }\n } else {\n /// @solidity memory-safe-assembly\n assembly {\n // Clean the upper 96 bits.\n newOwner := shr(96, shl(96, newOwner))\n // Store the new value.\n sstore(_OWNER_SLOT, newOwner)\n // Emit the {OwnershipTransferred} event.\n log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner)\n }\n }\n }\n\n /// @dev Sets the owner directly without authorization guard.\n function _setOwner(address newOwner) internal virtual {\n if (_guardInitializeOwner()) {\n /// @solidity memory-safe-assembly\n assembly {\n let ownerSlot := _OWNER_SLOT\n // Clean the upper 96 bits.\n newOwner := shr(96, shl(96, newOwner))\n // Emit the {OwnershipTransferred} event.\n log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner)\n // Store the new value.\n sstore(ownerSlot, or(newOwner, shl(255, iszero(newOwner))))\n }\n } else {\n /// @solidity memory-safe-assembly\n assembly {\n let ownerSlot := _OWNER_SLOT\n // Clean the upper 96 bits.\n newOwner := shr(96, shl(96, newOwner))\n // Emit the {OwnershipTransferred} event.\n log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner)\n // Store the new value.\n sstore(ownerSlot, newOwner)\n }\n }\n }\n\n /// @dev Throws if the sender is not the owner.\n function _checkOwner() internal view virtual {\n /// @solidity memory-safe-assembly\n assembly {\n // If the caller is not the stored owner, revert.\n if iszero(eq(caller(), sload(_OWNER_SLOT))) {\n mstore(0x00, 0x82b42900) // `Unauthorized()`.\n revert(0x1c, 0x04)\n }\n }\n }\n\n /// @dev Returns how long a two-step ownership handover is valid for in seconds.\n /// Override to return a different value if needed.\n /// Made internal to conserve bytecode. Wrap it in a public function if needed.\n function _ownershipHandoverValidFor() internal view virtual returns (uint64) {\n return 48 * 3600;\n }\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* PUBLIC UPDATE FUNCTIONS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev Allows the owner to transfer the ownership to `newOwner`.\n function transferOwnership(address newOwner) public payable virtual onlyOwner {\n /// @solidity memory-safe-assembly\n assembly {\n if iszero(shl(96, newOwner)) {\n mstore(0x00, 0x7448fbae) // `NewOwnerIsZeroAddress()`.\n revert(0x1c, 0x04)\n }\n }\n _setOwner(newOwner);\n }\n\n /// @dev Allows the owner to renounce their ownership.\n function renounceOwnership() public payable virtual onlyOwner {\n _setOwner(address(0));\n }\n\n /// @dev Request a two-step ownership handover to the caller.\n /// The request will automatically expire in 48 hours (172800 seconds) by default.\n function requestOwnershipHandover() public payable virtual {\n unchecked {\n uint256 expires = block.timestamp + _ownershipHandoverValidFor();\n /// @solidity memory-safe-assembly\n assembly {\n // Compute and set the handover slot to `expires`.\n mstore(0x0c, _HANDOVER_SLOT_SEED)\n mstore(0x00, caller())\n sstore(keccak256(0x0c, 0x20), expires)\n // Emit the {OwnershipHandoverRequested} event.\n log2(0, 0, _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE, caller())\n }\n }\n }\n\n /// @dev Cancels the two-step ownership handover to the caller, if any.\n function cancelOwnershipHandover() public payable virtual {\n /// @solidity memory-safe-assembly\n assembly {\n // Compute and set the handover slot to 0.\n mstore(0x0c, _HANDOVER_SLOT_SEED)\n mstore(0x00, caller())\n sstore(keccak256(0x0c, 0x20), 0)\n // Emit the {OwnershipHandoverCanceled} event.\n log2(0, 0, _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE, caller())\n }\n }\n\n /// @dev Allows the owner to complete the two-step ownership handover to `pendingOwner`.\n /// Reverts if there is no existing ownership handover requested by `pendingOwner`.\n function completeOwnershipHandover(address pendingOwner) public payable virtual onlyOwner {\n /// @solidity memory-safe-assembly\n assembly {\n // Compute and set the handover slot to 0.\n mstore(0x0c, _HANDOVER_SLOT_SEED)\n mstore(0x00, pendingOwner)\n let handoverSlot := keccak256(0x0c, 0x20)\n // If the handover does not exist, or has expired.\n if gt(timestamp(), sload(handoverSlot)) {\n mstore(0x00, 0x6f5e8818) // `NoHandoverRequest()`.\n revert(0x1c, 0x04)\n }\n // Set the handover slot to 0.\n sstore(handoverSlot, 0)\n }\n _setOwner(pendingOwner);\n }\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* PUBLIC READ FUNCTIONS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev Returns the owner of the contract.\n function owner() public view virtual returns (address result) {\n /// @solidity memory-safe-assembly\n assembly {\n result := sload(_OWNER_SLOT)\n }\n }\n\n /// @dev Returns the expiry timestamp for the two-step ownership handover to `pendingOwner`.\n function ownershipHandoverExpiresAt(address pendingOwner)\n public\n view\n virtual\n returns (uint256 result)\n {\n /// @solidity memory-safe-assembly\n assembly {\n // Compute the handover slot.\n mstore(0x0c, _HANDOVER_SLOT_SEED)\n mstore(0x00, pendingOwner)\n // Load the handover slot.\n result := sload(keccak256(0x0c, 0x20))\n }\n }\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* MODIFIERS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev Marks a function as only callable by the owner.\n modifier onlyOwner() virtual {\n _checkOwner();\n _;\n }\n}\n"},"src/bases/DaoBase.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity >=0.8.0;\n\nabstract contract DaoBase {\n /**\n * @dev implement getter logic to retrieve attested data\n * @param attestationId maps to the data\n */\n function getAttestedData(bytes32 attestationId) public view virtual returns (bytes memory attestationData);\n\n /**\n * @dev must store the hash of a collateral (e.g. X509 Cert, TCBInfo JSON etc) in the attestation registry\n * @dev it is recommended to store hash as a separate attestation from the actual collateral\n * @dev this getter can be useful for checking the correctness of the queried attested collateral\n *\n * @dev may link the hash attestation with the attestation of the collateral\n * For example, the content of a hash attestation can be a tuple of bytes32 values consisting of:\n * (bytes32 collateralHash, bytes32 collateralAttestationId)\n * @param attestationId - the attestationId pointing to the hash attestation, or the collateral attestation\n * itself, if the hash is included as part of the attestation data, this varies by how you define the schema.\n */\n function getCollateralHash(bytes32 attestationId) public view virtual returns (bytes32 collateralHash);\n\n /// @dev https://github.com/Vectorized/solady/blob/4964e3e2da1bc86b0394f63a90821f51d60a260b/src/utils/JSONParserLib.sol#L339-L364\n /// @dev Parses an unsigned integer from a string (in hexadecimal, i.e. base 16).\n /// Reverts if `s` is not a valid uint256 hex string matching the RegEx\n /// `^(0[xX])?[0-9a-fA-F]+$`, or if the parsed number is too big for a uint256.\n function _parseUintFromHex(string memory s) internal pure returns (uint256 result) {\n /// @solidity memory-safe-assembly\n assembly {\n let n := mload(s)\n // Skip two if starts with '0x' or '0X'.\n let i := shl(1, and(eq(0x3078, or(shr(240, mload(add(s, 0x20))), 0x20)), gt(n, 1)))\n for {} 1 {} {\n i := add(i, 1)\n let c :=\n byte(\n and(0x1f, shr(and(mload(add(s, i)), 0xff), 0x3e4088843e41bac000000000000)),\n 0x3010a071000000b0104040208000c05090d060e0f\n )\n n := mul(n, iszero(or(iszero(c), shr(252, result))))\n result := add(shl(4, result), sub(c, 1))\n if iszero(lt(i, n)) { break }\n }\n if iszero(n) {\n mstore(0x00, 0x10182796) // `ParsingFailed()`.\n revert(0x1c, 0x04)\n }\n }\n }\n}\n"},"src/Common.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nenum CA {\n ROOT,\n PROCESSOR,\n PLATFORM,\n SIGNING\n}\n\n/// @notice Attestation Definition is taken from https://github.com/ethereum-attestation-service/eas-contracts/blob/52af661748bde9b40ae782907702f885852bc149/contracts/IEAS.sol#L9C1-L23C2\n/// @notice We opted for EAS Attestation Request Definition to ensure interoperability between Verax and EAS\n\nstruct AttestationRequestData {\n address recipient; // The recipient of the attestation.\n uint64 expirationTime; // The time when the attestation expires (Unix timestamp).\n bool revocable; // Whether the attestation is revocable.\n bytes32 refUID; // The UID of the related attestation.\n bytes data; // Custom attestation data.\n uint256 value; // An explicit ETH amount to send to the resolver. This is important to prevent accidental user errors.\n}\n\nstruct AttestationRequest {\n bytes32 schema; // The unique identifier of the schema.\n AttestationRequestData data; // The arguments of the attestation request.\n}\n\n/// @notice A struct representing a single attestation.\n/// https://github.com/ethereum-attestation-service/eas-contracts/blob/52af661748bde9b40ae782907702f885852bc149/contracts/Common.sol#L25C1-L37C2\nstruct Attestation {\n bytes32 uid; // A unique identifier of the attestation.\n bytes32 schema; // The unique identifier of the schema.\n uint64 time; // The time when the attestation was created (Unix timestamp).\n uint64 expirationTime; // The time when the attestation expires (Unix timestamp).\n uint64 revocationTime; // The time when the attestation was revoked (Unix timestamp).\n bytes32 refUID; // The UID of the related attestation.\n address recipient; // The recipient of the attestation.\n address attester; // The attester/sender of the attestation.\n bool revocable; // Whether the attestation is revocable.\n bytes data; // Custom attestation data.\n}\n"},"src/helpers/PCKHelper.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport {X509Helper, X509CertObj, Asn1Decode, NodePtr, BytesUtils} from \"./X509Helper.sol\";\n\ncontract PCKHelper is X509Helper {\n using Asn1Decode for bytes;\n using NodePtr for uint256;\n using BytesUtils for bytes;\n\n // 1.2.840.113741.1.13.1\n bytes constant SGX_EXTENSION_OID = hex\"2A864886F84D010D01\";\n // 1.2.840.113741.1.13.1.2\n bytes constant TCB_OID = hex\"2A864886F84D010D0102\";\n // 1.2.840.113741.1.13.1.2.17\n bytes constant PCESVN_OID = hex\"2A864886F84D010D010211\";\n // 1.2.840.113741.1.13.1.3\n bytes constant PCEID_OID = hex\"2A864886F84D010D0103\";\n // 1.2.840.113741.1.13.1.4\n bytes constant FMSPC_OID = hex\"2A864886F84D010D0104\";\n\n // https://github.com/intel/SGXDataCenterAttestationPrimitives/blob/e7604e02331b3377f3766ed3653250e03af72d45/QuoteVerification/QVL/Src/AttestationLibrary/src/CertVerification/X509Constants.h#L64\n uint256 constant SGX_TCB_CPUSVN_SIZE = 16;\n\n struct PCKTCBFlags {\n bool fmspcFound;\n bool pceidFound;\n bool tcbFound;\n }\n\n // 421k gas\n function parsePckExtension(bytes memory der, uint256 extensionPtr)\n external\n pure\n returns (uint16 pcesvn, uint8[] memory cpusvns, bytes memory fmspcBytes, bytes memory pceidBytes)\n {\n if (der[extensionPtr.ixs()] != 0xA3) {\n revert(\"Not an extension\");\n }\n uint256 parentPtr = der.firstChildOf(extensionPtr);\n uint256 childPtr = der.firstChildOf(parentPtr);\n bool success;\n (success, pcesvn, cpusvns, fmspcBytes, pceidBytes) = _findPckTcbInfo(der, childPtr, parentPtr);\n require(success, \"invalid SGX extension\");\n }\n\n function _findPckTcbInfo(bytes memory der, uint256 ptr, uint256 parentPtr)\n private\n pure\n returns (bool success, uint16 pcesvn, uint8[] memory cpusvns, bytes memory fmspcBytes, bytes memory pceidBytes)\n {\n // iterate through the elements in the Extension sequence\n // until we locate the SGX Extension OID\n while (ptr != 0) {\n uint256 internalPtr = der.firstChildOf(ptr);\n if (der[internalPtr.ixs()] != 0x06) {\n return (false, pcesvn, cpusvns, fmspcBytes, pceidBytes);\n }\n\n if (BytesUtils.compareBytes(der.bytesAt(internalPtr), SGX_EXTENSION_OID)) {\n // 1.2.840.113741.1.13.1\n internalPtr = der.nextSiblingOf(internalPtr);\n uint256 extnValueParentPtr = der.rootOfOctetStringAt(internalPtr);\n uint256 extnValuePtr = der.firstChildOf(extnValueParentPtr);\n\n // Copy flags to memory to avoid stack too deep\n PCKTCBFlags memory flags;\n\n while (!(flags.fmspcFound && flags.pceidFound && flags.tcbFound)) {\n uint256 extnValueOidPtr = der.firstChildOf(extnValuePtr);\n if (der[extnValueOidPtr.ixs()] != 0x06) {\n return (false, pcesvn, cpusvns, fmspcBytes, pceidBytes);\n }\n if (BytesUtils.compareBytes(der.bytesAt(extnValueOidPtr), TCB_OID)) {\n // 1.2.840.113741.1.13.1.2\n (flags.tcbFound, pcesvn, cpusvns) = _findTcb(der, extnValueOidPtr);\n }\n if (BytesUtils.compareBytes(der.bytesAt(extnValueOidPtr), PCEID_OID)) {\n // 1.2.840.113741.1.13.1.3\n uint256 pceidPtr = der.nextSiblingOf(extnValueOidPtr);\n pceidBytes = der.bytesAt(pceidPtr);\n flags.pceidFound = true;\n }\n if (BytesUtils.compareBytes(der.bytesAt(extnValueOidPtr), FMSPC_OID)) {\n // 1.2.840.113741.1.13.1.4\n uint256 fmspcPtr = der.nextSiblingOf(extnValueOidPtr);\n fmspcBytes = der.bytesAt(fmspcPtr);\n flags.fmspcFound = true;\n }\n\n if (extnValuePtr.ixl() < extnValueParentPtr.ixl()) {\n extnValuePtr = der.nextSiblingOf(extnValuePtr);\n } else {\n break;\n }\n }\n success = flags.fmspcFound && flags.pceidFound && flags.tcbFound;\n break;\n }\n\n if (ptr.ixl() < parentPtr.ixl()) {\n ptr = der.nextSiblingOf(ptr);\n } else {\n ptr = 0; // exit\n }\n }\n }\n\n function _findTcb(bytes memory der, uint256 oidPtr)\n private\n pure\n returns (bool success, uint16 pcesvn, uint8[] memory cpusvns)\n {\n // sibiling of tcbOid\n uint256 tcbPtr = der.nextSiblingOf(oidPtr);\n // get the first svn object in the sequence\n uint256 svnParentPtr = der.firstChildOf(tcbPtr);\n cpusvns = new uint8[](SGX_TCB_CPUSVN_SIZE);\n for (uint256 i = 0; i < SGX_TCB_CPUSVN_SIZE + 1; i++) {\n uint256 svnPtr = der.firstChildOf(svnParentPtr); // OID\n uint256 svnValuePtr = der.nextSiblingOf(svnPtr); // value\n bytes memory svnValueBytes = der.bytesAt(svnValuePtr);\n uint16 svnValue =\n svnValueBytes.length < 2 ? uint16(bytes2(svnValueBytes)) / 256 : uint16(bytes2(svnValueBytes));\n if (BytesUtils.compareBytes(der.bytesAt(svnPtr), PCESVN_OID)) {\n // pcesvn is 4 bytes in size\n pcesvn = uint16(svnValue);\n } else {\n uint8 cpusvn = uint8(svnValue);\n cpusvns[i] = cpusvn;\n }\n\n // iterate to the next svn object in the sequence\n svnParentPtr = der.nextSiblingOf(svnParentPtr);\n }\n success = true;\n }\n}\n"},"src/helpers/X509CRLHelper.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport {Asn1Decode, NodePtr} from \"../utils/Asn1Decode.sol\";\nimport {BytesUtils} from \"../utils/BytesUtils.sol\";\nimport {DateTimeUtils} from \"../utils/DateTimeUtils.sol\";\n\n/**\n * @title Solidity Structure representing X509 CRL\n * @notice This is a simplified structure of a DER-decoded X509 CRL\n */\nstruct X509CRLObj {\n uint256 serialNumber;\n string issuerCommonName;\n uint256 validityNotBefore;\n uint256 validityNotAfter;\n uint256[] serialNumbersRevoked;\n // for signature verification in the cert chain\n bytes signature;\n bytes tbs;\n}\n\n/**\n * @title X509 CRL Helper Contract\n * @notice This is a standalone contract that can be used by off-chain applications and smart contracts\n * to parse DER-encoded CRLs.\n */\ncontract X509CRLHelper {\n using Asn1Decode for bytes;\n using NodePtr for uint256;\n using BytesUtils for bytes;\n\n // 2.5.29.20\n bytes constant CRL_NUMBER_OID = hex\"551d14\";\n\n /// =================================================================================\n /// USE THE GETTERS BELOW IF YOU DON'T WANT TO PARSE THE ENTIRE X509 CRL\n /// =================================================================================\n\n function getTbsAndSig(bytes calldata der) external pure returns (bytes memory tbs, bytes memory sig) {\n uint256 root = der.root();\n uint256 tbsParentPtr = der.firstChildOf(root);\n uint256 sigPtr = der.nextSiblingOf(tbsParentPtr);\n sigPtr = der.nextSiblingOf(sigPtr);\n\n tbs = der.allBytesAt(tbsParentPtr);\n sig = _getSignature(der, sigPtr);\n }\n\n function getSerialNumber(bytes calldata der) external pure returns (uint256 serialNum) {\n uint256 root = der.root();\n uint256 tbsParentPtr = der.firstChildOf(root);\n uint256 tbsPtr = der.firstChildOf(tbsParentPtr);\n serialNum = _parseSerialNumber(der.bytesAt(tbsPtr));\n }\n\n function getIssuerCommonName(bytes calldata der) external pure returns (string memory issuerCommonName) {\n uint256 root = der.root();\n uint256 tbsParentPtr = der.firstChildOf(root);\n uint256 tbsPtr = der.firstChildOf(tbsParentPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n issuerCommonName = _getCommonName(der, der.firstChildOf(tbsPtr));\n }\n\n function crlIsNotExpired(bytes calldata der) external view returns (bool isValid) {\n uint256 root = der.root();\n uint256 tbsParentPtr = der.firstChildOf(root);\n uint256 tbsPtr = der.firstChildOf(tbsParentPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n (uint256 validityNotBefore, uint256 validityNotAfter) = _getValidity(der, tbsPtr);\n isValid = block.timestamp > validityNotBefore && block.timestamp < validityNotAfter;\n }\n\n function serialNumberIsRevoked(uint256 serialNumber, bytes calldata der) external pure returns (bool revoked) {\n uint256 root = der.root();\n uint256 tbsParentPtr = der.firstChildOf(root);\n uint256 tbsPtr = der.firstChildOf(tbsParentPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n uint256[] memory ret = _getRevokedSerialNumbers(der, tbsPtr, true, serialNumber);\n revoked = ret[0] == serialNumber;\n }\n\n /// x509 CRL generally contain a sequence of elements in the following order:\n /// 1. tbs\n /// - 1a. serial number\n /// - 1b. signature algorithm\n /// - 1c. issuer\n /// - - 1c(a). common name\n /// - - 1c(b). organization name\n /// - - 1c(c). locality name\n /// - - 1c(d). state or province name\n /// - - 1c(e). country name\n /// - 1d. not before\n /// - 1e. not after\n /// - 1f. revoked certificates\n /// - - A list consists of revoked serial numbers and reasons.\n /// - 1g. CRL extensions\n /// - - 1g(a) CRL number\n /// - - 1g(b) Authority Key Identifier\n /// 2. Signature Algorithm\n /// 3. Signature\n /// - 3a. X value\n /// - 3b. Y value\n function parseCRLDER(bytes calldata der) external pure returns (X509CRLObj memory crl) {\n uint256 root = der.root();\n\n uint256 tbsParentPtr = der.firstChildOf(root);\n\n uint256 tbsPtr = der.firstChildOf(tbsParentPtr);\n\n crl.serialNumber = uint256(bytes32(der.bytesAt(tbsPtr)));\n\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n\n crl.issuerCommonName = _getCommonName(der, der.firstChildOf(tbsPtr));\n\n tbsPtr = der.nextSiblingOf(tbsPtr);\n (crl.validityNotBefore, crl.validityNotAfter) = _getValidity(der, tbsPtr);\n\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n\n crl.serialNumbersRevoked = _getRevokedSerialNumbers(der, tbsPtr, false, 0);\n\n // tbs iteration completed\n // now we just need to look for the signature\n\n uint256 sigPtr = der.nextSiblingOf(tbsParentPtr);\n sigPtr = der.nextSiblingOf(sigPtr);\n crl.signature = _getSignature(der, sigPtr);\n }\n\n function _getCommonName(bytes calldata der, uint256 commonNameParentPtr)\n private\n pure\n returns (string memory commonName)\n {\n commonNameParentPtr = der.firstChildOf(commonNameParentPtr);\n commonNameParentPtr = der.firstChildOf(commonNameParentPtr);\n commonNameParentPtr = der.nextSiblingOf(commonNameParentPtr);\n commonName = string(der.bytesAt(commonNameParentPtr));\n }\n\n function _getValidity(bytes calldata der, uint256 validityPtr)\n private\n pure\n returns (uint256 notBefore, uint256 notAfter)\n {\n uint256 notBeforePtr = validityPtr;\n uint256 notAfterPtr = der.nextSiblingOf(notBeforePtr);\n notBefore = DateTimeUtils.fromDERToTimestamp(der.bytesAt(notBeforePtr));\n notAfter = DateTimeUtils.fromDERToTimestamp(der.bytesAt(notAfterPtr));\n }\n\n function _getRevokedSerialNumbers(bytes calldata der, uint256 revokedParentPtr, bool breakIfFound, uint256 filter)\n private\n pure\n returns (uint256[] memory serialNumbers)\n {\n uint256 revokedPtr = der.firstChildOf(revokedParentPtr);\n\n if (der[revokedPtr.ixs()] == 0xA0) {\n uint256 crlExtensionPtr = der.firstChildOf(revokedPtr);\n require(BytesUtils.compareBytes(der.bytesAt(crlExtensionPtr), CRL_NUMBER_OID), \"invalid CRL\");\n } else {\n bytes memory serials;\n while (revokedPtr.ixl() <= revokedParentPtr.ixl()) {\n uint256 serialPtr = der.firstChildOf(revokedPtr);\n bytes memory serialBytes = der.bytesAt(serialPtr);\n uint256 serialNumber = _parseSerialNumber(serialBytes);\n serials = abi.encodePacked(serials, serialNumber);\n if (breakIfFound && filter == serialNumber) {\n serialNumbers = new uint256[](1);\n serialNumbers[0] = filter;\n return serialNumbers;\n }\n revokedPtr = der.nextSiblingOf(revokedPtr);\n }\n uint256 count = serials.length / 32;\n // ABI encoding format for a dynamic uint256[] value\n serials = abi.encodePacked(abi.encode(0x20), abi.encode(count), serials);\n serialNumbers = new uint256[](count);\n serialNumbers = abi.decode(serials, (uint256[]));\n }\n }\n\n function _parseSerialNumber(bytes memory serialBytes) private pure returns (uint256 serial) {\n uint256 shift = 8 * (32 - serialBytes.length);\n serial = uint256(bytes32(serialBytes) >> shift);\n }\n\n function _getSignature(bytes calldata der, uint256 sigPtr) private pure returns (bytes memory sig) {\n sigPtr = der.rootOfBitStringAt(sigPtr);\n\n sigPtr = der.firstChildOf(sigPtr);\n bytes memory sigX = _trimBytes(der.bytesAt(sigPtr), 32);\n\n sigPtr = der.nextSiblingOf(sigPtr);\n bytes memory sigY = _trimBytes(der.bytesAt(sigPtr), 32);\n\n sig = abi.encodePacked(sigX, sigY);\n }\n\n /// @dev remove unnecessary prefix from the input\n /// @dev remove unnecessary prefix from the input\n function _trimBytes(bytes memory input, uint256 expectedLength) private pure returns (bytes memory output) {\n uint256 n = input.length;\n if (n == expectedLength) {\n output = input;\n } else if (n < expectedLength) {\n output = new bytes(expectedLength);\n uint256 padLength = expectedLength - n;\n for (uint256 i = 0; i < n; i++) {\n output[padLength + i] = input[i];\n }\n } else {\n uint256 lengthDiff = n - expectedLength;\n output = input.substring(lengthDiff, expectedLength);\n }\n }\n}\n"},"lib/openzeppelin-contracts/contracts/utils/structs/EnumerableSet.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (utils/structs/EnumerableSet.sol)\n// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.\n\npragma solidity ^0.8.20;\n\n/**\n * @dev Library for managing\n * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive\n * types.\n *\n * Sets have the following properties:\n *\n * - Elements are added, removed, and checked for existence in constant time\n * (O(1)).\n * - Elements are enumerated in O(n). No guarantees are made on the ordering.\n *\n * ```solidity\n * contract Example {\n * // Add the library methods\n * using EnumerableSet for EnumerableSet.AddressSet;\n *\n * // Declare a set state variable\n * EnumerableSet.AddressSet private mySet;\n * }\n * ```\n *\n * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)\n * and `uint256` (`UintSet`) are supported.\n *\n * [WARNING]\n * ====\n * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure\n * unusable.\n * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.\n *\n * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an\n * array of EnumerableSet.\n * ====\n */\nlibrary EnumerableSet {\n // To implement this library for multiple types with as little code\n // repetition as possible, we write it in terms of a generic Set type with\n // bytes32 values.\n // The Set implementation uses private functions, and user-facing\n // implementations (such as AddressSet) are just wrappers around the\n // underlying Set.\n // This means that we can only create new EnumerableSets for types that fit\n // in bytes32.\n\n struct Set {\n // Storage of set values\n bytes32[] _values;\n // Position is the index of the value in the `values` array plus 1.\n // Position 0 is used to mean a value is not in the set.\n mapping(bytes32 value => uint256) _positions;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function _add(Set storage set, bytes32 value) private returns (bool) {\n if (!_contains(set, value)) {\n set._values.push(value);\n // The value is stored at length-1, but we add 1 to all indexes\n // and use 0 as a sentinel value\n set._positions[value] = set._values.length;\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function _remove(Set storage set, bytes32 value) private returns (bool) {\n // We cache the value's position to prevent multiple reads from the same storage slot\n uint256 position = set._positions[value];\n\n if (position != 0) {\n // Equivalent to contains(set, value)\n // To delete an element from the _values array in O(1), we swap the element to delete with the last one in\n // the array, and then remove the last element (sometimes called as 'swap and pop').\n // This modifies the order of the array, as noted in {at}.\n\n uint256 valueIndex = position - 1;\n uint256 lastIndex = set._values.length - 1;\n\n if (valueIndex != lastIndex) {\n bytes32 lastValue = set._values[lastIndex];\n\n // Move the lastValue to the index where the value to delete is\n set._values[valueIndex] = lastValue;\n // Update the tracked position of the lastValue (that was just moved)\n set._positions[lastValue] = position;\n }\n\n // Delete the slot where the moved value was stored\n set._values.pop();\n\n // Delete the tracked position for the deleted slot\n delete set._positions[value];\n\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function _contains(Set storage set, bytes32 value) private view returns (bool) {\n return set._positions[value] != 0;\n }\n\n /**\n * @dev Returns the number of values on the set. O(1).\n */\n function _length(Set storage set) private view returns (uint256) {\n return set._values.length;\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function _at(Set storage set, uint256 index) private view returns (bytes32) {\n return set._values[index];\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function _values(Set storage set) private view returns (bytes32[] memory) {\n return set._values;\n }\n\n // Bytes32Set\n\n struct Bytes32Set {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n return _add(set._inner, value);\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n return _remove(set._inner, value);\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {\n return _contains(set._inner, value);\n }\n\n /**\n * @dev Returns the number of values in the set. O(1).\n */\n function length(Bytes32Set storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {\n return _at(set._inner, index);\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {\n bytes32[] memory store = _values(set._inner);\n bytes32[] memory result;\n\n /// @solidity memory-safe-assembly\n assembly {\n result := store\n }\n\n return result;\n }\n\n // AddressSet\n\n struct AddressSet {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(AddressSet storage set, address value) internal returns (bool) {\n return _add(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(AddressSet storage set, address value) internal returns (bool) {\n return _remove(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(AddressSet storage set, address value) internal view returns (bool) {\n return _contains(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Returns the number of values in the set. O(1).\n */\n function length(AddressSet storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(AddressSet storage set, uint256 index) internal view returns (address) {\n return address(uint160(uint256(_at(set._inner, index))));\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(AddressSet storage set) internal view returns (address[] memory) {\n bytes32[] memory store = _values(set._inner);\n address[] memory result;\n\n /// @solidity memory-safe-assembly\n assembly {\n result := store\n }\n\n return result;\n }\n\n // UintSet\n\n struct UintSet {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(UintSet storage set, uint256 value) internal returns (bool) {\n return _add(set._inner, bytes32(value));\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(UintSet storage set, uint256 value) internal returns (bool) {\n return _remove(set._inner, bytes32(value));\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(UintSet storage set, uint256 value) internal view returns (bool) {\n return _contains(set._inner, bytes32(value));\n }\n\n /**\n * @dev Returns the number of values in the set. O(1).\n */\n function length(UintSet storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(UintSet storage set, uint256 index) internal view returns (uint256) {\n return uint256(_at(set._inner, index));\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(UintSet storage set) internal view returns (uint256[] memory) {\n bytes32[] memory store = _values(set._inner);\n uint256[] memory result;\n\n /// @solidity memory-safe-assembly\n assembly {\n result := store\n }\n\n return result;\n }\n}\n"},"lib/solady/src/utils/LibString.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\n/// @notice Library for converting numbers into strings and other string operations.\n/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibString.sol)\n/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/LibString.sol)\n///\n/// Note:\n/// For performance and bytecode compactness, most of the string operations are restricted to\n/// byte strings (7-bit ASCII), except where otherwise specified.\n/// Usage of byte string operations on charsets with runes spanning two or more bytes\n/// can lead to undefined behavior.\nlibrary LibString {\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* CUSTOM ERRORS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev The length of the output is too small to contain all the hex digits.\n error HexLengthInsufficient();\n\n /// @dev The length of the string is more than 32 bytes.\n error TooBigForSmallString();\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* CONSTANTS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev The constant returned when the `search` is not found in the string.\n uint256 internal constant NOT_FOUND = type(uint256).max;\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* DECIMAL OPERATIONS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev Returns the base 10 decimal representation of `value`.\n function toString(uint256 value) internal pure returns (string memory str) {\n /// @solidity memory-safe-assembly\n assembly {\n // The maximum value of a uint256 contains 78 digits (1 byte per digit), but\n // we allocate 0xa0 bytes to keep the free memory pointer 32-byte word aligned.\n // We will need 1 word for the trailing zeros padding, 1 word for the length,\n // and 3 words for a maximum of 78 digits.\n str := add(mload(0x40), 0x80)\n // Update the free memory pointer to allocate.\n mstore(0x40, add(str, 0x20))\n // Zeroize the slot after the string.\n mstore(str, 0)\n\n // Cache the end of the memory to calculate the length later.\n let end := str\n\n let w := not(0) // Tsk.\n // We write the string from rightmost digit to leftmost digit.\n // The following is essentially a do-while loop that also handles the zero case.\n for { let temp := value } 1 {} {\n str := add(str, w) // `sub(str, 1)`.\n // Write the character to the pointer.\n // The ASCII index of the '0' character is 48.\n mstore8(str, add(48, mod(temp, 10)))\n // Keep dividing `temp` until zero.\n temp := div(temp, 10)\n if iszero(temp) { break }\n }\n\n let length := sub(end, str)\n // Move the pointer 32 bytes leftwards to make room for the length.\n str := sub(str, 0x20)\n // Store the length.\n mstore(str, length)\n }\n }\n\n /// @dev Returns the base 10 decimal representation of `value`.\n function toString(int256 value) internal pure returns (string memory str) {\n if (value >= 0) {\n return toString(uint256(value));\n }\n unchecked {\n str = toString(uint256(-value));\n }\n /// @solidity memory-safe-assembly\n assembly {\n // We still have some spare memory space on the left,\n // as we have allocated 3 words (96 bytes) for up to 78 digits.\n let length := mload(str) // Load the string length.\n mstore(str, 0x2d) // Store the '-' character.\n str := sub(str, 1) // Move back the string pointer by a byte.\n mstore(str, add(length, 1)) // Update the string length.\n }\n }\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* HEXADECIMAL OPERATIONS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev Returns the hexadecimal representation of `value`,\n /// left-padded to an input length of `length` bytes.\n /// The output is prefixed with \"0x\" encoded using 2 hexadecimal digits per byte,\n /// giving a total length of `length * 2 + 2` bytes.\n /// Reverts if `length` is too small for the output to contain all the digits.\n function toHexString(uint256 value, uint256 length) internal pure returns (string memory str) {\n str = toHexStringNoPrefix(value, length);\n /// @solidity memory-safe-assembly\n assembly {\n let strLength := add(mload(str), 2) // Compute the length.\n mstore(str, 0x3078) // Write the \"0x\" prefix.\n str := sub(str, 2) // Move the pointer.\n mstore(str, strLength) // Write the length.\n }\n }\n\n /// @dev Returns the hexadecimal representation of `value`,\n /// left-padded to an input length of `length` bytes.\n /// The output is prefixed with \"0x\" encoded using 2 hexadecimal digits per byte,\n /// giving a total length of `length * 2` bytes.\n /// Reverts if `length` is too small for the output to contain all the digits.\n function toHexStringNoPrefix(uint256 value, uint256 length)\n internal\n pure\n returns (string memory str)\n {\n /// @solidity memory-safe-assembly\n assembly {\n // We need 0x20 bytes for the trailing zeros padding, `length * 2` bytes\n // for the digits, 0x02 bytes for the prefix, and 0x20 bytes for the length.\n // We add 0x20 to the total and round down to a multiple of 0x20.\n // (0x20 + 0x20 + 0x02 + 0x20) = 0x62.\n str := add(mload(0x40), and(add(shl(1, length), 0x42), not(0x1f)))\n // Allocate the memory.\n mstore(0x40, add(str, 0x20))\n // Zeroize the slot after the string.\n mstore(str, 0)\n\n // Cache the end to calculate the length later.\n let end := str\n // Store \"0123456789abcdef\" in scratch space.\n mstore(0x0f, 0x30313233343536373839616263646566)\n\n let start := sub(str, add(length, length))\n let w := not(1) // Tsk.\n let temp := value\n // We write the string from rightmost digit to leftmost digit.\n // The following is essentially a do-while loop that also handles the zero case.\n for {} 1 {} {\n str := add(str, w) // `sub(str, 2)`.\n mstore8(add(str, 1), mload(and(temp, 15)))\n mstore8(str, mload(and(shr(4, temp), 15)))\n temp := shr(8, temp)\n if iszero(xor(str, start)) { break }\n }\n\n if temp {\n mstore(0x00, 0x2194895a) // `HexLengthInsufficient()`.\n revert(0x1c, 0x04)\n }\n\n // Compute the string's length.\n let strLength := sub(end, str)\n // Move the pointer and write the length.\n str := sub(str, 0x20)\n mstore(str, strLength)\n }\n }\n\n /// @dev Returns the hexadecimal representation of `value`.\n /// The output is prefixed with \"0x\" and encoded using 2 hexadecimal digits per byte.\n /// As address are 20 bytes long, the output will left-padded to have\n /// a length of `20 * 2 + 2` bytes.\n function toHexString(uint256 value) internal pure returns (string memory str) {\n str = toHexStringNoPrefix(value);\n /// @solidity memory-safe-assembly\n assembly {\n let strLength := add(mload(str), 2) // Compute the length.\n mstore(str, 0x3078) // Write the \"0x\" prefix.\n str := sub(str, 2) // Move the pointer.\n mstore(str, strLength) // Write the length.\n }\n }\n\n /// @dev Returns the hexadecimal representation of `value`.\n /// The output is prefixed with \"0x\".\n /// The output excludes leading \"0\" from the `toHexString` output.\n /// `0x00: \"0x0\", 0x01: \"0x1\", 0x12: \"0x12\", 0x123: \"0x123\"`.\n function toMinimalHexString(uint256 value) internal pure returns (string memory str) {\n str = toHexStringNoPrefix(value);\n /// @solidity memory-safe-assembly\n assembly {\n let o := eq(byte(0, mload(add(str, 0x20))), 0x30) // Whether leading zero is present.\n let strLength := add(mload(str), 2) // Compute the length.\n mstore(add(str, o), 0x3078) // Write the \"0x\" prefix, accounting for leading zero.\n str := sub(add(str, o), 2) // Move the pointer, accounting for leading zero.\n mstore(str, sub(strLength, o)) // Write the length, accounting for leading zero.\n }\n }\n\n /// @dev Returns the hexadecimal representation of `value`.\n /// The output excludes leading \"0\" from the `toHexStringNoPrefix` output.\n /// `0x00: \"0\", 0x01: \"1\", 0x12: \"12\", 0x123: \"123\"`.\n function toMinimalHexStringNoPrefix(uint256 value) internal pure returns (string memory str) {\n str = toHexStringNoPrefix(value);\n /// @solidity memory-safe-assembly\n assembly {\n let o := eq(byte(0, mload(add(str, 0x20))), 0x30) // Whether leading zero is present.\n let strLength := mload(str) // Get the length.\n str := add(str, o) // Move the pointer, accounting for leading zero.\n mstore(str, sub(strLength, o)) // Write the length, accounting for leading zero.\n }\n }\n\n /// @dev Returns the hexadecimal representation of `value`.\n /// The output is encoded using 2 hexadecimal digits per byte.\n /// As address are 20 bytes long, the output will left-padded to have\n /// a length of `20 * 2` bytes.\n function toHexStringNoPrefix(uint256 value) internal pure returns (string memory str) {\n /// @solidity memory-safe-assembly\n assembly {\n // We need 0x20 bytes for the trailing zeros padding, 0x20 bytes for the length,\n // 0x02 bytes for the prefix, and 0x40 bytes for the digits.\n // The next multiple of 0x20 above (0x20 + 0x20 + 0x02 + 0x40) is 0xa0.\n str := add(mload(0x40), 0x80)\n // Allocate the memory.\n mstore(0x40, add(str, 0x20))\n // Zeroize the slot after the string.\n mstore(str, 0)\n\n // Cache the end to calculate the length later.\n let end := str\n // Store \"0123456789abcdef\" in scratch space.\n mstore(0x0f, 0x30313233343536373839616263646566)\n\n let w := not(1) // Tsk.\n // We write the string from rightmost digit to leftmost digit.\n // The following is essentially a do-while loop that also handles the zero case.\n for { let temp := value } 1 {} {\n str := add(str, w) // `sub(str, 2)`.\n mstore8(add(str, 1), mload(and(temp, 15)))\n mstore8(str, mload(and(shr(4, temp), 15)))\n temp := shr(8, temp)\n if iszero(temp) { break }\n }\n\n // Compute the string's length.\n let strLength := sub(end, str)\n // Move the pointer and write the length.\n str := sub(str, 0x20)\n mstore(str, strLength)\n }\n }\n\n /// @dev Returns the hexadecimal representation of `value`.\n /// The output is prefixed with \"0x\", encoded using 2 hexadecimal digits per byte,\n /// and the alphabets are capitalized conditionally according to\n /// https://eips.ethereum.org/EIPS/eip-55\n function toHexStringChecksummed(address value) internal pure returns (string memory str) {\n str = toHexString(value);\n /// @solidity memory-safe-assembly\n assembly {\n let mask := shl(6, div(not(0), 255)) // `0b010000000100000000 ...`\n let o := add(str, 0x22)\n let hashed := and(keccak256(o, 40), mul(34, mask)) // `0b10001000 ... `\n let t := shl(240, 136) // `0b10001000 << 240`\n for { let i := 0 } 1 {} {\n mstore(add(i, i), mul(t, byte(i, hashed)))\n i := add(i, 1)\n if eq(i, 20) { break }\n }\n mstore(o, xor(mload(o), shr(1, and(mload(0x00), and(mload(o), mask)))))\n o := add(o, 0x20)\n mstore(o, xor(mload(o), shr(1, and(mload(0x20), and(mload(o), mask)))))\n }\n }\n\n /// @dev Returns the hexadecimal representation of `value`.\n /// The output is prefixed with \"0x\" and encoded using 2 hexadecimal digits per byte.\n function toHexString(address value) internal pure returns (string memory str) {\n str = toHexStringNoPrefix(value);\n /// @solidity memory-safe-assembly\n assembly {\n let strLength := add(mload(str), 2) // Compute the length.\n mstore(str, 0x3078) // Write the \"0x\" prefix.\n str := sub(str, 2) // Move the pointer.\n mstore(str, strLength) // Write the length.\n }\n }\n\n /// @dev Returns the hexadecimal representation of `value`.\n /// The output is encoded using 2 hexadecimal digits per byte.\n function toHexStringNoPrefix(address value) internal pure returns (string memory str) {\n /// @solidity memory-safe-assembly\n assembly {\n str := mload(0x40)\n\n // Allocate the memory.\n // We need 0x20 bytes for the trailing zeros padding, 0x20 bytes for the length,\n // 0x02 bytes for the prefix, and 0x28 bytes for the digits.\n // The next multiple of 0x20 above (0x20 + 0x20 + 0x02 + 0x28) is 0x80.\n mstore(0x40, add(str, 0x80))\n\n // Store \"0123456789abcdef\" in scratch space.\n mstore(0x0f, 0x30313233343536373839616263646566)\n\n str := add(str, 2)\n mstore(str, 40)\n\n let o := add(str, 0x20)\n mstore(add(o, 40), 0)\n\n value := shl(96, value)\n\n // We write the string from rightmost digit to leftmost digit.\n // The following is essentially a do-while loop that also handles the zero case.\n for { let i := 0 } 1 {} {\n let p := add(o, add(i, i))\n let temp := byte(i, value)\n mstore8(add(p, 1), mload(and(temp, 15)))\n mstore8(p, mload(shr(4, temp)))\n i := add(i, 1)\n if eq(i, 20) { break }\n }\n }\n }\n\n /// @dev Returns the hex encoded string from the raw bytes.\n /// The output is encoded using 2 hexadecimal digits per byte.\n function toHexString(bytes memory raw) internal pure returns (string memory str) {\n str = toHexStringNoPrefix(raw);\n /// @solidity memory-safe-assembly\n assembly {\n let strLength := add(mload(str), 2) // Compute the length.\n mstore(str, 0x3078) // Write the \"0x\" prefix.\n str := sub(str, 2) // Move the pointer.\n mstore(str, strLength) // Write the length.\n }\n }\n\n /// @dev Returns the hex encoded string from the raw bytes.\n /// The output is encoded using 2 hexadecimal digits per byte.\n function toHexStringNoPrefix(bytes memory raw) internal pure returns (string memory str) {\n /// @solidity memory-safe-assembly\n assembly {\n let length := mload(raw)\n str := add(mload(0x40), 2) // Skip 2 bytes for the optional prefix.\n mstore(str, add(length, length)) // Store the length of the output.\n\n // Store \"0123456789abcdef\" in scratch space.\n mstore(0x0f, 0x30313233343536373839616263646566)\n\n let o := add(str, 0x20)\n let end := add(raw, length)\n\n for {} iszero(eq(raw, end)) {} {\n raw := add(raw, 1)\n mstore8(add(o, 1), mload(and(mload(raw), 15)))\n mstore8(o, mload(and(shr(4, mload(raw)), 15)))\n o := add(o, 2)\n }\n mstore(o, 0) // Zeroize the slot after the string.\n mstore(0x40, add(o, 0x20)) // Allocate the memory.\n }\n }\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* RUNE STRING OPERATIONS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev Returns the number of UTF characters in the string.\n function runeCount(string memory s) internal pure returns (uint256 result) {\n /// @solidity memory-safe-assembly\n assembly {\n if mload(s) {\n mstore(0x00, div(not(0), 255))\n mstore(0x20, 0x0202020202020202020202020202020202020202020202020303030304040506)\n let o := add(s, 0x20)\n let end := add(o, mload(s))\n for { result := 1 } 1 { result := add(result, 1) } {\n o := add(o, byte(0, mload(shr(250, mload(o)))))\n if iszero(lt(o, end)) { break }\n }\n }\n }\n }\n\n /// @dev Returns if this string is a 7-bit ASCII string.\n /// (i.e. all characters codes are in [0..127])\n function is7BitASCII(string memory s) internal pure returns (bool result) {\n /// @solidity memory-safe-assembly\n assembly {\n let mask := shl(7, div(not(0), 255))\n result := 1\n let n := mload(s)\n if n {\n let o := add(s, 0x20)\n let end := add(o, n)\n let last := mload(end)\n mstore(end, 0)\n for {} 1 {} {\n if and(mask, mload(o)) {\n result := 0\n break\n }\n o := add(o, 0x20)\n if iszero(lt(o, end)) { break }\n }\n mstore(end, last)\n }\n }\n }\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* BYTE STRING OPERATIONS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n // For performance and bytecode compactness, byte string operations are restricted\n // to 7-bit ASCII strings. All offsets are byte offsets, not UTF character offsets.\n // Usage of byte string operations on charsets with runes spanning two or more bytes\n // can lead to undefined behavior.\n\n /// @dev Returns `subject` all occurrences of `search` replaced with `replacement`.\n function replace(string memory subject, string memory search, string memory replacement)\n internal\n pure\n returns (string memory result)\n {\n /// @solidity memory-safe-assembly\n assembly {\n let subjectLength := mload(subject)\n let searchLength := mload(search)\n let replacementLength := mload(replacement)\n\n subject := add(subject, 0x20)\n search := add(search, 0x20)\n replacement := add(replacement, 0x20)\n result := add(mload(0x40), 0x20)\n\n let subjectEnd := add(subject, subjectLength)\n if iszero(gt(searchLength, subjectLength)) {\n let subjectSearchEnd := add(sub(subjectEnd, searchLength), 1)\n let h := 0\n if iszero(lt(searchLength, 0x20)) { h := keccak256(search, searchLength) }\n let m := shl(3, sub(0x20, and(searchLength, 0x1f)))\n let s := mload(search)\n for {} 1 {} {\n let t := mload(subject)\n // Whether the first `searchLength % 32` bytes of\n // `subject` and `search` matches.\n if iszero(shr(m, xor(t, s))) {\n if h {\n if iszero(eq(keccak256(subject, searchLength), h)) {\n mstore(result, t)\n result := add(result, 1)\n subject := add(subject, 1)\n if iszero(lt(subject, subjectSearchEnd)) { break }\n continue\n }\n }\n // Copy the `replacement` one word at a time.\n for { let o := 0 } 1 {} {\n mstore(add(result, o), mload(add(replacement, o)))\n o := add(o, 0x20)\n if iszero(lt(o, replacementLength)) { break }\n }\n result := add(result, replacementLength)\n subject := add(subject, searchLength)\n if searchLength {\n if iszero(lt(subject, subjectSearchEnd)) { break }\n continue\n }\n }\n mstore(result, t)\n result := add(result, 1)\n subject := add(subject, 1)\n if iszero(lt(subject, subjectSearchEnd)) { break }\n }\n }\n\n let resultRemainder := result\n result := add(mload(0x40), 0x20)\n let k := add(sub(resultRemainder, result), sub(subjectEnd, subject))\n // Copy the rest of the string one word at a time.\n for {} lt(subject, subjectEnd) {} {\n mstore(resultRemainder, mload(subject))\n resultRemainder := add(resultRemainder, 0x20)\n subject := add(subject, 0x20)\n }\n result := sub(result, 0x20)\n let last := add(add(result, 0x20), k) // Zeroize the slot after the string.\n mstore(last, 0)\n mstore(0x40, add(last, 0x20)) // Allocate the memory.\n mstore(result, k) // Store the length.\n }\n }\n\n /// @dev Returns the byte index of the first location of `search` in `subject`,\n /// searching from left to right, starting from `from`.\n /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.\n function indexOf(string memory subject, string memory search, uint256 from)\n internal\n pure\n returns (uint256 result)\n {\n /// @solidity memory-safe-assembly\n assembly {\n for { let subjectLength := mload(subject) } 1 {} {\n if iszero(mload(search)) {\n if iszero(gt(from, subjectLength)) {\n result := from\n break\n }\n result := subjectLength\n break\n }\n let searchLength := mload(search)\n let subjectStart := add(subject, 0x20)\n\n result := not(0) // Initialize to `NOT_FOUND`.\n\n subject := add(subjectStart, from)\n let end := add(sub(add(subjectStart, subjectLength), searchLength), 1)\n\n let m := shl(3, sub(0x20, and(searchLength, 0x1f)))\n let s := mload(add(search, 0x20))\n\n if iszero(and(lt(subject, end), lt(from, subjectLength))) { break }\n\n if iszero(lt(searchLength, 0x20)) {\n for { let h := keccak256(add(search, 0x20), searchLength) } 1 {} {\n if iszero(shr(m, xor(mload(subject), s))) {\n if eq(keccak256(subject, searchLength), h) {\n result := sub(subject, subjectStart)\n break\n }\n }\n subject := add(subject, 1)\n if iszero(lt(subject, end)) { break }\n }\n break\n }\n for {} 1 {} {\n if iszero(shr(m, xor(mload(subject), s))) {\n result := sub(subject, subjectStart)\n break\n }\n subject := add(subject, 1)\n if iszero(lt(subject, end)) { break }\n }\n break\n }\n }\n }\n\n /// @dev Returns the byte index of the first location of `search` in `subject`,\n /// searching from left to right.\n /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.\n function indexOf(string memory subject, string memory search)\n internal\n pure\n returns (uint256 result)\n {\n result = indexOf(subject, search, 0);\n }\n\n /// @dev Returns the byte index of the first location of `search` in `subject`,\n /// searching from right to left, starting from `from`.\n /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.\n function lastIndexOf(string memory subject, string memory search, uint256 from)\n internal\n pure\n returns (uint256 result)\n {\n /// @solidity memory-safe-assembly\n assembly {\n for {} 1 {} {\n result := not(0) // Initialize to `NOT_FOUND`.\n let searchLength := mload(search)\n if gt(searchLength, mload(subject)) { break }\n let w := result\n\n let fromMax := sub(mload(subject), searchLength)\n if iszero(gt(fromMax, from)) { from := fromMax }\n\n let end := add(add(subject, 0x20), w)\n subject := add(add(subject, 0x20), from)\n if iszero(gt(subject, end)) { break }\n // As this function is not too often used,\n // we shall simply use keccak256 for smaller bytecode size.\n for { let h := keccak256(add(search, 0x20), searchLength) } 1 {} {\n if eq(keccak256(subject, searchLength), h) {\n result := sub(subject, add(end, 1))\n break\n }\n subject := add(subject, w) // `sub(subject, 1)`.\n if iszero(gt(subject, end)) { break }\n }\n break\n }\n }\n }\n\n /// @dev Returns the byte index of the first location of `search` in `subject`,\n /// searching from right to left.\n /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.\n function lastIndexOf(string memory subject, string memory search)\n internal\n pure\n returns (uint256 result)\n {\n result = lastIndexOf(subject, search, uint256(int256(-1)));\n }\n\n /// @dev Returns true if `search` is found in `subject`, false otherwise.\n function contains(string memory subject, string memory search) internal pure returns (bool) {\n return indexOf(subject, search) != NOT_FOUND;\n }\n\n /// @dev Returns whether `subject` starts with `search`.\n function startsWith(string memory subject, string memory search)\n internal\n pure\n returns (bool result)\n {\n /// @solidity memory-safe-assembly\n assembly {\n let searchLength := mload(search)\n // Just using keccak256 directly is actually cheaper.\n // forgefmt: disable-next-item\n result := and(\n iszero(gt(searchLength, mload(subject))),\n eq(\n keccak256(add(subject, 0x20), searchLength),\n keccak256(add(search, 0x20), searchLength)\n )\n )\n }\n }\n\n /// @dev Returns whether `subject` ends with `search`.\n function endsWith(string memory subject, string memory search)\n internal\n pure\n returns (bool result)\n {\n /// @solidity memory-safe-assembly\n assembly {\n let searchLength := mload(search)\n let subjectLength := mload(subject)\n // Whether `search` is not longer than `subject`.\n let withinRange := iszero(gt(searchLength, subjectLength))\n // Just using keccak256 directly is actually cheaper.\n // forgefmt: disable-next-item\n result := and(\n withinRange,\n eq(\n keccak256(\n // `subject + 0x20 + max(subjectLength - searchLength, 0)`.\n add(add(subject, 0x20), mul(withinRange, sub(subjectLength, searchLength))),\n searchLength\n ),\n keccak256(add(search, 0x20), searchLength)\n )\n )\n }\n }\n\n /// @dev Returns `subject` repeated `times`.\n function repeat(string memory subject, uint256 times)\n internal\n pure\n returns (string memory result)\n {\n /// @solidity memory-safe-assembly\n assembly {\n let subjectLength := mload(subject)\n if iszero(or(iszero(times), iszero(subjectLength))) {\n subject := add(subject, 0x20)\n result := mload(0x40)\n let output := add(result, 0x20)\n for {} 1 {} {\n // Copy the `subject` one word at a time.\n for { let o := 0 } 1 {} {\n mstore(add(output, o), mload(add(subject, o)))\n o := add(o, 0x20)\n if iszero(lt(o, subjectLength)) { break }\n }\n output := add(output, subjectLength)\n times := sub(times, 1)\n if iszero(times) { break }\n }\n mstore(output, 0) // Zeroize the slot after the string.\n let resultLength := sub(output, add(result, 0x20))\n mstore(result, resultLength) // Store the length.\n // Allocate the memory.\n mstore(0x40, add(result, add(resultLength, 0x20)))\n }\n }\n }\n\n /// @dev Returns a copy of `subject` sliced from `start` to `end` (exclusive).\n /// `start` and `end` are byte offsets.\n function slice(string memory subject, uint256 start, uint256 end)\n internal\n pure\n returns (string memory result)\n {\n /// @solidity memory-safe-assembly\n assembly {\n let subjectLength := mload(subject)\n if iszero(gt(subjectLength, end)) { end := subjectLength }\n if iszero(gt(subjectLength, start)) { start := subjectLength }\n if lt(start, end) {\n result := mload(0x40)\n let resultLength := sub(end, start)\n mstore(result, resultLength)\n subject := add(subject, start)\n let w := not(0x1f)\n // Copy the `subject` one word at a time, backwards.\n for { let o := and(add(resultLength, 0x1f), w) } 1 {} {\n mstore(add(result, o), mload(add(subject, o)))\n o := add(o, w) // `sub(o, 0x20)`.\n if iszero(o) { break }\n }\n // Zeroize the slot after the string.\n mstore(add(add(result, 0x20), resultLength), 0)\n // Allocate memory for the length and the bytes,\n // rounded up to a multiple of 32.\n mstore(0x40, add(result, and(add(resultLength, 0x3f), w)))\n }\n }\n }\n\n /// @dev Returns a copy of `subject` sliced from `start` to the end of the string.\n /// `start` is a byte offset.\n function slice(string memory subject, uint256 start)\n internal\n pure\n returns (string memory result)\n {\n result = slice(subject, start, uint256(int256(-1)));\n }\n\n /// @dev Returns all the indices of `search` in `subject`.\n /// The indices are byte offsets.\n function indicesOf(string memory subject, string memory search)\n internal\n pure\n returns (uint256[] memory result)\n {\n /// @solidity memory-safe-assembly\n assembly {\n let subjectLength := mload(subject)\n let searchLength := mload(search)\n\n if iszero(gt(searchLength, subjectLength)) {\n subject := add(subject, 0x20)\n search := add(search, 0x20)\n result := add(mload(0x40), 0x20)\n\n let subjectStart := subject\n let subjectSearchEnd := add(sub(add(subject, subjectLength), searchLength), 1)\n let h := 0\n if iszero(lt(searchLength, 0x20)) { h := keccak256(search, searchLength) }\n let m := shl(3, sub(0x20, and(searchLength, 0x1f)))\n let s := mload(search)\n for {} 1 {} {\n let t := mload(subject)\n // Whether the first `searchLength % 32` bytes of\n // `subject` and `search` matches.\n if iszero(shr(m, xor(t, s))) {\n if h {\n if iszero(eq(keccak256(subject, searchLength), h)) {\n subject := add(subject, 1)\n if iszero(lt(subject, subjectSearchEnd)) { break }\n continue\n }\n }\n // Append to `result`.\n mstore(result, sub(subject, subjectStart))\n result := add(result, 0x20)\n // Advance `subject` by `searchLength`.\n subject := add(subject, searchLength)\n if searchLength {\n if iszero(lt(subject, subjectSearchEnd)) { break }\n continue\n }\n }\n subject := add(subject, 1)\n if iszero(lt(subject, subjectSearchEnd)) { break }\n }\n let resultEnd := result\n // Assign `result` to the free memory pointer.\n result := mload(0x40)\n // Store the length of `result`.\n mstore(result, shr(5, sub(resultEnd, add(result, 0x20))))\n // Allocate memory for result.\n // We allocate one more word, so this array can be recycled for {split}.\n mstore(0x40, add(resultEnd, 0x20))\n }\n }\n }\n\n /// @dev Returns a arrays of strings based on the `delimiter` inside of the `subject` string.\n function split(string memory subject, string memory delimiter)\n internal\n pure\n returns (string[] memory result)\n {\n uint256[] memory indices = indicesOf(subject, delimiter);\n /// @solidity memory-safe-assembly\n assembly {\n let w := not(0x1f)\n let indexPtr := add(indices, 0x20)\n let indicesEnd := add(indexPtr, shl(5, add(mload(indices), 1)))\n mstore(add(indicesEnd, w), mload(subject))\n mstore(indices, add(mload(indices), 1))\n let prevIndex := 0\n for {} 1 {} {\n let index := mload(indexPtr)\n mstore(indexPtr, 0x60)\n if iszero(eq(index, prevIndex)) {\n let element := mload(0x40)\n let elementLength := sub(index, prevIndex)\n mstore(element, elementLength)\n // Copy the `subject` one word at a time, backwards.\n for { let o := and(add(elementLength, 0x1f), w) } 1 {} {\n mstore(add(element, o), mload(add(add(subject, prevIndex), o)))\n o := add(o, w) // `sub(o, 0x20)`.\n if iszero(o) { break }\n }\n // Zeroize the slot after the string.\n mstore(add(add(element, 0x20), elementLength), 0)\n // Allocate memory for the length and the bytes,\n // rounded up to a multiple of 32.\n mstore(0x40, add(element, and(add(elementLength, 0x3f), w)))\n // Store the `element` into the array.\n mstore(indexPtr, element)\n }\n prevIndex := add(index, mload(delimiter))\n indexPtr := add(indexPtr, 0x20)\n if iszero(lt(indexPtr, indicesEnd)) { break }\n }\n result := indices\n if iszero(mload(delimiter)) {\n result := add(indices, 0x20)\n mstore(result, sub(mload(indices), 2))\n }\n }\n }\n\n /// @dev Returns a concatenated string of `a` and `b`.\n /// Cheaper than `string.concat()` and does not de-align the free memory pointer.\n function concat(string memory a, string memory b)\n internal\n pure\n returns (string memory result)\n {\n /// @solidity memory-safe-assembly\n assembly {\n let w := not(0x1f)\n result := mload(0x40)\n let aLength := mload(a)\n // Copy `a` one word at a time, backwards.\n for { let o := and(add(aLength, 0x20), w) } 1 {} {\n mstore(add(result, o), mload(add(a, o)))\n o := add(o, w) // `sub(o, 0x20)`.\n if iszero(o) { break }\n }\n let bLength := mload(b)\n let output := add(result, aLength)\n // Copy `b` one word at a time, backwards.\n for { let o := and(add(bLength, 0x20), w) } 1 {} {\n mstore(add(output, o), mload(add(b, o)))\n o := add(o, w) // `sub(o, 0x20)`.\n if iszero(o) { break }\n }\n let totalLength := add(aLength, bLength)\n let last := add(add(result, 0x20), totalLength)\n // Zeroize the slot after the string.\n mstore(last, 0)\n // Stores the length.\n mstore(result, totalLength)\n // Allocate memory for the length and the bytes,\n // rounded up to a multiple of 32.\n mstore(0x40, and(add(last, 0x1f), w))\n }\n }\n\n /// @dev Returns a copy of the string in either lowercase or UPPERCASE.\n /// WARNING! This function is only compatible with 7-bit ASCII strings.\n function toCase(string memory subject, bool toUpper)\n internal\n pure\n returns (string memory result)\n {\n /// @solidity memory-safe-assembly\n assembly {\n let length := mload(subject)\n if length {\n result := add(mload(0x40), 0x20)\n subject := add(subject, 1)\n let flags := shl(add(70, shl(5, toUpper)), 0x3ffffff)\n let w := not(0)\n for { let o := length } 1 {} {\n o := add(o, w)\n let b := and(0xff, mload(add(subject, o)))\n mstore8(add(result, o), xor(b, and(shr(b, flags), 0x20)))\n if iszero(o) { break }\n }\n result := mload(0x40)\n mstore(result, length) // Store the length.\n let last := add(add(result, 0x20), length)\n mstore(last, 0) // Zeroize the slot after the string.\n mstore(0x40, add(last, 0x20)) // Allocate the memory.\n }\n }\n }\n\n /// @dev Returns a string from a small bytes32 string.\n /// `s` must be null-terminated, or behavior will be undefined.\n function fromSmallString(bytes32 s) internal pure returns (string memory result) {\n /// @solidity memory-safe-assembly\n assembly {\n result := mload(0x40)\n let n := 0\n for {} byte(n, s) { n := add(n, 1) } {} // Scan for '\\0'.\n mstore(result, n)\n let o := add(result, 0x20)\n mstore(o, s)\n mstore(add(o, n), 0)\n mstore(0x40, add(result, 0x40))\n }\n }\n\n /// @dev Returns the small string, with all bytes after the first null byte zeroized.\n function normalizeSmallString(bytes32 s) internal pure returns (bytes32 result) {\n /// @solidity memory-safe-assembly\n assembly {\n for {} byte(result, s) { result := add(result, 1) } {} // Scan for '\\0'.\n mstore(0x00, s)\n mstore(result, 0x00)\n result := mload(0x00)\n }\n }\n\n /// @dev Returns the string as a normalized null-terminated small string.\n function toSmallString(string memory s) internal pure returns (bytes32 result) {\n /// @solidity memory-safe-assembly\n assembly {\n result := mload(s)\n if iszero(lt(result, 33)) {\n mstore(0x00, 0xec92f9a3) // `TooBigForSmallString()`.\n revert(0x1c, 0x04)\n }\n result := shl(shl(3, sub(32, result)), mload(add(s, result)))\n }\n }\n\n /// @dev Returns a lowercased copy of the string.\n /// WARNING! This function is only compatible with 7-bit ASCII strings.\n function lower(string memory subject) internal pure returns (string memory result) {\n result = toCase(subject, false);\n }\n\n /// @dev Returns an UPPERCASED copy of the string.\n /// WARNING! This function is only compatible with 7-bit ASCII strings.\n function upper(string memory subject) internal pure returns (string memory result) {\n result = toCase(subject, true);\n }\n\n /// @dev Escapes the string to be used within HTML tags.\n function escapeHTML(string memory s) internal pure returns (string memory result) {\n /// @solidity memory-safe-assembly\n assembly {\n let end := add(s, mload(s))\n result := add(mload(0x40), 0x20)\n // Store the bytes of the packed offsets and strides into the scratch space.\n // `packed = (stride << 5) | offset`. Max offset is 20. Max stride is 6.\n mstore(0x1f, 0x900094)\n mstore(0x08, 0xc0000000a6ab)\n // Store \""&'<>\" into the scratch space.\n mstore(0x00, shl(64, 0x2671756f743b26616d703b262333393b266c743b2667743b))\n for {} iszero(eq(s, end)) {} {\n s := add(s, 1)\n let c := and(mload(s), 0xff)\n // Not in `[\"\\\"\",\"'\",\"&\",\"<\",\">\"]`.\n if iszero(and(shl(c, 1), 0x500000c400000000)) {\n mstore8(result, c)\n result := add(result, 1)\n continue\n }\n let t := shr(248, mload(c))\n mstore(result, mload(and(t, 0x1f)))\n result := add(result, shr(5, t))\n }\n let last := result\n mstore(last, 0) // Zeroize the slot after the string.\n result := mload(0x40)\n mstore(result, sub(last, add(result, 0x20))) // Store the length.\n mstore(0x40, add(last, 0x20)) // Allocate the memory.\n }\n }\n\n /// @dev Escapes the string to be used within double-quotes in a JSON.\n /// If `addDoubleQuotes` is true, the result will be enclosed in double-quotes.\n function escapeJSON(string memory s, bool addDoubleQuotes)\n internal\n pure\n returns (string memory result)\n {\n /// @solidity memory-safe-assembly\n assembly {\n let end := add(s, mload(s))\n result := add(mload(0x40), 0x20)\n if addDoubleQuotes {\n mstore8(result, 34)\n result := add(1, result)\n }\n // Store \"\\\\u0000\" in scratch space.\n // Store \"0123456789abcdef\" in scratch space.\n // Also, store `{0x08:\"b\", 0x09:\"t\", 0x0a:\"n\", 0x0c:\"f\", 0x0d:\"r\"}`.\n // into the scratch space.\n mstore(0x15, 0x5c75303030303031323334353637383961626364656662746e006672)\n // Bitmask for detecting `[\"\\\"\",\"\\\\\"]`.\n let e := or(shl(0x22, 1), shl(0x5c, 1))\n for {} iszero(eq(s, end)) {} {\n s := add(s, 1)\n let c := and(mload(s), 0xff)\n if iszero(lt(c, 0x20)) {\n if iszero(and(shl(c, 1), e)) {\n // Not in `[\"\\\"\",\"\\\\\"]`.\n mstore8(result, c)\n result := add(result, 1)\n continue\n }\n mstore8(result, 0x5c) // \"\\\\\".\n mstore8(add(result, 1), c)\n result := add(result, 2)\n continue\n }\n if iszero(and(shl(c, 1), 0x3700)) {\n // Not in `[\"\\b\",\"\\t\",\"\\n\",\"\\f\",\"\\d\"]`.\n mstore8(0x1d, mload(shr(4, c))) // Hex value.\n mstore8(0x1e, mload(and(c, 15))) // Hex value.\n mstore(result, mload(0x19)) // \"\\\\u00XX\".\n result := add(result, 6)\n continue\n }\n mstore8(result, 0x5c) // \"\\\\\".\n mstore8(add(result, 1), mload(add(c, 8)))\n result := add(result, 2)\n }\n if addDoubleQuotes {\n mstore8(result, 34)\n result := add(1, result)\n }\n let last := result\n mstore(last, 0) // Zeroize the slot after the string.\n result := mload(0x40)\n mstore(result, sub(last, add(result, 0x20))) // Store the length.\n mstore(0x40, add(last, 0x20)) // Allocate the memory.\n }\n }\n\n /// @dev Escapes the string to be used within double-quotes in a JSON.\n function escapeJSON(string memory s) internal pure returns (string memory result) {\n result = escapeJSON(s, false);\n }\n\n /// @dev Returns whether `a` equals `b`.\n function eq(string memory a, string memory b) internal pure returns (bool result) {\n /// @solidity memory-safe-assembly\n assembly {\n result := eq(keccak256(add(a, 0x20), mload(a)), keccak256(add(b, 0x20), mload(b)))\n }\n }\n\n /// @dev Returns whether `a` equals `b`, where `b` is a null-terminated small string.\n function eqs(string memory a, bytes32 b) internal pure returns (bool result) {\n /// @solidity memory-safe-assembly\n assembly {\n // These should be evaluated on compile time, as far as possible.\n let m := not(shl(7, div(not(iszero(b)), 255))) // `0x7f7f ...`.\n let x := not(or(m, or(b, add(m, and(b, m)))))\n let r := shl(7, iszero(iszero(shr(128, x))))\n r := or(r, shl(6, iszero(iszero(shr(64, shr(r, x))))))\n r := or(r, shl(5, lt(0xffffffff, shr(r, x))))\n r := or(r, shl(4, lt(0xffff, shr(r, x))))\n r := or(r, shl(3, lt(0xff, shr(r, x))))\n // forgefmt: disable-next-item\n result := gt(eq(mload(a), add(iszero(x), xor(31, shr(3, r)))),\n xor(shr(add(8, r), b), shr(add(8, r), mload(add(a, 0x20)))))\n }\n }\n\n /// @dev Packs a single string with its length into a single word.\n /// Returns `bytes32(0)` if the length is zero or greater than 31.\n function packOne(string memory a) internal pure returns (bytes32 result) {\n /// @solidity memory-safe-assembly\n assembly {\n // We don't need to zero right pad the string,\n // since this is our own custom non-standard packing scheme.\n result :=\n mul(\n // Load the length and the bytes.\n mload(add(a, 0x1f)),\n // `length != 0 && length < 32`. Abuses underflow.\n // Assumes that the length is valid and within the block gas limit.\n lt(sub(mload(a), 1), 0x1f)\n )\n }\n }\n\n /// @dev Unpacks a string packed using {packOne}.\n /// Returns the empty string if `packed` is `bytes32(0)`.\n /// If `packed` is not an output of {packOne}, the output behavior is undefined.\n function unpackOne(bytes32 packed) internal pure returns (string memory result) {\n /// @solidity memory-safe-assembly\n assembly {\n // Grab the free memory pointer.\n result := mload(0x40)\n // Allocate 2 words (1 for the length, 1 for the bytes).\n mstore(0x40, add(result, 0x40))\n // Zeroize the length slot.\n mstore(result, 0)\n // Store the length and bytes.\n mstore(add(result, 0x1f), packed)\n // Right pad with zeroes.\n mstore(add(add(result, 0x20), mload(result)), 0)\n }\n }\n\n /// @dev Packs two strings with their lengths into a single word.\n /// Returns `bytes32(0)` if combined length is zero or greater than 30.\n function packTwo(string memory a, string memory b) internal pure returns (bytes32 result) {\n /// @solidity memory-safe-assembly\n assembly {\n let aLength := mload(a)\n // We don't need to zero right pad the strings,\n // since this is our own custom non-standard packing scheme.\n result :=\n mul(\n // Load the length and the bytes of `a` and `b`.\n or(\n shl(shl(3, sub(0x1f, aLength)), mload(add(a, aLength))),\n mload(sub(add(b, 0x1e), aLength))\n ),\n // `totalLength != 0 && totalLength < 31`. Abuses underflow.\n // Assumes that the lengths are valid and within the block gas limit.\n lt(sub(add(aLength, mload(b)), 1), 0x1e)\n )\n }\n }\n\n /// @dev Unpacks strings packed using {packTwo}.\n /// Returns the empty strings if `packed` is `bytes32(0)`.\n /// If `packed` is not an output of {packTwo}, the output behavior is undefined.\n function unpackTwo(bytes32 packed)\n internal\n pure\n returns (string memory resultA, string memory resultB)\n {\n /// @solidity memory-safe-assembly\n assembly {\n // Grab the free memory pointer.\n resultA := mload(0x40)\n resultB := add(resultA, 0x40)\n // Allocate 2 words for each string (1 for the length, 1 for the byte). Total 4 words.\n mstore(0x40, add(resultB, 0x40))\n // Zeroize the length slots.\n mstore(resultA, 0)\n mstore(resultB, 0)\n // Store the lengths and bytes.\n mstore(add(resultA, 0x1f), packed)\n mstore(add(resultB, 0x1f), mload(add(add(resultA, 0x20), mload(resultA))))\n // Right pad with zeroes.\n mstore(add(add(resultA, 0x20), mload(resultA)), 0)\n mstore(add(add(resultB, 0x20), mload(resultB)), 0)\n }\n }\n\n /// @dev Directly returns `a` without copying.\n function directReturn(string memory a) internal pure {\n assembly {\n // Assumes that the string does not start from the scratch space.\n let retStart := sub(a, 0x20)\n let retSize := add(mload(a), 0x40)\n // Right pad with zeroes. Just in case the string is produced\n // by a method that doesn't zero right pad.\n mstore(add(retStart, retSize), 0)\n // Store the return offset.\n mstore(retStart, 0x20)\n // End the transaction, returning the string.\n return(retStart, retSize)\n }\n }\n}\n"},"src/bases/PcsDao.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport {CA, AttestationRequestData, AttestationRequest} from \"../Common.sol\";\nimport {X509Helper, X509CertObj} from \"../helpers/X509Helper.sol\";\nimport {X509CRLHelper, X509CRLObj} from \"../helpers/X509CRLHelper.sol\";\n\nimport {DaoBase} from \"./DaoBase.sol\";\nimport {SigVerifyBase} from \"./SigVerifyBase.sol\";\n\nimport {LibString} from \"solady/utils/LibString.sol\";\n\n/**\n * @title Intel PCS Data Access Object\n * @notice This is a core contract of our on-chain PCCS implementation as it provides methods\n * @notice to read/write essential collaterals such as the RootCA, Intermediate CAs and CRLs.\n * @notice All other DAOs are expected to configure and make external calls to this contract to fetch those collaterals.\n * @notice This contract is heavily inspired by Sections 4.2.5 and 4.2.6 in the Intel SGX PCCS Design Guideline\n * https://download.01.org/intel-sgx/sgx-dcap/1.19/linux/docs/SGX_DCAP_Caching_Service_Design_Guide.pdf\n */\nabstract contract PcsDao is DaoBase, SigVerifyBase {\n using LibString for string;\n\n X509CRLHelper public crlLib;\n\n /// @notice Fetches the attestationId of the attested PCS Certificate\n ///\n /// @dev Must ensure that the public key for the configured Intel Root CA matches with\n /// @dev the Intel source code at: https://github.com/intel/SGXDataCenterAttestationPrimitives/blob/39989a42bbbb0c968153a47254b6de79a27eb603/QuoteVerification/QvE/Enclave/qve.cpp#L92-L100\n ///\n /// @notice the schema of the attested data is the following:\n /// - bytes pcsCert\n mapping(CA => bytes32) public pcsCertAttestations;\n\n /// @notice Fetches the attestationId of the attested PCS CRLs\n ///\n /// @dev Verification of CRLs are conducted as part of the PCS attestation process\n ///\n /// @notice the schema of the attested data is the following:\n /// - bytes pcsCrl\n mapping(CA => bytes32) public pcsCrlAttestations;\n\n string constant PCK_PLATFORM_CA_COMMON_NAME = \"Intel SGX PCK Platform CA\";\n string constant PCK_PROCESSOR_CA_COMMON_NAME = \"Intel SGX PCK Processor CA\";\n string constant SIGNING_COMMON_NAME = \"Intel SGX TCB Signing\";\n string constant ROOT_CA_COMMON_NAME = \"Intel SGX Root CA\";\n\n // keccak256(hex\"0ba9c4c0c0c86193a3fe23d6b02cda10a8bbd4e88e48b4458561a36e705525f567918e2edc88e40d860bd0cc4ee26aacc988e505a953558c453f6b0904ae7394\")\n // the uncompressed (0x04) prefix is not included in the pubkey pre-image\n bytes32 constant ROOT_CA_PUBKEY_HASH = 0x89f72d7c488e5b53a77c23ebcb36970ef7eb5bcf6658e9b8292cfbe4703a8473;\n\n error Missing_Certificate(CA ca);\n error Invalid_PCK_CA(CA ca);\n error Invalid_Issuer_Name();\n error Invalid_Subject_Name();\n error Certificate_Expired();\n error Root_Key_Mismatch();\n error Certificate_Revoked(CA ca, uint256 serialNum);\n error Missing_Issuer();\n error Invalid_Signature();\n\n constructor(address _x509, address _crl) SigVerifyBase(_x509) {\n crlLib = X509CRLHelper(_crl);\n }\n\n modifier pckCACheck(CA ca) {\n if (ca == CA.ROOT || ca == CA.SIGNING) {\n revert Invalid_PCK_CA(ca);\n }\n _;\n }\n\n /**\n * @param ca see {Common.sol} for definition\n * @return cert - DER encoded certificate\n * @return crl - DER-encoded CRLs that is signed by the provided cert\n */\n function getCertificateById(CA ca) external view returns (bytes memory cert, bytes memory crl) {\n bytes32 pcsCertAttestationId = pcsCertAttestations[ca];\n if (pcsCertAttestationId == bytes32(0)) {\n revert Missing_Certificate(ca);\n }\n cert = getAttestedData(pcsCertAttestationId);\n\n bytes32 pcsCrlAttestationId = pcsCrlAttestations[ca];\n if (pcsCrlAttestationId != bytes32(0)) {\n crl = getAttestedData(pcsCrlAttestationId);\n }\n }\n\n /**\n * Section 4.2.6 (upsertPcsCertificates)\n * @param ca replaces the \"id\" value with the ca_id\n * @param cert the DER-encoded certificate\n */\n function upsertPcsCertificates(CA ca, bytes calldata cert) external returns (bytes32 attestationId) {\n bytes32 hash = _validatePcsCert(ca, cert);\n AttestationRequest memory req = _buildPcsAttestationRequest(false, ca, cert);\n attestationId = _attestPcs(req, hash);\n pcsCertAttestations[ca] = attestationId;\n }\n\n /**\n * Section 4.2.5 (upsertPckCrl)\n * @param ca either CA.PROCESSOR or CA.PLATFORM\n * @param crl the DER-encoded CRL\n */\n function upsertPckCrl(CA ca, bytes calldata crl) external pckCACheck(ca) returns (bytes32 attestationId) {\n attestationId = _upsertPcsCrl(ca, crl);\n }\n\n function upsertRootCACrl(bytes calldata rootcacrl) external returns (bytes32 attestationId) {\n attestationId = _upsertPcsCrl(CA.ROOT, rootcacrl);\n }\n\n function pcsCertSchemaID() public view virtual returns (bytes32 PCS_CERT_SCHEMA_ID);\n\n function pcsCrlSchemaID() public view virtual returns (bytes32 PCS_CRL_SCHEMA_ID);\n\n /**\n * @dev implement logic to validate and attest PCS Certificates or CRLs\n * @param req structure as defined by EAS\n * https://github.com/ethereum-attestation-service/eas-contracts/blob/52af661748bde9b40ae782907702f885852bc149/contracts/IEAS.sol#L9C1-L23C2\n * @return attestationId\n */\n function _attestPcs(AttestationRequest memory req, bytes32 hash) internal virtual returns (bytes32 attestationId);\n\n function _upsertPcsCrl(CA ca, bytes calldata crl) private returns (bytes32 attestationId) {\n bytes32 hash = _validatePcsCrl(ca, crl);\n AttestationRequest memory req = _buildPcsAttestationRequest(true, ca, crl);\n attestationId = _attestPcs(req, hash);\n pcsCrlAttestations[ca] = attestationId;\n }\n\n /**\n * @notice builds an EAS compliant attestation request\n * @param isCrl - true only if the attested data is a CRL\n * @param der - contains the DER encoded data, specified by isCrl and CA\n */\n function _buildPcsAttestationRequest(bool isCrl, CA ca, bytes calldata der)\n private\n view\n returns (AttestationRequest memory req)\n {\n bytes32 predecessorAttestationId = isCrl ? pcsCrlAttestations[ca] : pcsCertAttestations[ca];\n AttestationRequestData memory reqData = AttestationRequestData({\n recipient: msg.sender,\n expirationTime: 0, // assign zero here because this has already been checked\n revocable: true,\n refUID: predecessorAttestationId,\n data: der,\n value: 0\n });\n bytes32 schemaId = isCrl ? pcsCrlSchemaID() : pcsCertSchemaID();\n req = AttestationRequest({schema: schemaId, data: reqData});\n }\n\n function _validatePcsCert(CA ca, bytes calldata cert) private view returns (bytes32 hash) {\n X509Helper x509Lib = X509Helper(x509);\n\n // Step 1: Check whether cert has expired\n bool notExpired = x509Lib.certIsNotExpired(cert);\n if (!notExpired) {\n revert Certificate_Expired();\n }\n\n // Step 2: Check issuer and subject common names are valid\n string memory issuerName = x509Lib.getIssuerCommonName(cert);\n string memory subjectName = x509Lib.getSubjectCommonName(cert);\n string memory expectedIssuer = ROOT_CA_COMMON_NAME;\n string memory expectedSubject;\n if (ca == CA.PLATFORM) {\n expectedSubject = PCK_PLATFORM_CA_COMMON_NAME;\n } else if (ca == CA.PROCESSOR) {\n expectedSubject = PCK_PROCESSOR_CA_COMMON_NAME;\n } else if (ca == CA.SIGNING) {\n expectedSubject = SIGNING_COMMON_NAME;\n } else if (ca == CA.ROOT) {\n expectedSubject = ROOT_CA_COMMON_NAME;\n }\n\n if (!LibString.eq(issuerName, expectedIssuer)) {\n revert Invalid_Issuer_Name();\n }\n if (!LibString.eq(subjectName, expectedSubject)) {\n revert Invalid_Subject_Name();\n }\n\n // Step 3: Check Revocation Status\n bytes memory rootCrlData = getAttestedData(pcsCrlAttestations[CA.ROOT]);\n if (ca == CA.ROOT) {\n bytes memory pubKey = x509Lib.getSubjectPublicKey(cert);\n if (keccak256(pubKey) != ROOT_CA_PUBKEY_HASH) {\n revert Root_Key_Mismatch();\n }\n } else if (rootCrlData.length > 0) {\n uint256 serialNum = x509Lib.getSerialNumber(cert);\n bool revoked = crlLib.serialNumberIsRevoked(serialNum, rootCrlData);\n if (revoked) {\n revert Certificate_Revoked(ca, serialNum);\n }\n }\n\n // Step 4: Check signature\n bytes memory rootCert = _getIssuer(CA.ROOT);\n (bytes memory tbs, bytes memory signature) = x509Lib.getTbsAndSig(cert);\n bytes32 digest = sha256(tbs);\n bool sigVerified;\n if (ca == CA.ROOT) {\n // the root certificate is issued by its own key\n sigVerified = verifySignature(digest, signature, cert);\n } else if (rootCert.length > 0) {\n sigVerified = verifySignature(digest, signature, rootCert);\n } else {\n // all other certificates should already have an iusuer configured\n revert Missing_Issuer();\n }\n\n if (!sigVerified) {\n revert Invalid_Signature();\n }\n\n hash = keccak256(tbs);\n }\n\n function _validatePcsCrl(CA ca, bytes calldata crl) private view returns (bytes32 hash) {\n // Step 1: Check whether CRL has expired\n bool notExpired = crlLib.crlIsNotExpired(crl);\n if (!notExpired) {\n revert Certificate_Expired();\n }\n\n // Step 2: Check CRL issuer\n string memory issuerCommonName = crlLib.getIssuerCommonName(crl);\n string memory expectedIssuer;\n if (ca == CA.PLATFORM || ca == CA.PROCESSOR) {\n expectedIssuer = ca == CA.PLATFORM ? PCK_PLATFORM_CA_COMMON_NAME : PCK_PROCESSOR_CA_COMMON_NAME;\n } else {\n expectedIssuer = ROOT_CA_COMMON_NAME;\n }\n if (!LibString.eq(issuerCommonName, expectedIssuer)) {\n revert Invalid_Issuer_Name();\n }\n\n // Step 3: Verify signature\n (bytes memory tbs, bytes memory signature) = crlLib.getTbsAndSig(crl);\n bytes32 digest = sha256(tbs);\n bool sigVerified = verifySignature(digest, signature, _getIssuer(ca));\n if (!sigVerified) {\n revert Invalid_Signature();\n }\n\n hash = keccak256(tbs);\n }\n\n function _getIssuer(CA ca) private view returns (bytes memory issuerCert) {\n bytes32 intermediateCertAttestationId = pcsCertAttestations[ca];\n bytes32 rootCertAttestationId = pcsCertAttestations[CA.ROOT];\n if (ca == CA.PLATFORM || ca == CA.PROCESSOR) {\n // this is applicable to crls only\n // since all certs in the pcsdao are issued by the root\n issuerCert = getAttestedData(intermediateCertAttestationId);\n } else {\n issuerCert = getAttestedData(rootCertAttestationId);\n }\n }\n}\n"},"src/bases/SigVerifyBase.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport {BytesUtils} from \"../utils/BytesUtils.sol\";\nimport {P256Verifier} from \"../utils/P256Verifier.sol\";\n\ninterface IX509 {\n function getSubjectPublicKey(bytes memory der) external pure returns (bytes memory pubKey);\n}\n\nabstract contract SigVerifyBase {\n address public x509;\n\n using BytesUtils for bytes;\n\n constructor(address _x509helper) {\n x509 = _x509helper;\n }\n\n function verifySignature(bytes32 digest, bytes memory signature, bytes memory signingCertBlob)\n internal\n view\n returns (bool verified)\n {\n if (signature.length != 64) {\n return false;\n }\n\n bytes memory pubKey = IX509(x509).getSubjectPublicKey(signingCertBlob);\n if (pubKey.length != 64) {\n return false;\n }\n\n verified = P256Verifier.ecdsaVerify(digest, signature, pubKey);\n }\n}\n"},"src/helpers/X509Helper.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport {Asn1Decode, NodePtr} from \"../utils/Asn1Decode.sol\";\nimport {BytesUtils} from \"../utils/BytesUtils.sol\";\nimport {DateTimeUtils} from \"../utils/DateTimeUtils.sol\";\n\n/**\n * @title Solidity Structure representing X509 Certificates\n * @notice This is a simplified structure of a DER-decoded X509 Certificate\n */\nstruct X509CertObj {\n uint256 serialNumber;\n string issuerCommonName;\n uint256 validityNotBefore;\n uint256 validityNotAfter;\n string subjectCommonName;\n bytes subjectPublicKey;\n // the extension needs to be parsed further for PCK Certificates\n uint256 extensionPtr;\n // for signature verification in the cert chain\n bytes signature;\n bytes tbs;\n}\n\n/**\n * @title X509 Certificates Helper Contract\n * @notice This is a standalone contract that can be used by off-chain applications and smart contracts\n * to parse DER-encoded X509 certificates.\n * @dev The Extension sequence in Intel PCK Certificates is a custom ASN.1 Sequence that needs to be\n * @dev parsed further in a more specialized PCKHelper contract.\n */\ncontract X509Helper {\n using Asn1Decode for bytes;\n using NodePtr for uint256;\n using BytesUtils for bytes;\n\n /// =================================================================================\n /// USE THE GETTERS BELOW IF YOU DON'T WANT TO PARSE THE ENTIRE X509 CERTIFICATE\n /// =================================================================================\n\n function getTbsAndSig(bytes calldata der) external pure returns (bytes memory tbs, bytes memory sig) {\n uint256 root = der.root();\n uint256 tbsParentPtr = der.firstChildOf(root);\n uint256 sigPtr = der.nextSiblingOf(tbsParentPtr);\n sigPtr = der.nextSiblingOf(sigPtr);\n\n tbs = der.allBytesAt(tbsParentPtr);\n sig = _getSignature(der, sigPtr);\n }\n\n function getSerialNumber(bytes calldata der) external pure returns (uint256 serialNum) {\n uint256 root = der.root();\n uint256 tbsParentPtr = der.firstChildOf(root);\n uint256 tbsPtr = der.firstChildOf(tbsParentPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n serialNum = _parseSerialNumber(der.bytesAt(tbsPtr));\n }\n\n function getIssuerCommonName(bytes calldata der) external pure returns (string memory issuerCommonName) {\n uint256 root = der.root();\n uint256 tbsParentPtr = der.firstChildOf(root);\n uint256 tbsPtr = der.firstChildOf(tbsParentPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n issuerCommonName = _getCommonName(der, der.firstChildOf(tbsPtr));\n }\n\n function certIsNotExpired(bytes calldata der) external view returns (bool isValid) {\n uint256 root = der.root();\n uint256 tbsParentPtr = der.firstChildOf(root);\n uint256 tbsPtr = der.firstChildOf(tbsParentPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n (uint256 validityNotBefore, uint256 validityNotAfter) = _getValidity(der, tbsPtr);\n isValid = block.timestamp > validityNotBefore && block.timestamp < validityNotAfter;\n }\n\n function getSubjectCommonName(bytes calldata der) external pure returns (string memory subjectCommonName) {\n uint256 root = der.root();\n uint256 tbsParentPtr = der.firstChildOf(root);\n uint256 tbsPtr = der.firstChildOf(tbsParentPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n subjectCommonName = _getCommonName(der, der.firstChildOf(tbsPtr));\n }\n\n function getSubjectPublicKey(bytes calldata der) external pure returns (bytes memory pubKey) {\n uint256 root = der.root();\n uint256 tbsParentPtr = der.firstChildOf(root);\n uint256 tbsPtr = der.firstChildOf(tbsParentPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n pubKey = _getSubjectPublicKey(der, der.firstChildOf(tbsPtr));\n }\n\n /// x509 Certificates generally contain a sequence of elements in the following order:\n /// 1. tbs\n /// - 1a. version\n /// - 1b. serial number\n /// - 1c. siganture algorithm\n /// - 1d. issuer\n /// - - 1d(a). common name\n /// - - 1d(b). organization name\n /// - - 1d(c). locality name\n /// - - 1d(d). state or province name\n /// - - 1d(e). country name\n /// - 1e. validity\n /// - - 1e(a) notBefore\n /// - - 1e(b) notAfter\n /// - 1f. subject\n /// - - contains the same set of elements as 1d\n /// - 1g. subject public key info\n /// - - 1g(a). algorithm\n /// - - 1g(b). subject public key\n /// - 1h. Extensions\n /// 2. Signature Algorithm\n /// 3. Signature\n /// - 3a. X value\n /// - 3b. Y value\n function parseX509DER(bytes calldata der) external pure returns (X509CertObj memory cert) {\n uint256 root = der.root();\n\n uint256 tbsParentPtr = der.firstChildOf(root);\n cert.tbs = der.allBytesAt(tbsParentPtr);\n\n uint256 tbsPtr = der.firstChildOf(tbsParentPtr);\n\n tbsPtr = der.nextSiblingOf(tbsPtr);\n\n cert.serialNumber = _parseSerialNumber(der.bytesAt(tbsPtr));\n\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n\n cert.issuerCommonName = _getCommonName(der, der.firstChildOf(tbsPtr));\n\n tbsPtr = der.nextSiblingOf(tbsPtr);\n (cert.validityNotBefore, cert.validityNotAfter) = _getValidity(der, tbsPtr);\n\n tbsPtr = der.nextSiblingOf(tbsPtr);\n\n cert.subjectCommonName = _getCommonName(der, der.firstChildOf(tbsPtr));\n\n tbsPtr = der.nextSiblingOf(tbsPtr);\n cert.subjectPublicKey = _getSubjectPublicKey(der, der.firstChildOf(tbsPtr));\n\n cert.extensionPtr = der.nextSiblingOf(tbsPtr);\n\n // tbs iteration completed\n // now we just need to look for the signature\n\n uint256 sigPtr = der.nextSiblingOf(tbsParentPtr);\n sigPtr = der.nextSiblingOf(sigPtr);\n cert.signature = _getSignature(der, sigPtr);\n }\n\n function _getCommonName(bytes calldata der, uint256 commonNameParentPtr)\n private\n pure\n returns (string memory commonName)\n {\n commonNameParentPtr = der.firstChildOf(commonNameParentPtr);\n commonNameParentPtr = der.firstChildOf(commonNameParentPtr);\n commonNameParentPtr = der.nextSiblingOf(commonNameParentPtr);\n commonName = string(der.bytesAt(commonNameParentPtr));\n }\n\n function _getValidity(bytes calldata der, uint256 validityPtr)\n private\n pure\n returns (uint256 notBefore, uint256 notAfter)\n {\n uint256 notBeforePtr = der.firstChildOf(validityPtr);\n uint256 notAfterPtr = der.nextSiblingOf(notBeforePtr);\n notBefore = DateTimeUtils.fromDERToTimestamp(der.bytesAt(notBeforePtr));\n notAfter = DateTimeUtils.fromDERToTimestamp(der.bytesAt(notAfterPtr));\n }\n\n function _getSubjectPublicKey(bytes calldata der, uint256 subjectPublicKeyInfoPtr)\n private\n pure\n returns (bytes memory pubKey)\n {\n subjectPublicKeyInfoPtr = der.nextSiblingOf(subjectPublicKeyInfoPtr);\n pubKey = der.bitstringAt(subjectPublicKeyInfoPtr);\n if (pubKey.length != 65) {\n // TODO: we need to figure out how to handle key with prefix byte 0x02 or 0x03\n revert(\"compressed public key not supported\");\n }\n pubKey = _trimBytes(pubKey, 64);\n }\n\n function _parseSerialNumber(bytes memory serialBytes) private pure returns (uint256 serial) {\n uint256 shift = 8 * (32 - serialBytes.length);\n serial = uint256(bytes32(serialBytes) >> shift);\n }\n\n function _getSignature(bytes calldata der, uint256 sigPtr) private pure returns (bytes memory sig) {\n sigPtr = der.rootOfBitStringAt(sigPtr);\n\n sigPtr = der.firstChildOf(sigPtr);\n bytes memory sigX = _trimBytes(der.bytesAt(sigPtr), 32);\n\n sigPtr = der.nextSiblingOf(sigPtr);\n bytes memory sigY = _trimBytes(der.bytesAt(sigPtr), 32);\n\n sig = abi.encodePacked(sigX, sigY);\n }\n\n /// @dev remove unnecessary prefix from the input\n function _trimBytes(bytes memory input, uint256 expectedLength) private pure returns (bytes memory output) {\n uint256 n = input.length;\n if (n == expectedLength) {\n output = input;\n } else if (n < expectedLength) {\n output = new bytes(expectedLength);\n uint256 padLength = expectedLength - n;\n for (uint256 i = 0; i < n; i++) {\n output[padLength + i] = input[i];\n }\n } else {\n uint256 lengthDiff = n - expectedLength;\n output = input.substring(lengthDiff, expectedLength);\n }\n }\n}\n"},"src/utils/Asn1Decode.sol":{"content":"// SPDX-License-Identifier: MIT\n// Original source: https://github.com/JonahGroendal/asn1-decode\npragma solidity ^0.8.0;\n\n// Inspired by PufferFinance/rave - Apache-2.0 license\n// https://github.com/JonahGroendal/asn1-decode/blob/5c2d1469fc678513753786acb441e597969192ec/contracts/Asn1Decode.sol\n\nimport \"./BytesUtils.sol\";\n\nlibrary NodePtr {\n // Unpack first byte index\n function ixs(uint256 self) internal pure returns (uint256) {\n return uint80(self);\n }\n // Unpack first content byte index\n\n function ixf(uint256 self) internal pure returns (uint256) {\n return uint80(self >> 80);\n }\n // Unpack last content byte index\n\n function ixl(uint256 self) internal pure returns (uint256) {\n return uint80(self >> 160);\n }\n // Pack 3 uint80s into a uint256\n\n function getPtr(uint256 _ixs, uint256 _ixf, uint256 _ixl) internal pure returns (uint256) {\n _ixs |= _ixf << 80;\n _ixs |= _ixl << 160;\n return _ixs;\n }\n}\n\nlibrary Asn1Decode {\n using NodePtr for uint256;\n using BytesUtils for bytes;\n\n /*\n * @dev Get the root node. First step in traversing an ASN1 structure\n * @param der The DER-encoded ASN1 structure\n * @return A pointer to the outermost node\n */\n function root(bytes memory der) internal pure returns (uint256) {\n return readNodeLength(der, 0);\n }\n\n /*\n * @dev Get the root node of an ASN1 structure that's within a bit string value\n * @param der The DER-encoded ASN1 structure\n * @return A pointer to the outermost node\n */\n function rootOfBitStringAt(bytes memory der, uint256 ptr) internal pure returns (uint256) {\n require(der[ptr.ixs()] == 0x03, \"Not type BIT STRING\");\n return readNodeLength(der, ptr.ixf() + 1);\n }\n\n /*\n * @dev Get the root node of an ASN1 structure that's within an octet string value\n * @param der The DER-encoded ASN1 structure\n * @return A pointer to the outermost node\n */\n function rootOfOctetStringAt(bytes memory der, uint256 ptr) internal pure returns (uint256) {\n require(der[ptr.ixs()] == 0x04, \"Not type OCTET STRING\");\n return readNodeLength(der, ptr.ixf());\n }\n\n /*\n * @dev Get the next sibling node\n * @param der The DER-encoded ASN1 structure\n * @param ptr Points to the indices of the current node\n * @return A pointer to the next sibling node\n */\n function nextSiblingOf(bytes memory der, uint256 ptr) internal pure returns (uint256) {\n return readNodeLength(der, ptr.ixl() + 1);\n }\n\n /*\n * @dev Get the first child node of the current node\n * @param der The DER-encoded ASN1 structure\n * @param ptr Points to the indices of the current node\n * @return A pointer to the first child node\n */\n function firstChildOf(bytes memory der, uint256 ptr) internal pure returns (uint256) {\n require(der[ptr.ixs()] & 0x20 == 0x20, \"Not a constructed type\");\n return readNodeLength(der, ptr.ixf());\n }\n\n /*\n * @dev Use for looping through children of a node (either i or j).\n * @param i Pointer to an ASN1 node\n * @param j Pointer to another ASN1 node of the same ASN1 structure\n * @return True iff j is child of i or i is child of j.\n */\n function isChildOf(uint256 i, uint256 j) internal pure returns (bool) {\n return (((i.ixf() <= j.ixs()) && (j.ixl() <= i.ixl())) || ((j.ixf() <= i.ixs()) && (i.ixl() <= j.ixl())));\n }\n\n /*\n * @dev Extract value of node from DER-encoded structure\n * @param der The der-encoded ASN1 structure\n * @param ptr Points to the indices of the current node\n * @return Value bytes of node\n */\n function bytesAt(bytes memory der, uint256 ptr) internal pure returns (bytes memory) {\n return der.substring(ptr.ixf(), ptr.ixl() + 1 - ptr.ixf());\n }\n\n /*\n * @dev Extract entire node from DER-encoded structure\n * @param der The DER-encoded ASN1 structure\n * @param ptr Points to the indices of the current node\n * @return All bytes of node\n */\n function allBytesAt(bytes memory der, uint256 ptr) internal pure returns (bytes memory) {\n return der.substring(ptr.ixs(), ptr.ixl() + 1 - ptr.ixs());\n }\n\n /*\n * @dev Extract value of node from DER-encoded structure\n * @param der The DER-encoded ASN1 structure\n * @param ptr Points to the indices of the current node\n * @return Value bytes of node as bytes32\n */\n function bytes32At(bytes memory der, uint256 ptr) internal pure returns (bytes32) {\n return der.readBytesN(ptr.ixf(), ptr.ixl() + 1 - ptr.ixf());\n }\n\n /*\n * @dev Extract value of node from DER-encoded structure\n * @param der The der-encoded ASN1 structure\n * @param ptr Points to the indices of the current node\n * @return Uint value of node\n */\n function uintAt(bytes memory der, uint256 ptr) internal pure returns (uint256) {\n require(der[ptr.ixs()] == 0x02, \"Not type INTEGER\");\n require(der[ptr.ixf()] & 0x80 == 0, \"Not positive\");\n uint256 len = ptr.ixl() + 1 - ptr.ixf();\n return uint256(der.readBytesN(ptr.ixf(), len) >> (32 - len) * 8);\n }\n\n /*\n * @dev Extract value of a positive integer node from DER-encoded structure\n * @param der The DER-encoded ASN1 structure\n * @param ptr Points to the indices of the current node\n * @return Value bytes of a positive integer node\n */\n function uintBytesAt(bytes memory der, uint256 ptr) internal pure returns (bytes memory) {\n require(der[ptr.ixs()] == 0x02, \"Not type INTEGER\");\n require(der[ptr.ixf()] & 0x80 == 0, \"Not positive\");\n uint256 valueLength = ptr.ixl() + 1 - ptr.ixf();\n if (der[ptr.ixf()] == 0) {\n return der.substring(ptr.ixf() + 1, valueLength - 1);\n } else {\n return der.substring(ptr.ixf(), valueLength);\n }\n }\n\n function keccakOfBytesAt(bytes memory der, uint256 ptr) internal pure returns (bytes32) {\n return der.keccak(ptr.ixf(), ptr.ixl() + 1 - ptr.ixf());\n }\n\n function keccakOfAllBytesAt(bytes memory der, uint256 ptr) internal pure returns (bytes32) {\n return der.keccak(ptr.ixs(), ptr.ixl() + 1 - ptr.ixs());\n }\n\n /*\n * @dev Extract value of bitstring node from DER-encoded structure\n * @param der The DER-encoded ASN1 structure\n * @param ptr Points to the indices of the current node\n * @return Value of bitstring converted to bytes\n */\n function bitstringAt(bytes memory der, uint256 ptr) internal pure returns (bytes memory) {\n require(der[ptr.ixs()] == 0x03, \"Not type BIT STRING\");\n // Only 00 padded bitstr can be converted to bytestr!\n require(der[ptr.ixf()] == 0x00);\n uint256 valueLength = ptr.ixl() + 1 - ptr.ixf();\n return der.substring(ptr.ixf() + 1, valueLength - 1);\n }\n\n function readNodeLength(bytes memory der, uint256 ix) private pure returns (uint256) {\n uint256 length;\n uint80 ixFirstContentByte;\n uint80 ixLastContentByte;\n if ((der[ix + 1] & 0x80) == 0) {\n length = uint8(der[ix + 1]);\n ixFirstContentByte = uint80(ix + 2);\n ixLastContentByte = uint80(ixFirstContentByte + length - 1);\n } else {\n uint8 lengthbytesLength = uint8(der[ix + 1] & 0x7F);\n if (lengthbytesLength == 1) {\n length = der.readUint8(ix + 2);\n } else if (lengthbytesLength == 2) {\n length = der.readUint16(ix + 2);\n } else {\n length = uint256(der.readBytesN(ix + 2, lengthbytesLength) >> (32 - lengthbytesLength) * 8);\n }\n ixFirstContentByte = uint80(ix + 2 + lengthbytesLength);\n ixLastContentByte = uint80(ixFirstContentByte + length - 1);\n }\n return NodePtr.getPtr(ix, ixFirstContentByte, ixLastContentByte);\n }\n}\n"},"src/utils/BytesUtils.sol":{"content":"// SPDX-License-Identifier: BSD 2-Clause License\npragma solidity ^0.8.0;\n\n// Inspired by ensdomains/dnssec-oracle - BSD-2-Clause license\n// https://github.com/ensdomains/dnssec-oracle/blob/master/contracts/BytesUtils.sol\n\nlibrary BytesUtils {\n /*\n * @dev Returns the keccak-256 hash of a byte range.\n * @param self The byte string to hash.\n * @param offset The position to start hashing at.\n * @param len The number of bytes to hash.\n * @return The hash of the byte range.\n */\n function keccak(bytes memory self, uint256 offset, uint256 len) internal pure returns (bytes32 ret) {\n require(offset + len <= self.length);\n assembly {\n ret := keccak256(add(add(self, 32), offset), len)\n }\n }\n\n /*\n * @dev Returns a positive number if `other` comes lexicographically after\n * `self`, a negative number if it comes before, or zero if the\n * contents of the two bytes are equal.\n * @param self The first bytes to compare.\n * @param other The second bytes to compare.\n * @return The result of the comparison.\n */\n function compare(bytes memory self, bytes memory other) internal pure returns (int256) {\n return compare(self, 0, self.length, other, 0, other.length);\n }\n\n /*\n * @dev Returns a positive number if `other` comes lexicographically after\n * `self`, a negative number if it comes before, or zero if the\n * contents of the two bytes are equal. Comparison is done per-rune,\n * on unicode codepoints.\n * @param self The first bytes to compare.\n * @param offset The offset of self.\n * @param len The length of self.\n * @param other The second bytes to compare.\n * @param otheroffset The offset of the other string.\n * @param otherlen The length of the other string.\n * @return The result of the comparison.\n */\n function compare(\n bytes memory self,\n uint256 offset,\n uint256 len,\n bytes memory other,\n uint256 otheroffset,\n uint256 otherlen\n ) internal pure returns (int256) {\n uint256 shortest = len;\n if (otherlen < len) {\n shortest = otherlen;\n }\n\n uint256 selfptr;\n uint256 otherptr;\n\n assembly {\n selfptr := add(self, add(offset, 32))\n otherptr := add(other, add(otheroffset, 32))\n }\n for (uint256 idx = 0; idx < shortest; idx += 32) {\n uint256 a;\n uint256 b;\n assembly {\n a := mload(selfptr)\n b := mload(otherptr)\n }\n if (a != b) {\n // Mask out irrelevant bytes and check again\n uint256 mask;\n if (shortest > 32) {\n mask = type(uint256).max; // aka 0xffffff....\n } else {\n mask = ~(2 ** (8 * (32 - shortest + idx)) - 1);\n }\n uint256 diff = (a & mask) - (b & mask);\n if (diff != 0) {\n return int256(diff);\n }\n }\n selfptr += 32;\n otherptr += 32;\n }\n\n return int256(len) - int256(otherlen);\n }\n\n /*\n * @dev Returns true if the two byte ranges are equal.\n * @param self The first byte range to compare.\n * @param offset The offset into the first byte range.\n * @param other The second byte range to compare.\n * @param otherOffset The offset into the second byte range.\n * @param len The number of bytes to compare\n * @return True if the byte ranges are equal, false otherwise.\n */\n function equals(bytes memory self, uint256 offset, bytes memory other, uint256 otherOffset, uint256 len)\n internal\n pure\n returns (bool)\n {\n return keccak(self, offset, len) == keccak(other, otherOffset, len);\n }\n\n /*\n * @dev Returns true if the two byte ranges are equal with offsets.\n * @param self The first byte range to compare.\n * @param offset The offset into the first byte range.\n * @param other The second byte range to compare.\n * @param otherOffset The offset into the second byte range.\n * @return True if the byte ranges are equal, false otherwise.\n */\n function equals(bytes memory self, uint256 offset, bytes memory other, uint256 otherOffset)\n internal\n pure\n returns (bool)\n {\n return keccak(self, offset, self.length - offset) == keccak(other, otherOffset, other.length - otherOffset);\n }\n\n /*\n * @dev Compares a range of 'self' to all of 'other' and returns True iff\n * they are equal.\n * @param self The first byte range to compare.\n * @param offset The offset into the first byte range.\n * @param other The second byte range to compare.\n * @return True if the byte ranges are equal, false otherwise.\n */\n function equals(bytes memory self, uint256 offset, bytes memory other) internal pure returns (bool) {\n return self.length >= offset + other.length && equals(self, offset, other, 0, other.length);\n }\n\n /*\n * @dev Returns true if the two byte ranges are equal.\n * @param self The first byte range to compare.\n * @param other The second byte range to compare.\n * @return True if the byte ranges are equal, false otherwise.\n */\n function equals(bytes memory self, bytes memory other) internal pure returns (bool) {\n return self.length == other.length && equals(self, 0, other, 0, self.length);\n }\n\n /*\n * @dev Returns the 8-bit number at the specified index of self.\n * @param self The byte string.\n * @param idx The index into the bytes\n * @return The specified 8 bits of the string, interpreted as an integer.\n */\n function readUint8(bytes memory self, uint256 idx) internal pure returns (uint8 ret) {\n return uint8(self[idx]);\n }\n\n /*\n * @dev Returns the 16-bit number at the specified index of self.\n * @param self The byte string.\n * @param idx The index into the bytes\n * @return The specified 16 bits of the string, interpreted as an integer.\n */\n function readUint16(bytes memory self, uint256 idx) internal pure returns (uint16 ret) {\n require(idx + 2 <= self.length);\n assembly {\n ret := and(mload(add(add(self, 2), idx)), 0xFFFF)\n }\n }\n\n /*\n * @dev Returns the 32-bit number at the specified index of self.\n * @param self The byte string.\n * @param idx The index into the bytes\n * @return The specified 32 bits of the string, interpreted as an integer.\n */\n function readUint32(bytes memory self, uint256 idx) internal pure returns (uint32 ret) {\n require(idx + 4 <= self.length);\n assembly {\n ret := and(mload(add(add(self, 4), idx)), 0xFFFFFFFF)\n }\n }\n\n /*\n * @dev Returns the 32 byte value at the specified index of self.\n * @param self The byte string.\n * @param idx The index into the bytes\n * @return The specified 32 bytes of the string.\n */\n function readBytes32(bytes memory self, uint256 idx) internal pure returns (bytes32 ret) {\n require(idx + 32 <= self.length);\n assembly {\n ret := mload(add(add(self, 32), idx))\n }\n }\n\n /*\n * @dev Returns the 32 byte value at the specified index of self.\n * @param self The byte string.\n * @param idx The index into the bytes\n * @return The specified 32 bytes of the string.\n */\n function readBytes20(bytes memory self, uint256 idx) internal pure returns (bytes20 ret) {\n require(idx + 20 <= self.length);\n assembly {\n ret :=\n and(mload(add(add(self, 32), idx)), 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000000)\n }\n }\n\n /*\n * @dev Returns the n byte value at the specified index of self.\n * @param self The byte string.\n * @param idx The index into the bytes.\n * @param len The number of bytes.\n * @return The specified 32 bytes of the string.\n */\n function readBytesN(bytes memory self, uint256 idx, uint256 len) internal pure returns (bytes32 ret) {\n require(len <= 32);\n require(idx + len <= self.length);\n assembly {\n let mask := not(sub(exp(256, sub(32, len)), 1))\n ret := and(mload(add(add(self, 32), idx)), mask)\n }\n }\n\n function memcpy(uint256 dest, uint256 src, uint256 len) private pure {\n // Copy word-length chunks while possible\n for (; len >= 32; len -= 32) {\n assembly {\n mstore(dest, mload(src))\n }\n dest += 32;\n src += 32;\n }\n\n // Copy remaining bytes\n uint256 mask;\n if (len == 0) {\n mask = type(uint256).max; // Set to maximum value of uint256\n } else {\n mask = 256 ** (32 - len) - 1;\n }\n\n assembly {\n let srcpart := and(mload(src), not(mask))\n let destpart := and(mload(dest), mask)\n mstore(dest, or(destpart, srcpart))\n }\n }\n\n /*\n * @dev Copies a substring into a new byte string.\n * @param self The byte string to copy from.\n * @param offset The offset to start copying at.\n * @param len The number of bytes to copy.\n */\n function substring(bytes memory self, uint256 offset, uint256 len) internal pure returns (bytes memory) {\n require(offset + len <= self.length);\n\n bytes memory ret = new bytes(len);\n uint256 dest;\n uint256 src;\n\n assembly {\n dest := add(ret, 32)\n src := add(add(self, 32), offset)\n }\n memcpy(dest, src, len);\n\n return ret;\n }\n\n // Maps characters from 0x30 to 0x7A to their base32 values.\n // 0xFF represents invalid characters in that range.\n bytes constant base32HexTable =\n hex\"00010203040506070809FFFFFFFFFFFFFF0A0B0C0D0E0F101112131415161718191A1B1C1D1E1FFFFFFFFFFFFFFFFFFFFF0A0B0C0D0E0F101112131415161718191A1B1C1D1E1F\";\n\n /**\n * @dev Decodes unpadded base32 data of up to one word in length.\n * @param self The data to decode.\n * @param off Offset into the string to start at.\n * @param len Number of characters to decode.\n * @return The decoded data, left aligned.\n */\n function base32HexDecodeWord(bytes memory self, uint256 off, uint256 len) internal pure returns (bytes32) {\n require(len <= 52);\n\n uint256 ret = 0;\n uint8 decoded;\n for (uint256 i = 0; i < len; i++) {\n bytes1 char = self[off + i];\n require(char >= 0x30 && char <= 0x7A);\n decoded = uint8(base32HexTable[uint256(uint8(char)) - 0x30]);\n require(decoded <= 0x20);\n if (i == len - 1) {\n break;\n }\n ret = (ret << 5) | decoded;\n }\n\n uint256 bitlen = len * 5;\n if (len % 8 == 0) {\n // Multiple of 8 characters, no padding\n ret = (ret << 5) | decoded;\n } else if (len % 8 == 2) {\n // Two extra characters - 1 byte\n ret = (ret << 3) | (decoded >> 2);\n bitlen -= 2;\n } else if (len % 8 == 4) {\n // Four extra characters - 2 bytes\n ret = (ret << 1) | (decoded >> 4);\n bitlen -= 4;\n } else if (len % 8 == 5) {\n // Five extra characters - 3 bytes\n ret = (ret << 4) | (decoded >> 1);\n bitlen -= 1;\n } else if (len % 8 == 7) {\n // Seven extra characters - 4 bytes\n ret = (ret << 2) | (decoded >> 3);\n bitlen -= 3;\n } else {\n revert();\n }\n\n return bytes32(ret << (256 - bitlen));\n }\n\n function compareBytes(bytes memory a, bytes memory b) internal pure returns (bool) {\n if (a.length != b.length) {\n return false;\n }\n for (uint256 i = 0; i < a.length; i++) {\n if (a[i] != b[i]) {\n return false;\n }\n }\n return true;\n }\n}\n"},"src/utils/DateTimeUtils.sol":{"content":"// SPDX-License-Identifier: GPL-3.0\npragma solidity ^0.8.0;\n\nimport {DateTimeLib} from \"solady/utils/DateTimeLib.sol\";\nimport {LibString} from \"solady/utils/LibString.sol\";\n\nlibrary DateTimeUtils {\n using LibString for string;\n\n /*\n * @dev Convert a DER-encoded time to a unix timestamp\n * @param x509Time The DER-encoded time\n * @return The unix timestamp\n */\n function fromDERToTimestamp(bytes memory x509Time) internal pure returns (uint256) {\n uint16 yrs;\n uint8 mnths;\n uint8 dys;\n uint8 hrs;\n uint8 mins;\n uint8 secs;\n uint8 offset;\n\n if (x509Time.length == 13) {\n if (uint8(x509Time[0]) - 48 < 5) yrs += 2000;\n else yrs += 1900;\n } else {\n yrs += (uint8(x509Time[0]) - 48) * 1000 + (uint8(x509Time[1]) - 48) * 100;\n offset = 2;\n }\n yrs += (uint8(x509Time[offset + 0]) - 48) * 10 + uint8(x509Time[offset + 1]) - 48;\n mnths = (uint8(x509Time[offset + 2]) - 48) * 10 + uint8(x509Time[offset + 3]) - 48;\n dys += (uint8(x509Time[offset + 4]) - 48) * 10 + uint8(x509Time[offset + 5]) - 48;\n hrs += (uint8(x509Time[offset + 6]) - 48) * 10 + uint8(x509Time[offset + 7]) - 48;\n mins += (uint8(x509Time[offset + 8]) - 48) * 10 + uint8(x509Time[offset + 9]) - 48;\n secs += (uint8(x509Time[offset + 10]) - 48) * 10 + uint8(x509Time[offset + 11]) - 48;\n\n return DateTimeLib.dateTimeToTimestamp(yrs, mnths, dys, hrs, mins, secs);\n }\n\n /// @dev iso follows pattern: \"YYYY-MM-DDTHH:mm:ssZ\"\n function fromISOToTimestamp(string memory iso) internal pure returns (uint256) {\n require(bytes(iso).length == 20, \"invalid iso string length\");\n uint256 y = stringToUint(iso.slice(0, 4));\n uint256 m = stringToUint(iso.slice(5, 7));\n uint256 d = stringToUint(iso.slice(8, 10));\n uint256 h = stringToUint(iso.slice(11, 13));\n uint256 min = stringToUint(iso.slice(14, 16));\n uint256 s = stringToUint(iso.slice(17, 19));\n\n return DateTimeLib.dateTimeToTimestamp(y, m, d, h, min, s);\n }\n\n // https://ethereum.stackexchange.com/questions/10932/how-to-convert-string-to-int\n function stringToUint(string memory s) private pure returns (uint256 result) {\n bytes memory b = bytes(s);\n result = 0;\n for (uint256 i = 0; i < b.length; i++) {\n uint256 c = uint256(uint8(b[i]));\n if (c >= 48 && c <= 57) {\n result = result * 10 + (c - 48);\n }\n }\n }\n}\n"},"src/utils/P256Verifier.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport \"./BytesUtils.sol\";\n\n/**\n * @notice modified from https://github.com/daimo-eth/p256-verifier/\n */\nlibrary P256Verifier {\n using BytesUtils for bytes;\n\n address internal constant P256_VERIFIER = 0xc2b78104907F722DABAc4C69f826a522B2754De4;\n\n function ecdsaVerify(bytes32 messageHash, bytes memory signature, bytes memory key)\n internal\n view\n returns (bool verified)\n {\n bytes memory args = abi.encode(\n messageHash,\n uint256(bytes32(signature.substring(0, 32))),\n uint256(bytes32(signature.substring(32, 32))),\n uint256(bytes32(key.substring(0, 32))),\n uint256(bytes32(key.substring(32, 32)))\n );\n (bool success, bytes memory ret) = P256_VERIFIER.staticcall(args);\n assert(success); // never reverts, always returns 0 or 1\n\n verified = abi.decode(ret, (uint256)) == 1;\n }\n}\n"},"lib/solady/src/utils/DateTimeLib.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\n/// @notice Library for date time operations.\n/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/DateTimeLib.sol)\n///\n/// Conventions:\n/// --------------------------------------------------------------------+\n/// Unit | Range | Notes |\n/// --------------------------------------------------------------------|\n/// timestamp | 0..0x1e18549868c76ff | Unix timestamp. |\n/// epochDay | 0..0x16d3e098039 | Days since 1970-01-01. |\n/// year | 1970..0xffffffff | Gregorian calendar year. |\n/// month | 1..12 | Gregorian calendar month. |\n/// day | 1..31 | Gregorian calendar day of month. |\n/// weekday | 1..7 | The day of the week (1-indexed). |\n/// --------------------------------------------------------------------+\n/// All timestamps of days are rounded down to 00:00:00 UTC.\nlibrary DateTimeLib {\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* CONSTANTS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n // Weekdays are 1-indexed for a traditional rustic feel.\n\n // \"And on the seventh day God finished his work that he had done,\n // and he rested on the seventh day from all his work that he had done.\"\n // -- Genesis 2:2\n\n uint256 internal constant MON = 1;\n uint256 internal constant TUE = 2;\n uint256 internal constant WED = 3;\n uint256 internal constant THU = 4;\n uint256 internal constant FRI = 5;\n uint256 internal constant SAT = 6;\n uint256 internal constant SUN = 7;\n\n // Months and days of months are 1-indexed for ease of use.\n\n uint256 internal constant JAN = 1;\n uint256 internal constant FEB = 2;\n uint256 internal constant MAR = 3;\n uint256 internal constant APR = 4;\n uint256 internal constant MAY = 5;\n uint256 internal constant JUN = 6;\n uint256 internal constant JUL = 7;\n uint256 internal constant AUG = 8;\n uint256 internal constant SEP = 9;\n uint256 internal constant OCT = 10;\n uint256 internal constant NOV = 11;\n uint256 internal constant DEC = 12;\n\n // These limits are large enough for most practical purposes.\n // Inputs that exceed these limits result in undefined behavior.\n\n uint256 internal constant MAX_SUPPORTED_YEAR = 0xffffffff;\n uint256 internal constant MAX_SUPPORTED_EPOCH_DAY = 0x16d3e098039;\n uint256 internal constant MAX_SUPPORTED_TIMESTAMP = 0x1e18549868c76ff;\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* DATE TIME OPERATIONS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev Returns the number of days since 1970-01-01 from (`year`,`month`,`day`).\n /// See: https://howardhinnant.github.io/date_algorithms.html\n /// Note: Inputs outside the supported ranges result in undefined behavior.\n /// Use {isSupportedDate} to check if the inputs are supported.\n function dateToEpochDay(uint256 year, uint256 month, uint256 day)\n internal\n pure\n returns (uint256 epochDay)\n {\n /// @solidity memory-safe-assembly\n assembly {\n year := sub(year, lt(month, 3))\n let doy := add(shr(11, add(mul(62719, mod(add(month, 9), 12)), 769)), day)\n let yoe := mod(year, 400)\n let doe := sub(add(add(mul(yoe, 365), shr(2, yoe)), doy), div(yoe, 100))\n epochDay := sub(add(mul(div(year, 400), 146097), doe), 719469)\n }\n }\n\n /// @dev Returns (`year`,`month`,`day`) from the number of days since 1970-01-01.\n /// Note: Inputs outside the supported ranges result in undefined behavior.\n /// Use {isSupportedDays} to check if the inputs is supported.\n function epochDayToDate(uint256 epochDay)\n internal\n pure\n returns (uint256 year, uint256 month, uint256 day)\n {\n /// @solidity memory-safe-assembly\n assembly {\n epochDay := add(epochDay, 719468)\n let doe := mod(epochDay, 146097)\n let yoe :=\n div(sub(sub(add(doe, div(doe, 36524)), div(doe, 1460)), eq(doe, 146096)), 365)\n let doy := sub(doe, sub(add(mul(365, yoe), shr(2, yoe)), div(yoe, 100)))\n let mp := div(add(mul(5, doy), 2), 153)\n day := add(sub(doy, shr(11, add(mul(mp, 62719), 769))), 1)\n month := byte(mp, shl(160, 0x030405060708090a0b0c0102))\n year := add(add(yoe, mul(div(epochDay, 146097), 400)), lt(month, 3))\n }\n }\n\n /// @dev Returns the unix timestamp from (`year`,`month`,`day`).\n /// Note: Inputs outside the supported ranges result in undefined behavior.\n /// Use {isSupportedDate} to check if the inputs are supported.\n function dateToTimestamp(uint256 year, uint256 month, uint256 day)\n internal\n pure\n returns (uint256 result)\n {\n unchecked {\n result = dateToEpochDay(year, month, day) * 86400;\n }\n }\n\n /// @dev Returns (`year`,`month`,`day`) from the given unix timestamp.\n /// Note: Inputs outside the supported ranges result in undefined behavior.\n /// Use {isSupportedTimestamp} to check if the inputs are supported.\n function timestampToDate(uint256 timestamp)\n internal\n pure\n returns (uint256 year, uint256 month, uint256 day)\n {\n (year, month, day) = epochDayToDate(timestamp / 86400);\n }\n\n /// @dev Returns the unix timestamp from\n /// (`year`,`month`,`day`,`hour`,`minute`,`second`).\n /// Note: Inputs outside the supported ranges result in undefined behavior.\n /// Use {isSupportedDateTime} to check if the inputs are supported.\n function dateTimeToTimestamp(\n uint256 year,\n uint256 month,\n uint256 day,\n uint256 hour,\n uint256 minute,\n uint256 second\n ) internal pure returns (uint256 result) {\n unchecked {\n result = dateToEpochDay(year, month, day) * 86400 + hour * 3600 + minute * 60 + second;\n }\n }\n\n /// @dev Returns (`year`,`month`,`day`,`hour`,`minute`,`second`)\n /// from the given unix timestamp.\n /// Note: Inputs outside the supported ranges result in undefined behavior.\n /// Use {isSupportedTimestamp} to check if the inputs are supported.\n function timestampToDateTime(uint256 timestamp)\n internal\n pure\n returns (\n uint256 year,\n uint256 month,\n uint256 day,\n uint256 hour,\n uint256 minute,\n uint256 second\n )\n {\n unchecked {\n (year, month, day) = epochDayToDate(timestamp / 86400);\n uint256 secs = timestamp % 86400;\n hour = secs / 3600;\n secs = secs % 3600;\n minute = secs / 60;\n second = secs % 60;\n }\n }\n\n /// @dev Returns if the `year` is leap.\n function isLeapYear(uint256 year) internal pure returns (bool leap) {\n /// @solidity memory-safe-assembly\n assembly {\n leap := iszero(and(add(mul(iszero(mod(year, 25)), 12), 3), year))\n }\n }\n\n /// @dev Returns number of days in given `month` of `year`.\n function daysInMonth(uint256 year, uint256 month) internal pure returns (uint256 result) {\n bool flag = isLeapYear(year);\n /// @solidity memory-safe-assembly\n assembly {\n // `daysInMonths = [31,28,31,30,31,30,31,31,30,31,30,31]`.\n // `result = daysInMonths[month - 1] + isLeapYear(year)`.\n result :=\n add(byte(month, shl(152, 0x1F1C1F1E1F1E1F1F1E1F1E1F)), and(eq(month, 2), flag))\n }\n }\n\n /// @dev Returns the weekday from the unix timestamp.\n /// Monday: 1, Tuesday: 2, ....., Sunday: 7.\n function weekday(uint256 timestamp) internal pure returns (uint256 result) {\n unchecked {\n result = ((timestamp / 86400 + 3) % 7) + 1;\n }\n }\n\n /// @dev Returns if (`year`,`month`,`day`) is a supported date.\n /// - `1970 <= year <= MAX_SUPPORTED_YEAR`.\n /// - `1 <= month <= 12`.\n /// - `1 <= day <= daysInMonth(year, month)`.\n function isSupportedDate(uint256 year, uint256 month, uint256 day)\n internal\n pure\n returns (bool result)\n {\n uint256 md = daysInMonth(year, month);\n /// @solidity memory-safe-assembly\n assembly {\n let w := not(0)\n result :=\n and(\n lt(sub(year, 1970), sub(MAX_SUPPORTED_YEAR, 1969)),\n and(lt(add(month, w), 12), lt(add(day, w), md))\n )\n }\n }\n\n /// @dev Returns if (`year`,`month`,`day`,`hour`,`minute`,`second`) is a supported date time.\n /// - `1970 <= year <= MAX_SUPPORTED_YEAR`.\n /// - `1 <= month <= 12`.\n /// - `1 <= day <= daysInMonth(year, month)`.\n /// - `hour < 24`.\n /// - `minute < 60`.\n /// - `second < 60`.\n function isSupportedDateTime(\n uint256 year,\n uint256 month,\n uint256 day,\n uint256 hour,\n uint256 minute,\n uint256 second\n ) internal pure returns (bool result) {\n if (isSupportedDate(year, month, day)) {\n /// @solidity memory-safe-assembly\n assembly {\n result := and(lt(hour, 24), and(lt(minute, 60), lt(second, 60)))\n }\n }\n }\n\n /// @dev Returns if `epochDay` is a supported unix epoch day.\n function isSupportedEpochDay(uint256 epochDay) internal pure returns (bool result) {\n unchecked {\n result = epochDay < MAX_SUPPORTED_EPOCH_DAY + 1;\n }\n }\n\n /// @dev Returns if `timestamp` is a supported unix timestamp.\n function isSupportedTimestamp(uint256 timestamp) internal pure returns (bool result) {\n unchecked {\n result = timestamp < MAX_SUPPORTED_TIMESTAMP + 1;\n }\n }\n\n /// @dev Returns the unix timestamp of the given `n`th weekday `wd`, in `month` of `year`.\n /// Example: 3rd Friday of Feb 2022 is `nthWeekdayInMonthOfYearTimestamp(2022, 2, 3, 5)`\n /// Note: `n` is 1-indexed for traditional consistency.\n /// Invalid weekdays (i.e. `wd == 0 || wd > 7`) result in undefined behavior.\n function nthWeekdayInMonthOfYearTimestamp(uint256 year, uint256 month, uint256 n, uint256 wd)\n internal\n pure\n returns (uint256 result)\n {\n uint256 d = dateToEpochDay(year, month, 1);\n uint256 md = daysInMonth(year, month);\n /// @solidity memory-safe-assembly\n assembly {\n let diff := sub(wd, add(mod(add(d, 3), 7), 1))\n let date := add(mul(sub(n, 1), 7), add(mul(gt(diff, 6), 7), diff))\n result := mul(mul(86400, add(date, d)), and(lt(date, md), iszero(iszero(n))))\n }\n }\n\n /// @dev Returns the unix timestamp of the most recent Monday.\n function mondayTimestamp(uint256 timestamp) internal pure returns (uint256 result) {\n uint256 t = timestamp;\n /// @solidity memory-safe-assembly\n assembly {\n let day := div(t, 86400)\n result := mul(mul(sub(day, mod(add(day, 3), 7)), 86400), gt(t, 345599))\n }\n }\n\n /// @dev Returns whether the unix timestamp falls on a Saturday or Sunday.\n /// To check whether it is a week day, just take the negation of the result.\n function isWeekEnd(uint256 timestamp) internal pure returns (bool result) {\n result = weekday(timestamp) > FRI;\n }\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* DATE TIME ARITHMETIC OPERATIONS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev Adds `numYears` to the unix timestamp, and returns the result.\n /// Note: The result will share the same Gregorian calendar month,\n /// but different Gregorian calendar years for non-zero `numYears`.\n /// If the Gregorian calendar month of the result has less days\n /// than the Gregorian calendar month day of the `timestamp`,\n /// the result's month day will be the maximum possible value for the month.\n /// (e.g. from 29th Feb to 28th Feb)\n function addYears(uint256 timestamp, uint256 numYears) internal pure returns (uint256 result) {\n (uint256 year, uint256 month, uint256 day) = epochDayToDate(timestamp / 86400);\n result = _offsetted(year + numYears, month, day, timestamp);\n }\n\n /// @dev Adds `numMonths` to the unix timestamp, and returns the result.\n /// Note: If the Gregorian calendar month of the result has less days\n /// than the Gregorian calendar month day of the `timestamp`,\n /// the result's month day will be the maximum possible value for the month.\n /// (e.g. from 29th Feb to 28th Feb)\n function addMonths(uint256 timestamp, uint256 numMonths)\n internal\n pure\n returns (uint256 result)\n {\n (uint256 year, uint256 month, uint256 day) = epochDayToDate(timestamp / 86400);\n month = _sub(month + numMonths, 1);\n result = _offsetted(year + month / 12, _add(month % 12, 1), day, timestamp);\n }\n\n /// @dev Adds `numDays` to the unix timestamp, and returns the result.\n function addDays(uint256 timestamp, uint256 numDays) internal pure returns (uint256 result) {\n result = timestamp + numDays * 86400;\n }\n\n /// @dev Adds `numHours` to the unix timestamp, and returns the result.\n function addHours(uint256 timestamp, uint256 numHours) internal pure returns (uint256 result) {\n result = timestamp + numHours * 3600;\n }\n\n /// @dev Adds `numMinutes` to the unix timestamp, and returns the result.\n function addMinutes(uint256 timestamp, uint256 numMinutes)\n internal\n pure\n returns (uint256 result)\n {\n result = timestamp + numMinutes * 60;\n }\n\n /// @dev Adds `numSeconds` to the unix timestamp, and returns the result.\n function addSeconds(uint256 timestamp, uint256 numSeconds)\n internal\n pure\n returns (uint256 result)\n {\n result = timestamp + numSeconds;\n }\n\n /// @dev Subtracts `numYears` from the unix timestamp, and returns the result.\n /// Note: The result will share the same Gregorian calendar month,\n /// but different Gregorian calendar years for non-zero `numYears`.\n /// If the Gregorian calendar month of the result has less days\n /// than the Gregorian calendar month day of the `timestamp`,\n /// the result's month day will be the maximum possible value for the month.\n /// (e.g. from 29th Feb to 28th Feb)\n function subYears(uint256 timestamp, uint256 numYears) internal pure returns (uint256 result) {\n (uint256 year, uint256 month, uint256 day) = epochDayToDate(timestamp / 86400);\n result = _offsetted(year - numYears, month, day, timestamp);\n }\n\n /// @dev Subtracts `numYears` from the unix timestamp, and returns the result.\n /// Note: If the Gregorian calendar month of the result has less days\n /// than the Gregorian calendar month day of the `timestamp`,\n /// the result's month day will be the maximum possible value for the month.\n /// (e.g. from 29th Feb to 28th Feb)\n function subMonths(uint256 timestamp, uint256 numMonths)\n internal\n pure\n returns (uint256 result)\n {\n (uint256 year, uint256 month, uint256 day) = epochDayToDate(timestamp / 86400);\n uint256 yearMonth = _totalMonths(year, month) - _add(numMonths, 1);\n result = _offsetted(yearMonth / 12, _add(yearMonth % 12, 1), day, timestamp);\n }\n\n /// @dev Subtracts `numDays` from the unix timestamp, and returns the result.\n function subDays(uint256 timestamp, uint256 numDays) internal pure returns (uint256 result) {\n result = timestamp - numDays * 86400;\n }\n\n /// @dev Subtracts `numHours` from the unix timestamp, and returns the result.\n function subHours(uint256 timestamp, uint256 numHours) internal pure returns (uint256 result) {\n result = timestamp - numHours * 3600;\n }\n\n /// @dev Subtracts `numMinutes` from the unix timestamp, and returns the result.\n function subMinutes(uint256 timestamp, uint256 numMinutes)\n internal\n pure\n returns (uint256 result)\n {\n result = timestamp - numMinutes * 60;\n }\n\n /// @dev Subtracts `numSeconds` from the unix timestamp, and returns the result.\n function subSeconds(uint256 timestamp, uint256 numSeconds)\n internal\n pure\n returns (uint256 result)\n {\n result = timestamp - numSeconds;\n }\n\n /// @dev Returns the difference in Gregorian calendar years\n /// between `fromTimestamp` and `toTimestamp`.\n /// Note: Even if the true time difference is less than a year,\n /// the difference can be non-zero is the timestamps are\n /// from different Gregorian calendar years\n function diffYears(uint256 fromTimestamp, uint256 toTimestamp)\n internal\n pure\n returns (uint256 result)\n {\n toTimestamp - fromTimestamp;\n (uint256 fromYear,,) = epochDayToDate(fromTimestamp / 86400);\n (uint256 toYear,,) = epochDayToDate(toTimestamp / 86400);\n result = _sub(toYear, fromYear);\n }\n\n /// @dev Returns the difference in Gregorian calendar months\n /// between `fromTimestamp` and `toTimestamp`.\n /// Note: Even if the true time difference is less than a month,\n /// the difference can be non-zero is the timestamps are\n /// from different Gregorian calendar months.\n function diffMonths(uint256 fromTimestamp, uint256 toTimestamp)\n internal\n pure\n returns (uint256 result)\n {\n toTimestamp - fromTimestamp;\n (uint256 fromYear, uint256 fromMonth,) = epochDayToDate(fromTimestamp / 86400);\n (uint256 toYear, uint256 toMonth,) = epochDayToDate(toTimestamp / 86400);\n result = _sub(_totalMonths(toYear, toMonth), _totalMonths(fromYear, fromMonth));\n }\n\n /// @dev Returns the difference in days between `fromTimestamp` and `toTimestamp`.\n function diffDays(uint256 fromTimestamp, uint256 toTimestamp)\n internal\n pure\n returns (uint256 result)\n {\n result = (toTimestamp - fromTimestamp) / 86400;\n }\n\n /// @dev Returns the difference in hours between `fromTimestamp` and `toTimestamp`.\n function diffHours(uint256 fromTimestamp, uint256 toTimestamp)\n internal\n pure\n returns (uint256 result)\n {\n result = (toTimestamp - fromTimestamp) / 3600;\n }\n\n /// @dev Returns the difference in minutes between `fromTimestamp` and `toTimestamp`.\n function diffMinutes(uint256 fromTimestamp, uint256 toTimestamp)\n internal\n pure\n returns (uint256 result)\n {\n result = (toTimestamp - fromTimestamp) / 60;\n }\n\n /// @dev Returns the difference in seconds between `fromTimestamp` and `toTimestamp`.\n function diffSeconds(uint256 fromTimestamp, uint256 toTimestamp)\n internal\n pure\n returns (uint256 result)\n {\n result = toTimestamp - fromTimestamp;\n }\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* PRIVATE HELPERS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev Unchecked arithmetic for computing the total number of months.\n function _totalMonths(uint256 numYears, uint256 numMonths)\n private\n pure\n returns (uint256 total)\n {\n unchecked {\n total = numYears * 12 + numMonths;\n }\n }\n\n /// @dev Unchecked arithmetic for adding two numbers.\n function _add(uint256 a, uint256 b) private pure returns (uint256 c) {\n unchecked {\n c = a + b;\n }\n }\n\n /// @dev Unchecked arithmetic for subtracting two numbers.\n function _sub(uint256 a, uint256 b) private pure returns (uint256 c) {\n unchecked {\n c = a - b;\n }\n }\n\n /// @dev Returns the offsetted timestamp.\n function _offsetted(uint256 year, uint256 month, uint256 day, uint256 timestamp)\n private\n pure\n returns (uint256 result)\n {\n uint256 dm = daysInMonth(year, month);\n if (day >= dm) {\n day = dm;\n }\n result = dateToEpochDay(year, month, day) * 86400 + (timestamp % 86400);\n }\n}\n"}},"settings":{"remappings":["solady/=lib/solady/src/","@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/","ds-test/=lib/forge-std/lib/ds-test/src/","erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/","forge-std/=lib/forge-std/src/","halmos-cheatcodes/=lib/openzeppelin-contracts/lib/halmos-cheatcodes/src/","openzeppelin-contracts/=lib/openzeppelin-contracts/"],"optimizer":{"enabled":true,"runs":999999},"metadata":{"useLiteralContent":false,"bytecodeHash":"ipfs","appendCBOR":true},"outputSelection":{"*":{"*":["abi","evm.bytecode","evm.deployedBytecode","evm.methodIdentifiers","metadata"]}},"evmVersion":"paris","viaIR":true,"libraries":{}}} diff --git a/standard-json-input/pckhelper.json b/standard-json-input/pckhelper.json deleted file mode 100644 index 4b57571..0000000 --- a/standard-json-input/pckhelper.json +++ /dev/null @@ -1 +0,0 @@ -{"language":"Solidity","sources":{"src/helpers/PCKHelper.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport {X509Helper, X509CertObj, Asn1Decode, NodePtr, BytesUtils} from \"./X509Helper.sol\";\n\ncontract PCKHelper is X509Helper {\n using Asn1Decode for bytes;\n using NodePtr for uint256;\n using BytesUtils for bytes;\n\n // 1.2.840.113741.1.13.1\n bytes constant SGX_EXTENSION_OID = hex\"2A864886F84D010D01\";\n // 1.2.840.113741.1.13.1.2\n bytes constant TCB_OID = hex\"2A864886F84D010D0102\";\n // 1.2.840.113741.1.13.1.2.17\n bytes constant PCESVN_OID = hex\"2A864886F84D010D010211\";\n // 1.2.840.113741.1.13.1.3\n bytes constant PCEID_OID = hex\"2A864886F84D010D0103\";\n // 1.2.840.113741.1.13.1.4\n bytes constant FMSPC_OID = hex\"2A864886F84D010D0104\";\n\n // https://github.com/intel/SGXDataCenterAttestationPrimitives/blob/e7604e02331b3377f3766ed3653250e03af72d45/QuoteVerification/QVL/Src/AttestationLibrary/src/CertVerification/X509Constants.h#L64\n uint256 constant SGX_TCB_CPUSVN_SIZE = 16;\n\n struct PCKTCBFlags {\n bool fmspcFound;\n bool pceidFound;\n bool tcbFound;\n }\n\n // 421k gas\n function parsePckExtension(bytes memory der, uint256 extensionPtr)\n external\n pure\n returns (uint16 pcesvn, uint8[] memory cpusvns, bytes memory fmspcBytes, bytes memory pceidBytes)\n {\n if (der[extensionPtr.ixs()] != 0xA3) {\n revert(\"Not an extension\");\n }\n uint256 parentPtr = der.firstChildOf(extensionPtr);\n uint256 childPtr = der.firstChildOf(parentPtr);\n bool success;\n (success, pcesvn, cpusvns, fmspcBytes, pceidBytes) = _findPckTcbInfo(der, childPtr, parentPtr);\n require(success, \"invalid SGX extension\");\n }\n\n function _findPckTcbInfo(bytes memory der, uint256 ptr, uint256 parentPtr)\n private\n pure\n returns (bool success, uint16 pcesvn, uint8[] memory cpusvns, bytes memory fmspcBytes, bytes memory pceidBytes)\n {\n // iterate through the elements in the Extension sequence\n // until we locate the SGX Extension OID\n while (ptr != 0) {\n uint256 internalPtr = der.firstChildOf(ptr);\n if (der[internalPtr.ixs()] != 0x06) {\n return (false, pcesvn, cpusvns, fmspcBytes, pceidBytes);\n }\n\n if (BytesUtils.compareBytes(der.bytesAt(internalPtr), SGX_EXTENSION_OID)) {\n // 1.2.840.113741.1.13.1\n internalPtr = der.nextSiblingOf(internalPtr);\n uint256 extnValueParentPtr = der.rootOfOctetStringAt(internalPtr);\n uint256 extnValuePtr = der.firstChildOf(extnValueParentPtr);\n\n // Copy flags to memory to avoid stack too deep\n PCKTCBFlags memory flags;\n\n while (!(flags.fmspcFound && flags.pceidFound && flags.tcbFound)) {\n uint256 extnValueOidPtr = der.firstChildOf(extnValuePtr);\n if (der[extnValueOidPtr.ixs()] != 0x06) {\n return (false, pcesvn, cpusvns, fmspcBytes, pceidBytes);\n }\n if (BytesUtils.compareBytes(der.bytesAt(extnValueOidPtr), TCB_OID)) {\n // 1.2.840.113741.1.13.1.2\n (flags.tcbFound, pcesvn, cpusvns) = _findTcb(der, extnValueOidPtr);\n }\n if (BytesUtils.compareBytes(der.bytesAt(extnValueOidPtr), PCEID_OID)) {\n // 1.2.840.113741.1.13.1.3\n uint256 pceidPtr = der.nextSiblingOf(extnValueOidPtr);\n pceidBytes = der.bytesAt(pceidPtr);\n flags.pceidFound = true;\n }\n if (BytesUtils.compareBytes(der.bytesAt(extnValueOidPtr), FMSPC_OID)) {\n // 1.2.840.113741.1.13.1.4\n uint256 fmspcPtr = der.nextSiblingOf(extnValueOidPtr);\n fmspcBytes = der.bytesAt(fmspcPtr);\n flags.fmspcFound = true;\n }\n\n if (extnValuePtr.ixl() < extnValueParentPtr.ixl()) {\n extnValuePtr = der.nextSiblingOf(extnValuePtr);\n } else {\n break;\n }\n }\n success = flags.fmspcFound && flags.pceidFound && flags.tcbFound;\n break;\n }\n\n if (ptr.ixl() < parentPtr.ixl()) {\n ptr = der.nextSiblingOf(ptr);\n } else {\n ptr = 0; // exit\n }\n }\n }\n\n function _findTcb(bytes memory der, uint256 oidPtr)\n private\n pure\n returns (bool success, uint16 pcesvn, uint8[] memory cpusvns)\n {\n // sibiling of tcbOid\n uint256 tcbPtr = der.nextSiblingOf(oidPtr);\n // get the first svn object in the sequence\n uint256 svnParentPtr = der.firstChildOf(tcbPtr);\n cpusvns = new uint8[](SGX_TCB_CPUSVN_SIZE);\n for (uint256 i = 0; i < SGX_TCB_CPUSVN_SIZE + 1; i++) {\n uint256 svnPtr = der.firstChildOf(svnParentPtr); // OID\n uint256 svnValuePtr = der.nextSiblingOf(svnPtr); // value\n bytes memory svnValueBytes = der.bytesAt(svnValuePtr);\n uint16 svnValue =\n svnValueBytes.length < 2 ? uint16(bytes2(svnValueBytes)) / 256 : uint16(bytes2(svnValueBytes));\n if (BytesUtils.compareBytes(der.bytesAt(svnPtr), PCESVN_OID)) {\n // pcesvn is 4 bytes in size\n pcesvn = uint16(svnValue);\n } else {\n uint8 cpusvn = uint8(svnValue);\n cpusvns[i] = cpusvn;\n }\n\n // iterate to the next svn object in the sequence\n svnParentPtr = der.nextSiblingOf(svnParentPtr);\n }\n success = true;\n }\n}\n"},"src/helpers/X509Helper.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport {Asn1Decode, NodePtr} from \"../utils/Asn1Decode.sol\";\nimport {BytesUtils} from \"../utils/BytesUtils.sol\";\nimport {DateTimeUtils} from \"../utils/DateTimeUtils.sol\";\n\n/**\n * @title Solidity Structure representing X509 Certificates\n * @notice This is a simplified structure of a DER-decoded X509 Certificate\n */\nstruct X509CertObj {\n uint256 serialNumber;\n string issuerCommonName;\n uint256 validityNotBefore;\n uint256 validityNotAfter;\n string subjectCommonName;\n bytes subjectPublicKey;\n // the extension needs to be parsed further for PCK Certificates\n uint256 extensionPtr;\n // for signature verification in the cert chain\n bytes signature;\n bytes tbs;\n}\n\n/**\n * @title X509 Certificates Helper Contract\n * @notice This is a standalone contract that can be used by off-chain applications and smart contracts\n * to parse DER-encoded X509 certificates.\n * @dev The Extension sequence in Intel PCK Certificates is a custom ASN.1 Sequence that needs to be\n * @dev parsed further in a more specialized PCKHelper contract.\n */\ncontract X509Helper {\n using Asn1Decode for bytes;\n using NodePtr for uint256;\n using BytesUtils for bytes;\n\n /// =================================================================================\n /// USE THE GETTERS BELOW IF YOU DON'T WANT TO PARSE THE ENTIRE X509 CERTIFICATE\n /// =================================================================================\n\n function getTbsAndSig(bytes calldata der) external pure returns (bytes memory tbs, bytes memory sig) {\n uint256 root = der.root();\n uint256 tbsParentPtr = der.firstChildOf(root);\n uint256 sigPtr = der.nextSiblingOf(tbsParentPtr);\n sigPtr = der.nextSiblingOf(sigPtr);\n\n tbs = der.allBytesAt(tbsParentPtr);\n sig = _getSignature(der, sigPtr);\n }\n\n function getSerialNumber(bytes calldata der) external pure returns (uint256 serialNum) {\n uint256 root = der.root();\n uint256 tbsParentPtr = der.firstChildOf(root);\n uint256 tbsPtr = der.firstChildOf(tbsParentPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n serialNum = _parseSerialNumber(der.bytesAt(tbsPtr));\n }\n\n function getIssuerCommonName(bytes calldata der) external pure returns (string memory issuerCommonName) {\n uint256 root = der.root();\n uint256 tbsParentPtr = der.firstChildOf(root);\n uint256 tbsPtr = der.firstChildOf(tbsParentPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n issuerCommonName = _getCommonName(der, der.firstChildOf(tbsPtr));\n }\n\n function certIsNotExpired(bytes calldata der) external view returns (bool isValid) {\n uint256 root = der.root();\n uint256 tbsParentPtr = der.firstChildOf(root);\n uint256 tbsPtr = der.firstChildOf(tbsParentPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n (uint256 validityNotBefore, uint256 validityNotAfter) = _getValidity(der, tbsPtr);\n isValid = block.timestamp > validityNotBefore && block.timestamp < validityNotAfter;\n }\n\n function getSubjectCommonName(bytes calldata der) external pure returns (string memory subjectCommonName) {\n uint256 root = der.root();\n uint256 tbsParentPtr = der.firstChildOf(root);\n uint256 tbsPtr = der.firstChildOf(tbsParentPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n subjectCommonName = _getCommonName(der, der.firstChildOf(tbsPtr));\n }\n\n function getSubjectPublicKey(bytes calldata der) external pure returns (bytes memory pubKey) {\n uint256 root = der.root();\n uint256 tbsParentPtr = der.firstChildOf(root);\n uint256 tbsPtr = der.firstChildOf(tbsParentPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n pubKey = _getSubjectPublicKey(der, der.firstChildOf(tbsPtr));\n }\n\n /// x509 Certificates generally contain a sequence of elements in the following order:\n /// 1. tbs\n /// - 1a. version\n /// - 1b. serial number\n /// - 1c. siganture algorithm\n /// - 1d. issuer\n /// - - 1d(a). common name\n /// - - 1d(b). organization name\n /// - - 1d(c). locality name\n /// - - 1d(d). state or province name\n /// - - 1d(e). country name\n /// - 1e. validity\n /// - - 1e(a) notBefore\n /// - - 1e(b) notAfter\n /// - 1f. subject\n /// - - contains the same set of elements as 1d\n /// - 1g. subject public key info\n /// - - 1g(a). algorithm\n /// - - 1g(b). subject public key\n /// - 1h. Extensions\n /// 2. Signature Algorithm\n /// 3. Signature\n /// - 3a. X value\n /// - 3b. Y value\n function parseX509DER(bytes calldata der) external pure returns (X509CertObj memory cert) {\n uint256 root = der.root();\n\n uint256 tbsParentPtr = der.firstChildOf(root);\n cert.tbs = der.allBytesAt(tbsParentPtr);\n\n uint256 tbsPtr = der.firstChildOf(tbsParentPtr);\n\n tbsPtr = der.nextSiblingOf(tbsPtr);\n\n cert.serialNumber = _parseSerialNumber(der.bytesAt(tbsPtr));\n\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n\n cert.issuerCommonName = _getCommonName(der, der.firstChildOf(tbsPtr));\n\n tbsPtr = der.nextSiblingOf(tbsPtr);\n (cert.validityNotBefore, cert.validityNotAfter) = _getValidity(der, tbsPtr);\n\n tbsPtr = der.nextSiblingOf(tbsPtr);\n\n cert.subjectCommonName = _getCommonName(der, der.firstChildOf(tbsPtr));\n\n tbsPtr = der.nextSiblingOf(tbsPtr);\n cert.subjectPublicKey = _getSubjectPublicKey(der, der.firstChildOf(tbsPtr));\n\n cert.extensionPtr = der.nextSiblingOf(tbsPtr);\n\n // tbs iteration completed\n // now we just need to look for the signature\n\n uint256 sigPtr = der.nextSiblingOf(tbsParentPtr);\n sigPtr = der.nextSiblingOf(sigPtr);\n cert.signature = _getSignature(der, sigPtr);\n }\n\n function _getCommonName(bytes calldata der, uint256 commonNameParentPtr)\n private\n pure\n returns (string memory commonName)\n {\n commonNameParentPtr = der.firstChildOf(commonNameParentPtr);\n commonNameParentPtr = der.firstChildOf(commonNameParentPtr);\n commonNameParentPtr = der.nextSiblingOf(commonNameParentPtr);\n commonName = string(der.bytesAt(commonNameParentPtr));\n }\n\n function _getValidity(bytes calldata der, uint256 validityPtr)\n private\n pure\n returns (uint256 notBefore, uint256 notAfter)\n {\n uint256 notBeforePtr = der.firstChildOf(validityPtr);\n uint256 notAfterPtr = der.nextSiblingOf(notBeforePtr);\n notBefore = DateTimeUtils.fromDERToTimestamp(der.bytesAt(notBeforePtr));\n notAfter = DateTimeUtils.fromDERToTimestamp(der.bytesAt(notAfterPtr));\n }\n\n function _getSubjectPublicKey(bytes calldata der, uint256 subjectPublicKeyInfoPtr)\n private\n pure\n returns (bytes memory pubKey)\n {\n subjectPublicKeyInfoPtr = der.nextSiblingOf(subjectPublicKeyInfoPtr);\n pubKey = der.bitstringAt(subjectPublicKeyInfoPtr);\n if (pubKey.length != 65) {\n // TODO: we need to figure out how to handle key with prefix byte 0x02 or 0x03\n revert(\"compressed public key not supported\");\n }\n pubKey = _trimBytes(pubKey, 64);\n }\n\n function _parseSerialNumber(bytes memory serialBytes) private pure returns (uint256 serial) {\n uint256 shift = 8 * (32 - serialBytes.length);\n serial = uint256(bytes32(serialBytes) >> shift);\n }\n\n function _getSignature(bytes calldata der, uint256 sigPtr) private pure returns (bytes memory sig) {\n sigPtr = der.rootOfBitStringAt(sigPtr);\n\n sigPtr = der.firstChildOf(sigPtr);\n bytes memory sigX = _trimBytes(der.bytesAt(sigPtr), 32);\n\n sigPtr = der.nextSiblingOf(sigPtr);\n bytes memory sigY = _trimBytes(der.bytesAt(sigPtr), 32);\n\n sig = abi.encodePacked(sigX, sigY);\n }\n\n /// @dev remove unnecessary prefix from the input\n function _trimBytes(bytes memory input, uint256 expectedLength) private pure returns (bytes memory output) {\n uint256 n = input.length;\n if (n == expectedLength) {\n output = input;\n } else if (n < expectedLength) {\n output = new bytes(expectedLength);\n uint256 padLength = expectedLength - n;\n for (uint256 i = 0; i < n; i++) {\n output[padLength + i] = input[i];\n }\n } else {\n uint256 lengthDiff = n - expectedLength;\n output = input.substring(lengthDiff, expectedLength);\n }\n }\n}\n"},"src/utils/Asn1Decode.sol":{"content":"// SPDX-License-Identifier: MIT\n// Original source: https://github.com/JonahGroendal/asn1-decode\npragma solidity ^0.8.0;\n\n// Inspired by PufferFinance/rave - Apache-2.0 license\n// https://github.com/JonahGroendal/asn1-decode/blob/5c2d1469fc678513753786acb441e597969192ec/contracts/Asn1Decode.sol\n\nimport \"./BytesUtils.sol\";\n\nlibrary NodePtr {\n // Unpack first byte index\n function ixs(uint256 self) internal pure returns (uint256) {\n return uint80(self);\n }\n // Unpack first content byte index\n\n function ixf(uint256 self) internal pure returns (uint256) {\n return uint80(self >> 80);\n }\n // Unpack last content byte index\n\n function ixl(uint256 self) internal pure returns (uint256) {\n return uint80(self >> 160);\n }\n // Pack 3 uint80s into a uint256\n\n function getPtr(uint256 _ixs, uint256 _ixf, uint256 _ixl) internal pure returns (uint256) {\n _ixs |= _ixf << 80;\n _ixs |= _ixl << 160;\n return _ixs;\n }\n}\n\nlibrary Asn1Decode {\n using NodePtr for uint256;\n using BytesUtils for bytes;\n\n /*\n * @dev Get the root node. First step in traversing an ASN1 structure\n * @param der The DER-encoded ASN1 structure\n * @return A pointer to the outermost node\n */\n function root(bytes memory der) internal pure returns (uint256) {\n return readNodeLength(der, 0);\n }\n\n /*\n * @dev Get the root node of an ASN1 structure that's within a bit string value\n * @param der The DER-encoded ASN1 structure\n * @return A pointer to the outermost node\n */\n function rootOfBitStringAt(bytes memory der, uint256 ptr) internal pure returns (uint256) {\n require(der[ptr.ixs()] == 0x03, \"Not type BIT STRING\");\n return readNodeLength(der, ptr.ixf() + 1);\n }\n\n /*\n * @dev Get the root node of an ASN1 structure that's within an octet string value\n * @param der The DER-encoded ASN1 structure\n * @return A pointer to the outermost node\n */\n function rootOfOctetStringAt(bytes memory der, uint256 ptr) internal pure returns (uint256) {\n require(der[ptr.ixs()] == 0x04, \"Not type OCTET STRING\");\n return readNodeLength(der, ptr.ixf());\n }\n\n /*\n * @dev Get the next sibling node\n * @param der The DER-encoded ASN1 structure\n * @param ptr Points to the indices of the current node\n * @return A pointer to the next sibling node\n */\n function nextSiblingOf(bytes memory der, uint256 ptr) internal pure returns (uint256) {\n return readNodeLength(der, ptr.ixl() + 1);\n }\n\n /*\n * @dev Get the first child node of the current node\n * @param der The DER-encoded ASN1 structure\n * @param ptr Points to the indices of the current node\n * @return A pointer to the first child node\n */\n function firstChildOf(bytes memory der, uint256 ptr) internal pure returns (uint256) {\n require(der[ptr.ixs()] & 0x20 == 0x20, \"Not a constructed type\");\n return readNodeLength(der, ptr.ixf());\n }\n\n /*\n * @dev Use for looping through children of a node (either i or j).\n * @param i Pointer to an ASN1 node\n * @param j Pointer to another ASN1 node of the same ASN1 structure\n * @return True iff j is child of i or i is child of j.\n */\n function isChildOf(uint256 i, uint256 j) internal pure returns (bool) {\n return (((i.ixf() <= j.ixs()) && (j.ixl() <= i.ixl())) || ((j.ixf() <= i.ixs()) && (i.ixl() <= j.ixl())));\n }\n\n /*\n * @dev Extract value of node from DER-encoded structure\n * @param der The der-encoded ASN1 structure\n * @param ptr Points to the indices of the current node\n * @return Value bytes of node\n */\n function bytesAt(bytes memory der, uint256 ptr) internal pure returns (bytes memory) {\n return der.substring(ptr.ixf(), ptr.ixl() + 1 - ptr.ixf());\n }\n\n /*\n * @dev Extract entire node from DER-encoded structure\n * @param der The DER-encoded ASN1 structure\n * @param ptr Points to the indices of the current node\n * @return All bytes of node\n */\n function allBytesAt(bytes memory der, uint256 ptr) internal pure returns (bytes memory) {\n return der.substring(ptr.ixs(), ptr.ixl() + 1 - ptr.ixs());\n }\n\n /*\n * @dev Extract value of node from DER-encoded structure\n * @param der The DER-encoded ASN1 structure\n * @param ptr Points to the indices of the current node\n * @return Value bytes of node as bytes32\n */\n function bytes32At(bytes memory der, uint256 ptr) internal pure returns (bytes32) {\n return der.readBytesN(ptr.ixf(), ptr.ixl() + 1 - ptr.ixf());\n }\n\n /*\n * @dev Extract value of node from DER-encoded structure\n * @param der The der-encoded ASN1 structure\n * @param ptr Points to the indices of the current node\n * @return Uint value of node\n */\n function uintAt(bytes memory der, uint256 ptr) internal pure returns (uint256) {\n require(der[ptr.ixs()] == 0x02, \"Not type INTEGER\");\n require(der[ptr.ixf()] & 0x80 == 0, \"Not positive\");\n uint256 len = ptr.ixl() + 1 - ptr.ixf();\n return uint256(der.readBytesN(ptr.ixf(), len) >> (32 - len) * 8);\n }\n\n /*\n * @dev Extract value of a positive integer node from DER-encoded structure\n * @param der The DER-encoded ASN1 structure\n * @param ptr Points to the indices of the current node\n * @return Value bytes of a positive integer node\n */\n function uintBytesAt(bytes memory der, uint256 ptr) internal pure returns (bytes memory) {\n require(der[ptr.ixs()] == 0x02, \"Not type INTEGER\");\n require(der[ptr.ixf()] & 0x80 == 0, \"Not positive\");\n uint256 valueLength = ptr.ixl() + 1 - ptr.ixf();\n if (der[ptr.ixf()] == 0) {\n return der.substring(ptr.ixf() + 1, valueLength - 1);\n } else {\n return der.substring(ptr.ixf(), valueLength);\n }\n }\n\n function keccakOfBytesAt(bytes memory der, uint256 ptr) internal pure returns (bytes32) {\n return der.keccak(ptr.ixf(), ptr.ixl() + 1 - ptr.ixf());\n }\n\n function keccakOfAllBytesAt(bytes memory der, uint256 ptr) internal pure returns (bytes32) {\n return der.keccak(ptr.ixs(), ptr.ixl() + 1 - ptr.ixs());\n }\n\n /*\n * @dev Extract value of bitstring node from DER-encoded structure\n * @param der The DER-encoded ASN1 structure\n * @param ptr Points to the indices of the current node\n * @return Value of bitstring converted to bytes\n */\n function bitstringAt(bytes memory der, uint256 ptr) internal pure returns (bytes memory) {\n require(der[ptr.ixs()] == 0x03, \"Not type BIT STRING\");\n // Only 00 padded bitstr can be converted to bytestr!\n require(der[ptr.ixf()] == 0x00);\n uint256 valueLength = ptr.ixl() + 1 - ptr.ixf();\n return der.substring(ptr.ixf() + 1, valueLength - 1);\n }\n\n function readNodeLength(bytes memory der, uint256 ix) private pure returns (uint256) {\n uint256 length;\n uint80 ixFirstContentByte;\n uint80 ixLastContentByte;\n if ((der[ix + 1] & 0x80) == 0) {\n length = uint8(der[ix + 1]);\n ixFirstContentByte = uint80(ix + 2);\n ixLastContentByte = uint80(ixFirstContentByte + length - 1);\n } else {\n uint8 lengthbytesLength = uint8(der[ix + 1] & 0x7F);\n if (lengthbytesLength == 1) {\n length = der.readUint8(ix + 2);\n } else if (lengthbytesLength == 2) {\n length = der.readUint16(ix + 2);\n } else {\n length = uint256(der.readBytesN(ix + 2, lengthbytesLength) >> (32 - lengthbytesLength) * 8);\n }\n ixFirstContentByte = uint80(ix + 2 + lengthbytesLength);\n ixLastContentByte = uint80(ixFirstContentByte + length - 1);\n }\n return NodePtr.getPtr(ix, ixFirstContentByte, ixLastContentByte);\n }\n}\n"},"src/utils/BytesUtils.sol":{"content":"// SPDX-License-Identifier: BSD 2-Clause License\npragma solidity ^0.8.0;\n\n// Inspired by ensdomains/dnssec-oracle - BSD-2-Clause license\n// https://github.com/ensdomains/dnssec-oracle/blob/master/contracts/BytesUtils.sol\n\nlibrary BytesUtils {\n /*\n * @dev Returns the keccak-256 hash of a byte range.\n * @param self The byte string to hash.\n * @param offset The position to start hashing at.\n * @param len The number of bytes to hash.\n * @return The hash of the byte range.\n */\n function keccak(bytes memory self, uint256 offset, uint256 len) internal pure returns (bytes32 ret) {\n require(offset + len <= self.length);\n assembly {\n ret := keccak256(add(add(self, 32), offset), len)\n }\n }\n\n /*\n * @dev Returns a positive number if `other` comes lexicographically after\n * `self`, a negative number if it comes before, or zero if the\n * contents of the two bytes are equal.\n * @param self The first bytes to compare.\n * @param other The second bytes to compare.\n * @return The result of the comparison.\n */\n function compare(bytes memory self, bytes memory other) internal pure returns (int256) {\n return compare(self, 0, self.length, other, 0, other.length);\n }\n\n /*\n * @dev Returns a positive number if `other` comes lexicographically after\n * `self`, a negative number if it comes before, or zero if the\n * contents of the two bytes are equal. Comparison is done per-rune,\n * on unicode codepoints.\n * @param self The first bytes to compare.\n * @param offset The offset of self.\n * @param len The length of self.\n * @param other The second bytes to compare.\n * @param otheroffset The offset of the other string.\n * @param otherlen The length of the other string.\n * @return The result of the comparison.\n */\n function compare(\n bytes memory self,\n uint256 offset,\n uint256 len,\n bytes memory other,\n uint256 otheroffset,\n uint256 otherlen\n ) internal pure returns (int256) {\n uint256 shortest = len;\n if (otherlen < len) {\n shortest = otherlen;\n }\n\n uint256 selfptr;\n uint256 otherptr;\n\n assembly {\n selfptr := add(self, add(offset, 32))\n otherptr := add(other, add(otheroffset, 32))\n }\n for (uint256 idx = 0; idx < shortest; idx += 32) {\n uint256 a;\n uint256 b;\n assembly {\n a := mload(selfptr)\n b := mload(otherptr)\n }\n if (a != b) {\n // Mask out irrelevant bytes and check again\n uint256 mask;\n if (shortest > 32) {\n mask = type(uint256).max; // aka 0xffffff....\n } else {\n mask = ~(2 ** (8 * (32 - shortest + idx)) - 1);\n }\n uint256 diff = (a & mask) - (b & mask);\n if (diff != 0) {\n return int256(diff);\n }\n }\n selfptr += 32;\n otherptr += 32;\n }\n\n return int256(len) - int256(otherlen);\n }\n\n /*\n * @dev Returns true if the two byte ranges are equal.\n * @param self The first byte range to compare.\n * @param offset The offset into the first byte range.\n * @param other The second byte range to compare.\n * @param otherOffset The offset into the second byte range.\n * @param len The number of bytes to compare\n * @return True if the byte ranges are equal, false otherwise.\n */\n function equals(bytes memory self, uint256 offset, bytes memory other, uint256 otherOffset, uint256 len)\n internal\n pure\n returns (bool)\n {\n return keccak(self, offset, len) == keccak(other, otherOffset, len);\n }\n\n /*\n * @dev Returns true if the two byte ranges are equal with offsets.\n * @param self The first byte range to compare.\n * @param offset The offset into the first byte range.\n * @param other The second byte range to compare.\n * @param otherOffset The offset into the second byte range.\n * @return True if the byte ranges are equal, false otherwise.\n */\n function equals(bytes memory self, uint256 offset, bytes memory other, uint256 otherOffset)\n internal\n pure\n returns (bool)\n {\n return keccak(self, offset, self.length - offset) == keccak(other, otherOffset, other.length - otherOffset);\n }\n\n /*\n * @dev Compares a range of 'self' to all of 'other' and returns True iff\n * they are equal.\n * @param self The first byte range to compare.\n * @param offset The offset into the first byte range.\n * @param other The second byte range to compare.\n * @return True if the byte ranges are equal, false otherwise.\n */\n function equals(bytes memory self, uint256 offset, bytes memory other) internal pure returns (bool) {\n return self.length >= offset + other.length && equals(self, offset, other, 0, other.length);\n }\n\n /*\n * @dev Returns true if the two byte ranges are equal.\n * @param self The first byte range to compare.\n * @param other The second byte range to compare.\n * @return True if the byte ranges are equal, false otherwise.\n */\n function equals(bytes memory self, bytes memory other) internal pure returns (bool) {\n return self.length == other.length && equals(self, 0, other, 0, self.length);\n }\n\n /*\n * @dev Returns the 8-bit number at the specified index of self.\n * @param self The byte string.\n * @param idx The index into the bytes\n * @return The specified 8 bits of the string, interpreted as an integer.\n */\n function readUint8(bytes memory self, uint256 idx) internal pure returns (uint8 ret) {\n return uint8(self[idx]);\n }\n\n /*\n * @dev Returns the 16-bit number at the specified index of self.\n * @param self The byte string.\n * @param idx The index into the bytes\n * @return The specified 16 bits of the string, interpreted as an integer.\n */\n function readUint16(bytes memory self, uint256 idx) internal pure returns (uint16 ret) {\n require(idx + 2 <= self.length);\n assembly {\n ret := and(mload(add(add(self, 2), idx)), 0xFFFF)\n }\n }\n\n /*\n * @dev Returns the 32-bit number at the specified index of self.\n * @param self The byte string.\n * @param idx The index into the bytes\n * @return The specified 32 bits of the string, interpreted as an integer.\n */\n function readUint32(bytes memory self, uint256 idx) internal pure returns (uint32 ret) {\n require(idx + 4 <= self.length);\n assembly {\n ret := and(mload(add(add(self, 4), idx)), 0xFFFFFFFF)\n }\n }\n\n /*\n * @dev Returns the 32 byte value at the specified index of self.\n * @param self The byte string.\n * @param idx The index into the bytes\n * @return The specified 32 bytes of the string.\n */\n function readBytes32(bytes memory self, uint256 idx) internal pure returns (bytes32 ret) {\n require(idx + 32 <= self.length);\n assembly {\n ret := mload(add(add(self, 32), idx))\n }\n }\n\n /*\n * @dev Returns the 32 byte value at the specified index of self.\n * @param self The byte string.\n * @param idx The index into the bytes\n * @return The specified 32 bytes of the string.\n */\n function readBytes20(bytes memory self, uint256 idx) internal pure returns (bytes20 ret) {\n require(idx + 20 <= self.length);\n assembly {\n ret :=\n and(mload(add(add(self, 32), idx)), 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000000)\n }\n }\n\n /*\n * @dev Returns the n byte value at the specified index of self.\n * @param self The byte string.\n * @param idx The index into the bytes.\n * @param len The number of bytes.\n * @return The specified 32 bytes of the string.\n */\n function readBytesN(bytes memory self, uint256 idx, uint256 len) internal pure returns (bytes32 ret) {\n require(len <= 32);\n require(idx + len <= self.length);\n assembly {\n let mask := not(sub(exp(256, sub(32, len)), 1))\n ret := and(mload(add(add(self, 32), idx)), mask)\n }\n }\n\n function memcpy(uint256 dest, uint256 src, uint256 len) private pure {\n // Copy word-length chunks while possible\n for (; len >= 32; len -= 32) {\n assembly {\n mstore(dest, mload(src))\n }\n dest += 32;\n src += 32;\n }\n\n // Copy remaining bytes\n uint256 mask;\n if (len == 0) {\n mask = type(uint256).max; // Set to maximum value of uint256\n } else {\n mask = 256 ** (32 - len) - 1;\n }\n\n assembly {\n let srcpart := and(mload(src), not(mask))\n let destpart := and(mload(dest), mask)\n mstore(dest, or(destpart, srcpart))\n }\n }\n\n /*\n * @dev Copies a substring into a new byte string.\n * @param self The byte string to copy from.\n * @param offset The offset to start copying at.\n * @param len The number of bytes to copy.\n */\n function substring(bytes memory self, uint256 offset, uint256 len) internal pure returns (bytes memory) {\n require(offset + len <= self.length);\n\n bytes memory ret = new bytes(len);\n uint256 dest;\n uint256 src;\n\n assembly {\n dest := add(ret, 32)\n src := add(add(self, 32), offset)\n }\n memcpy(dest, src, len);\n\n return ret;\n }\n\n // Maps characters from 0x30 to 0x7A to their base32 values.\n // 0xFF represents invalid characters in that range.\n bytes constant base32HexTable =\n hex\"00010203040506070809FFFFFFFFFFFFFF0A0B0C0D0E0F101112131415161718191A1B1C1D1E1FFFFFFFFFFFFFFFFFFFFF0A0B0C0D0E0F101112131415161718191A1B1C1D1E1F\";\n\n /**\n * @dev Decodes unpadded base32 data of up to one word in length.\n * @param self The data to decode.\n * @param off Offset into the string to start at.\n * @param len Number of characters to decode.\n * @return The decoded data, left aligned.\n */\n function base32HexDecodeWord(bytes memory self, uint256 off, uint256 len) internal pure returns (bytes32) {\n require(len <= 52);\n\n uint256 ret = 0;\n uint8 decoded;\n for (uint256 i = 0; i < len; i++) {\n bytes1 char = self[off + i];\n require(char >= 0x30 && char <= 0x7A);\n decoded = uint8(base32HexTable[uint256(uint8(char)) - 0x30]);\n require(decoded <= 0x20);\n if (i == len - 1) {\n break;\n }\n ret = (ret << 5) | decoded;\n }\n\n uint256 bitlen = len * 5;\n if (len % 8 == 0) {\n // Multiple of 8 characters, no padding\n ret = (ret << 5) | decoded;\n } else if (len % 8 == 2) {\n // Two extra characters - 1 byte\n ret = (ret << 3) | (decoded >> 2);\n bitlen -= 2;\n } else if (len % 8 == 4) {\n // Four extra characters - 2 bytes\n ret = (ret << 1) | (decoded >> 4);\n bitlen -= 4;\n } else if (len % 8 == 5) {\n // Five extra characters - 3 bytes\n ret = (ret << 4) | (decoded >> 1);\n bitlen -= 1;\n } else if (len % 8 == 7) {\n // Seven extra characters - 4 bytes\n ret = (ret << 2) | (decoded >> 3);\n bitlen -= 3;\n } else {\n revert();\n }\n\n return bytes32(ret << (256 - bitlen));\n }\n\n function compareBytes(bytes memory a, bytes memory b) internal pure returns (bool) {\n if (a.length != b.length) {\n return false;\n }\n for (uint256 i = 0; i < a.length; i++) {\n if (a[i] != b[i]) {\n return false;\n }\n }\n return true;\n }\n}\n"},"src/utils/DateTimeUtils.sol":{"content":"// SPDX-License-Identifier: GPL-3.0\npragma solidity ^0.8.0;\n\nimport {DateTimeLib} from \"solady/utils/DateTimeLib.sol\";\nimport {LibString} from \"solady/utils/LibString.sol\";\n\nlibrary DateTimeUtils {\n using LibString for string;\n\n /*\n * @dev Convert a DER-encoded time to a unix timestamp\n * @param x509Time The DER-encoded time\n * @return The unix timestamp\n */\n function fromDERToTimestamp(bytes memory x509Time) internal pure returns (uint256) {\n uint16 yrs;\n uint8 mnths;\n uint8 dys;\n uint8 hrs;\n uint8 mins;\n uint8 secs;\n uint8 offset;\n\n if (x509Time.length == 13) {\n if (uint8(x509Time[0]) - 48 < 5) yrs += 2000;\n else yrs += 1900;\n } else {\n yrs += (uint8(x509Time[0]) - 48) * 1000 + (uint8(x509Time[1]) - 48) * 100;\n offset = 2;\n }\n yrs += (uint8(x509Time[offset + 0]) - 48) * 10 + uint8(x509Time[offset + 1]) - 48;\n mnths = (uint8(x509Time[offset + 2]) - 48) * 10 + uint8(x509Time[offset + 3]) - 48;\n dys += (uint8(x509Time[offset + 4]) - 48) * 10 + uint8(x509Time[offset + 5]) - 48;\n hrs += (uint8(x509Time[offset + 6]) - 48) * 10 + uint8(x509Time[offset + 7]) - 48;\n mins += (uint8(x509Time[offset + 8]) - 48) * 10 + uint8(x509Time[offset + 9]) - 48;\n secs += (uint8(x509Time[offset + 10]) - 48) * 10 + uint8(x509Time[offset + 11]) - 48;\n\n return DateTimeLib.dateTimeToTimestamp(yrs, mnths, dys, hrs, mins, secs);\n }\n\n /// @dev iso follows pattern: \"YYYY-MM-DDTHH:mm:ssZ\"\n function fromISOToTimestamp(string memory iso) internal pure returns (uint256) {\n require(bytes(iso).length == 20, \"invalid iso string length\");\n uint256 y = stringToUint(iso.slice(0, 4));\n uint256 m = stringToUint(iso.slice(5, 7));\n uint256 d = stringToUint(iso.slice(8, 10));\n uint256 h = stringToUint(iso.slice(11, 13));\n uint256 min = stringToUint(iso.slice(14, 16));\n uint256 s = stringToUint(iso.slice(17, 19));\n\n return DateTimeLib.dateTimeToTimestamp(y, m, d, h, min, s);\n }\n\n // https://ethereum.stackexchange.com/questions/10932/how-to-convert-string-to-int\n function stringToUint(string memory s) private pure returns (uint256 result) {\n bytes memory b = bytes(s);\n result = 0;\n for (uint256 i = 0; i < b.length; i++) {\n uint256 c = uint256(uint8(b[i]));\n if (c >= 48 && c <= 57) {\n result = result * 10 + (c - 48);\n }\n }\n }\n}\n"},"lib/solady/src/utils/DateTimeLib.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\n/// @notice Library for date time operations.\n/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/DateTimeLib.sol)\n///\n/// Conventions:\n/// --------------------------------------------------------------------+\n/// Unit | Range | Notes |\n/// --------------------------------------------------------------------|\n/// timestamp | 0..0x1e18549868c76ff | Unix timestamp. |\n/// epochDay | 0..0x16d3e098039 | Days since 1970-01-01. |\n/// year | 1970..0xffffffff | Gregorian calendar year. |\n/// month | 1..12 | Gregorian calendar month. |\n/// day | 1..31 | Gregorian calendar day of month. |\n/// weekday | 1..7 | The day of the week (1-indexed). |\n/// --------------------------------------------------------------------+\n/// All timestamps of days are rounded down to 00:00:00 UTC.\nlibrary DateTimeLib {\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* CONSTANTS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n // Weekdays are 1-indexed for a traditional rustic feel.\n\n // \"And on the seventh day God finished his work that he had done,\n // and he rested on the seventh day from all his work that he had done.\"\n // -- Genesis 2:2\n\n uint256 internal constant MON = 1;\n uint256 internal constant TUE = 2;\n uint256 internal constant WED = 3;\n uint256 internal constant THU = 4;\n uint256 internal constant FRI = 5;\n uint256 internal constant SAT = 6;\n uint256 internal constant SUN = 7;\n\n // Months and days of months are 1-indexed for ease of use.\n\n uint256 internal constant JAN = 1;\n uint256 internal constant FEB = 2;\n uint256 internal constant MAR = 3;\n uint256 internal constant APR = 4;\n uint256 internal constant MAY = 5;\n uint256 internal constant JUN = 6;\n uint256 internal constant JUL = 7;\n uint256 internal constant AUG = 8;\n uint256 internal constant SEP = 9;\n uint256 internal constant OCT = 10;\n uint256 internal constant NOV = 11;\n uint256 internal constant DEC = 12;\n\n // These limits are large enough for most practical purposes.\n // Inputs that exceed these limits result in undefined behavior.\n\n uint256 internal constant MAX_SUPPORTED_YEAR = 0xffffffff;\n uint256 internal constant MAX_SUPPORTED_EPOCH_DAY = 0x16d3e098039;\n uint256 internal constant MAX_SUPPORTED_TIMESTAMP = 0x1e18549868c76ff;\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* DATE TIME OPERATIONS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev Returns the number of days since 1970-01-01 from (`year`,`month`,`day`).\n /// See: https://howardhinnant.github.io/date_algorithms.html\n /// Note: Inputs outside the supported ranges result in undefined behavior.\n /// Use {isSupportedDate} to check if the inputs are supported.\n function dateToEpochDay(uint256 year, uint256 month, uint256 day)\n internal\n pure\n returns (uint256 epochDay)\n {\n /// @solidity memory-safe-assembly\n assembly {\n year := sub(year, lt(month, 3))\n let doy := add(shr(11, add(mul(62719, mod(add(month, 9), 12)), 769)), day)\n let yoe := mod(year, 400)\n let doe := sub(add(add(mul(yoe, 365), shr(2, yoe)), doy), div(yoe, 100))\n epochDay := sub(add(mul(div(year, 400), 146097), doe), 719469)\n }\n }\n\n /// @dev Returns (`year`,`month`,`day`) from the number of days since 1970-01-01.\n /// Note: Inputs outside the supported ranges result in undefined behavior.\n /// Use {isSupportedDays} to check if the inputs is supported.\n function epochDayToDate(uint256 epochDay)\n internal\n pure\n returns (uint256 year, uint256 month, uint256 day)\n {\n /// @solidity memory-safe-assembly\n assembly {\n epochDay := add(epochDay, 719468)\n let doe := mod(epochDay, 146097)\n let yoe :=\n div(sub(sub(add(doe, div(doe, 36524)), div(doe, 1460)), eq(doe, 146096)), 365)\n let doy := sub(doe, sub(add(mul(365, yoe), shr(2, yoe)), div(yoe, 100)))\n let mp := div(add(mul(5, doy), 2), 153)\n day := add(sub(doy, shr(11, add(mul(mp, 62719), 769))), 1)\n month := byte(mp, shl(160, 0x030405060708090a0b0c0102))\n year := add(add(yoe, mul(div(epochDay, 146097), 400)), lt(month, 3))\n }\n }\n\n /// @dev Returns the unix timestamp from (`year`,`month`,`day`).\n /// Note: Inputs outside the supported ranges result in undefined behavior.\n /// Use {isSupportedDate} to check if the inputs are supported.\n function dateToTimestamp(uint256 year, uint256 month, uint256 day)\n internal\n pure\n returns (uint256 result)\n {\n unchecked {\n result = dateToEpochDay(year, month, day) * 86400;\n }\n }\n\n /// @dev Returns (`year`,`month`,`day`) from the given unix timestamp.\n /// Note: Inputs outside the supported ranges result in undefined behavior.\n /// Use {isSupportedTimestamp} to check if the inputs are supported.\n function timestampToDate(uint256 timestamp)\n internal\n pure\n returns (uint256 year, uint256 month, uint256 day)\n {\n (year, month, day) = epochDayToDate(timestamp / 86400);\n }\n\n /// @dev Returns the unix timestamp from\n /// (`year`,`month`,`day`,`hour`,`minute`,`second`).\n /// Note: Inputs outside the supported ranges result in undefined behavior.\n /// Use {isSupportedDateTime} to check if the inputs are supported.\n function dateTimeToTimestamp(\n uint256 year,\n uint256 month,\n uint256 day,\n uint256 hour,\n uint256 minute,\n uint256 second\n ) internal pure returns (uint256 result) {\n unchecked {\n result = dateToEpochDay(year, month, day) * 86400 + hour * 3600 + minute * 60 + second;\n }\n }\n\n /// @dev Returns (`year`,`month`,`day`,`hour`,`minute`,`second`)\n /// from the given unix timestamp.\n /// Note: Inputs outside the supported ranges result in undefined behavior.\n /// Use {isSupportedTimestamp} to check if the inputs are supported.\n function timestampToDateTime(uint256 timestamp)\n internal\n pure\n returns (\n uint256 year,\n uint256 month,\n uint256 day,\n uint256 hour,\n uint256 minute,\n uint256 second\n )\n {\n unchecked {\n (year, month, day) = epochDayToDate(timestamp / 86400);\n uint256 secs = timestamp % 86400;\n hour = secs / 3600;\n secs = secs % 3600;\n minute = secs / 60;\n second = secs % 60;\n }\n }\n\n /// @dev Returns if the `year` is leap.\n function isLeapYear(uint256 year) internal pure returns (bool leap) {\n /// @solidity memory-safe-assembly\n assembly {\n leap := iszero(and(add(mul(iszero(mod(year, 25)), 12), 3), year))\n }\n }\n\n /// @dev Returns number of days in given `month` of `year`.\n function daysInMonth(uint256 year, uint256 month) internal pure returns (uint256 result) {\n bool flag = isLeapYear(year);\n /// @solidity memory-safe-assembly\n assembly {\n // `daysInMonths = [31,28,31,30,31,30,31,31,30,31,30,31]`.\n // `result = daysInMonths[month - 1] + isLeapYear(year)`.\n result :=\n add(byte(month, shl(152, 0x1F1C1F1E1F1E1F1F1E1F1E1F)), and(eq(month, 2), flag))\n }\n }\n\n /// @dev Returns the weekday from the unix timestamp.\n /// Monday: 1, Tuesday: 2, ....., Sunday: 7.\n function weekday(uint256 timestamp) internal pure returns (uint256 result) {\n unchecked {\n result = ((timestamp / 86400 + 3) % 7) + 1;\n }\n }\n\n /// @dev Returns if (`year`,`month`,`day`) is a supported date.\n /// - `1970 <= year <= MAX_SUPPORTED_YEAR`.\n /// - `1 <= month <= 12`.\n /// - `1 <= day <= daysInMonth(year, month)`.\n function isSupportedDate(uint256 year, uint256 month, uint256 day)\n internal\n pure\n returns (bool result)\n {\n uint256 md = daysInMonth(year, month);\n /// @solidity memory-safe-assembly\n assembly {\n let w := not(0)\n result :=\n and(\n lt(sub(year, 1970), sub(MAX_SUPPORTED_YEAR, 1969)),\n and(lt(add(month, w), 12), lt(add(day, w), md))\n )\n }\n }\n\n /// @dev Returns if (`year`,`month`,`day`,`hour`,`minute`,`second`) is a supported date time.\n /// - `1970 <= year <= MAX_SUPPORTED_YEAR`.\n /// - `1 <= month <= 12`.\n /// - `1 <= day <= daysInMonth(year, month)`.\n /// - `hour < 24`.\n /// - `minute < 60`.\n /// - `second < 60`.\n function isSupportedDateTime(\n uint256 year,\n uint256 month,\n uint256 day,\n uint256 hour,\n uint256 minute,\n uint256 second\n ) internal pure returns (bool result) {\n if (isSupportedDate(year, month, day)) {\n /// @solidity memory-safe-assembly\n assembly {\n result := and(lt(hour, 24), and(lt(minute, 60), lt(second, 60)))\n }\n }\n }\n\n /// @dev Returns if `epochDay` is a supported unix epoch day.\n function isSupportedEpochDay(uint256 epochDay) internal pure returns (bool result) {\n unchecked {\n result = epochDay < MAX_SUPPORTED_EPOCH_DAY + 1;\n }\n }\n\n /// @dev Returns if `timestamp` is a supported unix timestamp.\n function isSupportedTimestamp(uint256 timestamp) internal pure returns (bool result) {\n unchecked {\n result = timestamp < MAX_SUPPORTED_TIMESTAMP + 1;\n }\n }\n\n /// @dev Returns the unix timestamp of the given `n`th weekday `wd`, in `month` of `year`.\n /// Example: 3rd Friday of Feb 2022 is `nthWeekdayInMonthOfYearTimestamp(2022, 2, 3, 5)`\n /// Note: `n` is 1-indexed for traditional consistency.\n /// Invalid weekdays (i.e. `wd == 0 || wd > 7`) result in undefined behavior.\n function nthWeekdayInMonthOfYearTimestamp(uint256 year, uint256 month, uint256 n, uint256 wd)\n internal\n pure\n returns (uint256 result)\n {\n uint256 d = dateToEpochDay(year, month, 1);\n uint256 md = daysInMonth(year, month);\n /// @solidity memory-safe-assembly\n assembly {\n let diff := sub(wd, add(mod(add(d, 3), 7), 1))\n let date := add(mul(sub(n, 1), 7), add(mul(gt(diff, 6), 7), diff))\n result := mul(mul(86400, add(date, d)), and(lt(date, md), iszero(iszero(n))))\n }\n }\n\n /// @dev Returns the unix timestamp of the most recent Monday.\n function mondayTimestamp(uint256 timestamp) internal pure returns (uint256 result) {\n uint256 t = timestamp;\n /// @solidity memory-safe-assembly\n assembly {\n let day := div(t, 86400)\n result := mul(mul(sub(day, mod(add(day, 3), 7)), 86400), gt(t, 345599))\n }\n }\n\n /// @dev Returns whether the unix timestamp falls on a Saturday or Sunday.\n /// To check whether it is a week day, just take the negation of the result.\n function isWeekEnd(uint256 timestamp) internal pure returns (bool result) {\n result = weekday(timestamp) > FRI;\n }\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* DATE TIME ARITHMETIC OPERATIONS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev Adds `numYears` to the unix timestamp, and returns the result.\n /// Note: The result will share the same Gregorian calendar month,\n /// but different Gregorian calendar years for non-zero `numYears`.\n /// If the Gregorian calendar month of the result has less days\n /// than the Gregorian calendar month day of the `timestamp`,\n /// the result's month day will be the maximum possible value for the month.\n /// (e.g. from 29th Feb to 28th Feb)\n function addYears(uint256 timestamp, uint256 numYears) internal pure returns (uint256 result) {\n (uint256 year, uint256 month, uint256 day) = epochDayToDate(timestamp / 86400);\n result = _offsetted(year + numYears, month, day, timestamp);\n }\n\n /// @dev Adds `numMonths` to the unix timestamp, and returns the result.\n /// Note: If the Gregorian calendar month of the result has less days\n /// than the Gregorian calendar month day of the `timestamp`,\n /// the result's month day will be the maximum possible value for the month.\n /// (e.g. from 29th Feb to 28th Feb)\n function addMonths(uint256 timestamp, uint256 numMonths)\n internal\n pure\n returns (uint256 result)\n {\n (uint256 year, uint256 month, uint256 day) = epochDayToDate(timestamp / 86400);\n month = _sub(month + numMonths, 1);\n result = _offsetted(year + month / 12, _add(month % 12, 1), day, timestamp);\n }\n\n /// @dev Adds `numDays` to the unix timestamp, and returns the result.\n function addDays(uint256 timestamp, uint256 numDays) internal pure returns (uint256 result) {\n result = timestamp + numDays * 86400;\n }\n\n /// @dev Adds `numHours` to the unix timestamp, and returns the result.\n function addHours(uint256 timestamp, uint256 numHours) internal pure returns (uint256 result) {\n result = timestamp + numHours * 3600;\n }\n\n /// @dev Adds `numMinutes` to the unix timestamp, and returns the result.\n function addMinutes(uint256 timestamp, uint256 numMinutes)\n internal\n pure\n returns (uint256 result)\n {\n result = timestamp + numMinutes * 60;\n }\n\n /// @dev Adds `numSeconds` to the unix timestamp, and returns the result.\n function addSeconds(uint256 timestamp, uint256 numSeconds)\n internal\n pure\n returns (uint256 result)\n {\n result = timestamp + numSeconds;\n }\n\n /// @dev Subtracts `numYears` from the unix timestamp, and returns the result.\n /// Note: The result will share the same Gregorian calendar month,\n /// but different Gregorian calendar years for non-zero `numYears`.\n /// If the Gregorian calendar month of the result has less days\n /// than the Gregorian calendar month day of the `timestamp`,\n /// the result's month day will be the maximum possible value for the month.\n /// (e.g. from 29th Feb to 28th Feb)\n function subYears(uint256 timestamp, uint256 numYears) internal pure returns (uint256 result) {\n (uint256 year, uint256 month, uint256 day) = epochDayToDate(timestamp / 86400);\n result = _offsetted(year - numYears, month, day, timestamp);\n }\n\n /// @dev Subtracts `numYears` from the unix timestamp, and returns the result.\n /// Note: If the Gregorian calendar month of the result has less days\n /// than the Gregorian calendar month day of the `timestamp`,\n /// the result's month day will be the maximum possible value for the month.\n /// (e.g. from 29th Feb to 28th Feb)\n function subMonths(uint256 timestamp, uint256 numMonths)\n internal\n pure\n returns (uint256 result)\n {\n (uint256 year, uint256 month, uint256 day) = epochDayToDate(timestamp / 86400);\n uint256 yearMonth = _totalMonths(year, month) - _add(numMonths, 1);\n result = _offsetted(yearMonth / 12, _add(yearMonth % 12, 1), day, timestamp);\n }\n\n /// @dev Subtracts `numDays` from the unix timestamp, and returns the result.\n function subDays(uint256 timestamp, uint256 numDays) internal pure returns (uint256 result) {\n result = timestamp - numDays * 86400;\n }\n\n /// @dev Subtracts `numHours` from the unix timestamp, and returns the result.\n function subHours(uint256 timestamp, uint256 numHours) internal pure returns (uint256 result) {\n result = timestamp - numHours * 3600;\n }\n\n /// @dev Subtracts `numMinutes` from the unix timestamp, and returns the result.\n function subMinutes(uint256 timestamp, uint256 numMinutes)\n internal\n pure\n returns (uint256 result)\n {\n result = timestamp - numMinutes * 60;\n }\n\n /// @dev Subtracts `numSeconds` from the unix timestamp, and returns the result.\n function subSeconds(uint256 timestamp, uint256 numSeconds)\n internal\n pure\n returns (uint256 result)\n {\n result = timestamp - numSeconds;\n }\n\n /// @dev Returns the difference in Gregorian calendar years\n /// between `fromTimestamp` and `toTimestamp`.\n /// Note: Even if the true time difference is less than a year,\n /// the difference can be non-zero is the timestamps are\n /// from different Gregorian calendar years\n function diffYears(uint256 fromTimestamp, uint256 toTimestamp)\n internal\n pure\n returns (uint256 result)\n {\n toTimestamp - fromTimestamp;\n (uint256 fromYear,,) = epochDayToDate(fromTimestamp / 86400);\n (uint256 toYear,,) = epochDayToDate(toTimestamp / 86400);\n result = _sub(toYear, fromYear);\n }\n\n /// @dev Returns the difference in Gregorian calendar months\n /// between `fromTimestamp` and `toTimestamp`.\n /// Note: Even if the true time difference is less than a month,\n /// the difference can be non-zero is the timestamps are\n /// from different Gregorian calendar months.\n function diffMonths(uint256 fromTimestamp, uint256 toTimestamp)\n internal\n pure\n returns (uint256 result)\n {\n toTimestamp - fromTimestamp;\n (uint256 fromYear, uint256 fromMonth,) = epochDayToDate(fromTimestamp / 86400);\n (uint256 toYear, uint256 toMonth,) = epochDayToDate(toTimestamp / 86400);\n result = _sub(_totalMonths(toYear, toMonth), _totalMonths(fromYear, fromMonth));\n }\n\n /// @dev Returns the difference in days between `fromTimestamp` and `toTimestamp`.\n function diffDays(uint256 fromTimestamp, uint256 toTimestamp)\n internal\n pure\n returns (uint256 result)\n {\n result = (toTimestamp - fromTimestamp) / 86400;\n }\n\n /// @dev Returns the difference in hours between `fromTimestamp` and `toTimestamp`.\n function diffHours(uint256 fromTimestamp, uint256 toTimestamp)\n internal\n pure\n returns (uint256 result)\n {\n result = (toTimestamp - fromTimestamp) / 3600;\n }\n\n /// @dev Returns the difference in minutes between `fromTimestamp` and `toTimestamp`.\n function diffMinutes(uint256 fromTimestamp, uint256 toTimestamp)\n internal\n pure\n returns (uint256 result)\n {\n result = (toTimestamp - fromTimestamp) / 60;\n }\n\n /// @dev Returns the difference in seconds between `fromTimestamp` and `toTimestamp`.\n function diffSeconds(uint256 fromTimestamp, uint256 toTimestamp)\n internal\n pure\n returns (uint256 result)\n {\n result = toTimestamp - fromTimestamp;\n }\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* PRIVATE HELPERS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev Unchecked arithmetic for computing the total number of months.\n function _totalMonths(uint256 numYears, uint256 numMonths)\n private\n pure\n returns (uint256 total)\n {\n unchecked {\n total = numYears * 12 + numMonths;\n }\n }\n\n /// @dev Unchecked arithmetic for adding two numbers.\n function _add(uint256 a, uint256 b) private pure returns (uint256 c) {\n unchecked {\n c = a + b;\n }\n }\n\n /// @dev Unchecked arithmetic for subtracting two numbers.\n function _sub(uint256 a, uint256 b) private pure returns (uint256 c) {\n unchecked {\n c = a - b;\n }\n }\n\n /// @dev Returns the offsetted timestamp.\n function _offsetted(uint256 year, uint256 month, uint256 day, uint256 timestamp)\n private\n pure\n returns (uint256 result)\n {\n uint256 dm = daysInMonth(year, month);\n if (day >= dm) {\n day = dm;\n }\n result = dateToEpochDay(year, month, day) * 86400 + (timestamp % 86400);\n }\n}\n"},"lib/solady/src/utils/LibString.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\n/// @notice Library for converting numbers into strings and other string operations.\n/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibString.sol)\n/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/LibString.sol)\n///\n/// Note:\n/// For performance and bytecode compactness, most of the string operations are restricted to\n/// byte strings (7-bit ASCII), except where otherwise specified.\n/// Usage of byte string operations on charsets with runes spanning two or more bytes\n/// can lead to undefined behavior.\nlibrary LibString {\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* CUSTOM ERRORS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev The length of the output is too small to contain all the hex digits.\n error HexLengthInsufficient();\n\n /// @dev The length of the string is more than 32 bytes.\n error TooBigForSmallString();\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* CONSTANTS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev The constant returned when the `search` is not found in the string.\n uint256 internal constant NOT_FOUND = type(uint256).max;\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* DECIMAL OPERATIONS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev Returns the base 10 decimal representation of `value`.\n function toString(uint256 value) internal pure returns (string memory str) {\n /// @solidity memory-safe-assembly\n assembly {\n // The maximum value of a uint256 contains 78 digits (1 byte per digit), but\n // we allocate 0xa0 bytes to keep the free memory pointer 32-byte word aligned.\n // We will need 1 word for the trailing zeros padding, 1 word for the length,\n // and 3 words for a maximum of 78 digits.\n str := add(mload(0x40), 0x80)\n // Update the free memory pointer to allocate.\n mstore(0x40, add(str, 0x20))\n // Zeroize the slot after the string.\n mstore(str, 0)\n\n // Cache the end of the memory to calculate the length later.\n let end := str\n\n let w := not(0) // Tsk.\n // We write the string from rightmost digit to leftmost digit.\n // The following is essentially a do-while loop that also handles the zero case.\n for { let temp := value } 1 {} {\n str := add(str, w) // `sub(str, 1)`.\n // Write the character to the pointer.\n // The ASCII index of the '0' character is 48.\n mstore8(str, add(48, mod(temp, 10)))\n // Keep dividing `temp` until zero.\n temp := div(temp, 10)\n if iszero(temp) { break }\n }\n\n let length := sub(end, str)\n // Move the pointer 32 bytes leftwards to make room for the length.\n str := sub(str, 0x20)\n // Store the length.\n mstore(str, length)\n }\n }\n\n /// @dev Returns the base 10 decimal representation of `value`.\n function toString(int256 value) internal pure returns (string memory str) {\n if (value >= 0) {\n return toString(uint256(value));\n }\n unchecked {\n str = toString(uint256(-value));\n }\n /// @solidity memory-safe-assembly\n assembly {\n // We still have some spare memory space on the left,\n // as we have allocated 3 words (96 bytes) for up to 78 digits.\n let length := mload(str) // Load the string length.\n mstore(str, 0x2d) // Store the '-' character.\n str := sub(str, 1) // Move back the string pointer by a byte.\n mstore(str, add(length, 1)) // Update the string length.\n }\n }\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* HEXADECIMAL OPERATIONS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev Returns the hexadecimal representation of `value`,\n /// left-padded to an input length of `length` bytes.\n /// The output is prefixed with \"0x\" encoded using 2 hexadecimal digits per byte,\n /// giving a total length of `length * 2 + 2` bytes.\n /// Reverts if `length` is too small for the output to contain all the digits.\n function toHexString(uint256 value, uint256 length) internal pure returns (string memory str) {\n str = toHexStringNoPrefix(value, length);\n /// @solidity memory-safe-assembly\n assembly {\n let strLength := add(mload(str), 2) // Compute the length.\n mstore(str, 0x3078) // Write the \"0x\" prefix.\n str := sub(str, 2) // Move the pointer.\n mstore(str, strLength) // Write the length.\n }\n }\n\n /// @dev Returns the hexadecimal representation of `value`,\n /// left-padded to an input length of `length` bytes.\n /// The output is prefixed with \"0x\" encoded using 2 hexadecimal digits per byte,\n /// giving a total length of `length * 2` bytes.\n /// Reverts if `length` is too small for the output to contain all the digits.\n function toHexStringNoPrefix(uint256 value, uint256 length)\n internal\n pure\n returns (string memory str)\n {\n /// @solidity memory-safe-assembly\n assembly {\n // We need 0x20 bytes for the trailing zeros padding, `length * 2` bytes\n // for the digits, 0x02 bytes for the prefix, and 0x20 bytes for the length.\n // We add 0x20 to the total and round down to a multiple of 0x20.\n // (0x20 + 0x20 + 0x02 + 0x20) = 0x62.\n str := add(mload(0x40), and(add(shl(1, length), 0x42), not(0x1f)))\n // Allocate the memory.\n mstore(0x40, add(str, 0x20))\n // Zeroize the slot after the string.\n mstore(str, 0)\n\n // Cache the end to calculate the length later.\n let end := str\n // Store \"0123456789abcdef\" in scratch space.\n mstore(0x0f, 0x30313233343536373839616263646566)\n\n let start := sub(str, add(length, length))\n let w := not(1) // Tsk.\n let temp := value\n // We write the string from rightmost digit to leftmost digit.\n // The following is essentially a do-while loop that also handles the zero case.\n for {} 1 {} {\n str := add(str, w) // `sub(str, 2)`.\n mstore8(add(str, 1), mload(and(temp, 15)))\n mstore8(str, mload(and(shr(4, temp), 15)))\n temp := shr(8, temp)\n if iszero(xor(str, start)) { break }\n }\n\n if temp {\n mstore(0x00, 0x2194895a) // `HexLengthInsufficient()`.\n revert(0x1c, 0x04)\n }\n\n // Compute the string's length.\n let strLength := sub(end, str)\n // Move the pointer and write the length.\n str := sub(str, 0x20)\n mstore(str, strLength)\n }\n }\n\n /// @dev Returns the hexadecimal representation of `value`.\n /// The output is prefixed with \"0x\" and encoded using 2 hexadecimal digits per byte.\n /// As address are 20 bytes long, the output will left-padded to have\n /// a length of `20 * 2 + 2` bytes.\n function toHexString(uint256 value) internal pure returns (string memory str) {\n str = toHexStringNoPrefix(value);\n /// @solidity memory-safe-assembly\n assembly {\n let strLength := add(mload(str), 2) // Compute the length.\n mstore(str, 0x3078) // Write the \"0x\" prefix.\n str := sub(str, 2) // Move the pointer.\n mstore(str, strLength) // Write the length.\n }\n }\n\n /// @dev Returns the hexadecimal representation of `value`.\n /// The output is prefixed with \"0x\".\n /// The output excludes leading \"0\" from the `toHexString` output.\n /// `0x00: \"0x0\", 0x01: \"0x1\", 0x12: \"0x12\", 0x123: \"0x123\"`.\n function toMinimalHexString(uint256 value) internal pure returns (string memory str) {\n str = toHexStringNoPrefix(value);\n /// @solidity memory-safe-assembly\n assembly {\n let o := eq(byte(0, mload(add(str, 0x20))), 0x30) // Whether leading zero is present.\n let strLength := add(mload(str), 2) // Compute the length.\n mstore(add(str, o), 0x3078) // Write the \"0x\" prefix, accounting for leading zero.\n str := sub(add(str, o), 2) // Move the pointer, accounting for leading zero.\n mstore(str, sub(strLength, o)) // Write the length, accounting for leading zero.\n }\n }\n\n /// @dev Returns the hexadecimal representation of `value`.\n /// The output excludes leading \"0\" from the `toHexStringNoPrefix` output.\n /// `0x00: \"0\", 0x01: \"1\", 0x12: \"12\", 0x123: \"123\"`.\n function toMinimalHexStringNoPrefix(uint256 value) internal pure returns (string memory str) {\n str = toHexStringNoPrefix(value);\n /// @solidity memory-safe-assembly\n assembly {\n let o := eq(byte(0, mload(add(str, 0x20))), 0x30) // Whether leading zero is present.\n let strLength := mload(str) // Get the length.\n str := add(str, o) // Move the pointer, accounting for leading zero.\n mstore(str, sub(strLength, o)) // Write the length, accounting for leading zero.\n }\n }\n\n /// @dev Returns the hexadecimal representation of `value`.\n /// The output is encoded using 2 hexadecimal digits per byte.\n /// As address are 20 bytes long, the output will left-padded to have\n /// a length of `20 * 2` bytes.\n function toHexStringNoPrefix(uint256 value) internal pure returns (string memory str) {\n /// @solidity memory-safe-assembly\n assembly {\n // We need 0x20 bytes for the trailing zeros padding, 0x20 bytes for the length,\n // 0x02 bytes for the prefix, and 0x40 bytes for the digits.\n // The next multiple of 0x20 above (0x20 + 0x20 + 0x02 + 0x40) is 0xa0.\n str := add(mload(0x40), 0x80)\n // Allocate the memory.\n mstore(0x40, add(str, 0x20))\n // Zeroize the slot after the string.\n mstore(str, 0)\n\n // Cache the end to calculate the length later.\n let end := str\n // Store \"0123456789abcdef\" in scratch space.\n mstore(0x0f, 0x30313233343536373839616263646566)\n\n let w := not(1) // Tsk.\n // We write the string from rightmost digit to leftmost digit.\n // The following is essentially a do-while loop that also handles the zero case.\n for { let temp := value } 1 {} {\n str := add(str, w) // `sub(str, 2)`.\n mstore8(add(str, 1), mload(and(temp, 15)))\n mstore8(str, mload(and(shr(4, temp), 15)))\n temp := shr(8, temp)\n if iszero(temp) { break }\n }\n\n // Compute the string's length.\n let strLength := sub(end, str)\n // Move the pointer and write the length.\n str := sub(str, 0x20)\n mstore(str, strLength)\n }\n }\n\n /// @dev Returns the hexadecimal representation of `value`.\n /// The output is prefixed with \"0x\", encoded using 2 hexadecimal digits per byte,\n /// and the alphabets are capitalized conditionally according to\n /// https://eips.ethereum.org/EIPS/eip-55\n function toHexStringChecksummed(address value) internal pure returns (string memory str) {\n str = toHexString(value);\n /// @solidity memory-safe-assembly\n assembly {\n let mask := shl(6, div(not(0), 255)) // `0b010000000100000000 ...`\n let o := add(str, 0x22)\n let hashed := and(keccak256(o, 40), mul(34, mask)) // `0b10001000 ... `\n let t := shl(240, 136) // `0b10001000 << 240`\n for { let i := 0 } 1 {} {\n mstore(add(i, i), mul(t, byte(i, hashed)))\n i := add(i, 1)\n if eq(i, 20) { break }\n }\n mstore(o, xor(mload(o), shr(1, and(mload(0x00), and(mload(o), mask)))))\n o := add(o, 0x20)\n mstore(o, xor(mload(o), shr(1, and(mload(0x20), and(mload(o), mask)))))\n }\n }\n\n /// @dev Returns the hexadecimal representation of `value`.\n /// The output is prefixed with \"0x\" and encoded using 2 hexadecimal digits per byte.\n function toHexString(address value) internal pure returns (string memory str) {\n str = toHexStringNoPrefix(value);\n /// @solidity memory-safe-assembly\n assembly {\n let strLength := add(mload(str), 2) // Compute the length.\n mstore(str, 0x3078) // Write the \"0x\" prefix.\n str := sub(str, 2) // Move the pointer.\n mstore(str, strLength) // Write the length.\n }\n }\n\n /// @dev Returns the hexadecimal representation of `value`.\n /// The output is encoded using 2 hexadecimal digits per byte.\n function toHexStringNoPrefix(address value) internal pure returns (string memory str) {\n /// @solidity memory-safe-assembly\n assembly {\n str := mload(0x40)\n\n // Allocate the memory.\n // We need 0x20 bytes for the trailing zeros padding, 0x20 bytes for the length,\n // 0x02 bytes for the prefix, and 0x28 bytes for the digits.\n // The next multiple of 0x20 above (0x20 + 0x20 + 0x02 + 0x28) is 0x80.\n mstore(0x40, add(str, 0x80))\n\n // Store \"0123456789abcdef\" in scratch space.\n mstore(0x0f, 0x30313233343536373839616263646566)\n\n str := add(str, 2)\n mstore(str, 40)\n\n let o := add(str, 0x20)\n mstore(add(o, 40), 0)\n\n value := shl(96, value)\n\n // We write the string from rightmost digit to leftmost digit.\n // The following is essentially a do-while loop that also handles the zero case.\n for { let i := 0 } 1 {} {\n let p := add(o, add(i, i))\n let temp := byte(i, value)\n mstore8(add(p, 1), mload(and(temp, 15)))\n mstore8(p, mload(shr(4, temp)))\n i := add(i, 1)\n if eq(i, 20) { break }\n }\n }\n }\n\n /// @dev Returns the hex encoded string from the raw bytes.\n /// The output is encoded using 2 hexadecimal digits per byte.\n function toHexString(bytes memory raw) internal pure returns (string memory str) {\n str = toHexStringNoPrefix(raw);\n /// @solidity memory-safe-assembly\n assembly {\n let strLength := add(mload(str), 2) // Compute the length.\n mstore(str, 0x3078) // Write the \"0x\" prefix.\n str := sub(str, 2) // Move the pointer.\n mstore(str, strLength) // Write the length.\n }\n }\n\n /// @dev Returns the hex encoded string from the raw bytes.\n /// The output is encoded using 2 hexadecimal digits per byte.\n function toHexStringNoPrefix(bytes memory raw) internal pure returns (string memory str) {\n /// @solidity memory-safe-assembly\n assembly {\n let length := mload(raw)\n str := add(mload(0x40), 2) // Skip 2 bytes for the optional prefix.\n mstore(str, add(length, length)) // Store the length of the output.\n\n // Store \"0123456789abcdef\" in scratch space.\n mstore(0x0f, 0x30313233343536373839616263646566)\n\n let o := add(str, 0x20)\n let end := add(raw, length)\n\n for {} iszero(eq(raw, end)) {} {\n raw := add(raw, 1)\n mstore8(add(o, 1), mload(and(mload(raw), 15)))\n mstore8(o, mload(and(shr(4, mload(raw)), 15)))\n o := add(o, 2)\n }\n mstore(o, 0) // Zeroize the slot after the string.\n mstore(0x40, add(o, 0x20)) // Allocate the memory.\n }\n }\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* RUNE STRING OPERATIONS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev Returns the number of UTF characters in the string.\n function runeCount(string memory s) internal pure returns (uint256 result) {\n /// @solidity memory-safe-assembly\n assembly {\n if mload(s) {\n mstore(0x00, div(not(0), 255))\n mstore(0x20, 0x0202020202020202020202020202020202020202020202020303030304040506)\n let o := add(s, 0x20)\n let end := add(o, mload(s))\n for { result := 1 } 1 { result := add(result, 1) } {\n o := add(o, byte(0, mload(shr(250, mload(o)))))\n if iszero(lt(o, end)) { break }\n }\n }\n }\n }\n\n /// @dev Returns if this string is a 7-bit ASCII string.\n /// (i.e. all characters codes are in [0..127])\n function is7BitASCII(string memory s) internal pure returns (bool result) {\n /// @solidity memory-safe-assembly\n assembly {\n let mask := shl(7, div(not(0), 255))\n result := 1\n let n := mload(s)\n if n {\n let o := add(s, 0x20)\n let end := add(o, n)\n let last := mload(end)\n mstore(end, 0)\n for {} 1 {} {\n if and(mask, mload(o)) {\n result := 0\n break\n }\n o := add(o, 0x20)\n if iszero(lt(o, end)) { break }\n }\n mstore(end, last)\n }\n }\n }\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* BYTE STRING OPERATIONS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n // For performance and bytecode compactness, byte string operations are restricted\n // to 7-bit ASCII strings. All offsets are byte offsets, not UTF character offsets.\n // Usage of byte string operations on charsets with runes spanning two or more bytes\n // can lead to undefined behavior.\n\n /// @dev Returns `subject` all occurrences of `search` replaced with `replacement`.\n function replace(string memory subject, string memory search, string memory replacement)\n internal\n pure\n returns (string memory result)\n {\n /// @solidity memory-safe-assembly\n assembly {\n let subjectLength := mload(subject)\n let searchLength := mload(search)\n let replacementLength := mload(replacement)\n\n subject := add(subject, 0x20)\n search := add(search, 0x20)\n replacement := add(replacement, 0x20)\n result := add(mload(0x40), 0x20)\n\n let subjectEnd := add(subject, subjectLength)\n if iszero(gt(searchLength, subjectLength)) {\n let subjectSearchEnd := add(sub(subjectEnd, searchLength), 1)\n let h := 0\n if iszero(lt(searchLength, 0x20)) { h := keccak256(search, searchLength) }\n let m := shl(3, sub(0x20, and(searchLength, 0x1f)))\n let s := mload(search)\n for {} 1 {} {\n let t := mload(subject)\n // Whether the first `searchLength % 32` bytes of\n // `subject` and `search` matches.\n if iszero(shr(m, xor(t, s))) {\n if h {\n if iszero(eq(keccak256(subject, searchLength), h)) {\n mstore(result, t)\n result := add(result, 1)\n subject := add(subject, 1)\n if iszero(lt(subject, subjectSearchEnd)) { break }\n continue\n }\n }\n // Copy the `replacement` one word at a time.\n for { let o := 0 } 1 {} {\n mstore(add(result, o), mload(add(replacement, o)))\n o := add(o, 0x20)\n if iszero(lt(o, replacementLength)) { break }\n }\n result := add(result, replacementLength)\n subject := add(subject, searchLength)\n if searchLength {\n if iszero(lt(subject, subjectSearchEnd)) { break }\n continue\n }\n }\n mstore(result, t)\n result := add(result, 1)\n subject := add(subject, 1)\n if iszero(lt(subject, subjectSearchEnd)) { break }\n }\n }\n\n let resultRemainder := result\n result := add(mload(0x40), 0x20)\n let k := add(sub(resultRemainder, result), sub(subjectEnd, subject))\n // Copy the rest of the string one word at a time.\n for {} lt(subject, subjectEnd) {} {\n mstore(resultRemainder, mload(subject))\n resultRemainder := add(resultRemainder, 0x20)\n subject := add(subject, 0x20)\n }\n result := sub(result, 0x20)\n let last := add(add(result, 0x20), k) // Zeroize the slot after the string.\n mstore(last, 0)\n mstore(0x40, add(last, 0x20)) // Allocate the memory.\n mstore(result, k) // Store the length.\n }\n }\n\n /// @dev Returns the byte index of the first location of `search` in `subject`,\n /// searching from left to right, starting from `from`.\n /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.\n function indexOf(string memory subject, string memory search, uint256 from)\n internal\n pure\n returns (uint256 result)\n {\n /// @solidity memory-safe-assembly\n assembly {\n for { let subjectLength := mload(subject) } 1 {} {\n if iszero(mload(search)) {\n if iszero(gt(from, subjectLength)) {\n result := from\n break\n }\n result := subjectLength\n break\n }\n let searchLength := mload(search)\n let subjectStart := add(subject, 0x20)\n\n result := not(0) // Initialize to `NOT_FOUND`.\n\n subject := add(subjectStart, from)\n let end := add(sub(add(subjectStart, subjectLength), searchLength), 1)\n\n let m := shl(3, sub(0x20, and(searchLength, 0x1f)))\n let s := mload(add(search, 0x20))\n\n if iszero(and(lt(subject, end), lt(from, subjectLength))) { break }\n\n if iszero(lt(searchLength, 0x20)) {\n for { let h := keccak256(add(search, 0x20), searchLength) } 1 {} {\n if iszero(shr(m, xor(mload(subject), s))) {\n if eq(keccak256(subject, searchLength), h) {\n result := sub(subject, subjectStart)\n break\n }\n }\n subject := add(subject, 1)\n if iszero(lt(subject, end)) { break }\n }\n break\n }\n for {} 1 {} {\n if iszero(shr(m, xor(mload(subject), s))) {\n result := sub(subject, subjectStart)\n break\n }\n subject := add(subject, 1)\n if iszero(lt(subject, end)) { break }\n }\n break\n }\n }\n }\n\n /// @dev Returns the byte index of the first location of `search` in `subject`,\n /// searching from left to right.\n /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.\n function indexOf(string memory subject, string memory search)\n internal\n pure\n returns (uint256 result)\n {\n result = indexOf(subject, search, 0);\n }\n\n /// @dev Returns the byte index of the first location of `search` in `subject`,\n /// searching from right to left, starting from `from`.\n /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.\n function lastIndexOf(string memory subject, string memory search, uint256 from)\n internal\n pure\n returns (uint256 result)\n {\n /// @solidity memory-safe-assembly\n assembly {\n for {} 1 {} {\n result := not(0) // Initialize to `NOT_FOUND`.\n let searchLength := mload(search)\n if gt(searchLength, mload(subject)) { break }\n let w := result\n\n let fromMax := sub(mload(subject), searchLength)\n if iszero(gt(fromMax, from)) { from := fromMax }\n\n let end := add(add(subject, 0x20), w)\n subject := add(add(subject, 0x20), from)\n if iszero(gt(subject, end)) { break }\n // As this function is not too often used,\n // we shall simply use keccak256 for smaller bytecode size.\n for { let h := keccak256(add(search, 0x20), searchLength) } 1 {} {\n if eq(keccak256(subject, searchLength), h) {\n result := sub(subject, add(end, 1))\n break\n }\n subject := add(subject, w) // `sub(subject, 1)`.\n if iszero(gt(subject, end)) { break }\n }\n break\n }\n }\n }\n\n /// @dev Returns the byte index of the first location of `search` in `subject`,\n /// searching from right to left.\n /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.\n function lastIndexOf(string memory subject, string memory search)\n internal\n pure\n returns (uint256 result)\n {\n result = lastIndexOf(subject, search, uint256(int256(-1)));\n }\n\n /// @dev Returns true if `search` is found in `subject`, false otherwise.\n function contains(string memory subject, string memory search) internal pure returns (bool) {\n return indexOf(subject, search) != NOT_FOUND;\n }\n\n /// @dev Returns whether `subject` starts with `search`.\n function startsWith(string memory subject, string memory search)\n internal\n pure\n returns (bool result)\n {\n /// @solidity memory-safe-assembly\n assembly {\n let searchLength := mload(search)\n // Just using keccak256 directly is actually cheaper.\n // forgefmt: disable-next-item\n result := and(\n iszero(gt(searchLength, mload(subject))),\n eq(\n keccak256(add(subject, 0x20), searchLength),\n keccak256(add(search, 0x20), searchLength)\n )\n )\n }\n }\n\n /// @dev Returns whether `subject` ends with `search`.\n function endsWith(string memory subject, string memory search)\n internal\n pure\n returns (bool result)\n {\n /// @solidity memory-safe-assembly\n assembly {\n let searchLength := mload(search)\n let subjectLength := mload(subject)\n // Whether `search` is not longer than `subject`.\n let withinRange := iszero(gt(searchLength, subjectLength))\n // Just using keccak256 directly is actually cheaper.\n // forgefmt: disable-next-item\n result := and(\n withinRange,\n eq(\n keccak256(\n // `subject + 0x20 + max(subjectLength - searchLength, 0)`.\n add(add(subject, 0x20), mul(withinRange, sub(subjectLength, searchLength))),\n searchLength\n ),\n keccak256(add(search, 0x20), searchLength)\n )\n )\n }\n }\n\n /// @dev Returns `subject` repeated `times`.\n function repeat(string memory subject, uint256 times)\n internal\n pure\n returns (string memory result)\n {\n /// @solidity memory-safe-assembly\n assembly {\n let subjectLength := mload(subject)\n if iszero(or(iszero(times), iszero(subjectLength))) {\n subject := add(subject, 0x20)\n result := mload(0x40)\n let output := add(result, 0x20)\n for {} 1 {} {\n // Copy the `subject` one word at a time.\n for { let o := 0 } 1 {} {\n mstore(add(output, o), mload(add(subject, o)))\n o := add(o, 0x20)\n if iszero(lt(o, subjectLength)) { break }\n }\n output := add(output, subjectLength)\n times := sub(times, 1)\n if iszero(times) { break }\n }\n mstore(output, 0) // Zeroize the slot after the string.\n let resultLength := sub(output, add(result, 0x20))\n mstore(result, resultLength) // Store the length.\n // Allocate the memory.\n mstore(0x40, add(result, add(resultLength, 0x20)))\n }\n }\n }\n\n /// @dev Returns a copy of `subject` sliced from `start` to `end` (exclusive).\n /// `start` and `end` are byte offsets.\n function slice(string memory subject, uint256 start, uint256 end)\n internal\n pure\n returns (string memory result)\n {\n /// @solidity memory-safe-assembly\n assembly {\n let subjectLength := mload(subject)\n if iszero(gt(subjectLength, end)) { end := subjectLength }\n if iszero(gt(subjectLength, start)) { start := subjectLength }\n if lt(start, end) {\n result := mload(0x40)\n let resultLength := sub(end, start)\n mstore(result, resultLength)\n subject := add(subject, start)\n let w := not(0x1f)\n // Copy the `subject` one word at a time, backwards.\n for { let o := and(add(resultLength, 0x1f), w) } 1 {} {\n mstore(add(result, o), mload(add(subject, o)))\n o := add(o, w) // `sub(o, 0x20)`.\n if iszero(o) { break }\n }\n // Zeroize the slot after the string.\n mstore(add(add(result, 0x20), resultLength), 0)\n // Allocate memory for the length and the bytes,\n // rounded up to a multiple of 32.\n mstore(0x40, add(result, and(add(resultLength, 0x3f), w)))\n }\n }\n }\n\n /// @dev Returns a copy of `subject` sliced from `start` to the end of the string.\n /// `start` is a byte offset.\n function slice(string memory subject, uint256 start)\n internal\n pure\n returns (string memory result)\n {\n result = slice(subject, start, uint256(int256(-1)));\n }\n\n /// @dev Returns all the indices of `search` in `subject`.\n /// The indices are byte offsets.\n function indicesOf(string memory subject, string memory search)\n internal\n pure\n returns (uint256[] memory result)\n {\n /// @solidity memory-safe-assembly\n assembly {\n let subjectLength := mload(subject)\n let searchLength := mload(search)\n\n if iszero(gt(searchLength, subjectLength)) {\n subject := add(subject, 0x20)\n search := add(search, 0x20)\n result := add(mload(0x40), 0x20)\n\n let subjectStart := subject\n let subjectSearchEnd := add(sub(add(subject, subjectLength), searchLength), 1)\n let h := 0\n if iszero(lt(searchLength, 0x20)) { h := keccak256(search, searchLength) }\n let m := shl(3, sub(0x20, and(searchLength, 0x1f)))\n let s := mload(search)\n for {} 1 {} {\n let t := mload(subject)\n // Whether the first `searchLength % 32` bytes of\n // `subject` and `search` matches.\n if iszero(shr(m, xor(t, s))) {\n if h {\n if iszero(eq(keccak256(subject, searchLength), h)) {\n subject := add(subject, 1)\n if iszero(lt(subject, subjectSearchEnd)) { break }\n continue\n }\n }\n // Append to `result`.\n mstore(result, sub(subject, subjectStart))\n result := add(result, 0x20)\n // Advance `subject` by `searchLength`.\n subject := add(subject, searchLength)\n if searchLength {\n if iszero(lt(subject, subjectSearchEnd)) { break }\n continue\n }\n }\n subject := add(subject, 1)\n if iszero(lt(subject, subjectSearchEnd)) { break }\n }\n let resultEnd := result\n // Assign `result` to the free memory pointer.\n result := mload(0x40)\n // Store the length of `result`.\n mstore(result, shr(5, sub(resultEnd, add(result, 0x20))))\n // Allocate memory for result.\n // We allocate one more word, so this array can be recycled for {split}.\n mstore(0x40, add(resultEnd, 0x20))\n }\n }\n }\n\n /// @dev Returns a arrays of strings based on the `delimiter` inside of the `subject` string.\n function split(string memory subject, string memory delimiter)\n internal\n pure\n returns (string[] memory result)\n {\n uint256[] memory indices = indicesOf(subject, delimiter);\n /// @solidity memory-safe-assembly\n assembly {\n let w := not(0x1f)\n let indexPtr := add(indices, 0x20)\n let indicesEnd := add(indexPtr, shl(5, add(mload(indices), 1)))\n mstore(add(indicesEnd, w), mload(subject))\n mstore(indices, add(mload(indices), 1))\n let prevIndex := 0\n for {} 1 {} {\n let index := mload(indexPtr)\n mstore(indexPtr, 0x60)\n if iszero(eq(index, prevIndex)) {\n let element := mload(0x40)\n let elementLength := sub(index, prevIndex)\n mstore(element, elementLength)\n // Copy the `subject` one word at a time, backwards.\n for { let o := and(add(elementLength, 0x1f), w) } 1 {} {\n mstore(add(element, o), mload(add(add(subject, prevIndex), o)))\n o := add(o, w) // `sub(o, 0x20)`.\n if iszero(o) { break }\n }\n // Zeroize the slot after the string.\n mstore(add(add(element, 0x20), elementLength), 0)\n // Allocate memory for the length and the bytes,\n // rounded up to a multiple of 32.\n mstore(0x40, add(element, and(add(elementLength, 0x3f), w)))\n // Store the `element` into the array.\n mstore(indexPtr, element)\n }\n prevIndex := add(index, mload(delimiter))\n indexPtr := add(indexPtr, 0x20)\n if iszero(lt(indexPtr, indicesEnd)) { break }\n }\n result := indices\n if iszero(mload(delimiter)) {\n result := add(indices, 0x20)\n mstore(result, sub(mload(indices), 2))\n }\n }\n }\n\n /// @dev Returns a concatenated string of `a` and `b`.\n /// Cheaper than `string.concat()` and does not de-align the free memory pointer.\n function concat(string memory a, string memory b)\n internal\n pure\n returns (string memory result)\n {\n /// @solidity memory-safe-assembly\n assembly {\n let w := not(0x1f)\n result := mload(0x40)\n let aLength := mload(a)\n // Copy `a` one word at a time, backwards.\n for { let o := and(add(aLength, 0x20), w) } 1 {} {\n mstore(add(result, o), mload(add(a, o)))\n o := add(o, w) // `sub(o, 0x20)`.\n if iszero(o) { break }\n }\n let bLength := mload(b)\n let output := add(result, aLength)\n // Copy `b` one word at a time, backwards.\n for { let o := and(add(bLength, 0x20), w) } 1 {} {\n mstore(add(output, o), mload(add(b, o)))\n o := add(o, w) // `sub(o, 0x20)`.\n if iszero(o) { break }\n }\n let totalLength := add(aLength, bLength)\n let last := add(add(result, 0x20), totalLength)\n // Zeroize the slot after the string.\n mstore(last, 0)\n // Stores the length.\n mstore(result, totalLength)\n // Allocate memory for the length and the bytes,\n // rounded up to a multiple of 32.\n mstore(0x40, and(add(last, 0x1f), w))\n }\n }\n\n /// @dev Returns a copy of the string in either lowercase or UPPERCASE.\n /// WARNING! This function is only compatible with 7-bit ASCII strings.\n function toCase(string memory subject, bool toUpper)\n internal\n pure\n returns (string memory result)\n {\n /// @solidity memory-safe-assembly\n assembly {\n let length := mload(subject)\n if length {\n result := add(mload(0x40), 0x20)\n subject := add(subject, 1)\n let flags := shl(add(70, shl(5, toUpper)), 0x3ffffff)\n let w := not(0)\n for { let o := length } 1 {} {\n o := add(o, w)\n let b := and(0xff, mload(add(subject, o)))\n mstore8(add(result, o), xor(b, and(shr(b, flags), 0x20)))\n if iszero(o) { break }\n }\n result := mload(0x40)\n mstore(result, length) // Store the length.\n let last := add(add(result, 0x20), length)\n mstore(last, 0) // Zeroize the slot after the string.\n mstore(0x40, add(last, 0x20)) // Allocate the memory.\n }\n }\n }\n\n /// @dev Returns a string from a small bytes32 string.\n /// `s` must be null-terminated, or behavior will be undefined.\n function fromSmallString(bytes32 s) internal pure returns (string memory result) {\n /// @solidity memory-safe-assembly\n assembly {\n result := mload(0x40)\n let n := 0\n for {} byte(n, s) { n := add(n, 1) } {} // Scan for '\\0'.\n mstore(result, n)\n let o := add(result, 0x20)\n mstore(o, s)\n mstore(add(o, n), 0)\n mstore(0x40, add(result, 0x40))\n }\n }\n\n /// @dev Returns the small string, with all bytes after the first null byte zeroized.\n function normalizeSmallString(bytes32 s) internal pure returns (bytes32 result) {\n /// @solidity memory-safe-assembly\n assembly {\n for {} byte(result, s) { result := add(result, 1) } {} // Scan for '\\0'.\n mstore(0x00, s)\n mstore(result, 0x00)\n result := mload(0x00)\n }\n }\n\n /// @dev Returns the string as a normalized null-terminated small string.\n function toSmallString(string memory s) internal pure returns (bytes32 result) {\n /// @solidity memory-safe-assembly\n assembly {\n result := mload(s)\n if iszero(lt(result, 33)) {\n mstore(0x00, 0xec92f9a3) // `TooBigForSmallString()`.\n revert(0x1c, 0x04)\n }\n result := shl(shl(3, sub(32, result)), mload(add(s, result)))\n }\n }\n\n /// @dev Returns a lowercased copy of the string.\n /// WARNING! This function is only compatible with 7-bit ASCII strings.\n function lower(string memory subject) internal pure returns (string memory result) {\n result = toCase(subject, false);\n }\n\n /// @dev Returns an UPPERCASED copy of the string.\n /// WARNING! This function is only compatible with 7-bit ASCII strings.\n function upper(string memory subject) internal pure returns (string memory result) {\n result = toCase(subject, true);\n }\n\n /// @dev Escapes the string to be used within HTML tags.\n function escapeHTML(string memory s) internal pure returns (string memory result) {\n /// @solidity memory-safe-assembly\n assembly {\n let end := add(s, mload(s))\n result := add(mload(0x40), 0x20)\n // Store the bytes of the packed offsets and strides into the scratch space.\n // `packed = (stride << 5) | offset`. Max offset is 20. Max stride is 6.\n mstore(0x1f, 0x900094)\n mstore(0x08, 0xc0000000a6ab)\n // Store \""&'<>\" into the scratch space.\n mstore(0x00, shl(64, 0x2671756f743b26616d703b262333393b266c743b2667743b))\n for {} iszero(eq(s, end)) {} {\n s := add(s, 1)\n let c := and(mload(s), 0xff)\n // Not in `[\"\\\"\",\"'\",\"&\",\"<\",\">\"]`.\n if iszero(and(shl(c, 1), 0x500000c400000000)) {\n mstore8(result, c)\n result := add(result, 1)\n continue\n }\n let t := shr(248, mload(c))\n mstore(result, mload(and(t, 0x1f)))\n result := add(result, shr(5, t))\n }\n let last := result\n mstore(last, 0) // Zeroize the slot after the string.\n result := mload(0x40)\n mstore(result, sub(last, add(result, 0x20))) // Store the length.\n mstore(0x40, add(last, 0x20)) // Allocate the memory.\n }\n }\n\n /// @dev Escapes the string to be used within double-quotes in a JSON.\n /// If `addDoubleQuotes` is true, the result will be enclosed in double-quotes.\n function escapeJSON(string memory s, bool addDoubleQuotes)\n internal\n pure\n returns (string memory result)\n {\n /// @solidity memory-safe-assembly\n assembly {\n let end := add(s, mload(s))\n result := add(mload(0x40), 0x20)\n if addDoubleQuotes {\n mstore8(result, 34)\n result := add(1, result)\n }\n // Store \"\\\\u0000\" in scratch space.\n // Store \"0123456789abcdef\" in scratch space.\n // Also, store `{0x08:\"b\", 0x09:\"t\", 0x0a:\"n\", 0x0c:\"f\", 0x0d:\"r\"}`.\n // into the scratch space.\n mstore(0x15, 0x5c75303030303031323334353637383961626364656662746e006672)\n // Bitmask for detecting `[\"\\\"\",\"\\\\\"]`.\n let e := or(shl(0x22, 1), shl(0x5c, 1))\n for {} iszero(eq(s, end)) {} {\n s := add(s, 1)\n let c := and(mload(s), 0xff)\n if iszero(lt(c, 0x20)) {\n if iszero(and(shl(c, 1), e)) {\n // Not in `[\"\\\"\",\"\\\\\"]`.\n mstore8(result, c)\n result := add(result, 1)\n continue\n }\n mstore8(result, 0x5c) // \"\\\\\".\n mstore8(add(result, 1), c)\n result := add(result, 2)\n continue\n }\n if iszero(and(shl(c, 1), 0x3700)) {\n // Not in `[\"\\b\",\"\\t\",\"\\n\",\"\\f\",\"\\d\"]`.\n mstore8(0x1d, mload(shr(4, c))) // Hex value.\n mstore8(0x1e, mload(and(c, 15))) // Hex value.\n mstore(result, mload(0x19)) // \"\\\\u00XX\".\n result := add(result, 6)\n continue\n }\n mstore8(result, 0x5c) // \"\\\\\".\n mstore8(add(result, 1), mload(add(c, 8)))\n result := add(result, 2)\n }\n if addDoubleQuotes {\n mstore8(result, 34)\n result := add(1, result)\n }\n let last := result\n mstore(last, 0) // Zeroize the slot after the string.\n result := mload(0x40)\n mstore(result, sub(last, add(result, 0x20))) // Store the length.\n mstore(0x40, add(last, 0x20)) // Allocate the memory.\n }\n }\n\n /// @dev Escapes the string to be used within double-quotes in a JSON.\n function escapeJSON(string memory s) internal pure returns (string memory result) {\n result = escapeJSON(s, false);\n }\n\n /// @dev Returns whether `a` equals `b`.\n function eq(string memory a, string memory b) internal pure returns (bool result) {\n /// @solidity memory-safe-assembly\n assembly {\n result := eq(keccak256(add(a, 0x20), mload(a)), keccak256(add(b, 0x20), mload(b)))\n }\n }\n\n /// @dev Returns whether `a` equals `b`, where `b` is a null-terminated small string.\n function eqs(string memory a, bytes32 b) internal pure returns (bool result) {\n /// @solidity memory-safe-assembly\n assembly {\n // These should be evaluated on compile time, as far as possible.\n let m := not(shl(7, div(not(iszero(b)), 255))) // `0x7f7f ...`.\n let x := not(or(m, or(b, add(m, and(b, m)))))\n let r := shl(7, iszero(iszero(shr(128, x))))\n r := or(r, shl(6, iszero(iszero(shr(64, shr(r, x))))))\n r := or(r, shl(5, lt(0xffffffff, shr(r, x))))\n r := or(r, shl(4, lt(0xffff, shr(r, x))))\n r := or(r, shl(3, lt(0xff, shr(r, x))))\n // forgefmt: disable-next-item\n result := gt(eq(mload(a), add(iszero(x), xor(31, shr(3, r)))),\n xor(shr(add(8, r), b), shr(add(8, r), mload(add(a, 0x20)))))\n }\n }\n\n /// @dev Packs a single string with its length into a single word.\n /// Returns `bytes32(0)` if the length is zero or greater than 31.\n function packOne(string memory a) internal pure returns (bytes32 result) {\n /// @solidity memory-safe-assembly\n assembly {\n // We don't need to zero right pad the string,\n // since this is our own custom non-standard packing scheme.\n result :=\n mul(\n // Load the length and the bytes.\n mload(add(a, 0x1f)),\n // `length != 0 && length < 32`. Abuses underflow.\n // Assumes that the length is valid and within the block gas limit.\n lt(sub(mload(a), 1), 0x1f)\n )\n }\n }\n\n /// @dev Unpacks a string packed using {packOne}.\n /// Returns the empty string if `packed` is `bytes32(0)`.\n /// If `packed` is not an output of {packOne}, the output behavior is undefined.\n function unpackOne(bytes32 packed) internal pure returns (string memory result) {\n /// @solidity memory-safe-assembly\n assembly {\n // Grab the free memory pointer.\n result := mload(0x40)\n // Allocate 2 words (1 for the length, 1 for the bytes).\n mstore(0x40, add(result, 0x40))\n // Zeroize the length slot.\n mstore(result, 0)\n // Store the length and bytes.\n mstore(add(result, 0x1f), packed)\n // Right pad with zeroes.\n mstore(add(add(result, 0x20), mload(result)), 0)\n }\n }\n\n /// @dev Packs two strings with their lengths into a single word.\n /// Returns `bytes32(0)` if combined length is zero or greater than 30.\n function packTwo(string memory a, string memory b) internal pure returns (bytes32 result) {\n /// @solidity memory-safe-assembly\n assembly {\n let aLength := mload(a)\n // We don't need to zero right pad the strings,\n // since this is our own custom non-standard packing scheme.\n result :=\n mul(\n // Load the length and the bytes of `a` and `b`.\n or(\n shl(shl(3, sub(0x1f, aLength)), mload(add(a, aLength))),\n mload(sub(add(b, 0x1e), aLength))\n ),\n // `totalLength != 0 && totalLength < 31`. Abuses underflow.\n // Assumes that the lengths are valid and within the block gas limit.\n lt(sub(add(aLength, mload(b)), 1), 0x1e)\n )\n }\n }\n\n /// @dev Unpacks strings packed using {packTwo}.\n /// Returns the empty strings if `packed` is `bytes32(0)`.\n /// If `packed` is not an output of {packTwo}, the output behavior is undefined.\n function unpackTwo(bytes32 packed)\n internal\n pure\n returns (string memory resultA, string memory resultB)\n {\n /// @solidity memory-safe-assembly\n assembly {\n // Grab the free memory pointer.\n resultA := mload(0x40)\n resultB := add(resultA, 0x40)\n // Allocate 2 words for each string (1 for the length, 1 for the byte). Total 4 words.\n mstore(0x40, add(resultB, 0x40))\n // Zeroize the length slots.\n mstore(resultA, 0)\n mstore(resultB, 0)\n // Store the lengths and bytes.\n mstore(add(resultA, 0x1f), packed)\n mstore(add(resultB, 0x1f), mload(add(add(resultA, 0x20), mload(resultA))))\n // Right pad with zeroes.\n mstore(add(add(resultA, 0x20), mload(resultA)), 0)\n mstore(add(add(resultB, 0x20), mload(resultB)), 0)\n }\n }\n\n /// @dev Directly returns `a` without copying.\n function directReturn(string memory a) internal pure {\n assembly {\n // Assumes that the string does not start from the scratch space.\n let retStart := sub(a, 0x20)\n let retSize := add(mload(a), 0x40)\n // Right pad with zeroes. Just in case the string is produced\n // by a method that doesn't zero right pad.\n mstore(add(retStart, retSize), 0)\n // Store the return offset.\n mstore(retStart, 0x20)\n // End the transaction, returning the string.\n return(retStart, retSize)\n }\n }\n}\n"}},"settings":{"remappings":["solady/=lib/solady/src/","@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/","ds-test/=lib/forge-std/lib/ds-test/src/","erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/","forge-std/=lib/forge-std/src/","halmos-cheatcodes/=lib/openzeppelin-contracts/lib/halmos-cheatcodes/src/","openzeppelin-contracts/=lib/openzeppelin-contracts/"],"optimizer":{"enabled":true,"runs":999999},"metadata":{"useLiteralContent":false,"bytecodeHash":"ipfs","appendCBOR":true},"outputSelection":{"*":{"*":["abi","evm.bytecode","evm.deployedBytecode","evm.methodIdentifiers","metadata"]}},"evmVersion":"paris","viaIR":true,"libraries":{}}} diff --git a/standard-json-input/pcs.json b/standard-json-input/pcs.json deleted file mode 100644 index 58ca50a..0000000 --- a/standard-json-input/pcs.json +++ /dev/null @@ -1 +0,0 @@ -{"language":"Solidity","sources":{"src/automata_pccs/AutomataPcsDao.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport {AutomataDaoBase} from \"./shared/AutomataDaoBase.sol\";\nimport {PcsDao, AttestationRequest, X509CRLHelper} from \"../bases/PcsDao.sol\";\n\nimport {Ownable} from \"solady/auth/Ownable.sol\";\n\ncontract AutomataPcsDao is AutomataDaoBase, PcsDao, Ownable {\n constructor(address _storage, address _x509, address _crl) AutomataDaoBase(_storage) PcsDao(_x509, _crl) {\n _initializeOwner(msg.sender);\n }\n\n function updateDeps(address _x509, address _crl) external onlyOwner {\n x509 = _x509;\n crlLib = X509CRLHelper(_crl);\n }\n\n function pcsCertSchemaID() public pure override returns (bytes32) {\n // NOT-APPLICABLE FOR OUR USE CASE\n // but this is required by most attestation services, such as EAS, Verax etc\n return bytes32(0);\n }\n\n function pcsCrlSchemaID() public pure override returns (bytes32) {\n // NOT-APPLICABLE FOR OUR USE CASE\n // but this is required by most attestation services, such as EAS, Verax etc\n return bytes32(0);\n }\n\n function _attestPcs(AttestationRequest memory req, bytes32 hash)\n internal\n override\n returns (bytes32 attestationId)\n {\n // delete the predecessor if replacing\n _deletePredecessor(req.data.refUID);\n _attestCollateral(hash, req.data.data);\n attestationId = hash;\n }\n}\n"},"src/automata_pccs/shared/AutomataDaoBase.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport {DaoBase} from \"../../bases/DaoBase.sol\";\nimport {CA} from \"../../Common.sol\";\n\ninterface IAutomataDaoStorage {\n function writeToPccs(bytes32 attId, bytes memory attData) external;\n\n function readPccs(bytes32 attId) external view returns (bytes memory attData);\n\n function deleteData(bytes32 attId) external;\n}\n\nabstract contract AutomataDaoBase is DaoBase {\n IAutomataDaoStorage pccsStorage;\n\n constructor(address _storage) {\n pccsStorage = IAutomataDaoStorage(_storage);\n }\n\n function getAttestedData(bytes32 attestationId) public view override returns (bytes memory attestationData) {\n attestationData = pccsStorage.readPccs(attestationId);\n }\n\n /// @dev we simply map the collateral hash to the data itself in our use case\n /// @dev however, this may not be the case when the dao integrates an attestation service, such as EAS\n /// @dev it is recommended to store the hash of the collateral as a separate attestation from the collateral\n /// to reduce the size of data read\n function getCollateralHash(bytes32 attestationId) public pure override returns (bytes32) {\n return attestationId;\n }\n\n function _attestCollateral(bytes32 collateralHash, bytes memory data) internal {\n pccsStorage.writeToPccs(collateralHash, data);\n }\n\n function _deletePredecessor(bytes32 predecessor) internal {\n if (getAttestedData(predecessor).length > 0) {\n pccsStorage.deleteData(predecessor);\n }\n }\n}\n"},"src/bases/PcsDao.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport {CA, AttestationRequestData, AttestationRequest} from \"../Common.sol\";\nimport {X509Helper, X509CertObj} from \"../helpers/X509Helper.sol\";\nimport {X509CRLHelper, X509CRLObj} from \"../helpers/X509CRLHelper.sol\";\n\nimport {DaoBase} from \"./DaoBase.sol\";\nimport {SigVerifyBase} from \"./SigVerifyBase.sol\";\n\nimport {LibString} from \"solady/utils/LibString.sol\";\n\n/**\n * @title Intel PCS Data Access Object\n * @notice This is a core contract of our on-chain PCCS implementation as it provides methods\n * @notice to read/write essential collaterals such as the RootCA, Intermediate CAs and CRLs.\n * @notice All other DAOs are expected to configure and make external calls to this contract to fetch those collaterals.\n * @notice This contract is heavily inspired by Sections 4.2.5 and 4.2.6 in the Intel SGX PCCS Design Guideline\n * https://download.01.org/intel-sgx/sgx-dcap/1.19/linux/docs/SGX_DCAP_Caching_Service_Design_Guide.pdf\n */\nabstract contract PcsDao is DaoBase, SigVerifyBase {\n using LibString for string;\n\n X509CRLHelper public crlLib;\n\n /// @notice Fetches the attestationId of the attested PCS Certificate\n ///\n /// @dev Must ensure that the public key for the configured Intel Root CA matches with\n /// @dev the Intel source code at: https://github.com/intel/SGXDataCenterAttestationPrimitives/blob/39989a42bbbb0c968153a47254b6de79a27eb603/QuoteVerification/QvE/Enclave/qve.cpp#L92-L100\n ///\n /// @notice the schema of the attested data is the following:\n /// - bytes pcsCert\n mapping(CA => bytes32) public pcsCertAttestations;\n\n /// @notice Fetches the attestationId of the attested PCS CRLs\n ///\n /// @dev Verification of CRLs are conducted as part of the PCS attestation process\n ///\n /// @notice the schema of the attested data is the following:\n /// - bytes pcsCrl\n mapping(CA => bytes32) public pcsCrlAttestations;\n\n string constant PCK_PLATFORM_CA_COMMON_NAME = \"Intel SGX PCK Platform CA\";\n string constant PCK_PROCESSOR_CA_COMMON_NAME = \"Intel SGX PCK Processor CA\";\n string constant SIGNING_COMMON_NAME = \"Intel SGX TCB Signing\";\n string constant ROOT_CA_COMMON_NAME = \"Intel SGX Root CA\";\n\n // keccak256(hex\"0ba9c4c0c0c86193a3fe23d6b02cda10a8bbd4e88e48b4458561a36e705525f567918e2edc88e40d860bd0cc4ee26aacc988e505a953558c453f6b0904ae7394\")\n // the uncompressed (0x04) prefix is not included in the pubkey pre-image\n bytes32 constant ROOT_CA_PUBKEY_HASH = 0x89f72d7c488e5b53a77c23ebcb36970ef7eb5bcf6658e9b8292cfbe4703a8473;\n\n error Missing_Certificate(CA ca);\n error Invalid_PCK_CA(CA ca);\n error Invalid_Issuer_Name();\n error Invalid_Subject_Name();\n error Certificate_Expired();\n error Root_Key_Mismatch();\n error Certificate_Revoked(CA ca, uint256 serialNum);\n error Missing_Issuer();\n error Invalid_Signature();\n\n constructor(address _x509, address _crl) SigVerifyBase(_x509) {\n crlLib = X509CRLHelper(_crl);\n }\n\n modifier pckCACheck(CA ca) {\n if (ca == CA.ROOT || ca == CA.SIGNING) {\n revert Invalid_PCK_CA(ca);\n }\n _;\n }\n\n /**\n * @param ca see {Common.sol} for definition\n * @return cert - DER encoded certificate\n * @return crl - DER-encoded CRLs that is signed by the provided cert\n */\n function getCertificateById(CA ca) external view returns (bytes memory cert, bytes memory crl) {\n bytes32 pcsCertAttestationId = pcsCertAttestations[ca];\n if (pcsCertAttestationId == bytes32(0)) {\n revert Missing_Certificate(ca);\n }\n cert = getAttestedData(pcsCertAttestationId);\n\n bytes32 pcsCrlAttestationId = pcsCrlAttestations[ca];\n if (pcsCrlAttestationId != bytes32(0)) {\n crl = getAttestedData(pcsCrlAttestationId);\n }\n }\n\n /**\n * Section 4.2.6 (upsertPcsCertificates)\n * @param ca replaces the \"id\" value with the ca_id\n * @param cert the DER-encoded certificate\n */\n function upsertPcsCertificates(CA ca, bytes calldata cert) external returns (bytes32 attestationId) {\n bytes32 hash = _validatePcsCert(ca, cert);\n AttestationRequest memory req = _buildPcsAttestationRequest(false, ca, cert);\n attestationId = _attestPcs(req, hash);\n pcsCertAttestations[ca] = attestationId;\n }\n\n /**\n * Section 4.2.5 (upsertPckCrl)\n * @param ca either CA.PROCESSOR or CA.PLATFORM\n * @param crl the DER-encoded CRL\n */\n function upsertPckCrl(CA ca, bytes calldata crl) external pckCACheck(ca) returns (bytes32 attestationId) {\n attestationId = _upsertPcsCrl(ca, crl);\n }\n\n function upsertRootCACrl(bytes calldata rootcacrl) external returns (bytes32 attestationId) {\n attestationId = _upsertPcsCrl(CA.ROOT, rootcacrl);\n }\n\n function pcsCertSchemaID() public view virtual returns (bytes32 PCS_CERT_SCHEMA_ID);\n\n function pcsCrlSchemaID() public view virtual returns (bytes32 PCS_CRL_SCHEMA_ID);\n\n /**\n * @dev implement logic to validate and attest PCS Certificates or CRLs\n * @param req structure as defined by EAS\n * https://github.com/ethereum-attestation-service/eas-contracts/blob/52af661748bde9b40ae782907702f885852bc149/contracts/IEAS.sol#L9C1-L23C2\n * @return attestationId\n */\n function _attestPcs(AttestationRequest memory req, bytes32 hash) internal virtual returns (bytes32 attestationId);\n\n function _upsertPcsCrl(CA ca, bytes calldata crl) private returns (bytes32 attestationId) {\n bytes32 hash = _validatePcsCrl(ca, crl);\n AttestationRequest memory req = _buildPcsAttestationRequest(true, ca, crl);\n attestationId = _attestPcs(req, hash);\n pcsCrlAttestations[ca] = attestationId;\n }\n\n /**\n * @notice builds an EAS compliant attestation request\n * @param isCrl - true only if the attested data is a CRL\n * @param der - contains the DER encoded data, specified by isCrl and CA\n */\n function _buildPcsAttestationRequest(bool isCrl, CA ca, bytes calldata der)\n private\n view\n returns (AttestationRequest memory req)\n {\n bytes32 predecessorAttestationId = isCrl ? pcsCrlAttestations[ca] : pcsCertAttestations[ca];\n AttestationRequestData memory reqData = AttestationRequestData({\n recipient: msg.sender,\n expirationTime: 0, // assign zero here because this has already been checked\n revocable: true,\n refUID: predecessorAttestationId,\n data: der,\n value: 0\n });\n bytes32 schemaId = isCrl ? pcsCrlSchemaID() : pcsCertSchemaID();\n req = AttestationRequest({schema: schemaId, data: reqData});\n }\n\n function _validatePcsCert(CA ca, bytes calldata cert) private view returns (bytes32 hash) {\n X509Helper x509Lib = X509Helper(x509);\n\n // Step 1: Check whether cert has expired\n bool notExpired = x509Lib.certIsNotExpired(cert);\n if (!notExpired) {\n revert Certificate_Expired();\n }\n\n // Step 2: Check issuer and subject common names are valid\n string memory issuerName = x509Lib.getIssuerCommonName(cert);\n string memory subjectName = x509Lib.getSubjectCommonName(cert);\n string memory expectedIssuer = ROOT_CA_COMMON_NAME;\n string memory expectedSubject;\n if (ca == CA.PLATFORM) {\n expectedSubject = PCK_PLATFORM_CA_COMMON_NAME;\n } else if (ca == CA.PROCESSOR) {\n expectedSubject = PCK_PROCESSOR_CA_COMMON_NAME;\n } else if (ca == CA.SIGNING) {\n expectedSubject = SIGNING_COMMON_NAME;\n } else if (ca == CA.ROOT) {\n expectedSubject = ROOT_CA_COMMON_NAME;\n }\n\n if (!LibString.eq(issuerName, expectedIssuer)) {\n revert Invalid_Issuer_Name();\n }\n if (!LibString.eq(subjectName, expectedSubject)) {\n revert Invalid_Subject_Name();\n }\n\n // Step 3: Check Revocation Status\n bytes memory rootCrlData = getAttestedData(pcsCrlAttestations[CA.ROOT]);\n if (ca == CA.ROOT) {\n bytes memory pubKey = x509Lib.getSubjectPublicKey(cert);\n if (keccak256(pubKey) != ROOT_CA_PUBKEY_HASH) {\n revert Root_Key_Mismatch();\n }\n } else if (rootCrlData.length > 0) {\n uint256 serialNum = x509Lib.getSerialNumber(cert);\n bool revoked = crlLib.serialNumberIsRevoked(serialNum, rootCrlData);\n if (revoked) {\n revert Certificate_Revoked(ca, serialNum);\n }\n }\n\n // Step 4: Check signature\n bytes memory rootCert = _getIssuer(CA.ROOT);\n (bytes memory tbs, bytes memory signature) = x509Lib.getTbsAndSig(cert);\n bytes32 digest = sha256(tbs);\n bool sigVerified;\n if (ca == CA.ROOT) {\n // the root certificate is issued by its own key\n sigVerified = verifySignature(digest, signature, cert);\n } else if (rootCert.length > 0) {\n sigVerified = verifySignature(digest, signature, rootCert);\n } else {\n // all other certificates should already have an iusuer configured\n revert Missing_Issuer();\n }\n\n if (!sigVerified) {\n revert Invalid_Signature();\n }\n\n hash = keccak256(tbs);\n }\n\n function _validatePcsCrl(CA ca, bytes calldata crl) private view returns (bytes32 hash) {\n // Step 1: Check whether CRL has expired\n bool notExpired = crlLib.crlIsNotExpired(crl);\n if (!notExpired) {\n revert Certificate_Expired();\n }\n\n // Step 2: Check CRL issuer\n string memory issuerCommonName = crlLib.getIssuerCommonName(crl);\n string memory expectedIssuer;\n if (ca == CA.PLATFORM || ca == CA.PROCESSOR) {\n expectedIssuer = ca == CA.PLATFORM ? PCK_PLATFORM_CA_COMMON_NAME : PCK_PROCESSOR_CA_COMMON_NAME;\n } else {\n expectedIssuer = ROOT_CA_COMMON_NAME;\n }\n if (!LibString.eq(issuerCommonName, expectedIssuer)) {\n revert Invalid_Issuer_Name();\n }\n\n // Step 3: Verify signature\n (bytes memory tbs, bytes memory signature) = crlLib.getTbsAndSig(crl);\n bytes32 digest = sha256(tbs);\n bool sigVerified = verifySignature(digest, signature, _getIssuer(ca));\n if (!sigVerified) {\n revert Invalid_Signature();\n }\n\n hash = keccak256(tbs);\n }\n\n function _getIssuer(CA ca) private view returns (bytes memory issuerCert) {\n bytes32 intermediateCertAttestationId = pcsCertAttestations[ca];\n bytes32 rootCertAttestationId = pcsCertAttestations[CA.ROOT];\n if (ca == CA.PLATFORM || ca == CA.PROCESSOR) {\n // this is applicable to crls only\n // since all certs in the pcsdao are issued by the root\n issuerCert = getAttestedData(intermediateCertAttestationId);\n } else {\n issuerCert = getAttestedData(rootCertAttestationId);\n }\n }\n}\n"},"lib/solady/src/auth/Ownable.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\n/// @notice Simple single owner authorization mixin.\n/// @author Solady (https://github.com/vectorized/solady/blob/main/src/auth/Ownable.sol)\n///\n/// @dev Note:\n/// This implementation does NOT auto-initialize the owner to `msg.sender`.\n/// You MUST call the `_initializeOwner` in the constructor / initializer.\n///\n/// While the ownable portion follows\n/// [EIP-173](https://eips.ethereum.org/EIPS/eip-173) for compatibility,\n/// the nomenclature for the 2-step ownership handover may be unique to this codebase.\nabstract contract Ownable {\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* CUSTOM ERRORS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev The caller is not authorized to call the function.\n error Unauthorized();\n\n /// @dev The `newOwner` cannot be the zero address.\n error NewOwnerIsZeroAddress();\n\n /// @dev The `pendingOwner` does not have a valid handover request.\n error NoHandoverRequest();\n\n /// @dev Cannot double-initialize.\n error AlreadyInitialized();\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* EVENTS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev The ownership is transferred from `oldOwner` to `newOwner`.\n /// This event is intentionally kept the same as OpenZeppelin's Ownable to be\n /// compatible with indexers and [EIP-173](https://eips.ethereum.org/EIPS/eip-173),\n /// despite it not being as lightweight as a single argument event.\n event OwnershipTransferred(address indexed oldOwner, address indexed newOwner);\n\n /// @dev An ownership handover to `pendingOwner` has been requested.\n event OwnershipHandoverRequested(address indexed pendingOwner);\n\n /// @dev The ownership handover to `pendingOwner` has been canceled.\n event OwnershipHandoverCanceled(address indexed pendingOwner);\n\n /// @dev `keccak256(bytes(\"OwnershipTransferred(address,address)\"))`.\n uint256 private constant _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE =\n 0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0;\n\n /// @dev `keccak256(bytes(\"OwnershipHandoverRequested(address)\"))`.\n uint256 private constant _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE =\n 0xdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d;\n\n /// @dev `keccak256(bytes(\"OwnershipHandoverCanceled(address)\"))`.\n uint256 private constant _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE =\n 0xfa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92;\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* STORAGE */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev The owner slot is given by:\n /// `bytes32(~uint256(uint32(bytes4(keccak256(\"_OWNER_SLOT_NOT\")))))`.\n /// It is intentionally chosen to be a high value\n /// to avoid collision with lower slots.\n /// The choice of manual storage layout is to enable compatibility\n /// with both regular and upgradeable contracts.\n bytes32 internal constant _OWNER_SLOT =\n 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff74873927;\n\n /// The ownership handover slot of `newOwner` is given by:\n /// ```\n /// mstore(0x00, or(shl(96, user), _HANDOVER_SLOT_SEED))\n /// let handoverSlot := keccak256(0x00, 0x20)\n /// ```\n /// It stores the expiry timestamp of the two-step ownership handover.\n uint256 private constant _HANDOVER_SLOT_SEED = 0x389a75e1;\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* INTERNAL FUNCTIONS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev Override to return true to make `_initializeOwner` prevent double-initialization.\n function _guardInitializeOwner() internal pure virtual returns (bool guard) {}\n\n /// @dev Initializes the owner directly without authorization guard.\n /// This function must be called upon initialization,\n /// regardless of whether the contract is upgradeable or not.\n /// This is to enable generalization to both regular and upgradeable contracts,\n /// and to save gas in case the initial owner is not the caller.\n /// For performance reasons, this function will not check if there\n /// is an existing owner.\n function _initializeOwner(address newOwner) internal virtual {\n if (_guardInitializeOwner()) {\n /// @solidity memory-safe-assembly\n assembly {\n let ownerSlot := _OWNER_SLOT\n if sload(ownerSlot) {\n mstore(0x00, 0x0dc149f0) // `AlreadyInitialized()`.\n revert(0x1c, 0x04)\n }\n // Clean the upper 96 bits.\n newOwner := shr(96, shl(96, newOwner))\n // Store the new value.\n sstore(ownerSlot, or(newOwner, shl(255, iszero(newOwner))))\n // Emit the {OwnershipTransferred} event.\n log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner)\n }\n } else {\n /// @solidity memory-safe-assembly\n assembly {\n // Clean the upper 96 bits.\n newOwner := shr(96, shl(96, newOwner))\n // Store the new value.\n sstore(_OWNER_SLOT, newOwner)\n // Emit the {OwnershipTransferred} event.\n log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner)\n }\n }\n }\n\n /// @dev Sets the owner directly without authorization guard.\n function _setOwner(address newOwner) internal virtual {\n if (_guardInitializeOwner()) {\n /// @solidity memory-safe-assembly\n assembly {\n let ownerSlot := _OWNER_SLOT\n // Clean the upper 96 bits.\n newOwner := shr(96, shl(96, newOwner))\n // Emit the {OwnershipTransferred} event.\n log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner)\n // Store the new value.\n sstore(ownerSlot, or(newOwner, shl(255, iszero(newOwner))))\n }\n } else {\n /// @solidity memory-safe-assembly\n assembly {\n let ownerSlot := _OWNER_SLOT\n // Clean the upper 96 bits.\n newOwner := shr(96, shl(96, newOwner))\n // Emit the {OwnershipTransferred} event.\n log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner)\n // Store the new value.\n sstore(ownerSlot, newOwner)\n }\n }\n }\n\n /// @dev Throws if the sender is not the owner.\n function _checkOwner() internal view virtual {\n /// @solidity memory-safe-assembly\n assembly {\n // If the caller is not the stored owner, revert.\n if iszero(eq(caller(), sload(_OWNER_SLOT))) {\n mstore(0x00, 0x82b42900) // `Unauthorized()`.\n revert(0x1c, 0x04)\n }\n }\n }\n\n /// @dev Returns how long a two-step ownership handover is valid for in seconds.\n /// Override to return a different value if needed.\n /// Made internal to conserve bytecode. Wrap it in a public function if needed.\n function _ownershipHandoverValidFor() internal view virtual returns (uint64) {\n return 48 * 3600;\n }\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* PUBLIC UPDATE FUNCTIONS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev Allows the owner to transfer the ownership to `newOwner`.\n function transferOwnership(address newOwner) public payable virtual onlyOwner {\n /// @solidity memory-safe-assembly\n assembly {\n if iszero(shl(96, newOwner)) {\n mstore(0x00, 0x7448fbae) // `NewOwnerIsZeroAddress()`.\n revert(0x1c, 0x04)\n }\n }\n _setOwner(newOwner);\n }\n\n /// @dev Allows the owner to renounce their ownership.\n function renounceOwnership() public payable virtual onlyOwner {\n _setOwner(address(0));\n }\n\n /// @dev Request a two-step ownership handover to the caller.\n /// The request will automatically expire in 48 hours (172800 seconds) by default.\n function requestOwnershipHandover() public payable virtual {\n unchecked {\n uint256 expires = block.timestamp + _ownershipHandoverValidFor();\n /// @solidity memory-safe-assembly\n assembly {\n // Compute and set the handover slot to `expires`.\n mstore(0x0c, _HANDOVER_SLOT_SEED)\n mstore(0x00, caller())\n sstore(keccak256(0x0c, 0x20), expires)\n // Emit the {OwnershipHandoverRequested} event.\n log2(0, 0, _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE, caller())\n }\n }\n }\n\n /// @dev Cancels the two-step ownership handover to the caller, if any.\n function cancelOwnershipHandover() public payable virtual {\n /// @solidity memory-safe-assembly\n assembly {\n // Compute and set the handover slot to 0.\n mstore(0x0c, _HANDOVER_SLOT_SEED)\n mstore(0x00, caller())\n sstore(keccak256(0x0c, 0x20), 0)\n // Emit the {OwnershipHandoverCanceled} event.\n log2(0, 0, _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE, caller())\n }\n }\n\n /// @dev Allows the owner to complete the two-step ownership handover to `pendingOwner`.\n /// Reverts if there is no existing ownership handover requested by `pendingOwner`.\n function completeOwnershipHandover(address pendingOwner) public payable virtual onlyOwner {\n /// @solidity memory-safe-assembly\n assembly {\n // Compute and set the handover slot to 0.\n mstore(0x0c, _HANDOVER_SLOT_SEED)\n mstore(0x00, pendingOwner)\n let handoverSlot := keccak256(0x0c, 0x20)\n // If the handover does not exist, or has expired.\n if gt(timestamp(), sload(handoverSlot)) {\n mstore(0x00, 0x6f5e8818) // `NoHandoverRequest()`.\n revert(0x1c, 0x04)\n }\n // Set the handover slot to 0.\n sstore(handoverSlot, 0)\n }\n _setOwner(pendingOwner);\n }\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* PUBLIC READ FUNCTIONS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev Returns the owner of the contract.\n function owner() public view virtual returns (address result) {\n /// @solidity memory-safe-assembly\n assembly {\n result := sload(_OWNER_SLOT)\n }\n }\n\n /// @dev Returns the expiry timestamp for the two-step ownership handover to `pendingOwner`.\n function ownershipHandoverExpiresAt(address pendingOwner)\n public\n view\n virtual\n returns (uint256 result)\n {\n /// @solidity memory-safe-assembly\n assembly {\n // Compute the handover slot.\n mstore(0x0c, _HANDOVER_SLOT_SEED)\n mstore(0x00, pendingOwner)\n // Load the handover slot.\n result := sload(keccak256(0x0c, 0x20))\n }\n }\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* MODIFIERS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev Marks a function as only callable by the owner.\n modifier onlyOwner() virtual {\n _checkOwner();\n _;\n }\n}\n"},"src/bases/DaoBase.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity >=0.8.0;\n\nabstract contract DaoBase {\n /**\n * @dev implement getter logic to retrieve attested data\n * @param attestationId maps to the data\n */\n function getAttestedData(bytes32 attestationId) public view virtual returns (bytes memory attestationData);\n\n /**\n * @dev must store the hash of a collateral (e.g. X509 Cert, TCBInfo JSON etc) in the attestation registry\n * @dev it is recommended to store hash as a separate attestation from the actual collateral\n * @dev this getter can be useful for checking the correctness of the queried attested collateral\n *\n * @dev may link the hash attestation with the attestation of the collateral\n * For example, the content of a hash attestation can be a tuple of bytes32 values consisting of:\n * (bytes32 collateralHash, bytes32 collateralAttestationId)\n * @param attestationId - the attestationId pointing to the hash attestation, or the collateral attestation\n * itself, if the hash is included as part of the attestation data, this varies by how you define the schema.\n */\n function getCollateralHash(bytes32 attestationId) public view virtual returns (bytes32 collateralHash);\n\n /// @dev https://github.com/Vectorized/solady/blob/4964e3e2da1bc86b0394f63a90821f51d60a260b/src/utils/JSONParserLib.sol#L339-L364\n /// @dev Parses an unsigned integer from a string (in hexadecimal, i.e. base 16).\n /// Reverts if `s` is not a valid uint256 hex string matching the RegEx\n /// `^(0[xX])?[0-9a-fA-F]+$`, or if the parsed number is too big for a uint256.\n function _parseUintFromHex(string memory s) internal pure returns (uint256 result) {\n /// @solidity memory-safe-assembly\n assembly {\n let n := mload(s)\n // Skip two if starts with '0x' or '0X'.\n let i := shl(1, and(eq(0x3078, or(shr(240, mload(add(s, 0x20))), 0x20)), gt(n, 1)))\n for {} 1 {} {\n i := add(i, 1)\n let c :=\n byte(\n and(0x1f, shr(and(mload(add(s, i)), 0xff), 0x3e4088843e41bac000000000000)),\n 0x3010a071000000b0104040208000c05090d060e0f\n )\n n := mul(n, iszero(or(iszero(c), shr(252, result))))\n result := add(shl(4, result), sub(c, 1))\n if iszero(lt(i, n)) { break }\n }\n if iszero(n) {\n mstore(0x00, 0x10182796) // `ParsingFailed()`.\n revert(0x1c, 0x04)\n }\n }\n }\n}\n"},"src/Common.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nenum CA {\n ROOT,\n PROCESSOR,\n PLATFORM,\n SIGNING\n}\n\n/// @notice Attestation Definition is taken from https://github.com/ethereum-attestation-service/eas-contracts/blob/52af661748bde9b40ae782907702f885852bc149/contracts/IEAS.sol#L9C1-L23C2\n/// @notice We opted for EAS Attestation Request Definition to ensure interoperability between Verax and EAS\n\nstruct AttestationRequestData {\n address recipient; // The recipient of the attestation.\n uint64 expirationTime; // The time when the attestation expires (Unix timestamp).\n bool revocable; // Whether the attestation is revocable.\n bytes32 refUID; // The UID of the related attestation.\n bytes data; // Custom attestation data.\n uint256 value; // An explicit ETH amount to send to the resolver. This is important to prevent accidental user errors.\n}\n\nstruct AttestationRequest {\n bytes32 schema; // The unique identifier of the schema.\n AttestationRequestData data; // The arguments of the attestation request.\n}\n\n/// @notice A struct representing a single attestation.\n/// https://github.com/ethereum-attestation-service/eas-contracts/blob/52af661748bde9b40ae782907702f885852bc149/contracts/Common.sol#L25C1-L37C2\nstruct Attestation {\n bytes32 uid; // A unique identifier of the attestation.\n bytes32 schema; // The unique identifier of the schema.\n uint64 time; // The time when the attestation was created (Unix timestamp).\n uint64 expirationTime; // The time when the attestation expires (Unix timestamp).\n uint64 revocationTime; // The time when the attestation was revoked (Unix timestamp).\n bytes32 refUID; // The UID of the related attestation.\n address recipient; // The recipient of the attestation.\n address attester; // The attester/sender of the attestation.\n bool revocable; // Whether the attestation is revocable.\n bytes data; // Custom attestation data.\n}\n"},"src/helpers/X509Helper.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport {Asn1Decode, NodePtr} from \"../utils/Asn1Decode.sol\";\nimport {BytesUtils} from \"../utils/BytesUtils.sol\";\nimport {DateTimeUtils} from \"../utils/DateTimeUtils.sol\";\n\n/**\n * @title Solidity Structure representing X509 Certificates\n * @notice This is a simplified structure of a DER-decoded X509 Certificate\n */\nstruct X509CertObj {\n uint256 serialNumber;\n string issuerCommonName;\n uint256 validityNotBefore;\n uint256 validityNotAfter;\n string subjectCommonName;\n bytes subjectPublicKey;\n // the extension needs to be parsed further for PCK Certificates\n uint256 extensionPtr;\n // for signature verification in the cert chain\n bytes signature;\n bytes tbs;\n}\n\n/**\n * @title X509 Certificates Helper Contract\n * @notice This is a standalone contract that can be used by off-chain applications and smart contracts\n * to parse DER-encoded X509 certificates.\n * @dev The Extension sequence in Intel PCK Certificates is a custom ASN.1 Sequence that needs to be\n * @dev parsed further in a more specialized PCKHelper contract.\n */\ncontract X509Helper {\n using Asn1Decode for bytes;\n using NodePtr for uint256;\n using BytesUtils for bytes;\n\n /// =================================================================================\n /// USE THE GETTERS BELOW IF YOU DON'T WANT TO PARSE THE ENTIRE X509 CERTIFICATE\n /// =================================================================================\n\n function getTbsAndSig(bytes calldata der) external pure returns (bytes memory tbs, bytes memory sig) {\n uint256 root = der.root();\n uint256 tbsParentPtr = der.firstChildOf(root);\n uint256 sigPtr = der.nextSiblingOf(tbsParentPtr);\n sigPtr = der.nextSiblingOf(sigPtr);\n\n tbs = der.allBytesAt(tbsParentPtr);\n sig = _getSignature(der, sigPtr);\n }\n\n function getSerialNumber(bytes calldata der) external pure returns (uint256 serialNum) {\n uint256 root = der.root();\n uint256 tbsParentPtr = der.firstChildOf(root);\n uint256 tbsPtr = der.firstChildOf(tbsParentPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n serialNum = _parseSerialNumber(der.bytesAt(tbsPtr));\n }\n\n function getIssuerCommonName(bytes calldata der) external pure returns (string memory issuerCommonName) {\n uint256 root = der.root();\n uint256 tbsParentPtr = der.firstChildOf(root);\n uint256 tbsPtr = der.firstChildOf(tbsParentPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n issuerCommonName = _getCommonName(der, der.firstChildOf(tbsPtr));\n }\n\n function certIsNotExpired(bytes calldata der) external view returns (bool isValid) {\n uint256 root = der.root();\n uint256 tbsParentPtr = der.firstChildOf(root);\n uint256 tbsPtr = der.firstChildOf(tbsParentPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n (uint256 validityNotBefore, uint256 validityNotAfter) = _getValidity(der, tbsPtr);\n isValid = block.timestamp > validityNotBefore && block.timestamp < validityNotAfter;\n }\n\n function getSubjectCommonName(bytes calldata der) external pure returns (string memory subjectCommonName) {\n uint256 root = der.root();\n uint256 tbsParentPtr = der.firstChildOf(root);\n uint256 tbsPtr = der.firstChildOf(tbsParentPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n subjectCommonName = _getCommonName(der, der.firstChildOf(tbsPtr));\n }\n\n function getSubjectPublicKey(bytes calldata der) external pure returns (bytes memory pubKey) {\n uint256 root = der.root();\n uint256 tbsParentPtr = der.firstChildOf(root);\n uint256 tbsPtr = der.firstChildOf(tbsParentPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n pubKey = _getSubjectPublicKey(der, der.firstChildOf(tbsPtr));\n }\n\n /// x509 Certificates generally contain a sequence of elements in the following order:\n /// 1. tbs\n /// - 1a. version\n /// - 1b. serial number\n /// - 1c. siganture algorithm\n /// - 1d. issuer\n /// - - 1d(a). common name\n /// - - 1d(b). organization name\n /// - - 1d(c). locality name\n /// - - 1d(d). state or province name\n /// - - 1d(e). country name\n /// - 1e. validity\n /// - - 1e(a) notBefore\n /// - - 1e(b) notAfter\n /// - 1f. subject\n /// - - contains the same set of elements as 1d\n /// - 1g. subject public key info\n /// - - 1g(a). algorithm\n /// - - 1g(b). subject public key\n /// - 1h. Extensions\n /// 2. Signature Algorithm\n /// 3. Signature\n /// - 3a. X value\n /// - 3b. Y value\n function parseX509DER(bytes calldata der) external pure returns (X509CertObj memory cert) {\n uint256 root = der.root();\n\n uint256 tbsParentPtr = der.firstChildOf(root);\n cert.tbs = der.allBytesAt(tbsParentPtr);\n\n uint256 tbsPtr = der.firstChildOf(tbsParentPtr);\n\n tbsPtr = der.nextSiblingOf(tbsPtr);\n\n cert.serialNumber = _parseSerialNumber(der.bytesAt(tbsPtr));\n\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n\n cert.issuerCommonName = _getCommonName(der, der.firstChildOf(tbsPtr));\n\n tbsPtr = der.nextSiblingOf(tbsPtr);\n (cert.validityNotBefore, cert.validityNotAfter) = _getValidity(der, tbsPtr);\n\n tbsPtr = der.nextSiblingOf(tbsPtr);\n\n cert.subjectCommonName = _getCommonName(der, der.firstChildOf(tbsPtr));\n\n tbsPtr = der.nextSiblingOf(tbsPtr);\n cert.subjectPublicKey = _getSubjectPublicKey(der, der.firstChildOf(tbsPtr));\n\n cert.extensionPtr = der.nextSiblingOf(tbsPtr);\n\n // tbs iteration completed\n // now we just need to look for the signature\n\n uint256 sigPtr = der.nextSiblingOf(tbsParentPtr);\n sigPtr = der.nextSiblingOf(sigPtr);\n cert.signature = _getSignature(der, sigPtr);\n }\n\n function _getCommonName(bytes calldata der, uint256 commonNameParentPtr)\n private\n pure\n returns (string memory commonName)\n {\n commonNameParentPtr = der.firstChildOf(commonNameParentPtr);\n commonNameParentPtr = der.firstChildOf(commonNameParentPtr);\n commonNameParentPtr = der.nextSiblingOf(commonNameParentPtr);\n commonName = string(der.bytesAt(commonNameParentPtr));\n }\n\n function _getValidity(bytes calldata der, uint256 validityPtr)\n private\n pure\n returns (uint256 notBefore, uint256 notAfter)\n {\n uint256 notBeforePtr = der.firstChildOf(validityPtr);\n uint256 notAfterPtr = der.nextSiblingOf(notBeforePtr);\n notBefore = DateTimeUtils.fromDERToTimestamp(der.bytesAt(notBeforePtr));\n notAfter = DateTimeUtils.fromDERToTimestamp(der.bytesAt(notAfterPtr));\n }\n\n function _getSubjectPublicKey(bytes calldata der, uint256 subjectPublicKeyInfoPtr)\n private\n pure\n returns (bytes memory pubKey)\n {\n subjectPublicKeyInfoPtr = der.nextSiblingOf(subjectPublicKeyInfoPtr);\n pubKey = der.bitstringAt(subjectPublicKeyInfoPtr);\n if (pubKey.length != 65) {\n // TODO: we need to figure out how to handle key with prefix byte 0x02 or 0x03\n revert(\"compressed public key not supported\");\n }\n pubKey = _trimBytes(pubKey, 64);\n }\n\n function _parseSerialNumber(bytes memory serialBytes) private pure returns (uint256 serial) {\n uint256 shift = 8 * (32 - serialBytes.length);\n serial = uint256(bytes32(serialBytes) >> shift);\n }\n\n function _getSignature(bytes calldata der, uint256 sigPtr) private pure returns (bytes memory sig) {\n sigPtr = der.rootOfBitStringAt(sigPtr);\n\n sigPtr = der.firstChildOf(sigPtr);\n bytes memory sigX = _trimBytes(der.bytesAt(sigPtr), 32);\n\n sigPtr = der.nextSiblingOf(sigPtr);\n bytes memory sigY = _trimBytes(der.bytesAt(sigPtr), 32);\n\n sig = abi.encodePacked(sigX, sigY);\n }\n\n /// @dev remove unnecessary prefix from the input\n function _trimBytes(bytes memory input, uint256 expectedLength) private pure returns (bytes memory output) {\n uint256 n = input.length;\n if (n == expectedLength) {\n output = input;\n } else if (n < expectedLength) {\n output = new bytes(expectedLength);\n uint256 padLength = expectedLength - n;\n for (uint256 i = 0; i < n; i++) {\n output[padLength + i] = input[i];\n }\n } else {\n uint256 lengthDiff = n - expectedLength;\n output = input.substring(lengthDiff, expectedLength);\n }\n }\n}\n"},"src/helpers/X509CRLHelper.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport {Asn1Decode, NodePtr} from \"../utils/Asn1Decode.sol\";\nimport {BytesUtils} from \"../utils/BytesUtils.sol\";\nimport {DateTimeUtils} from \"../utils/DateTimeUtils.sol\";\n\n/**\n * @title Solidity Structure representing X509 CRL\n * @notice This is a simplified structure of a DER-decoded X509 CRL\n */\nstruct X509CRLObj {\n uint256 serialNumber;\n string issuerCommonName;\n uint256 validityNotBefore;\n uint256 validityNotAfter;\n uint256[] serialNumbersRevoked;\n // for signature verification in the cert chain\n bytes signature;\n bytes tbs;\n}\n\n/**\n * @title X509 CRL Helper Contract\n * @notice This is a standalone contract that can be used by off-chain applications and smart contracts\n * to parse DER-encoded CRLs.\n */\ncontract X509CRLHelper {\n using Asn1Decode for bytes;\n using NodePtr for uint256;\n using BytesUtils for bytes;\n\n // 2.5.29.20\n bytes constant CRL_NUMBER_OID = hex\"551d14\";\n\n /// =================================================================================\n /// USE THE GETTERS BELOW IF YOU DON'T WANT TO PARSE THE ENTIRE X509 CRL\n /// =================================================================================\n\n function getTbsAndSig(bytes calldata der) external pure returns (bytes memory tbs, bytes memory sig) {\n uint256 root = der.root();\n uint256 tbsParentPtr = der.firstChildOf(root);\n uint256 sigPtr = der.nextSiblingOf(tbsParentPtr);\n sigPtr = der.nextSiblingOf(sigPtr);\n\n tbs = der.allBytesAt(tbsParentPtr);\n sig = _getSignature(der, sigPtr);\n }\n\n function getSerialNumber(bytes calldata der) external pure returns (uint256 serialNum) {\n uint256 root = der.root();\n uint256 tbsParentPtr = der.firstChildOf(root);\n uint256 tbsPtr = der.firstChildOf(tbsParentPtr);\n serialNum = _parseSerialNumber(der.bytesAt(tbsPtr));\n }\n\n function getIssuerCommonName(bytes calldata der) external pure returns (string memory issuerCommonName) {\n uint256 root = der.root();\n uint256 tbsParentPtr = der.firstChildOf(root);\n uint256 tbsPtr = der.firstChildOf(tbsParentPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n issuerCommonName = _getCommonName(der, der.firstChildOf(tbsPtr));\n }\n\n function crlIsNotExpired(bytes calldata der) external view returns (bool isValid) {\n uint256 root = der.root();\n uint256 tbsParentPtr = der.firstChildOf(root);\n uint256 tbsPtr = der.firstChildOf(tbsParentPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n (uint256 validityNotBefore, uint256 validityNotAfter) = _getValidity(der, tbsPtr);\n isValid = block.timestamp > validityNotBefore && block.timestamp < validityNotAfter;\n }\n\n function serialNumberIsRevoked(uint256 serialNumber, bytes calldata der) external pure returns (bool revoked) {\n uint256 root = der.root();\n uint256 tbsParentPtr = der.firstChildOf(root);\n uint256 tbsPtr = der.firstChildOf(tbsParentPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n uint256[] memory ret = _getRevokedSerialNumbers(der, tbsPtr, true, serialNumber);\n revoked = ret[0] == serialNumber;\n }\n\n /// x509 CRL generally contain a sequence of elements in the following order:\n /// 1. tbs\n /// - 1a. serial number\n /// - 1b. signature algorithm\n /// - 1c. issuer\n /// - - 1c(a). common name\n /// - - 1c(b). organization name\n /// - - 1c(c). locality name\n /// - - 1c(d). state or province name\n /// - - 1c(e). country name\n /// - 1d. not before\n /// - 1e. not after\n /// - 1f. revoked certificates\n /// - - A list consists of revoked serial numbers and reasons.\n /// - 1g. CRL extensions\n /// - - 1g(a) CRL number\n /// - - 1g(b) Authority Key Identifier\n /// 2. Signature Algorithm\n /// 3. Signature\n /// - 3a. X value\n /// - 3b. Y value\n function parseCRLDER(bytes calldata der) external pure returns (X509CRLObj memory crl) {\n uint256 root = der.root();\n\n uint256 tbsParentPtr = der.firstChildOf(root);\n\n uint256 tbsPtr = der.firstChildOf(tbsParentPtr);\n\n crl.serialNumber = uint256(bytes32(der.bytesAt(tbsPtr)));\n\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n\n crl.issuerCommonName = _getCommonName(der, der.firstChildOf(tbsPtr));\n\n tbsPtr = der.nextSiblingOf(tbsPtr);\n (crl.validityNotBefore, crl.validityNotAfter) = _getValidity(der, tbsPtr);\n\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n\n crl.serialNumbersRevoked = _getRevokedSerialNumbers(der, tbsPtr, false, 0);\n\n // tbs iteration completed\n // now we just need to look for the signature\n\n uint256 sigPtr = der.nextSiblingOf(tbsParentPtr);\n sigPtr = der.nextSiblingOf(sigPtr);\n crl.signature = _getSignature(der, sigPtr);\n }\n\n function _getCommonName(bytes calldata der, uint256 commonNameParentPtr)\n private\n pure\n returns (string memory commonName)\n {\n commonNameParentPtr = der.firstChildOf(commonNameParentPtr);\n commonNameParentPtr = der.firstChildOf(commonNameParentPtr);\n commonNameParentPtr = der.nextSiblingOf(commonNameParentPtr);\n commonName = string(der.bytesAt(commonNameParentPtr));\n }\n\n function _getValidity(bytes calldata der, uint256 validityPtr)\n private\n pure\n returns (uint256 notBefore, uint256 notAfter)\n {\n uint256 notBeforePtr = validityPtr;\n uint256 notAfterPtr = der.nextSiblingOf(notBeforePtr);\n notBefore = DateTimeUtils.fromDERToTimestamp(der.bytesAt(notBeforePtr));\n notAfter = DateTimeUtils.fromDERToTimestamp(der.bytesAt(notAfterPtr));\n }\n\n function _getRevokedSerialNumbers(bytes calldata der, uint256 revokedParentPtr, bool breakIfFound, uint256 filter)\n private\n pure\n returns (uint256[] memory serialNumbers)\n {\n uint256 revokedPtr = der.firstChildOf(revokedParentPtr);\n\n if (der[revokedPtr.ixs()] == 0xA0) {\n uint256 crlExtensionPtr = der.firstChildOf(revokedPtr);\n require(BytesUtils.compareBytes(der.bytesAt(crlExtensionPtr), CRL_NUMBER_OID), \"invalid CRL\");\n } else {\n bytes memory serials;\n while (revokedPtr.ixl() <= revokedParentPtr.ixl()) {\n uint256 serialPtr = der.firstChildOf(revokedPtr);\n bytes memory serialBytes = der.bytesAt(serialPtr);\n uint256 serialNumber = _parseSerialNumber(serialBytes);\n serials = abi.encodePacked(serials, serialNumber);\n if (breakIfFound && filter == serialNumber) {\n serialNumbers = new uint256[](1);\n serialNumbers[0] = filter;\n return serialNumbers;\n }\n revokedPtr = der.nextSiblingOf(revokedPtr);\n }\n uint256 count = serials.length / 32;\n // ABI encoding format for a dynamic uint256[] value\n serials = abi.encodePacked(abi.encode(0x20), abi.encode(count), serials);\n serialNumbers = new uint256[](count);\n serialNumbers = abi.decode(serials, (uint256[]));\n }\n }\n\n function _parseSerialNumber(bytes memory serialBytes) private pure returns (uint256 serial) {\n uint256 shift = 8 * (32 - serialBytes.length);\n serial = uint256(bytes32(serialBytes) >> shift);\n }\n\n function _getSignature(bytes calldata der, uint256 sigPtr) private pure returns (bytes memory sig) {\n sigPtr = der.rootOfBitStringAt(sigPtr);\n\n sigPtr = der.firstChildOf(sigPtr);\n bytes memory sigX = _trimBytes(der.bytesAt(sigPtr), 32);\n\n sigPtr = der.nextSiblingOf(sigPtr);\n bytes memory sigY = _trimBytes(der.bytesAt(sigPtr), 32);\n\n sig = abi.encodePacked(sigX, sigY);\n }\n\n /// @dev remove unnecessary prefix from the input\n /// @dev remove unnecessary prefix from the input\n function _trimBytes(bytes memory input, uint256 expectedLength) private pure returns (bytes memory output) {\n uint256 n = input.length;\n if (n == expectedLength) {\n output = input;\n } else if (n < expectedLength) {\n output = new bytes(expectedLength);\n uint256 padLength = expectedLength - n;\n for (uint256 i = 0; i < n; i++) {\n output[padLength + i] = input[i];\n }\n } else {\n uint256 lengthDiff = n - expectedLength;\n output = input.substring(lengthDiff, expectedLength);\n }\n }\n}\n"},"src/bases/SigVerifyBase.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport {BytesUtils} from \"../utils/BytesUtils.sol\";\nimport {P256Verifier} from \"../utils/P256Verifier.sol\";\n\ninterface IX509 {\n function getSubjectPublicKey(bytes memory der) external pure returns (bytes memory pubKey);\n}\n\nabstract contract SigVerifyBase {\n address public x509;\n\n using BytesUtils for bytes;\n\n constructor(address _x509helper) {\n x509 = _x509helper;\n }\n\n function verifySignature(bytes32 digest, bytes memory signature, bytes memory signingCertBlob)\n internal\n view\n returns (bool verified)\n {\n if (signature.length != 64) {\n return false;\n }\n\n bytes memory pubKey = IX509(x509).getSubjectPublicKey(signingCertBlob);\n if (pubKey.length != 64) {\n return false;\n }\n\n verified = P256Verifier.ecdsaVerify(digest, signature, pubKey);\n }\n}\n"},"lib/solady/src/utils/LibString.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\n/// @notice Library for converting numbers into strings and other string operations.\n/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibString.sol)\n/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/LibString.sol)\n///\n/// Note:\n/// For performance and bytecode compactness, most of the string operations are restricted to\n/// byte strings (7-bit ASCII), except where otherwise specified.\n/// Usage of byte string operations on charsets with runes spanning two or more bytes\n/// can lead to undefined behavior.\nlibrary LibString {\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* CUSTOM ERRORS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev The length of the output is too small to contain all the hex digits.\n error HexLengthInsufficient();\n\n /// @dev The length of the string is more than 32 bytes.\n error TooBigForSmallString();\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* CONSTANTS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev The constant returned when the `search` is not found in the string.\n uint256 internal constant NOT_FOUND = type(uint256).max;\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* DECIMAL OPERATIONS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev Returns the base 10 decimal representation of `value`.\n function toString(uint256 value) internal pure returns (string memory str) {\n /// @solidity memory-safe-assembly\n assembly {\n // The maximum value of a uint256 contains 78 digits (1 byte per digit), but\n // we allocate 0xa0 bytes to keep the free memory pointer 32-byte word aligned.\n // We will need 1 word for the trailing zeros padding, 1 word for the length,\n // and 3 words for a maximum of 78 digits.\n str := add(mload(0x40), 0x80)\n // Update the free memory pointer to allocate.\n mstore(0x40, add(str, 0x20))\n // Zeroize the slot after the string.\n mstore(str, 0)\n\n // Cache the end of the memory to calculate the length later.\n let end := str\n\n let w := not(0) // Tsk.\n // We write the string from rightmost digit to leftmost digit.\n // The following is essentially a do-while loop that also handles the zero case.\n for { let temp := value } 1 {} {\n str := add(str, w) // `sub(str, 1)`.\n // Write the character to the pointer.\n // The ASCII index of the '0' character is 48.\n mstore8(str, add(48, mod(temp, 10)))\n // Keep dividing `temp` until zero.\n temp := div(temp, 10)\n if iszero(temp) { break }\n }\n\n let length := sub(end, str)\n // Move the pointer 32 bytes leftwards to make room for the length.\n str := sub(str, 0x20)\n // Store the length.\n mstore(str, length)\n }\n }\n\n /// @dev Returns the base 10 decimal representation of `value`.\n function toString(int256 value) internal pure returns (string memory str) {\n if (value >= 0) {\n return toString(uint256(value));\n }\n unchecked {\n str = toString(uint256(-value));\n }\n /// @solidity memory-safe-assembly\n assembly {\n // We still have some spare memory space on the left,\n // as we have allocated 3 words (96 bytes) for up to 78 digits.\n let length := mload(str) // Load the string length.\n mstore(str, 0x2d) // Store the '-' character.\n str := sub(str, 1) // Move back the string pointer by a byte.\n mstore(str, add(length, 1)) // Update the string length.\n }\n }\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* HEXADECIMAL OPERATIONS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev Returns the hexadecimal representation of `value`,\n /// left-padded to an input length of `length` bytes.\n /// The output is prefixed with \"0x\" encoded using 2 hexadecimal digits per byte,\n /// giving a total length of `length * 2 + 2` bytes.\n /// Reverts if `length` is too small for the output to contain all the digits.\n function toHexString(uint256 value, uint256 length) internal pure returns (string memory str) {\n str = toHexStringNoPrefix(value, length);\n /// @solidity memory-safe-assembly\n assembly {\n let strLength := add(mload(str), 2) // Compute the length.\n mstore(str, 0x3078) // Write the \"0x\" prefix.\n str := sub(str, 2) // Move the pointer.\n mstore(str, strLength) // Write the length.\n }\n }\n\n /// @dev Returns the hexadecimal representation of `value`,\n /// left-padded to an input length of `length` bytes.\n /// The output is prefixed with \"0x\" encoded using 2 hexadecimal digits per byte,\n /// giving a total length of `length * 2` bytes.\n /// Reverts if `length` is too small for the output to contain all the digits.\n function toHexStringNoPrefix(uint256 value, uint256 length)\n internal\n pure\n returns (string memory str)\n {\n /// @solidity memory-safe-assembly\n assembly {\n // We need 0x20 bytes for the trailing zeros padding, `length * 2` bytes\n // for the digits, 0x02 bytes for the prefix, and 0x20 bytes for the length.\n // We add 0x20 to the total and round down to a multiple of 0x20.\n // (0x20 + 0x20 + 0x02 + 0x20) = 0x62.\n str := add(mload(0x40), and(add(shl(1, length), 0x42), not(0x1f)))\n // Allocate the memory.\n mstore(0x40, add(str, 0x20))\n // Zeroize the slot after the string.\n mstore(str, 0)\n\n // Cache the end to calculate the length later.\n let end := str\n // Store \"0123456789abcdef\" in scratch space.\n mstore(0x0f, 0x30313233343536373839616263646566)\n\n let start := sub(str, add(length, length))\n let w := not(1) // Tsk.\n let temp := value\n // We write the string from rightmost digit to leftmost digit.\n // The following is essentially a do-while loop that also handles the zero case.\n for {} 1 {} {\n str := add(str, w) // `sub(str, 2)`.\n mstore8(add(str, 1), mload(and(temp, 15)))\n mstore8(str, mload(and(shr(4, temp), 15)))\n temp := shr(8, temp)\n if iszero(xor(str, start)) { break }\n }\n\n if temp {\n mstore(0x00, 0x2194895a) // `HexLengthInsufficient()`.\n revert(0x1c, 0x04)\n }\n\n // Compute the string's length.\n let strLength := sub(end, str)\n // Move the pointer and write the length.\n str := sub(str, 0x20)\n mstore(str, strLength)\n }\n }\n\n /// @dev Returns the hexadecimal representation of `value`.\n /// The output is prefixed with \"0x\" and encoded using 2 hexadecimal digits per byte.\n /// As address are 20 bytes long, the output will left-padded to have\n /// a length of `20 * 2 + 2` bytes.\n function toHexString(uint256 value) internal pure returns (string memory str) {\n str = toHexStringNoPrefix(value);\n /// @solidity memory-safe-assembly\n assembly {\n let strLength := add(mload(str), 2) // Compute the length.\n mstore(str, 0x3078) // Write the \"0x\" prefix.\n str := sub(str, 2) // Move the pointer.\n mstore(str, strLength) // Write the length.\n }\n }\n\n /// @dev Returns the hexadecimal representation of `value`.\n /// The output is prefixed with \"0x\".\n /// The output excludes leading \"0\" from the `toHexString` output.\n /// `0x00: \"0x0\", 0x01: \"0x1\", 0x12: \"0x12\", 0x123: \"0x123\"`.\n function toMinimalHexString(uint256 value) internal pure returns (string memory str) {\n str = toHexStringNoPrefix(value);\n /// @solidity memory-safe-assembly\n assembly {\n let o := eq(byte(0, mload(add(str, 0x20))), 0x30) // Whether leading zero is present.\n let strLength := add(mload(str), 2) // Compute the length.\n mstore(add(str, o), 0x3078) // Write the \"0x\" prefix, accounting for leading zero.\n str := sub(add(str, o), 2) // Move the pointer, accounting for leading zero.\n mstore(str, sub(strLength, o)) // Write the length, accounting for leading zero.\n }\n }\n\n /// @dev Returns the hexadecimal representation of `value`.\n /// The output excludes leading \"0\" from the `toHexStringNoPrefix` output.\n /// `0x00: \"0\", 0x01: \"1\", 0x12: \"12\", 0x123: \"123\"`.\n function toMinimalHexStringNoPrefix(uint256 value) internal pure returns (string memory str) {\n str = toHexStringNoPrefix(value);\n /// @solidity memory-safe-assembly\n assembly {\n let o := eq(byte(0, mload(add(str, 0x20))), 0x30) // Whether leading zero is present.\n let strLength := mload(str) // Get the length.\n str := add(str, o) // Move the pointer, accounting for leading zero.\n mstore(str, sub(strLength, o)) // Write the length, accounting for leading zero.\n }\n }\n\n /// @dev Returns the hexadecimal representation of `value`.\n /// The output is encoded using 2 hexadecimal digits per byte.\n /// As address are 20 bytes long, the output will left-padded to have\n /// a length of `20 * 2` bytes.\n function toHexStringNoPrefix(uint256 value) internal pure returns (string memory str) {\n /// @solidity memory-safe-assembly\n assembly {\n // We need 0x20 bytes for the trailing zeros padding, 0x20 bytes for the length,\n // 0x02 bytes for the prefix, and 0x40 bytes for the digits.\n // The next multiple of 0x20 above (0x20 + 0x20 + 0x02 + 0x40) is 0xa0.\n str := add(mload(0x40), 0x80)\n // Allocate the memory.\n mstore(0x40, add(str, 0x20))\n // Zeroize the slot after the string.\n mstore(str, 0)\n\n // Cache the end to calculate the length later.\n let end := str\n // Store \"0123456789abcdef\" in scratch space.\n mstore(0x0f, 0x30313233343536373839616263646566)\n\n let w := not(1) // Tsk.\n // We write the string from rightmost digit to leftmost digit.\n // The following is essentially a do-while loop that also handles the zero case.\n for { let temp := value } 1 {} {\n str := add(str, w) // `sub(str, 2)`.\n mstore8(add(str, 1), mload(and(temp, 15)))\n mstore8(str, mload(and(shr(4, temp), 15)))\n temp := shr(8, temp)\n if iszero(temp) { break }\n }\n\n // Compute the string's length.\n let strLength := sub(end, str)\n // Move the pointer and write the length.\n str := sub(str, 0x20)\n mstore(str, strLength)\n }\n }\n\n /// @dev Returns the hexadecimal representation of `value`.\n /// The output is prefixed with \"0x\", encoded using 2 hexadecimal digits per byte,\n /// and the alphabets are capitalized conditionally according to\n /// https://eips.ethereum.org/EIPS/eip-55\n function toHexStringChecksummed(address value) internal pure returns (string memory str) {\n str = toHexString(value);\n /// @solidity memory-safe-assembly\n assembly {\n let mask := shl(6, div(not(0), 255)) // `0b010000000100000000 ...`\n let o := add(str, 0x22)\n let hashed := and(keccak256(o, 40), mul(34, mask)) // `0b10001000 ... `\n let t := shl(240, 136) // `0b10001000 << 240`\n for { let i := 0 } 1 {} {\n mstore(add(i, i), mul(t, byte(i, hashed)))\n i := add(i, 1)\n if eq(i, 20) { break }\n }\n mstore(o, xor(mload(o), shr(1, and(mload(0x00), and(mload(o), mask)))))\n o := add(o, 0x20)\n mstore(o, xor(mload(o), shr(1, and(mload(0x20), and(mload(o), mask)))))\n }\n }\n\n /// @dev Returns the hexadecimal representation of `value`.\n /// The output is prefixed with \"0x\" and encoded using 2 hexadecimal digits per byte.\n function toHexString(address value) internal pure returns (string memory str) {\n str = toHexStringNoPrefix(value);\n /// @solidity memory-safe-assembly\n assembly {\n let strLength := add(mload(str), 2) // Compute the length.\n mstore(str, 0x3078) // Write the \"0x\" prefix.\n str := sub(str, 2) // Move the pointer.\n mstore(str, strLength) // Write the length.\n }\n }\n\n /// @dev Returns the hexadecimal representation of `value`.\n /// The output is encoded using 2 hexadecimal digits per byte.\n function toHexStringNoPrefix(address value) internal pure returns (string memory str) {\n /// @solidity memory-safe-assembly\n assembly {\n str := mload(0x40)\n\n // Allocate the memory.\n // We need 0x20 bytes for the trailing zeros padding, 0x20 bytes for the length,\n // 0x02 bytes for the prefix, and 0x28 bytes for the digits.\n // The next multiple of 0x20 above (0x20 + 0x20 + 0x02 + 0x28) is 0x80.\n mstore(0x40, add(str, 0x80))\n\n // Store \"0123456789abcdef\" in scratch space.\n mstore(0x0f, 0x30313233343536373839616263646566)\n\n str := add(str, 2)\n mstore(str, 40)\n\n let o := add(str, 0x20)\n mstore(add(o, 40), 0)\n\n value := shl(96, value)\n\n // We write the string from rightmost digit to leftmost digit.\n // The following is essentially a do-while loop that also handles the zero case.\n for { let i := 0 } 1 {} {\n let p := add(o, add(i, i))\n let temp := byte(i, value)\n mstore8(add(p, 1), mload(and(temp, 15)))\n mstore8(p, mload(shr(4, temp)))\n i := add(i, 1)\n if eq(i, 20) { break }\n }\n }\n }\n\n /// @dev Returns the hex encoded string from the raw bytes.\n /// The output is encoded using 2 hexadecimal digits per byte.\n function toHexString(bytes memory raw) internal pure returns (string memory str) {\n str = toHexStringNoPrefix(raw);\n /// @solidity memory-safe-assembly\n assembly {\n let strLength := add(mload(str), 2) // Compute the length.\n mstore(str, 0x3078) // Write the \"0x\" prefix.\n str := sub(str, 2) // Move the pointer.\n mstore(str, strLength) // Write the length.\n }\n }\n\n /// @dev Returns the hex encoded string from the raw bytes.\n /// The output is encoded using 2 hexadecimal digits per byte.\n function toHexStringNoPrefix(bytes memory raw) internal pure returns (string memory str) {\n /// @solidity memory-safe-assembly\n assembly {\n let length := mload(raw)\n str := add(mload(0x40), 2) // Skip 2 bytes for the optional prefix.\n mstore(str, add(length, length)) // Store the length of the output.\n\n // Store \"0123456789abcdef\" in scratch space.\n mstore(0x0f, 0x30313233343536373839616263646566)\n\n let o := add(str, 0x20)\n let end := add(raw, length)\n\n for {} iszero(eq(raw, end)) {} {\n raw := add(raw, 1)\n mstore8(add(o, 1), mload(and(mload(raw), 15)))\n mstore8(o, mload(and(shr(4, mload(raw)), 15)))\n o := add(o, 2)\n }\n mstore(o, 0) // Zeroize the slot after the string.\n mstore(0x40, add(o, 0x20)) // Allocate the memory.\n }\n }\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* RUNE STRING OPERATIONS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev Returns the number of UTF characters in the string.\n function runeCount(string memory s) internal pure returns (uint256 result) {\n /// @solidity memory-safe-assembly\n assembly {\n if mload(s) {\n mstore(0x00, div(not(0), 255))\n mstore(0x20, 0x0202020202020202020202020202020202020202020202020303030304040506)\n let o := add(s, 0x20)\n let end := add(o, mload(s))\n for { result := 1 } 1 { result := add(result, 1) } {\n o := add(o, byte(0, mload(shr(250, mload(o)))))\n if iszero(lt(o, end)) { break }\n }\n }\n }\n }\n\n /// @dev Returns if this string is a 7-bit ASCII string.\n /// (i.e. all characters codes are in [0..127])\n function is7BitASCII(string memory s) internal pure returns (bool result) {\n /// @solidity memory-safe-assembly\n assembly {\n let mask := shl(7, div(not(0), 255))\n result := 1\n let n := mload(s)\n if n {\n let o := add(s, 0x20)\n let end := add(o, n)\n let last := mload(end)\n mstore(end, 0)\n for {} 1 {} {\n if and(mask, mload(o)) {\n result := 0\n break\n }\n o := add(o, 0x20)\n if iszero(lt(o, end)) { break }\n }\n mstore(end, last)\n }\n }\n }\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* BYTE STRING OPERATIONS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n // For performance and bytecode compactness, byte string operations are restricted\n // to 7-bit ASCII strings. All offsets are byte offsets, not UTF character offsets.\n // Usage of byte string operations on charsets with runes spanning two or more bytes\n // can lead to undefined behavior.\n\n /// @dev Returns `subject` all occurrences of `search` replaced with `replacement`.\n function replace(string memory subject, string memory search, string memory replacement)\n internal\n pure\n returns (string memory result)\n {\n /// @solidity memory-safe-assembly\n assembly {\n let subjectLength := mload(subject)\n let searchLength := mload(search)\n let replacementLength := mload(replacement)\n\n subject := add(subject, 0x20)\n search := add(search, 0x20)\n replacement := add(replacement, 0x20)\n result := add(mload(0x40), 0x20)\n\n let subjectEnd := add(subject, subjectLength)\n if iszero(gt(searchLength, subjectLength)) {\n let subjectSearchEnd := add(sub(subjectEnd, searchLength), 1)\n let h := 0\n if iszero(lt(searchLength, 0x20)) { h := keccak256(search, searchLength) }\n let m := shl(3, sub(0x20, and(searchLength, 0x1f)))\n let s := mload(search)\n for {} 1 {} {\n let t := mload(subject)\n // Whether the first `searchLength % 32` bytes of\n // `subject` and `search` matches.\n if iszero(shr(m, xor(t, s))) {\n if h {\n if iszero(eq(keccak256(subject, searchLength), h)) {\n mstore(result, t)\n result := add(result, 1)\n subject := add(subject, 1)\n if iszero(lt(subject, subjectSearchEnd)) { break }\n continue\n }\n }\n // Copy the `replacement` one word at a time.\n for { let o := 0 } 1 {} {\n mstore(add(result, o), mload(add(replacement, o)))\n o := add(o, 0x20)\n if iszero(lt(o, replacementLength)) { break }\n }\n result := add(result, replacementLength)\n subject := add(subject, searchLength)\n if searchLength {\n if iszero(lt(subject, subjectSearchEnd)) { break }\n continue\n }\n }\n mstore(result, t)\n result := add(result, 1)\n subject := add(subject, 1)\n if iszero(lt(subject, subjectSearchEnd)) { break }\n }\n }\n\n let resultRemainder := result\n result := add(mload(0x40), 0x20)\n let k := add(sub(resultRemainder, result), sub(subjectEnd, subject))\n // Copy the rest of the string one word at a time.\n for {} lt(subject, subjectEnd) {} {\n mstore(resultRemainder, mload(subject))\n resultRemainder := add(resultRemainder, 0x20)\n subject := add(subject, 0x20)\n }\n result := sub(result, 0x20)\n let last := add(add(result, 0x20), k) // Zeroize the slot after the string.\n mstore(last, 0)\n mstore(0x40, add(last, 0x20)) // Allocate the memory.\n mstore(result, k) // Store the length.\n }\n }\n\n /// @dev Returns the byte index of the first location of `search` in `subject`,\n /// searching from left to right, starting from `from`.\n /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.\n function indexOf(string memory subject, string memory search, uint256 from)\n internal\n pure\n returns (uint256 result)\n {\n /// @solidity memory-safe-assembly\n assembly {\n for { let subjectLength := mload(subject) } 1 {} {\n if iszero(mload(search)) {\n if iszero(gt(from, subjectLength)) {\n result := from\n break\n }\n result := subjectLength\n break\n }\n let searchLength := mload(search)\n let subjectStart := add(subject, 0x20)\n\n result := not(0) // Initialize to `NOT_FOUND`.\n\n subject := add(subjectStart, from)\n let end := add(sub(add(subjectStart, subjectLength), searchLength), 1)\n\n let m := shl(3, sub(0x20, and(searchLength, 0x1f)))\n let s := mload(add(search, 0x20))\n\n if iszero(and(lt(subject, end), lt(from, subjectLength))) { break }\n\n if iszero(lt(searchLength, 0x20)) {\n for { let h := keccak256(add(search, 0x20), searchLength) } 1 {} {\n if iszero(shr(m, xor(mload(subject), s))) {\n if eq(keccak256(subject, searchLength), h) {\n result := sub(subject, subjectStart)\n break\n }\n }\n subject := add(subject, 1)\n if iszero(lt(subject, end)) { break }\n }\n break\n }\n for {} 1 {} {\n if iszero(shr(m, xor(mload(subject), s))) {\n result := sub(subject, subjectStart)\n break\n }\n subject := add(subject, 1)\n if iszero(lt(subject, end)) { break }\n }\n break\n }\n }\n }\n\n /// @dev Returns the byte index of the first location of `search` in `subject`,\n /// searching from left to right.\n /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.\n function indexOf(string memory subject, string memory search)\n internal\n pure\n returns (uint256 result)\n {\n result = indexOf(subject, search, 0);\n }\n\n /// @dev Returns the byte index of the first location of `search` in `subject`,\n /// searching from right to left, starting from `from`.\n /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.\n function lastIndexOf(string memory subject, string memory search, uint256 from)\n internal\n pure\n returns (uint256 result)\n {\n /// @solidity memory-safe-assembly\n assembly {\n for {} 1 {} {\n result := not(0) // Initialize to `NOT_FOUND`.\n let searchLength := mload(search)\n if gt(searchLength, mload(subject)) { break }\n let w := result\n\n let fromMax := sub(mload(subject), searchLength)\n if iszero(gt(fromMax, from)) { from := fromMax }\n\n let end := add(add(subject, 0x20), w)\n subject := add(add(subject, 0x20), from)\n if iszero(gt(subject, end)) { break }\n // As this function is not too often used,\n // we shall simply use keccak256 for smaller bytecode size.\n for { let h := keccak256(add(search, 0x20), searchLength) } 1 {} {\n if eq(keccak256(subject, searchLength), h) {\n result := sub(subject, add(end, 1))\n break\n }\n subject := add(subject, w) // `sub(subject, 1)`.\n if iszero(gt(subject, end)) { break }\n }\n break\n }\n }\n }\n\n /// @dev Returns the byte index of the first location of `search` in `subject`,\n /// searching from right to left.\n /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.\n function lastIndexOf(string memory subject, string memory search)\n internal\n pure\n returns (uint256 result)\n {\n result = lastIndexOf(subject, search, uint256(int256(-1)));\n }\n\n /// @dev Returns true if `search` is found in `subject`, false otherwise.\n function contains(string memory subject, string memory search) internal pure returns (bool) {\n return indexOf(subject, search) != NOT_FOUND;\n }\n\n /// @dev Returns whether `subject` starts with `search`.\n function startsWith(string memory subject, string memory search)\n internal\n pure\n returns (bool result)\n {\n /// @solidity memory-safe-assembly\n assembly {\n let searchLength := mload(search)\n // Just using keccak256 directly is actually cheaper.\n // forgefmt: disable-next-item\n result := and(\n iszero(gt(searchLength, mload(subject))),\n eq(\n keccak256(add(subject, 0x20), searchLength),\n keccak256(add(search, 0x20), searchLength)\n )\n )\n }\n }\n\n /// @dev Returns whether `subject` ends with `search`.\n function endsWith(string memory subject, string memory search)\n internal\n pure\n returns (bool result)\n {\n /// @solidity memory-safe-assembly\n assembly {\n let searchLength := mload(search)\n let subjectLength := mload(subject)\n // Whether `search` is not longer than `subject`.\n let withinRange := iszero(gt(searchLength, subjectLength))\n // Just using keccak256 directly is actually cheaper.\n // forgefmt: disable-next-item\n result := and(\n withinRange,\n eq(\n keccak256(\n // `subject + 0x20 + max(subjectLength - searchLength, 0)`.\n add(add(subject, 0x20), mul(withinRange, sub(subjectLength, searchLength))),\n searchLength\n ),\n keccak256(add(search, 0x20), searchLength)\n )\n )\n }\n }\n\n /// @dev Returns `subject` repeated `times`.\n function repeat(string memory subject, uint256 times)\n internal\n pure\n returns (string memory result)\n {\n /// @solidity memory-safe-assembly\n assembly {\n let subjectLength := mload(subject)\n if iszero(or(iszero(times), iszero(subjectLength))) {\n subject := add(subject, 0x20)\n result := mload(0x40)\n let output := add(result, 0x20)\n for {} 1 {} {\n // Copy the `subject` one word at a time.\n for { let o := 0 } 1 {} {\n mstore(add(output, o), mload(add(subject, o)))\n o := add(o, 0x20)\n if iszero(lt(o, subjectLength)) { break }\n }\n output := add(output, subjectLength)\n times := sub(times, 1)\n if iszero(times) { break }\n }\n mstore(output, 0) // Zeroize the slot after the string.\n let resultLength := sub(output, add(result, 0x20))\n mstore(result, resultLength) // Store the length.\n // Allocate the memory.\n mstore(0x40, add(result, add(resultLength, 0x20)))\n }\n }\n }\n\n /// @dev Returns a copy of `subject` sliced from `start` to `end` (exclusive).\n /// `start` and `end` are byte offsets.\n function slice(string memory subject, uint256 start, uint256 end)\n internal\n pure\n returns (string memory result)\n {\n /// @solidity memory-safe-assembly\n assembly {\n let subjectLength := mload(subject)\n if iszero(gt(subjectLength, end)) { end := subjectLength }\n if iszero(gt(subjectLength, start)) { start := subjectLength }\n if lt(start, end) {\n result := mload(0x40)\n let resultLength := sub(end, start)\n mstore(result, resultLength)\n subject := add(subject, start)\n let w := not(0x1f)\n // Copy the `subject` one word at a time, backwards.\n for { let o := and(add(resultLength, 0x1f), w) } 1 {} {\n mstore(add(result, o), mload(add(subject, o)))\n o := add(o, w) // `sub(o, 0x20)`.\n if iszero(o) { break }\n }\n // Zeroize the slot after the string.\n mstore(add(add(result, 0x20), resultLength), 0)\n // Allocate memory for the length and the bytes,\n // rounded up to a multiple of 32.\n mstore(0x40, add(result, and(add(resultLength, 0x3f), w)))\n }\n }\n }\n\n /// @dev Returns a copy of `subject` sliced from `start` to the end of the string.\n /// `start` is a byte offset.\n function slice(string memory subject, uint256 start)\n internal\n pure\n returns (string memory result)\n {\n result = slice(subject, start, uint256(int256(-1)));\n }\n\n /// @dev Returns all the indices of `search` in `subject`.\n /// The indices are byte offsets.\n function indicesOf(string memory subject, string memory search)\n internal\n pure\n returns (uint256[] memory result)\n {\n /// @solidity memory-safe-assembly\n assembly {\n let subjectLength := mload(subject)\n let searchLength := mload(search)\n\n if iszero(gt(searchLength, subjectLength)) {\n subject := add(subject, 0x20)\n search := add(search, 0x20)\n result := add(mload(0x40), 0x20)\n\n let subjectStart := subject\n let subjectSearchEnd := add(sub(add(subject, subjectLength), searchLength), 1)\n let h := 0\n if iszero(lt(searchLength, 0x20)) { h := keccak256(search, searchLength) }\n let m := shl(3, sub(0x20, and(searchLength, 0x1f)))\n let s := mload(search)\n for {} 1 {} {\n let t := mload(subject)\n // Whether the first `searchLength % 32` bytes of\n // `subject` and `search` matches.\n if iszero(shr(m, xor(t, s))) {\n if h {\n if iszero(eq(keccak256(subject, searchLength), h)) {\n subject := add(subject, 1)\n if iszero(lt(subject, subjectSearchEnd)) { break }\n continue\n }\n }\n // Append to `result`.\n mstore(result, sub(subject, subjectStart))\n result := add(result, 0x20)\n // Advance `subject` by `searchLength`.\n subject := add(subject, searchLength)\n if searchLength {\n if iszero(lt(subject, subjectSearchEnd)) { break }\n continue\n }\n }\n subject := add(subject, 1)\n if iszero(lt(subject, subjectSearchEnd)) { break }\n }\n let resultEnd := result\n // Assign `result` to the free memory pointer.\n result := mload(0x40)\n // Store the length of `result`.\n mstore(result, shr(5, sub(resultEnd, add(result, 0x20))))\n // Allocate memory for result.\n // We allocate one more word, so this array can be recycled for {split}.\n mstore(0x40, add(resultEnd, 0x20))\n }\n }\n }\n\n /// @dev Returns a arrays of strings based on the `delimiter` inside of the `subject` string.\n function split(string memory subject, string memory delimiter)\n internal\n pure\n returns (string[] memory result)\n {\n uint256[] memory indices = indicesOf(subject, delimiter);\n /// @solidity memory-safe-assembly\n assembly {\n let w := not(0x1f)\n let indexPtr := add(indices, 0x20)\n let indicesEnd := add(indexPtr, shl(5, add(mload(indices), 1)))\n mstore(add(indicesEnd, w), mload(subject))\n mstore(indices, add(mload(indices), 1))\n let prevIndex := 0\n for {} 1 {} {\n let index := mload(indexPtr)\n mstore(indexPtr, 0x60)\n if iszero(eq(index, prevIndex)) {\n let element := mload(0x40)\n let elementLength := sub(index, prevIndex)\n mstore(element, elementLength)\n // Copy the `subject` one word at a time, backwards.\n for { let o := and(add(elementLength, 0x1f), w) } 1 {} {\n mstore(add(element, o), mload(add(add(subject, prevIndex), o)))\n o := add(o, w) // `sub(o, 0x20)`.\n if iszero(o) { break }\n }\n // Zeroize the slot after the string.\n mstore(add(add(element, 0x20), elementLength), 0)\n // Allocate memory for the length and the bytes,\n // rounded up to a multiple of 32.\n mstore(0x40, add(element, and(add(elementLength, 0x3f), w)))\n // Store the `element` into the array.\n mstore(indexPtr, element)\n }\n prevIndex := add(index, mload(delimiter))\n indexPtr := add(indexPtr, 0x20)\n if iszero(lt(indexPtr, indicesEnd)) { break }\n }\n result := indices\n if iszero(mload(delimiter)) {\n result := add(indices, 0x20)\n mstore(result, sub(mload(indices), 2))\n }\n }\n }\n\n /// @dev Returns a concatenated string of `a` and `b`.\n /// Cheaper than `string.concat()` and does not de-align the free memory pointer.\n function concat(string memory a, string memory b)\n internal\n pure\n returns (string memory result)\n {\n /// @solidity memory-safe-assembly\n assembly {\n let w := not(0x1f)\n result := mload(0x40)\n let aLength := mload(a)\n // Copy `a` one word at a time, backwards.\n for { let o := and(add(aLength, 0x20), w) } 1 {} {\n mstore(add(result, o), mload(add(a, o)))\n o := add(o, w) // `sub(o, 0x20)`.\n if iszero(o) { break }\n }\n let bLength := mload(b)\n let output := add(result, aLength)\n // Copy `b` one word at a time, backwards.\n for { let o := and(add(bLength, 0x20), w) } 1 {} {\n mstore(add(output, o), mload(add(b, o)))\n o := add(o, w) // `sub(o, 0x20)`.\n if iszero(o) { break }\n }\n let totalLength := add(aLength, bLength)\n let last := add(add(result, 0x20), totalLength)\n // Zeroize the slot after the string.\n mstore(last, 0)\n // Stores the length.\n mstore(result, totalLength)\n // Allocate memory for the length and the bytes,\n // rounded up to a multiple of 32.\n mstore(0x40, and(add(last, 0x1f), w))\n }\n }\n\n /// @dev Returns a copy of the string in either lowercase or UPPERCASE.\n /// WARNING! This function is only compatible with 7-bit ASCII strings.\n function toCase(string memory subject, bool toUpper)\n internal\n pure\n returns (string memory result)\n {\n /// @solidity memory-safe-assembly\n assembly {\n let length := mload(subject)\n if length {\n result := add(mload(0x40), 0x20)\n subject := add(subject, 1)\n let flags := shl(add(70, shl(5, toUpper)), 0x3ffffff)\n let w := not(0)\n for { let o := length } 1 {} {\n o := add(o, w)\n let b := and(0xff, mload(add(subject, o)))\n mstore8(add(result, o), xor(b, and(shr(b, flags), 0x20)))\n if iszero(o) { break }\n }\n result := mload(0x40)\n mstore(result, length) // Store the length.\n let last := add(add(result, 0x20), length)\n mstore(last, 0) // Zeroize the slot after the string.\n mstore(0x40, add(last, 0x20)) // Allocate the memory.\n }\n }\n }\n\n /// @dev Returns a string from a small bytes32 string.\n /// `s` must be null-terminated, or behavior will be undefined.\n function fromSmallString(bytes32 s) internal pure returns (string memory result) {\n /// @solidity memory-safe-assembly\n assembly {\n result := mload(0x40)\n let n := 0\n for {} byte(n, s) { n := add(n, 1) } {} // Scan for '\\0'.\n mstore(result, n)\n let o := add(result, 0x20)\n mstore(o, s)\n mstore(add(o, n), 0)\n mstore(0x40, add(result, 0x40))\n }\n }\n\n /// @dev Returns the small string, with all bytes after the first null byte zeroized.\n function normalizeSmallString(bytes32 s) internal pure returns (bytes32 result) {\n /// @solidity memory-safe-assembly\n assembly {\n for {} byte(result, s) { result := add(result, 1) } {} // Scan for '\\0'.\n mstore(0x00, s)\n mstore(result, 0x00)\n result := mload(0x00)\n }\n }\n\n /// @dev Returns the string as a normalized null-terminated small string.\n function toSmallString(string memory s) internal pure returns (bytes32 result) {\n /// @solidity memory-safe-assembly\n assembly {\n result := mload(s)\n if iszero(lt(result, 33)) {\n mstore(0x00, 0xec92f9a3) // `TooBigForSmallString()`.\n revert(0x1c, 0x04)\n }\n result := shl(shl(3, sub(32, result)), mload(add(s, result)))\n }\n }\n\n /// @dev Returns a lowercased copy of the string.\n /// WARNING! This function is only compatible with 7-bit ASCII strings.\n function lower(string memory subject) internal pure returns (string memory result) {\n result = toCase(subject, false);\n }\n\n /// @dev Returns an UPPERCASED copy of the string.\n /// WARNING! This function is only compatible with 7-bit ASCII strings.\n function upper(string memory subject) internal pure returns (string memory result) {\n result = toCase(subject, true);\n }\n\n /// @dev Escapes the string to be used within HTML tags.\n function escapeHTML(string memory s) internal pure returns (string memory result) {\n /// @solidity memory-safe-assembly\n assembly {\n let end := add(s, mload(s))\n result := add(mload(0x40), 0x20)\n // Store the bytes of the packed offsets and strides into the scratch space.\n // `packed = (stride << 5) | offset`. Max offset is 20. Max stride is 6.\n mstore(0x1f, 0x900094)\n mstore(0x08, 0xc0000000a6ab)\n // Store \""&'<>\" into the scratch space.\n mstore(0x00, shl(64, 0x2671756f743b26616d703b262333393b266c743b2667743b))\n for {} iszero(eq(s, end)) {} {\n s := add(s, 1)\n let c := and(mload(s), 0xff)\n // Not in `[\"\\\"\",\"'\",\"&\",\"<\",\">\"]`.\n if iszero(and(shl(c, 1), 0x500000c400000000)) {\n mstore8(result, c)\n result := add(result, 1)\n continue\n }\n let t := shr(248, mload(c))\n mstore(result, mload(and(t, 0x1f)))\n result := add(result, shr(5, t))\n }\n let last := result\n mstore(last, 0) // Zeroize the slot after the string.\n result := mload(0x40)\n mstore(result, sub(last, add(result, 0x20))) // Store the length.\n mstore(0x40, add(last, 0x20)) // Allocate the memory.\n }\n }\n\n /// @dev Escapes the string to be used within double-quotes in a JSON.\n /// If `addDoubleQuotes` is true, the result will be enclosed in double-quotes.\n function escapeJSON(string memory s, bool addDoubleQuotes)\n internal\n pure\n returns (string memory result)\n {\n /// @solidity memory-safe-assembly\n assembly {\n let end := add(s, mload(s))\n result := add(mload(0x40), 0x20)\n if addDoubleQuotes {\n mstore8(result, 34)\n result := add(1, result)\n }\n // Store \"\\\\u0000\" in scratch space.\n // Store \"0123456789abcdef\" in scratch space.\n // Also, store `{0x08:\"b\", 0x09:\"t\", 0x0a:\"n\", 0x0c:\"f\", 0x0d:\"r\"}`.\n // into the scratch space.\n mstore(0x15, 0x5c75303030303031323334353637383961626364656662746e006672)\n // Bitmask for detecting `[\"\\\"\",\"\\\\\"]`.\n let e := or(shl(0x22, 1), shl(0x5c, 1))\n for {} iszero(eq(s, end)) {} {\n s := add(s, 1)\n let c := and(mload(s), 0xff)\n if iszero(lt(c, 0x20)) {\n if iszero(and(shl(c, 1), e)) {\n // Not in `[\"\\\"\",\"\\\\\"]`.\n mstore8(result, c)\n result := add(result, 1)\n continue\n }\n mstore8(result, 0x5c) // \"\\\\\".\n mstore8(add(result, 1), c)\n result := add(result, 2)\n continue\n }\n if iszero(and(shl(c, 1), 0x3700)) {\n // Not in `[\"\\b\",\"\\t\",\"\\n\",\"\\f\",\"\\d\"]`.\n mstore8(0x1d, mload(shr(4, c))) // Hex value.\n mstore8(0x1e, mload(and(c, 15))) // Hex value.\n mstore(result, mload(0x19)) // \"\\\\u00XX\".\n result := add(result, 6)\n continue\n }\n mstore8(result, 0x5c) // \"\\\\\".\n mstore8(add(result, 1), mload(add(c, 8)))\n result := add(result, 2)\n }\n if addDoubleQuotes {\n mstore8(result, 34)\n result := add(1, result)\n }\n let last := result\n mstore(last, 0) // Zeroize the slot after the string.\n result := mload(0x40)\n mstore(result, sub(last, add(result, 0x20))) // Store the length.\n mstore(0x40, add(last, 0x20)) // Allocate the memory.\n }\n }\n\n /// @dev Escapes the string to be used within double-quotes in a JSON.\n function escapeJSON(string memory s) internal pure returns (string memory result) {\n result = escapeJSON(s, false);\n }\n\n /// @dev Returns whether `a` equals `b`.\n function eq(string memory a, string memory b) internal pure returns (bool result) {\n /// @solidity memory-safe-assembly\n assembly {\n result := eq(keccak256(add(a, 0x20), mload(a)), keccak256(add(b, 0x20), mload(b)))\n }\n }\n\n /// @dev Returns whether `a` equals `b`, where `b` is a null-terminated small string.\n function eqs(string memory a, bytes32 b) internal pure returns (bool result) {\n /// @solidity memory-safe-assembly\n assembly {\n // These should be evaluated on compile time, as far as possible.\n let m := not(shl(7, div(not(iszero(b)), 255))) // `0x7f7f ...`.\n let x := not(or(m, or(b, add(m, and(b, m)))))\n let r := shl(7, iszero(iszero(shr(128, x))))\n r := or(r, shl(6, iszero(iszero(shr(64, shr(r, x))))))\n r := or(r, shl(5, lt(0xffffffff, shr(r, x))))\n r := or(r, shl(4, lt(0xffff, shr(r, x))))\n r := or(r, shl(3, lt(0xff, shr(r, x))))\n // forgefmt: disable-next-item\n result := gt(eq(mload(a), add(iszero(x), xor(31, shr(3, r)))),\n xor(shr(add(8, r), b), shr(add(8, r), mload(add(a, 0x20)))))\n }\n }\n\n /// @dev Packs a single string with its length into a single word.\n /// Returns `bytes32(0)` if the length is zero or greater than 31.\n function packOne(string memory a) internal pure returns (bytes32 result) {\n /// @solidity memory-safe-assembly\n assembly {\n // We don't need to zero right pad the string,\n // since this is our own custom non-standard packing scheme.\n result :=\n mul(\n // Load the length and the bytes.\n mload(add(a, 0x1f)),\n // `length != 0 && length < 32`. Abuses underflow.\n // Assumes that the length is valid and within the block gas limit.\n lt(sub(mload(a), 1), 0x1f)\n )\n }\n }\n\n /// @dev Unpacks a string packed using {packOne}.\n /// Returns the empty string if `packed` is `bytes32(0)`.\n /// If `packed` is not an output of {packOne}, the output behavior is undefined.\n function unpackOne(bytes32 packed) internal pure returns (string memory result) {\n /// @solidity memory-safe-assembly\n assembly {\n // Grab the free memory pointer.\n result := mload(0x40)\n // Allocate 2 words (1 for the length, 1 for the bytes).\n mstore(0x40, add(result, 0x40))\n // Zeroize the length slot.\n mstore(result, 0)\n // Store the length and bytes.\n mstore(add(result, 0x1f), packed)\n // Right pad with zeroes.\n mstore(add(add(result, 0x20), mload(result)), 0)\n }\n }\n\n /// @dev Packs two strings with their lengths into a single word.\n /// Returns `bytes32(0)` if combined length is zero or greater than 30.\n function packTwo(string memory a, string memory b) internal pure returns (bytes32 result) {\n /// @solidity memory-safe-assembly\n assembly {\n let aLength := mload(a)\n // We don't need to zero right pad the strings,\n // since this is our own custom non-standard packing scheme.\n result :=\n mul(\n // Load the length and the bytes of `a` and `b`.\n or(\n shl(shl(3, sub(0x1f, aLength)), mload(add(a, aLength))),\n mload(sub(add(b, 0x1e), aLength))\n ),\n // `totalLength != 0 && totalLength < 31`. Abuses underflow.\n // Assumes that the lengths are valid and within the block gas limit.\n lt(sub(add(aLength, mload(b)), 1), 0x1e)\n )\n }\n }\n\n /// @dev Unpacks strings packed using {packTwo}.\n /// Returns the empty strings if `packed` is `bytes32(0)`.\n /// If `packed` is not an output of {packTwo}, the output behavior is undefined.\n function unpackTwo(bytes32 packed)\n internal\n pure\n returns (string memory resultA, string memory resultB)\n {\n /// @solidity memory-safe-assembly\n assembly {\n // Grab the free memory pointer.\n resultA := mload(0x40)\n resultB := add(resultA, 0x40)\n // Allocate 2 words for each string (1 for the length, 1 for the byte). Total 4 words.\n mstore(0x40, add(resultB, 0x40))\n // Zeroize the length slots.\n mstore(resultA, 0)\n mstore(resultB, 0)\n // Store the lengths and bytes.\n mstore(add(resultA, 0x1f), packed)\n mstore(add(resultB, 0x1f), mload(add(add(resultA, 0x20), mload(resultA))))\n // Right pad with zeroes.\n mstore(add(add(resultA, 0x20), mload(resultA)), 0)\n mstore(add(add(resultB, 0x20), mload(resultB)), 0)\n }\n }\n\n /// @dev Directly returns `a` without copying.\n function directReturn(string memory a) internal pure {\n assembly {\n // Assumes that the string does not start from the scratch space.\n let retStart := sub(a, 0x20)\n let retSize := add(mload(a), 0x40)\n // Right pad with zeroes. Just in case the string is produced\n // by a method that doesn't zero right pad.\n mstore(add(retStart, retSize), 0)\n // Store the return offset.\n mstore(retStart, 0x20)\n // End the transaction, returning the string.\n return(retStart, retSize)\n }\n }\n}\n"},"src/utils/Asn1Decode.sol":{"content":"// SPDX-License-Identifier: MIT\n// Original source: https://github.com/JonahGroendal/asn1-decode\npragma solidity ^0.8.0;\n\n// Inspired by PufferFinance/rave - Apache-2.0 license\n// https://github.com/JonahGroendal/asn1-decode/blob/5c2d1469fc678513753786acb441e597969192ec/contracts/Asn1Decode.sol\n\nimport \"./BytesUtils.sol\";\n\nlibrary NodePtr {\n // Unpack first byte index\n function ixs(uint256 self) internal pure returns (uint256) {\n return uint80(self);\n }\n // Unpack first content byte index\n\n function ixf(uint256 self) internal pure returns (uint256) {\n return uint80(self >> 80);\n }\n // Unpack last content byte index\n\n function ixl(uint256 self) internal pure returns (uint256) {\n return uint80(self >> 160);\n }\n // Pack 3 uint80s into a uint256\n\n function getPtr(uint256 _ixs, uint256 _ixf, uint256 _ixl) internal pure returns (uint256) {\n _ixs |= _ixf << 80;\n _ixs |= _ixl << 160;\n return _ixs;\n }\n}\n\nlibrary Asn1Decode {\n using NodePtr for uint256;\n using BytesUtils for bytes;\n\n /*\n * @dev Get the root node. First step in traversing an ASN1 structure\n * @param der The DER-encoded ASN1 structure\n * @return A pointer to the outermost node\n */\n function root(bytes memory der) internal pure returns (uint256) {\n return readNodeLength(der, 0);\n }\n\n /*\n * @dev Get the root node of an ASN1 structure that's within a bit string value\n * @param der The DER-encoded ASN1 structure\n * @return A pointer to the outermost node\n */\n function rootOfBitStringAt(bytes memory der, uint256 ptr) internal pure returns (uint256) {\n require(der[ptr.ixs()] == 0x03, \"Not type BIT STRING\");\n return readNodeLength(der, ptr.ixf() + 1);\n }\n\n /*\n * @dev Get the root node of an ASN1 structure that's within an octet string value\n * @param der The DER-encoded ASN1 structure\n * @return A pointer to the outermost node\n */\n function rootOfOctetStringAt(bytes memory der, uint256 ptr) internal pure returns (uint256) {\n require(der[ptr.ixs()] == 0x04, \"Not type OCTET STRING\");\n return readNodeLength(der, ptr.ixf());\n }\n\n /*\n * @dev Get the next sibling node\n * @param der The DER-encoded ASN1 structure\n * @param ptr Points to the indices of the current node\n * @return A pointer to the next sibling node\n */\n function nextSiblingOf(bytes memory der, uint256 ptr) internal pure returns (uint256) {\n return readNodeLength(der, ptr.ixl() + 1);\n }\n\n /*\n * @dev Get the first child node of the current node\n * @param der The DER-encoded ASN1 structure\n * @param ptr Points to the indices of the current node\n * @return A pointer to the first child node\n */\n function firstChildOf(bytes memory der, uint256 ptr) internal pure returns (uint256) {\n require(der[ptr.ixs()] & 0x20 == 0x20, \"Not a constructed type\");\n return readNodeLength(der, ptr.ixf());\n }\n\n /*\n * @dev Use for looping through children of a node (either i or j).\n * @param i Pointer to an ASN1 node\n * @param j Pointer to another ASN1 node of the same ASN1 structure\n * @return True iff j is child of i or i is child of j.\n */\n function isChildOf(uint256 i, uint256 j) internal pure returns (bool) {\n return (((i.ixf() <= j.ixs()) && (j.ixl() <= i.ixl())) || ((j.ixf() <= i.ixs()) && (i.ixl() <= j.ixl())));\n }\n\n /*\n * @dev Extract value of node from DER-encoded structure\n * @param der The der-encoded ASN1 structure\n * @param ptr Points to the indices of the current node\n * @return Value bytes of node\n */\n function bytesAt(bytes memory der, uint256 ptr) internal pure returns (bytes memory) {\n return der.substring(ptr.ixf(), ptr.ixl() + 1 - ptr.ixf());\n }\n\n /*\n * @dev Extract entire node from DER-encoded structure\n * @param der The DER-encoded ASN1 structure\n * @param ptr Points to the indices of the current node\n * @return All bytes of node\n */\n function allBytesAt(bytes memory der, uint256 ptr) internal pure returns (bytes memory) {\n return der.substring(ptr.ixs(), ptr.ixl() + 1 - ptr.ixs());\n }\n\n /*\n * @dev Extract value of node from DER-encoded structure\n * @param der The DER-encoded ASN1 structure\n * @param ptr Points to the indices of the current node\n * @return Value bytes of node as bytes32\n */\n function bytes32At(bytes memory der, uint256 ptr) internal pure returns (bytes32) {\n return der.readBytesN(ptr.ixf(), ptr.ixl() + 1 - ptr.ixf());\n }\n\n /*\n * @dev Extract value of node from DER-encoded structure\n * @param der The der-encoded ASN1 structure\n * @param ptr Points to the indices of the current node\n * @return Uint value of node\n */\n function uintAt(bytes memory der, uint256 ptr) internal pure returns (uint256) {\n require(der[ptr.ixs()] == 0x02, \"Not type INTEGER\");\n require(der[ptr.ixf()] & 0x80 == 0, \"Not positive\");\n uint256 len = ptr.ixl() + 1 - ptr.ixf();\n return uint256(der.readBytesN(ptr.ixf(), len) >> (32 - len) * 8);\n }\n\n /*\n * @dev Extract value of a positive integer node from DER-encoded structure\n * @param der The DER-encoded ASN1 structure\n * @param ptr Points to the indices of the current node\n * @return Value bytes of a positive integer node\n */\n function uintBytesAt(bytes memory der, uint256 ptr) internal pure returns (bytes memory) {\n require(der[ptr.ixs()] == 0x02, \"Not type INTEGER\");\n require(der[ptr.ixf()] & 0x80 == 0, \"Not positive\");\n uint256 valueLength = ptr.ixl() + 1 - ptr.ixf();\n if (der[ptr.ixf()] == 0) {\n return der.substring(ptr.ixf() + 1, valueLength - 1);\n } else {\n return der.substring(ptr.ixf(), valueLength);\n }\n }\n\n function keccakOfBytesAt(bytes memory der, uint256 ptr) internal pure returns (bytes32) {\n return der.keccak(ptr.ixf(), ptr.ixl() + 1 - ptr.ixf());\n }\n\n function keccakOfAllBytesAt(bytes memory der, uint256 ptr) internal pure returns (bytes32) {\n return der.keccak(ptr.ixs(), ptr.ixl() + 1 - ptr.ixs());\n }\n\n /*\n * @dev Extract value of bitstring node from DER-encoded structure\n * @param der The DER-encoded ASN1 structure\n * @param ptr Points to the indices of the current node\n * @return Value of bitstring converted to bytes\n */\n function bitstringAt(bytes memory der, uint256 ptr) internal pure returns (bytes memory) {\n require(der[ptr.ixs()] == 0x03, \"Not type BIT STRING\");\n // Only 00 padded bitstr can be converted to bytestr!\n require(der[ptr.ixf()] == 0x00);\n uint256 valueLength = ptr.ixl() + 1 - ptr.ixf();\n return der.substring(ptr.ixf() + 1, valueLength - 1);\n }\n\n function readNodeLength(bytes memory der, uint256 ix) private pure returns (uint256) {\n uint256 length;\n uint80 ixFirstContentByte;\n uint80 ixLastContentByte;\n if ((der[ix + 1] & 0x80) == 0) {\n length = uint8(der[ix + 1]);\n ixFirstContentByte = uint80(ix + 2);\n ixLastContentByte = uint80(ixFirstContentByte + length - 1);\n } else {\n uint8 lengthbytesLength = uint8(der[ix + 1] & 0x7F);\n if (lengthbytesLength == 1) {\n length = der.readUint8(ix + 2);\n } else if (lengthbytesLength == 2) {\n length = der.readUint16(ix + 2);\n } else {\n length = uint256(der.readBytesN(ix + 2, lengthbytesLength) >> (32 - lengthbytesLength) * 8);\n }\n ixFirstContentByte = uint80(ix + 2 + lengthbytesLength);\n ixLastContentByte = uint80(ixFirstContentByte + length - 1);\n }\n return NodePtr.getPtr(ix, ixFirstContentByte, ixLastContentByte);\n }\n}\n"},"src/utils/BytesUtils.sol":{"content":"// SPDX-License-Identifier: BSD 2-Clause License\npragma solidity ^0.8.0;\n\n// Inspired by ensdomains/dnssec-oracle - BSD-2-Clause license\n// https://github.com/ensdomains/dnssec-oracle/blob/master/contracts/BytesUtils.sol\n\nlibrary BytesUtils {\n /*\n * @dev Returns the keccak-256 hash of a byte range.\n * @param self The byte string to hash.\n * @param offset The position to start hashing at.\n * @param len The number of bytes to hash.\n * @return The hash of the byte range.\n */\n function keccak(bytes memory self, uint256 offset, uint256 len) internal pure returns (bytes32 ret) {\n require(offset + len <= self.length);\n assembly {\n ret := keccak256(add(add(self, 32), offset), len)\n }\n }\n\n /*\n * @dev Returns a positive number if `other` comes lexicographically after\n * `self`, a negative number if it comes before, or zero if the\n * contents of the two bytes are equal.\n * @param self The first bytes to compare.\n * @param other The second bytes to compare.\n * @return The result of the comparison.\n */\n function compare(bytes memory self, bytes memory other) internal pure returns (int256) {\n return compare(self, 0, self.length, other, 0, other.length);\n }\n\n /*\n * @dev Returns a positive number if `other` comes lexicographically after\n * `self`, a negative number if it comes before, or zero if the\n * contents of the two bytes are equal. Comparison is done per-rune,\n * on unicode codepoints.\n * @param self The first bytes to compare.\n * @param offset The offset of self.\n * @param len The length of self.\n * @param other The second bytes to compare.\n * @param otheroffset The offset of the other string.\n * @param otherlen The length of the other string.\n * @return The result of the comparison.\n */\n function compare(\n bytes memory self,\n uint256 offset,\n uint256 len,\n bytes memory other,\n uint256 otheroffset,\n uint256 otherlen\n ) internal pure returns (int256) {\n uint256 shortest = len;\n if (otherlen < len) {\n shortest = otherlen;\n }\n\n uint256 selfptr;\n uint256 otherptr;\n\n assembly {\n selfptr := add(self, add(offset, 32))\n otherptr := add(other, add(otheroffset, 32))\n }\n for (uint256 idx = 0; idx < shortest; idx += 32) {\n uint256 a;\n uint256 b;\n assembly {\n a := mload(selfptr)\n b := mload(otherptr)\n }\n if (a != b) {\n // Mask out irrelevant bytes and check again\n uint256 mask;\n if (shortest > 32) {\n mask = type(uint256).max; // aka 0xffffff....\n } else {\n mask = ~(2 ** (8 * (32 - shortest + idx)) - 1);\n }\n uint256 diff = (a & mask) - (b & mask);\n if (diff != 0) {\n return int256(diff);\n }\n }\n selfptr += 32;\n otherptr += 32;\n }\n\n return int256(len) - int256(otherlen);\n }\n\n /*\n * @dev Returns true if the two byte ranges are equal.\n * @param self The first byte range to compare.\n * @param offset The offset into the first byte range.\n * @param other The second byte range to compare.\n * @param otherOffset The offset into the second byte range.\n * @param len The number of bytes to compare\n * @return True if the byte ranges are equal, false otherwise.\n */\n function equals(bytes memory self, uint256 offset, bytes memory other, uint256 otherOffset, uint256 len)\n internal\n pure\n returns (bool)\n {\n return keccak(self, offset, len) == keccak(other, otherOffset, len);\n }\n\n /*\n * @dev Returns true if the two byte ranges are equal with offsets.\n * @param self The first byte range to compare.\n * @param offset The offset into the first byte range.\n * @param other The second byte range to compare.\n * @param otherOffset The offset into the second byte range.\n * @return True if the byte ranges are equal, false otherwise.\n */\n function equals(bytes memory self, uint256 offset, bytes memory other, uint256 otherOffset)\n internal\n pure\n returns (bool)\n {\n return keccak(self, offset, self.length - offset) == keccak(other, otherOffset, other.length - otherOffset);\n }\n\n /*\n * @dev Compares a range of 'self' to all of 'other' and returns True iff\n * they are equal.\n * @param self The first byte range to compare.\n * @param offset The offset into the first byte range.\n * @param other The second byte range to compare.\n * @return True if the byte ranges are equal, false otherwise.\n */\n function equals(bytes memory self, uint256 offset, bytes memory other) internal pure returns (bool) {\n return self.length >= offset + other.length && equals(self, offset, other, 0, other.length);\n }\n\n /*\n * @dev Returns true if the two byte ranges are equal.\n * @param self The first byte range to compare.\n * @param other The second byte range to compare.\n * @return True if the byte ranges are equal, false otherwise.\n */\n function equals(bytes memory self, bytes memory other) internal pure returns (bool) {\n return self.length == other.length && equals(self, 0, other, 0, self.length);\n }\n\n /*\n * @dev Returns the 8-bit number at the specified index of self.\n * @param self The byte string.\n * @param idx The index into the bytes\n * @return The specified 8 bits of the string, interpreted as an integer.\n */\n function readUint8(bytes memory self, uint256 idx) internal pure returns (uint8 ret) {\n return uint8(self[idx]);\n }\n\n /*\n * @dev Returns the 16-bit number at the specified index of self.\n * @param self The byte string.\n * @param idx The index into the bytes\n * @return The specified 16 bits of the string, interpreted as an integer.\n */\n function readUint16(bytes memory self, uint256 idx) internal pure returns (uint16 ret) {\n require(idx + 2 <= self.length);\n assembly {\n ret := and(mload(add(add(self, 2), idx)), 0xFFFF)\n }\n }\n\n /*\n * @dev Returns the 32-bit number at the specified index of self.\n * @param self The byte string.\n * @param idx The index into the bytes\n * @return The specified 32 bits of the string, interpreted as an integer.\n */\n function readUint32(bytes memory self, uint256 idx) internal pure returns (uint32 ret) {\n require(idx + 4 <= self.length);\n assembly {\n ret := and(mload(add(add(self, 4), idx)), 0xFFFFFFFF)\n }\n }\n\n /*\n * @dev Returns the 32 byte value at the specified index of self.\n * @param self The byte string.\n * @param idx The index into the bytes\n * @return The specified 32 bytes of the string.\n */\n function readBytes32(bytes memory self, uint256 idx) internal pure returns (bytes32 ret) {\n require(idx + 32 <= self.length);\n assembly {\n ret := mload(add(add(self, 32), idx))\n }\n }\n\n /*\n * @dev Returns the 32 byte value at the specified index of self.\n * @param self The byte string.\n * @param idx The index into the bytes\n * @return The specified 32 bytes of the string.\n */\n function readBytes20(bytes memory self, uint256 idx) internal pure returns (bytes20 ret) {\n require(idx + 20 <= self.length);\n assembly {\n ret :=\n and(mload(add(add(self, 32), idx)), 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000000)\n }\n }\n\n /*\n * @dev Returns the n byte value at the specified index of self.\n * @param self The byte string.\n * @param idx The index into the bytes.\n * @param len The number of bytes.\n * @return The specified 32 bytes of the string.\n */\n function readBytesN(bytes memory self, uint256 idx, uint256 len) internal pure returns (bytes32 ret) {\n require(len <= 32);\n require(idx + len <= self.length);\n assembly {\n let mask := not(sub(exp(256, sub(32, len)), 1))\n ret := and(mload(add(add(self, 32), idx)), mask)\n }\n }\n\n function memcpy(uint256 dest, uint256 src, uint256 len) private pure {\n // Copy word-length chunks while possible\n for (; len >= 32; len -= 32) {\n assembly {\n mstore(dest, mload(src))\n }\n dest += 32;\n src += 32;\n }\n\n // Copy remaining bytes\n uint256 mask;\n if (len == 0) {\n mask = type(uint256).max; // Set to maximum value of uint256\n } else {\n mask = 256 ** (32 - len) - 1;\n }\n\n assembly {\n let srcpart := and(mload(src), not(mask))\n let destpart := and(mload(dest), mask)\n mstore(dest, or(destpart, srcpart))\n }\n }\n\n /*\n * @dev Copies a substring into a new byte string.\n * @param self The byte string to copy from.\n * @param offset The offset to start copying at.\n * @param len The number of bytes to copy.\n */\n function substring(bytes memory self, uint256 offset, uint256 len) internal pure returns (bytes memory) {\n require(offset + len <= self.length);\n\n bytes memory ret = new bytes(len);\n uint256 dest;\n uint256 src;\n\n assembly {\n dest := add(ret, 32)\n src := add(add(self, 32), offset)\n }\n memcpy(dest, src, len);\n\n return ret;\n }\n\n // Maps characters from 0x30 to 0x7A to their base32 values.\n // 0xFF represents invalid characters in that range.\n bytes constant base32HexTable =\n hex\"00010203040506070809FFFFFFFFFFFFFF0A0B0C0D0E0F101112131415161718191A1B1C1D1E1FFFFFFFFFFFFFFFFFFFFF0A0B0C0D0E0F101112131415161718191A1B1C1D1E1F\";\n\n /**\n * @dev Decodes unpadded base32 data of up to one word in length.\n * @param self The data to decode.\n * @param off Offset into the string to start at.\n * @param len Number of characters to decode.\n * @return The decoded data, left aligned.\n */\n function base32HexDecodeWord(bytes memory self, uint256 off, uint256 len) internal pure returns (bytes32) {\n require(len <= 52);\n\n uint256 ret = 0;\n uint8 decoded;\n for (uint256 i = 0; i < len; i++) {\n bytes1 char = self[off + i];\n require(char >= 0x30 && char <= 0x7A);\n decoded = uint8(base32HexTable[uint256(uint8(char)) - 0x30]);\n require(decoded <= 0x20);\n if (i == len - 1) {\n break;\n }\n ret = (ret << 5) | decoded;\n }\n\n uint256 bitlen = len * 5;\n if (len % 8 == 0) {\n // Multiple of 8 characters, no padding\n ret = (ret << 5) | decoded;\n } else if (len % 8 == 2) {\n // Two extra characters - 1 byte\n ret = (ret << 3) | (decoded >> 2);\n bitlen -= 2;\n } else if (len % 8 == 4) {\n // Four extra characters - 2 bytes\n ret = (ret << 1) | (decoded >> 4);\n bitlen -= 4;\n } else if (len % 8 == 5) {\n // Five extra characters - 3 bytes\n ret = (ret << 4) | (decoded >> 1);\n bitlen -= 1;\n } else if (len % 8 == 7) {\n // Seven extra characters - 4 bytes\n ret = (ret << 2) | (decoded >> 3);\n bitlen -= 3;\n } else {\n revert();\n }\n\n return bytes32(ret << (256 - bitlen));\n }\n\n function compareBytes(bytes memory a, bytes memory b) internal pure returns (bool) {\n if (a.length != b.length) {\n return false;\n }\n for (uint256 i = 0; i < a.length; i++) {\n if (a[i] != b[i]) {\n return false;\n }\n }\n return true;\n }\n}\n"},"src/utils/DateTimeUtils.sol":{"content":"// SPDX-License-Identifier: GPL-3.0\npragma solidity ^0.8.0;\n\nimport {DateTimeLib} from \"solady/utils/DateTimeLib.sol\";\nimport {LibString} from \"solady/utils/LibString.sol\";\n\nlibrary DateTimeUtils {\n using LibString for string;\n\n /*\n * @dev Convert a DER-encoded time to a unix timestamp\n * @param x509Time The DER-encoded time\n * @return The unix timestamp\n */\n function fromDERToTimestamp(bytes memory x509Time) internal pure returns (uint256) {\n uint16 yrs;\n uint8 mnths;\n uint8 dys;\n uint8 hrs;\n uint8 mins;\n uint8 secs;\n uint8 offset;\n\n if (x509Time.length == 13) {\n if (uint8(x509Time[0]) - 48 < 5) yrs += 2000;\n else yrs += 1900;\n } else {\n yrs += (uint8(x509Time[0]) - 48) * 1000 + (uint8(x509Time[1]) - 48) * 100;\n offset = 2;\n }\n yrs += (uint8(x509Time[offset + 0]) - 48) * 10 + uint8(x509Time[offset + 1]) - 48;\n mnths = (uint8(x509Time[offset + 2]) - 48) * 10 + uint8(x509Time[offset + 3]) - 48;\n dys += (uint8(x509Time[offset + 4]) - 48) * 10 + uint8(x509Time[offset + 5]) - 48;\n hrs += (uint8(x509Time[offset + 6]) - 48) * 10 + uint8(x509Time[offset + 7]) - 48;\n mins += (uint8(x509Time[offset + 8]) - 48) * 10 + uint8(x509Time[offset + 9]) - 48;\n secs += (uint8(x509Time[offset + 10]) - 48) * 10 + uint8(x509Time[offset + 11]) - 48;\n\n return DateTimeLib.dateTimeToTimestamp(yrs, mnths, dys, hrs, mins, secs);\n }\n\n /// @dev iso follows pattern: \"YYYY-MM-DDTHH:mm:ssZ\"\n function fromISOToTimestamp(string memory iso) internal pure returns (uint256) {\n require(bytes(iso).length == 20, \"invalid iso string length\");\n uint256 y = stringToUint(iso.slice(0, 4));\n uint256 m = stringToUint(iso.slice(5, 7));\n uint256 d = stringToUint(iso.slice(8, 10));\n uint256 h = stringToUint(iso.slice(11, 13));\n uint256 min = stringToUint(iso.slice(14, 16));\n uint256 s = stringToUint(iso.slice(17, 19));\n\n return DateTimeLib.dateTimeToTimestamp(y, m, d, h, min, s);\n }\n\n // https://ethereum.stackexchange.com/questions/10932/how-to-convert-string-to-int\n function stringToUint(string memory s) private pure returns (uint256 result) {\n bytes memory b = bytes(s);\n result = 0;\n for (uint256 i = 0; i < b.length; i++) {\n uint256 c = uint256(uint8(b[i]));\n if (c >= 48 && c <= 57) {\n result = result * 10 + (c - 48);\n }\n }\n }\n}\n"},"src/utils/P256Verifier.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport \"./BytesUtils.sol\";\n\n/**\n * @notice modified from https://github.com/daimo-eth/p256-verifier/\n */\nlibrary P256Verifier {\n using BytesUtils for bytes;\n\n address internal constant P256_VERIFIER = 0xc2b78104907F722DABAc4C69f826a522B2754De4;\n\n function ecdsaVerify(bytes32 messageHash, bytes memory signature, bytes memory key)\n internal\n view\n returns (bool verified)\n {\n bytes memory args = abi.encode(\n messageHash,\n uint256(bytes32(signature.substring(0, 32))),\n uint256(bytes32(signature.substring(32, 32))),\n uint256(bytes32(key.substring(0, 32))),\n uint256(bytes32(key.substring(32, 32)))\n );\n (bool success, bytes memory ret) = P256_VERIFIER.staticcall(args);\n assert(success); // never reverts, always returns 0 or 1\n\n verified = abi.decode(ret, (uint256)) == 1;\n }\n}\n"},"lib/solady/src/utils/DateTimeLib.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\n/// @notice Library for date time operations.\n/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/DateTimeLib.sol)\n///\n/// Conventions:\n/// --------------------------------------------------------------------+\n/// Unit | Range | Notes |\n/// --------------------------------------------------------------------|\n/// timestamp | 0..0x1e18549868c76ff | Unix timestamp. |\n/// epochDay | 0..0x16d3e098039 | Days since 1970-01-01. |\n/// year | 1970..0xffffffff | Gregorian calendar year. |\n/// month | 1..12 | Gregorian calendar month. |\n/// day | 1..31 | Gregorian calendar day of month. |\n/// weekday | 1..7 | The day of the week (1-indexed). |\n/// --------------------------------------------------------------------+\n/// All timestamps of days are rounded down to 00:00:00 UTC.\nlibrary DateTimeLib {\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* CONSTANTS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n // Weekdays are 1-indexed for a traditional rustic feel.\n\n // \"And on the seventh day God finished his work that he had done,\n // and he rested on the seventh day from all his work that he had done.\"\n // -- Genesis 2:2\n\n uint256 internal constant MON = 1;\n uint256 internal constant TUE = 2;\n uint256 internal constant WED = 3;\n uint256 internal constant THU = 4;\n uint256 internal constant FRI = 5;\n uint256 internal constant SAT = 6;\n uint256 internal constant SUN = 7;\n\n // Months and days of months are 1-indexed for ease of use.\n\n uint256 internal constant JAN = 1;\n uint256 internal constant FEB = 2;\n uint256 internal constant MAR = 3;\n uint256 internal constant APR = 4;\n uint256 internal constant MAY = 5;\n uint256 internal constant JUN = 6;\n uint256 internal constant JUL = 7;\n uint256 internal constant AUG = 8;\n uint256 internal constant SEP = 9;\n uint256 internal constant OCT = 10;\n uint256 internal constant NOV = 11;\n uint256 internal constant DEC = 12;\n\n // These limits are large enough for most practical purposes.\n // Inputs that exceed these limits result in undefined behavior.\n\n uint256 internal constant MAX_SUPPORTED_YEAR = 0xffffffff;\n uint256 internal constant MAX_SUPPORTED_EPOCH_DAY = 0x16d3e098039;\n uint256 internal constant MAX_SUPPORTED_TIMESTAMP = 0x1e18549868c76ff;\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* DATE TIME OPERATIONS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev Returns the number of days since 1970-01-01 from (`year`,`month`,`day`).\n /// See: https://howardhinnant.github.io/date_algorithms.html\n /// Note: Inputs outside the supported ranges result in undefined behavior.\n /// Use {isSupportedDate} to check if the inputs are supported.\n function dateToEpochDay(uint256 year, uint256 month, uint256 day)\n internal\n pure\n returns (uint256 epochDay)\n {\n /// @solidity memory-safe-assembly\n assembly {\n year := sub(year, lt(month, 3))\n let doy := add(shr(11, add(mul(62719, mod(add(month, 9), 12)), 769)), day)\n let yoe := mod(year, 400)\n let doe := sub(add(add(mul(yoe, 365), shr(2, yoe)), doy), div(yoe, 100))\n epochDay := sub(add(mul(div(year, 400), 146097), doe), 719469)\n }\n }\n\n /// @dev Returns (`year`,`month`,`day`) from the number of days since 1970-01-01.\n /// Note: Inputs outside the supported ranges result in undefined behavior.\n /// Use {isSupportedDays} to check if the inputs is supported.\n function epochDayToDate(uint256 epochDay)\n internal\n pure\n returns (uint256 year, uint256 month, uint256 day)\n {\n /// @solidity memory-safe-assembly\n assembly {\n epochDay := add(epochDay, 719468)\n let doe := mod(epochDay, 146097)\n let yoe :=\n div(sub(sub(add(doe, div(doe, 36524)), div(doe, 1460)), eq(doe, 146096)), 365)\n let doy := sub(doe, sub(add(mul(365, yoe), shr(2, yoe)), div(yoe, 100)))\n let mp := div(add(mul(5, doy), 2), 153)\n day := add(sub(doy, shr(11, add(mul(mp, 62719), 769))), 1)\n month := byte(mp, shl(160, 0x030405060708090a0b0c0102))\n year := add(add(yoe, mul(div(epochDay, 146097), 400)), lt(month, 3))\n }\n }\n\n /// @dev Returns the unix timestamp from (`year`,`month`,`day`).\n /// Note: Inputs outside the supported ranges result in undefined behavior.\n /// Use {isSupportedDate} to check if the inputs are supported.\n function dateToTimestamp(uint256 year, uint256 month, uint256 day)\n internal\n pure\n returns (uint256 result)\n {\n unchecked {\n result = dateToEpochDay(year, month, day) * 86400;\n }\n }\n\n /// @dev Returns (`year`,`month`,`day`) from the given unix timestamp.\n /// Note: Inputs outside the supported ranges result in undefined behavior.\n /// Use {isSupportedTimestamp} to check if the inputs are supported.\n function timestampToDate(uint256 timestamp)\n internal\n pure\n returns (uint256 year, uint256 month, uint256 day)\n {\n (year, month, day) = epochDayToDate(timestamp / 86400);\n }\n\n /// @dev Returns the unix timestamp from\n /// (`year`,`month`,`day`,`hour`,`minute`,`second`).\n /// Note: Inputs outside the supported ranges result in undefined behavior.\n /// Use {isSupportedDateTime} to check if the inputs are supported.\n function dateTimeToTimestamp(\n uint256 year,\n uint256 month,\n uint256 day,\n uint256 hour,\n uint256 minute,\n uint256 second\n ) internal pure returns (uint256 result) {\n unchecked {\n result = dateToEpochDay(year, month, day) * 86400 + hour * 3600 + minute * 60 + second;\n }\n }\n\n /// @dev Returns (`year`,`month`,`day`,`hour`,`minute`,`second`)\n /// from the given unix timestamp.\n /// Note: Inputs outside the supported ranges result in undefined behavior.\n /// Use {isSupportedTimestamp} to check if the inputs are supported.\n function timestampToDateTime(uint256 timestamp)\n internal\n pure\n returns (\n uint256 year,\n uint256 month,\n uint256 day,\n uint256 hour,\n uint256 minute,\n uint256 second\n )\n {\n unchecked {\n (year, month, day) = epochDayToDate(timestamp / 86400);\n uint256 secs = timestamp % 86400;\n hour = secs / 3600;\n secs = secs % 3600;\n minute = secs / 60;\n second = secs % 60;\n }\n }\n\n /// @dev Returns if the `year` is leap.\n function isLeapYear(uint256 year) internal pure returns (bool leap) {\n /// @solidity memory-safe-assembly\n assembly {\n leap := iszero(and(add(mul(iszero(mod(year, 25)), 12), 3), year))\n }\n }\n\n /// @dev Returns number of days in given `month` of `year`.\n function daysInMonth(uint256 year, uint256 month) internal pure returns (uint256 result) {\n bool flag = isLeapYear(year);\n /// @solidity memory-safe-assembly\n assembly {\n // `daysInMonths = [31,28,31,30,31,30,31,31,30,31,30,31]`.\n // `result = daysInMonths[month - 1] + isLeapYear(year)`.\n result :=\n add(byte(month, shl(152, 0x1F1C1F1E1F1E1F1F1E1F1E1F)), and(eq(month, 2), flag))\n }\n }\n\n /// @dev Returns the weekday from the unix timestamp.\n /// Monday: 1, Tuesday: 2, ....., Sunday: 7.\n function weekday(uint256 timestamp) internal pure returns (uint256 result) {\n unchecked {\n result = ((timestamp / 86400 + 3) % 7) + 1;\n }\n }\n\n /// @dev Returns if (`year`,`month`,`day`) is a supported date.\n /// - `1970 <= year <= MAX_SUPPORTED_YEAR`.\n /// - `1 <= month <= 12`.\n /// - `1 <= day <= daysInMonth(year, month)`.\n function isSupportedDate(uint256 year, uint256 month, uint256 day)\n internal\n pure\n returns (bool result)\n {\n uint256 md = daysInMonth(year, month);\n /// @solidity memory-safe-assembly\n assembly {\n let w := not(0)\n result :=\n and(\n lt(sub(year, 1970), sub(MAX_SUPPORTED_YEAR, 1969)),\n and(lt(add(month, w), 12), lt(add(day, w), md))\n )\n }\n }\n\n /// @dev Returns if (`year`,`month`,`day`,`hour`,`minute`,`second`) is a supported date time.\n /// - `1970 <= year <= MAX_SUPPORTED_YEAR`.\n /// - `1 <= month <= 12`.\n /// - `1 <= day <= daysInMonth(year, month)`.\n /// - `hour < 24`.\n /// - `minute < 60`.\n /// - `second < 60`.\n function isSupportedDateTime(\n uint256 year,\n uint256 month,\n uint256 day,\n uint256 hour,\n uint256 minute,\n uint256 second\n ) internal pure returns (bool result) {\n if (isSupportedDate(year, month, day)) {\n /// @solidity memory-safe-assembly\n assembly {\n result := and(lt(hour, 24), and(lt(minute, 60), lt(second, 60)))\n }\n }\n }\n\n /// @dev Returns if `epochDay` is a supported unix epoch day.\n function isSupportedEpochDay(uint256 epochDay) internal pure returns (bool result) {\n unchecked {\n result = epochDay < MAX_SUPPORTED_EPOCH_DAY + 1;\n }\n }\n\n /// @dev Returns if `timestamp` is a supported unix timestamp.\n function isSupportedTimestamp(uint256 timestamp) internal pure returns (bool result) {\n unchecked {\n result = timestamp < MAX_SUPPORTED_TIMESTAMP + 1;\n }\n }\n\n /// @dev Returns the unix timestamp of the given `n`th weekday `wd`, in `month` of `year`.\n /// Example: 3rd Friday of Feb 2022 is `nthWeekdayInMonthOfYearTimestamp(2022, 2, 3, 5)`\n /// Note: `n` is 1-indexed for traditional consistency.\n /// Invalid weekdays (i.e. `wd == 0 || wd > 7`) result in undefined behavior.\n function nthWeekdayInMonthOfYearTimestamp(uint256 year, uint256 month, uint256 n, uint256 wd)\n internal\n pure\n returns (uint256 result)\n {\n uint256 d = dateToEpochDay(year, month, 1);\n uint256 md = daysInMonth(year, month);\n /// @solidity memory-safe-assembly\n assembly {\n let diff := sub(wd, add(mod(add(d, 3), 7), 1))\n let date := add(mul(sub(n, 1), 7), add(mul(gt(diff, 6), 7), diff))\n result := mul(mul(86400, add(date, d)), and(lt(date, md), iszero(iszero(n))))\n }\n }\n\n /// @dev Returns the unix timestamp of the most recent Monday.\n function mondayTimestamp(uint256 timestamp) internal pure returns (uint256 result) {\n uint256 t = timestamp;\n /// @solidity memory-safe-assembly\n assembly {\n let day := div(t, 86400)\n result := mul(mul(sub(day, mod(add(day, 3), 7)), 86400), gt(t, 345599))\n }\n }\n\n /// @dev Returns whether the unix timestamp falls on a Saturday or Sunday.\n /// To check whether it is a week day, just take the negation of the result.\n function isWeekEnd(uint256 timestamp) internal pure returns (bool result) {\n result = weekday(timestamp) > FRI;\n }\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* DATE TIME ARITHMETIC OPERATIONS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev Adds `numYears` to the unix timestamp, and returns the result.\n /// Note: The result will share the same Gregorian calendar month,\n /// but different Gregorian calendar years for non-zero `numYears`.\n /// If the Gregorian calendar month of the result has less days\n /// than the Gregorian calendar month day of the `timestamp`,\n /// the result's month day will be the maximum possible value for the month.\n /// (e.g. from 29th Feb to 28th Feb)\n function addYears(uint256 timestamp, uint256 numYears) internal pure returns (uint256 result) {\n (uint256 year, uint256 month, uint256 day) = epochDayToDate(timestamp / 86400);\n result = _offsetted(year + numYears, month, day, timestamp);\n }\n\n /// @dev Adds `numMonths` to the unix timestamp, and returns the result.\n /// Note: If the Gregorian calendar month of the result has less days\n /// than the Gregorian calendar month day of the `timestamp`,\n /// the result's month day will be the maximum possible value for the month.\n /// (e.g. from 29th Feb to 28th Feb)\n function addMonths(uint256 timestamp, uint256 numMonths)\n internal\n pure\n returns (uint256 result)\n {\n (uint256 year, uint256 month, uint256 day) = epochDayToDate(timestamp / 86400);\n month = _sub(month + numMonths, 1);\n result = _offsetted(year + month / 12, _add(month % 12, 1), day, timestamp);\n }\n\n /// @dev Adds `numDays` to the unix timestamp, and returns the result.\n function addDays(uint256 timestamp, uint256 numDays) internal pure returns (uint256 result) {\n result = timestamp + numDays * 86400;\n }\n\n /// @dev Adds `numHours` to the unix timestamp, and returns the result.\n function addHours(uint256 timestamp, uint256 numHours) internal pure returns (uint256 result) {\n result = timestamp + numHours * 3600;\n }\n\n /// @dev Adds `numMinutes` to the unix timestamp, and returns the result.\n function addMinutes(uint256 timestamp, uint256 numMinutes)\n internal\n pure\n returns (uint256 result)\n {\n result = timestamp + numMinutes * 60;\n }\n\n /// @dev Adds `numSeconds` to the unix timestamp, and returns the result.\n function addSeconds(uint256 timestamp, uint256 numSeconds)\n internal\n pure\n returns (uint256 result)\n {\n result = timestamp + numSeconds;\n }\n\n /// @dev Subtracts `numYears` from the unix timestamp, and returns the result.\n /// Note: The result will share the same Gregorian calendar month,\n /// but different Gregorian calendar years for non-zero `numYears`.\n /// If the Gregorian calendar month of the result has less days\n /// than the Gregorian calendar month day of the `timestamp`,\n /// the result's month day will be the maximum possible value for the month.\n /// (e.g. from 29th Feb to 28th Feb)\n function subYears(uint256 timestamp, uint256 numYears) internal pure returns (uint256 result) {\n (uint256 year, uint256 month, uint256 day) = epochDayToDate(timestamp / 86400);\n result = _offsetted(year - numYears, month, day, timestamp);\n }\n\n /// @dev Subtracts `numYears` from the unix timestamp, and returns the result.\n /// Note: If the Gregorian calendar month of the result has less days\n /// than the Gregorian calendar month day of the `timestamp`,\n /// the result's month day will be the maximum possible value for the month.\n /// (e.g. from 29th Feb to 28th Feb)\n function subMonths(uint256 timestamp, uint256 numMonths)\n internal\n pure\n returns (uint256 result)\n {\n (uint256 year, uint256 month, uint256 day) = epochDayToDate(timestamp / 86400);\n uint256 yearMonth = _totalMonths(year, month) - _add(numMonths, 1);\n result = _offsetted(yearMonth / 12, _add(yearMonth % 12, 1), day, timestamp);\n }\n\n /// @dev Subtracts `numDays` from the unix timestamp, and returns the result.\n function subDays(uint256 timestamp, uint256 numDays) internal pure returns (uint256 result) {\n result = timestamp - numDays * 86400;\n }\n\n /// @dev Subtracts `numHours` from the unix timestamp, and returns the result.\n function subHours(uint256 timestamp, uint256 numHours) internal pure returns (uint256 result) {\n result = timestamp - numHours * 3600;\n }\n\n /// @dev Subtracts `numMinutes` from the unix timestamp, and returns the result.\n function subMinutes(uint256 timestamp, uint256 numMinutes)\n internal\n pure\n returns (uint256 result)\n {\n result = timestamp - numMinutes * 60;\n }\n\n /// @dev Subtracts `numSeconds` from the unix timestamp, and returns the result.\n function subSeconds(uint256 timestamp, uint256 numSeconds)\n internal\n pure\n returns (uint256 result)\n {\n result = timestamp - numSeconds;\n }\n\n /// @dev Returns the difference in Gregorian calendar years\n /// between `fromTimestamp` and `toTimestamp`.\n /// Note: Even if the true time difference is less than a year,\n /// the difference can be non-zero is the timestamps are\n /// from different Gregorian calendar years\n function diffYears(uint256 fromTimestamp, uint256 toTimestamp)\n internal\n pure\n returns (uint256 result)\n {\n toTimestamp - fromTimestamp;\n (uint256 fromYear,,) = epochDayToDate(fromTimestamp / 86400);\n (uint256 toYear,,) = epochDayToDate(toTimestamp / 86400);\n result = _sub(toYear, fromYear);\n }\n\n /// @dev Returns the difference in Gregorian calendar months\n /// between `fromTimestamp` and `toTimestamp`.\n /// Note: Even if the true time difference is less than a month,\n /// the difference can be non-zero is the timestamps are\n /// from different Gregorian calendar months.\n function diffMonths(uint256 fromTimestamp, uint256 toTimestamp)\n internal\n pure\n returns (uint256 result)\n {\n toTimestamp - fromTimestamp;\n (uint256 fromYear, uint256 fromMonth,) = epochDayToDate(fromTimestamp / 86400);\n (uint256 toYear, uint256 toMonth,) = epochDayToDate(toTimestamp / 86400);\n result = _sub(_totalMonths(toYear, toMonth), _totalMonths(fromYear, fromMonth));\n }\n\n /// @dev Returns the difference in days between `fromTimestamp` and `toTimestamp`.\n function diffDays(uint256 fromTimestamp, uint256 toTimestamp)\n internal\n pure\n returns (uint256 result)\n {\n result = (toTimestamp - fromTimestamp) / 86400;\n }\n\n /// @dev Returns the difference in hours between `fromTimestamp` and `toTimestamp`.\n function diffHours(uint256 fromTimestamp, uint256 toTimestamp)\n internal\n pure\n returns (uint256 result)\n {\n result = (toTimestamp - fromTimestamp) / 3600;\n }\n\n /// @dev Returns the difference in minutes between `fromTimestamp` and `toTimestamp`.\n function diffMinutes(uint256 fromTimestamp, uint256 toTimestamp)\n internal\n pure\n returns (uint256 result)\n {\n result = (toTimestamp - fromTimestamp) / 60;\n }\n\n /// @dev Returns the difference in seconds between `fromTimestamp` and `toTimestamp`.\n function diffSeconds(uint256 fromTimestamp, uint256 toTimestamp)\n internal\n pure\n returns (uint256 result)\n {\n result = toTimestamp - fromTimestamp;\n }\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* PRIVATE HELPERS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev Unchecked arithmetic for computing the total number of months.\n function _totalMonths(uint256 numYears, uint256 numMonths)\n private\n pure\n returns (uint256 total)\n {\n unchecked {\n total = numYears * 12 + numMonths;\n }\n }\n\n /// @dev Unchecked arithmetic for adding two numbers.\n function _add(uint256 a, uint256 b) private pure returns (uint256 c) {\n unchecked {\n c = a + b;\n }\n }\n\n /// @dev Unchecked arithmetic for subtracting two numbers.\n function _sub(uint256 a, uint256 b) private pure returns (uint256 c) {\n unchecked {\n c = a - b;\n }\n }\n\n /// @dev Returns the offsetted timestamp.\n function _offsetted(uint256 year, uint256 month, uint256 day, uint256 timestamp)\n private\n pure\n returns (uint256 result)\n {\n uint256 dm = daysInMonth(year, month);\n if (day >= dm) {\n day = dm;\n }\n result = dateToEpochDay(year, month, day) * 86400 + (timestamp % 86400);\n }\n}\n"}},"settings":{"remappings":["solady/=lib/solady/src/","@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/","ds-test/=lib/forge-std/lib/ds-test/src/","erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/","forge-std/=lib/forge-std/src/","halmos-cheatcodes/=lib/openzeppelin-contracts/lib/halmos-cheatcodes/src/","openzeppelin-contracts/=lib/openzeppelin-contracts/"],"optimizer":{"enabled":true,"runs":999999},"metadata":{"useLiteralContent":false,"bytecodeHash":"ipfs","appendCBOR":true},"outputSelection":{"*":{"*":["abi","evm.bytecode","evm.deployedBytecode","evm.methodIdentifiers","metadata"]}},"evmVersion":"paris","viaIR":true,"libraries":{}}} diff --git a/standard-json-input/qeiddao.json b/standard-json-input/qeiddao.json deleted file mode 100644 index c29cea7..0000000 --- a/standard-json-input/qeiddao.json +++ /dev/null @@ -1 +0,0 @@ -{"language":"Solidity","sources":{"src/automata_pccs/AutomataEnclaveIdentityDao.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport {AutomataDaoBase} from \"./shared/AutomataDaoBase.sol\";\nimport {EnclaveIdentityDao, AttestationRequest, PcsDao} from \"../bases/EnclaveIdentityDao.sol\";\n\nimport {Ownable} from \"solady/auth/Ownable.sol\";\n\ncontract AutomataEnclaveIdentityDao is Ownable, AutomataDaoBase, EnclaveIdentityDao {\n constructor(address _storage, address _pcs, address _enclaveIdentityHelper, address _x509Helper)\n EnclaveIdentityDao(_pcs, _enclaveIdentityHelper, _x509Helper)\n AutomataDaoBase(_storage)\n {\n _initializeOwner(msg.sender);\n }\n\n function setPcs(address _pcs) external onlyOwner {\n Pcs = PcsDao(_pcs);\n }\n\n function enclaveIdentitySchemaID() public pure override returns (bytes32) {\n // NOT-APPLICABLE FOR OUR USE CASE\n // but this is required by most attestation services, such as EAS, Verax etc\n return bytes32(0);\n }\n\n function _attestEnclaveIdentity(AttestationRequest memory req, bytes32 hash)\n internal\n override\n returns (bytes32 attestationId)\n {\n // delete the predecessor if replacing\n _deletePredecessor(req.data.refUID);\n _attestCollateral(hash, req.data.data);\n attestationId = hash;\n }\n}\n"},"src/automata_pccs/shared/AutomataDaoBase.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport {DaoBase} from \"../../bases/DaoBase.sol\";\nimport {CA} from \"../../Common.sol\";\n\ninterface IAutomataDaoStorage {\n function writeToPccs(bytes32 attId, bytes memory attData) external;\n\n function readPccs(bytes32 attId) external view returns (bytes memory attData);\n\n function deleteData(bytes32 attId) external;\n}\n\nabstract contract AutomataDaoBase is DaoBase {\n IAutomataDaoStorage pccsStorage;\n\n constructor(address _storage) {\n pccsStorage = IAutomataDaoStorage(_storage);\n }\n\n function getAttestedData(bytes32 attestationId) public view override returns (bytes memory attestationData) {\n attestationData = pccsStorage.readPccs(attestationId);\n }\n\n /// @dev we simply map the collateral hash to the data itself in our use case\n /// @dev however, this may not be the case when the dao integrates an attestation service, such as EAS\n /// @dev it is recommended to store the hash of the collateral as a separate attestation from the collateral\n /// to reduce the size of data read\n function getCollateralHash(bytes32 attestationId) public pure override returns (bytes32) {\n return attestationId;\n }\n\n function _attestCollateral(bytes32 collateralHash, bytes memory data) internal {\n pccsStorage.writeToPccs(collateralHash, data);\n }\n\n function _deletePredecessor(bytes32 predecessor) internal {\n if (getAttestedData(predecessor).length > 0) {\n pccsStorage.deleteData(predecessor);\n }\n }\n}\n"},"src/bases/EnclaveIdentityDao.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport {CA, AttestationRequestData, AttestationRequest} from \"../Common.sol\";\nimport {\n EnclaveIdentityHelper, EnclaveIdentityJsonObj, EnclaveId, IdentityObj\n} from \"../helpers/EnclaveIdentityHelper.sol\";\n\nimport {DaoBase} from \"./DaoBase.sol\";\nimport {SigVerifyBase} from \"./SigVerifyBase.sol\";\nimport {PcsDao} from \"./PcsDao.sol\";\n\n/**\n * @title Enclave Identity Data Access Object\n * @notice This contract is heavily inspired by Section 4.2.9 in the Intel SGX PCCS Design Guideline\n * https://download.01.org/intel-sgx/sgx-dcap/1.19/linux/docs/SGX_DCAP_Caching_Service_Design_Guide.pdf\n * @dev should extends this contract and use the provided read/write methods to interact with Enclave\n * Identity JSON data published on-chain.\n */\nabstract contract EnclaveIdentityDao is DaoBase, SigVerifyBase {\n PcsDao public Pcs;\n EnclaveIdentityHelper public EnclaveIdentityLib;\n\n /// @notice retrieves the attestationId of the attested EnclaveIdentity from the registry\n /// key: keccak256(id ++ version)\n /// NOTE: the \"version\" indicated here is taken from the input parameter (e.g. v3 vs v4);\n /// NOT the \"version\" value found in the Enclave Identity JSON\n ///\n /// @notice the schema of the attested data is the following:\n /// An ABI-encoded tuple of (EnclaveIdentityHelper.IdentityObj, string, bytes)\n /// see {{ EnclaveIdentityHelper.IdentityObj }} for struct definition\n /// - string qeidentityObj\n /// - bytes signature\n mapping(bytes32 => bytes32) public enclaveIdentityAttestations;\n\n error Enclave_Id_Mismatch();\n error Invalid_TCB_Cert_Signature();\n error Enclave_Id_Expired();\n\n constructor(address _pcs, address _enclaveIdentityHelper, address _x509Helper) SigVerifyBase(_x509Helper) {\n Pcs = PcsDao(_pcs);\n EnclaveIdentityLib = EnclaveIdentityHelper(_enclaveIdentityHelper);\n }\n\n /**\n * @notice Section 4.2.9 (getEnclaveIdentity)\n * @notice Gets the enclave identity.\n * @param id 0: QE; 1: QVE; 2: TD_QE\n * https://github.com/intel/SGXDataCenterAttestationPrimitives/blob/39989a42bbbb0c968153a47254b6de79a27eb603/QuoteVerification/QVL/Src/AttestationLibrary/src/Verifiers/EnclaveIdentityV2.h#L49-L52\n * @param version the input version parameter\n * @return enclaveIdObj See {EnclaveIdentityHelper.sol} to learn more about the structure definition\n */\n function getEnclaveIdentity(uint256 id, uint256 version)\n external\n view\n returns (EnclaveIdentityJsonObj memory enclaveIdObj)\n {\n bytes32 attestationId = _getAttestationId(id, version);\n if (attestationId != bytes32(0)) {\n bytes memory attestedIdentityData = getAttestedData(attestationId);\n (, enclaveIdObj.identityStr, enclaveIdObj.signature) =\n abi.decode(attestedIdentityData, (IdentityObj, string, bytes));\n }\n }\n\n /// @question is there a way we can validate the version input?\n /// TEMP: Currently, there is no way to quickly distinguish between QuoteV3 vs QuoteV4 Enclave Identity\n\n /**\n * @notice Section 4.2.9 (upsertEnclaveIdentity)\n * @dev Attestation Registry Entrypoint Contracts, such as Portals on Verax are responsible\n * @dev for performing ECDSA verification on the provided Enclave Identity\n * against the Signing CA key prior to attestations\n * @param id 0: QE; 1: QVE; 2: TD_QE\n * https://github.com/intel/SGXDataCenterAttestationPrimitives/blob/39989a42bbbb0c968153a47254b6de79a27eb603/QuoteVerification/QVL/Src/AttestationLibrary/src/Verifiers/EnclaveIdentityV2.h#L49-L52\n * @param version the input version parameter\n * @param enclaveIdentityObj See {EnclaveIdentityHelper.sol} to learn more about the structure definition\n */\n function upsertEnclaveIdentity(uint256 id, uint256 version, EnclaveIdentityJsonObj calldata enclaveIdentityObj)\n external\n returns (bytes32 attestationId)\n {\n _validateQeIdentity(enclaveIdentityObj);\n AttestationRequest memory req = _buildEnclaveIdentityAttestationRequest(id, version, enclaveIdentityObj);\n bytes32 hash = sha256(bytes(enclaveIdentityObj.identityStr));\n attestationId = _attestEnclaveIdentity(req, hash);\n enclaveIdentityAttestations[keccak256(abi.encodePacked(id, version))] = attestationId;\n }\n\n /**\n * @notice Fetches the Enclave Identity issuer chain\n * @return signingCert - DER encoded Intel TCB Signing Certificate\n * @return rootCert - DER encoded Intel SGX Root CA\n */\n function getEnclaveIdentityIssuerChain() public view returns (bytes memory signingCert, bytes memory rootCert) {\n bytes32 signingCertAttestationId = Pcs.pcsCertAttestations(CA.SIGNING);\n bytes32 rootCertAttestationId = Pcs.pcsCertAttestations(CA.ROOT);\n signingCert = getAttestedData(signingCertAttestationId);\n rootCert = getAttestedData(rootCertAttestationId);\n }\n\n /**\n * @dev overwrite this method to define the schemaID for the attestation of Enclave Identities\n */\n function enclaveIdentitySchemaID() public view virtual returns (bytes32 ENCLAVE_IDENTITY_SCHEMA_ID);\n\n /**\n * @dev implement logic to validate and attest the enclave identity\n * @param req structure as defined by EAS\n * https://github.com/ethereum-attestation-service/eas-contracts/blob/52af661748bde9b40ae782907702f885852bc149/contracts/IEAS.sol#L9C1-L23C2\n * @return attestationId\n */\n function _attestEnclaveIdentity(AttestationRequest memory req, bytes32 hash)\n internal\n virtual\n returns (bytes32 attestationId);\n\n /**\n * @notice computes the key that maps to the corresponding attestation ID\n */\n function _getAttestationId(uint256 id, uint256 version) private view returns (bytes32 attestationId) {\n attestationId = enclaveIdentityAttestations[keccak256(abi.encodePacked(id, version))];\n }\n\n /**\n * @notice builds an EAS compliant attestation request\n */\n function _buildEnclaveIdentityAttestationRequest(\n uint256 id,\n uint256 version,\n EnclaveIdentityJsonObj calldata enclaveIdentityObj\n ) private view returns (AttestationRequest memory req) {\n bytes32 predecessorAttestationId = _getAttestationId(id, version);\n IdentityObj memory identity = EnclaveIdentityLib.parseIdentityString(enclaveIdentityObj.identityStr);\n if (id != uint256(identity.id)) {\n revert Enclave_Id_Mismatch();\n }\n\n if (block.timestamp < identity.issueDateTimestamp || block.timestamp > identity.nextUpdateTimestamp) {\n revert Enclave_Id_Expired();\n }\n\n bytes memory attestationData =\n abi.encode(identity, enclaveIdentityObj.identityStr, enclaveIdentityObj.signature);\n AttestationRequestData memory reqData = AttestationRequestData({\n recipient: msg.sender,\n expirationTime: uint64(identity.nextUpdateTimestamp),\n revocable: true,\n refUID: predecessorAttestationId,\n data: attestationData,\n value: 0\n });\n req = AttestationRequest({schema: enclaveIdentitySchemaID(), data: reqData});\n }\n\n /**\n * @notice validates QEIdentity is signed by Intel TCB Signing Cert\n */\n function _validateQeIdentity(EnclaveIdentityJsonObj calldata enclaveIdentityObj) private view {\n // Get TCB Signing Cert\n bytes32 tcbSigningAttestationId = Pcs.pcsCertAttestations(CA.SIGNING);\n bytes memory signingDer = getAttestedData(tcbSigningAttestationId);\n\n // Validate signature\n bool sigVerified =\n verifySignature(sha256(bytes(enclaveIdentityObj.identityStr)), enclaveIdentityObj.signature, signingDer);\n\n if (!sigVerified) {\n revert Invalid_TCB_Cert_Signature();\n }\n }\n}\n"},"lib/solady/src/auth/Ownable.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\n/// @notice Simple single owner authorization mixin.\n/// @author Solady (https://github.com/vectorized/solady/blob/main/src/auth/Ownable.sol)\n///\n/// @dev Note:\n/// This implementation does NOT auto-initialize the owner to `msg.sender`.\n/// You MUST call the `_initializeOwner` in the constructor / initializer.\n///\n/// While the ownable portion follows\n/// [EIP-173](https://eips.ethereum.org/EIPS/eip-173) for compatibility,\n/// the nomenclature for the 2-step ownership handover may be unique to this codebase.\nabstract contract Ownable {\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* CUSTOM ERRORS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev The caller is not authorized to call the function.\n error Unauthorized();\n\n /// @dev The `newOwner` cannot be the zero address.\n error NewOwnerIsZeroAddress();\n\n /// @dev The `pendingOwner` does not have a valid handover request.\n error NoHandoverRequest();\n\n /// @dev Cannot double-initialize.\n error AlreadyInitialized();\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* EVENTS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev The ownership is transferred from `oldOwner` to `newOwner`.\n /// This event is intentionally kept the same as OpenZeppelin's Ownable to be\n /// compatible with indexers and [EIP-173](https://eips.ethereum.org/EIPS/eip-173),\n /// despite it not being as lightweight as a single argument event.\n event OwnershipTransferred(address indexed oldOwner, address indexed newOwner);\n\n /// @dev An ownership handover to `pendingOwner` has been requested.\n event OwnershipHandoverRequested(address indexed pendingOwner);\n\n /// @dev The ownership handover to `pendingOwner` has been canceled.\n event OwnershipHandoverCanceled(address indexed pendingOwner);\n\n /// @dev `keccak256(bytes(\"OwnershipTransferred(address,address)\"))`.\n uint256 private constant _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE =\n 0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0;\n\n /// @dev `keccak256(bytes(\"OwnershipHandoverRequested(address)\"))`.\n uint256 private constant _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE =\n 0xdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d;\n\n /// @dev `keccak256(bytes(\"OwnershipHandoverCanceled(address)\"))`.\n uint256 private constant _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE =\n 0xfa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92;\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* STORAGE */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev The owner slot is given by:\n /// `bytes32(~uint256(uint32(bytes4(keccak256(\"_OWNER_SLOT_NOT\")))))`.\n /// It is intentionally chosen to be a high value\n /// to avoid collision with lower slots.\n /// The choice of manual storage layout is to enable compatibility\n /// with both regular and upgradeable contracts.\n bytes32 internal constant _OWNER_SLOT =\n 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff74873927;\n\n /// The ownership handover slot of `newOwner` is given by:\n /// ```\n /// mstore(0x00, or(shl(96, user), _HANDOVER_SLOT_SEED))\n /// let handoverSlot := keccak256(0x00, 0x20)\n /// ```\n /// It stores the expiry timestamp of the two-step ownership handover.\n uint256 private constant _HANDOVER_SLOT_SEED = 0x389a75e1;\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* INTERNAL FUNCTIONS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev Override to return true to make `_initializeOwner` prevent double-initialization.\n function _guardInitializeOwner() internal pure virtual returns (bool guard) {}\n\n /// @dev Initializes the owner directly without authorization guard.\n /// This function must be called upon initialization,\n /// regardless of whether the contract is upgradeable or not.\n /// This is to enable generalization to both regular and upgradeable contracts,\n /// and to save gas in case the initial owner is not the caller.\n /// For performance reasons, this function will not check if there\n /// is an existing owner.\n function _initializeOwner(address newOwner) internal virtual {\n if (_guardInitializeOwner()) {\n /// @solidity memory-safe-assembly\n assembly {\n let ownerSlot := _OWNER_SLOT\n if sload(ownerSlot) {\n mstore(0x00, 0x0dc149f0) // `AlreadyInitialized()`.\n revert(0x1c, 0x04)\n }\n // Clean the upper 96 bits.\n newOwner := shr(96, shl(96, newOwner))\n // Store the new value.\n sstore(ownerSlot, or(newOwner, shl(255, iszero(newOwner))))\n // Emit the {OwnershipTransferred} event.\n log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner)\n }\n } else {\n /// @solidity memory-safe-assembly\n assembly {\n // Clean the upper 96 bits.\n newOwner := shr(96, shl(96, newOwner))\n // Store the new value.\n sstore(_OWNER_SLOT, newOwner)\n // Emit the {OwnershipTransferred} event.\n log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner)\n }\n }\n }\n\n /// @dev Sets the owner directly without authorization guard.\n function _setOwner(address newOwner) internal virtual {\n if (_guardInitializeOwner()) {\n /// @solidity memory-safe-assembly\n assembly {\n let ownerSlot := _OWNER_SLOT\n // Clean the upper 96 bits.\n newOwner := shr(96, shl(96, newOwner))\n // Emit the {OwnershipTransferred} event.\n log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner)\n // Store the new value.\n sstore(ownerSlot, or(newOwner, shl(255, iszero(newOwner))))\n }\n } else {\n /// @solidity memory-safe-assembly\n assembly {\n let ownerSlot := _OWNER_SLOT\n // Clean the upper 96 bits.\n newOwner := shr(96, shl(96, newOwner))\n // Emit the {OwnershipTransferred} event.\n log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner)\n // Store the new value.\n sstore(ownerSlot, newOwner)\n }\n }\n }\n\n /// @dev Throws if the sender is not the owner.\n function _checkOwner() internal view virtual {\n /// @solidity memory-safe-assembly\n assembly {\n // If the caller is not the stored owner, revert.\n if iszero(eq(caller(), sload(_OWNER_SLOT))) {\n mstore(0x00, 0x82b42900) // `Unauthorized()`.\n revert(0x1c, 0x04)\n }\n }\n }\n\n /// @dev Returns how long a two-step ownership handover is valid for in seconds.\n /// Override to return a different value if needed.\n /// Made internal to conserve bytecode. Wrap it in a public function if needed.\n function _ownershipHandoverValidFor() internal view virtual returns (uint64) {\n return 48 * 3600;\n }\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* PUBLIC UPDATE FUNCTIONS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev Allows the owner to transfer the ownership to `newOwner`.\n function transferOwnership(address newOwner) public payable virtual onlyOwner {\n /// @solidity memory-safe-assembly\n assembly {\n if iszero(shl(96, newOwner)) {\n mstore(0x00, 0x7448fbae) // `NewOwnerIsZeroAddress()`.\n revert(0x1c, 0x04)\n }\n }\n _setOwner(newOwner);\n }\n\n /// @dev Allows the owner to renounce their ownership.\n function renounceOwnership() public payable virtual onlyOwner {\n _setOwner(address(0));\n }\n\n /// @dev Request a two-step ownership handover to the caller.\n /// The request will automatically expire in 48 hours (172800 seconds) by default.\n function requestOwnershipHandover() public payable virtual {\n unchecked {\n uint256 expires = block.timestamp + _ownershipHandoverValidFor();\n /// @solidity memory-safe-assembly\n assembly {\n // Compute and set the handover slot to `expires`.\n mstore(0x0c, _HANDOVER_SLOT_SEED)\n mstore(0x00, caller())\n sstore(keccak256(0x0c, 0x20), expires)\n // Emit the {OwnershipHandoverRequested} event.\n log2(0, 0, _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE, caller())\n }\n }\n }\n\n /// @dev Cancels the two-step ownership handover to the caller, if any.\n function cancelOwnershipHandover() public payable virtual {\n /// @solidity memory-safe-assembly\n assembly {\n // Compute and set the handover slot to 0.\n mstore(0x0c, _HANDOVER_SLOT_SEED)\n mstore(0x00, caller())\n sstore(keccak256(0x0c, 0x20), 0)\n // Emit the {OwnershipHandoverCanceled} event.\n log2(0, 0, _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE, caller())\n }\n }\n\n /// @dev Allows the owner to complete the two-step ownership handover to `pendingOwner`.\n /// Reverts if there is no existing ownership handover requested by `pendingOwner`.\n function completeOwnershipHandover(address pendingOwner) public payable virtual onlyOwner {\n /// @solidity memory-safe-assembly\n assembly {\n // Compute and set the handover slot to 0.\n mstore(0x0c, _HANDOVER_SLOT_SEED)\n mstore(0x00, pendingOwner)\n let handoverSlot := keccak256(0x0c, 0x20)\n // If the handover does not exist, or has expired.\n if gt(timestamp(), sload(handoverSlot)) {\n mstore(0x00, 0x6f5e8818) // `NoHandoverRequest()`.\n revert(0x1c, 0x04)\n }\n // Set the handover slot to 0.\n sstore(handoverSlot, 0)\n }\n _setOwner(pendingOwner);\n }\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* PUBLIC READ FUNCTIONS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev Returns the owner of the contract.\n function owner() public view virtual returns (address result) {\n /// @solidity memory-safe-assembly\n assembly {\n result := sload(_OWNER_SLOT)\n }\n }\n\n /// @dev Returns the expiry timestamp for the two-step ownership handover to `pendingOwner`.\n function ownershipHandoverExpiresAt(address pendingOwner)\n public\n view\n virtual\n returns (uint256 result)\n {\n /// @solidity memory-safe-assembly\n assembly {\n // Compute the handover slot.\n mstore(0x0c, _HANDOVER_SLOT_SEED)\n mstore(0x00, pendingOwner)\n // Load the handover slot.\n result := sload(keccak256(0x0c, 0x20))\n }\n }\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* MODIFIERS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev Marks a function as only callable by the owner.\n modifier onlyOwner() virtual {\n _checkOwner();\n _;\n }\n}\n"},"src/bases/DaoBase.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity >=0.8.0;\n\nabstract contract DaoBase {\n /**\n * @dev implement getter logic to retrieve attested data\n * @param attestationId maps to the data\n */\n function getAttestedData(bytes32 attestationId) public view virtual returns (bytes memory attestationData);\n\n /**\n * @dev must store the hash of a collateral (e.g. X509 Cert, TCBInfo JSON etc) in the attestation registry\n * @dev it is recommended to store hash as a separate attestation from the actual collateral\n * @dev this getter can be useful for checking the correctness of the queried attested collateral\n *\n * @dev may link the hash attestation with the attestation of the collateral\n * For example, the content of a hash attestation can be a tuple of bytes32 values consisting of:\n * (bytes32 collateralHash, bytes32 collateralAttestationId)\n * @param attestationId - the attestationId pointing to the hash attestation, or the collateral attestation\n * itself, if the hash is included as part of the attestation data, this varies by how you define the schema.\n */\n function getCollateralHash(bytes32 attestationId) public view virtual returns (bytes32 collateralHash);\n\n /// @dev https://github.com/Vectorized/solady/blob/4964e3e2da1bc86b0394f63a90821f51d60a260b/src/utils/JSONParserLib.sol#L339-L364\n /// @dev Parses an unsigned integer from a string (in hexadecimal, i.e. base 16).\n /// Reverts if `s` is not a valid uint256 hex string matching the RegEx\n /// `^(0[xX])?[0-9a-fA-F]+$`, or if the parsed number is too big for a uint256.\n function _parseUintFromHex(string memory s) internal pure returns (uint256 result) {\n /// @solidity memory-safe-assembly\n assembly {\n let n := mload(s)\n // Skip two if starts with '0x' or '0X'.\n let i := shl(1, and(eq(0x3078, or(shr(240, mload(add(s, 0x20))), 0x20)), gt(n, 1)))\n for {} 1 {} {\n i := add(i, 1)\n let c :=\n byte(\n and(0x1f, shr(and(mload(add(s, i)), 0xff), 0x3e4088843e41bac000000000000)),\n 0x3010a071000000b0104040208000c05090d060e0f\n )\n n := mul(n, iszero(or(iszero(c), shr(252, result))))\n result := add(shl(4, result), sub(c, 1))\n if iszero(lt(i, n)) { break }\n }\n if iszero(n) {\n mstore(0x00, 0x10182796) // `ParsingFailed()`.\n revert(0x1c, 0x04)\n }\n }\n }\n}\n"},"src/Common.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nenum CA {\n ROOT,\n PROCESSOR,\n PLATFORM,\n SIGNING\n}\n\n/// @notice Attestation Definition is taken from https://github.com/ethereum-attestation-service/eas-contracts/blob/52af661748bde9b40ae782907702f885852bc149/contracts/IEAS.sol#L9C1-L23C2\n/// @notice We opted for EAS Attestation Request Definition to ensure interoperability between Verax and EAS\n\nstruct AttestationRequestData {\n address recipient; // The recipient of the attestation.\n uint64 expirationTime; // The time when the attestation expires (Unix timestamp).\n bool revocable; // Whether the attestation is revocable.\n bytes32 refUID; // The UID of the related attestation.\n bytes data; // Custom attestation data.\n uint256 value; // An explicit ETH amount to send to the resolver. This is important to prevent accidental user errors.\n}\n\nstruct AttestationRequest {\n bytes32 schema; // The unique identifier of the schema.\n AttestationRequestData data; // The arguments of the attestation request.\n}\n\n/// @notice A struct representing a single attestation.\n/// https://github.com/ethereum-attestation-service/eas-contracts/blob/52af661748bde9b40ae782907702f885852bc149/contracts/Common.sol#L25C1-L37C2\nstruct Attestation {\n bytes32 uid; // A unique identifier of the attestation.\n bytes32 schema; // The unique identifier of the schema.\n uint64 time; // The time when the attestation was created (Unix timestamp).\n uint64 expirationTime; // The time when the attestation expires (Unix timestamp).\n uint64 revocationTime; // The time when the attestation was revoked (Unix timestamp).\n bytes32 refUID; // The UID of the related attestation.\n address recipient; // The recipient of the attestation.\n address attester; // The attester/sender of the attestation.\n bool revocable; // Whether the attestation is revocable.\n bytes data; // Custom attestation data.\n}\n"},"src/helpers/EnclaveIdentityHelper.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport {JSONParserLib} from \"solady/utils/JSONParserLib.sol\";\nimport {LibString} from \"solady/utils/LibString.sol\";\nimport {DateTimeUtils} from \"../utils/DateTimeUtils.sol\";\n\nenum EnclaveId {\n QE,\n QVE,\n TD_QE\n}\n\n/**\n * @title Solidity Structure representing the EnclaveIdentity JSON\n * @param identityStr Identity string object body\n * @param signature The signature to be passed as bytes array\n */\nstruct EnclaveIdentityJsonObj {\n string identityStr;\n bytes signature;\n}\n\n/// @dev Parsed IdentityStr to an object, except for TCBLevels\nstruct IdentityObj {\n EnclaveId id;\n uint32 version;\n uint64 issueDateTimestamp; // UNIX Epoch Timestamp in seconds\n uint64 nextUpdateTimestamp; // UNIX Epoch Timestamp in seconds\n uint32 tcbEvaluationDataNumber;\n bytes4 miscselect;\n bytes4 miscselectMask;\n bytes16 attributes;\n bytes16 attributesMask;\n bytes32 mrsigner;\n uint16 isvprodid;\n Tcb[] tcb;\n}\n\nenum EnclaveIdTcbStatus {\n SGX_ENCLAVE_REPORT_ISVSVN_NOT_SUPPORTED,\n OK,\n SGX_ENCLAVE_REPORT_ISVSVN_REVOKED,\n SGX_ENCLAVE_REPORT_ISVSVN_OUT_OF_DATE\n}\n\nstruct Tcb {\n uint16 isvsvn;\n uint256 dateTimestamp;\n EnclaveIdTcbStatus status;\n}\n\n/**\n * @title Enclave Identity Helper Contract\n * @notice This is a standalone contract that can be used by off-chain applications and smart contracts\n * to parse QEIdentity data\n */\ncontract EnclaveIdentityHelper {\n using JSONParserLib for JSONParserLib.Item;\n using LibString for string;\n\n error Invalid_ID();\n\n function parseIdentityString(string calldata identityStr) external pure returns (IdentityObj memory identity) {\n identity = _parseIdentity(identityStr);\n }\n\n function _parseIdentity(string calldata identityStr) private pure returns (IdentityObj memory identity) {\n JSONParserLib.Item memory root = JSONParserLib.parse(identityStr);\n JSONParserLib.Item[] memory identityObj = root.children();\n\n for (uint256 i = 0; i < root.size(); i++) {\n JSONParserLib.Item memory current = identityObj[i];\n string memory decodedKey = JSONParserLib.decodeString(current.key());\n\n if (decodedKey.eq(\"issueDate\")) {\n identity.issueDateTimestamp =\n uint64(DateTimeUtils.fromISOToTimestamp(JSONParserLib.decodeString(current.value())));\n } else if (decodedKey.eq(\"nextUpdate\")) {\n identity.nextUpdateTimestamp =\n uint64(DateTimeUtils.fromISOToTimestamp(JSONParserLib.decodeString(current.value())));\n } else if (decodedKey.eq(\"id\")) {\n string memory idStr = JSONParserLib.decodeString(current.value());\n if (LibString.eq(idStr, \"QE\")) {\n identity.id = EnclaveId.QE;\n } else if (LibString.eq(idStr, \"QVE\")) {\n identity.id = EnclaveId.QVE;\n } else if (LibString.eq(idStr, \"TD_QE\")) {\n identity.id = EnclaveId.TD_QE;\n } else {\n revert Invalid_ID();\n }\n } else if (decodedKey.eq(\"version\")) {\n identity.version = uint32(JSONParserLib.parseUint(current.value()));\n } else if (decodedKey.eq(\"tcbEvaluationDataNumber\")) {\n identity.tcbEvaluationDataNumber = uint32(JSONParserLib.parseUint(current.value()));\n } else if (decodedKey.eq(\"miscselect\")) {\n uint256 val = JSONParserLib.parseUintFromHex(JSONParserLib.decodeString(current.value()));\n identity.miscselect = bytes4(uint32(val));\n } else if (decodedKey.eq(\"miscselectMask\")) {\n uint256 val = JSONParserLib.parseUintFromHex(JSONParserLib.decodeString(current.value()));\n identity.miscselectMask = bytes4(uint32(val));\n } else if (decodedKey.eq(\"attributes\")) {\n uint256 val = JSONParserLib.parseUintFromHex(JSONParserLib.decodeString(current.value()));\n identity.attributes = bytes16(uint128(val));\n } else if (decodedKey.eq(\"attributesMask\")) {\n uint256 val = JSONParserLib.parseUintFromHex(JSONParserLib.decodeString(current.value()));\n identity.attributesMask = bytes16(uint128(val));\n } else if (decodedKey.eq(\"mrsigner\")) {\n uint256 val = JSONParserLib.parseUintFromHex(JSONParserLib.decodeString(current.value()));\n identity.mrsigner = bytes32(val);\n } else if (decodedKey.eq(\"isvprodid\")) {\n identity.isvprodid = uint16(JSONParserLib.parseUint(current.value()));\n } else if (decodedKey.eq(\"tcbLevels\")) {\n identity.tcb = _parseTcb(current.value());\n }\n }\n }\n\n function _parseTcb(string memory tcbLevelsStr) internal pure returns (Tcb[] memory tcb) {\n JSONParserLib.Item memory tcbLevelsParent = JSONParserLib.parse(tcbLevelsStr);\n JSONParserLib.Item[] memory tcbLevels = tcbLevelsParent.children();\n uint256 tcbLevelsSize = tcbLevelsParent.size();\n tcb = new Tcb[](tcbLevelsSize);\n for (uint256 i = 0; i < tcbLevelsSize; i++) {\n uint256 tcbLevelsChildSize = tcbLevels[i].size();\n JSONParserLib.Item[] memory tcbObj = tcbLevels[i].children();\n for (uint256 j = 0; j < tcbLevelsChildSize; j++) {\n string memory tcbKey = JSONParserLib.decodeString(tcbObj[j].key());\n if (tcbKey.eq(\"tcb\")) {\n JSONParserLib.Item[] memory tcbChild = tcbObj[j].children();\n string memory childKey = JSONParserLib.decodeString(tcbChild[0].key());\n if (childKey.eq(\"isvsvn\")) {\n tcb[i].isvsvn = uint16(JSONParserLib.parseUint(tcbChild[0].value()));\n }\n } else if (tcbKey.eq(\"tcbDate\")) {\n tcb[i].dateTimestamp =\n DateTimeUtils.fromISOToTimestamp(JSONParserLib.decodeString(tcbObj[j].value()));\n } else if (tcbKey.eq(\"tcbStatus\")) {\n string memory decodedValue = JSONParserLib.decodeString(tcbObj[j].value());\n if (decodedValue.eq(\"UpToDate\")) {\n tcb[i].status = EnclaveIdTcbStatus.OK;\n } else if (decodedValue.eq(\"Revoked\")) {\n tcb[i].status = EnclaveIdTcbStatus.SGX_ENCLAVE_REPORT_ISVSVN_REVOKED;\n } else if (decodedValue.eq(\"OutOfDate\")) {\n tcb[i].status = EnclaveIdTcbStatus.SGX_ENCLAVE_REPORT_ISVSVN_OUT_OF_DATE;\n }\n }\n }\n }\n }\n}\n"},"src/bases/SigVerifyBase.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport {BytesUtils} from \"../utils/BytesUtils.sol\";\nimport {P256Verifier} from \"../utils/P256Verifier.sol\";\n\ninterface IX509 {\n function getSubjectPublicKey(bytes memory der) external pure returns (bytes memory pubKey);\n}\n\nabstract contract SigVerifyBase {\n address public x509;\n\n using BytesUtils for bytes;\n\n constructor(address _x509helper) {\n x509 = _x509helper;\n }\n\n function verifySignature(bytes32 digest, bytes memory signature, bytes memory signingCertBlob)\n internal\n view\n returns (bool verified)\n {\n if (signature.length != 64) {\n return false;\n }\n\n bytes memory pubKey = IX509(x509).getSubjectPublicKey(signingCertBlob);\n if (pubKey.length != 64) {\n return false;\n }\n\n verified = P256Verifier.ecdsaVerify(digest, signature, pubKey);\n }\n}\n"},"src/bases/PcsDao.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport {CA, AttestationRequestData, AttestationRequest} from \"../Common.sol\";\nimport {X509Helper, X509CertObj} from \"../helpers/X509Helper.sol\";\nimport {X509CRLHelper, X509CRLObj} from \"../helpers/X509CRLHelper.sol\";\n\nimport {DaoBase} from \"./DaoBase.sol\";\nimport {SigVerifyBase} from \"./SigVerifyBase.sol\";\n\nimport {LibString} from \"solady/utils/LibString.sol\";\n\n/**\n * @title Intel PCS Data Access Object\n * @notice This is a core contract of our on-chain PCCS implementation as it provides methods\n * @notice to read/write essential collaterals such as the RootCA, Intermediate CAs and CRLs.\n * @notice All other DAOs are expected to configure and make external calls to this contract to fetch those collaterals.\n * @notice This contract is heavily inspired by Sections 4.2.5 and 4.2.6 in the Intel SGX PCCS Design Guideline\n * https://download.01.org/intel-sgx/sgx-dcap/1.19/linux/docs/SGX_DCAP_Caching_Service_Design_Guide.pdf\n */\nabstract contract PcsDao is DaoBase, SigVerifyBase {\n using LibString for string;\n\n X509CRLHelper public crlLib;\n\n /// @notice Fetches the attestationId of the attested PCS Certificate\n ///\n /// @dev Must ensure that the public key for the configured Intel Root CA matches with\n /// @dev the Intel source code at: https://github.com/intel/SGXDataCenterAttestationPrimitives/blob/39989a42bbbb0c968153a47254b6de79a27eb603/QuoteVerification/QvE/Enclave/qve.cpp#L92-L100\n ///\n /// @notice the schema of the attested data is the following:\n /// - bytes pcsCert\n mapping(CA => bytes32) public pcsCertAttestations;\n\n /// @notice Fetches the attestationId of the attested PCS CRLs\n ///\n /// @dev Verification of CRLs are conducted as part of the PCS attestation process\n ///\n /// @notice the schema of the attested data is the following:\n /// - bytes pcsCrl\n mapping(CA => bytes32) public pcsCrlAttestations;\n\n string constant PCK_PLATFORM_CA_COMMON_NAME = \"Intel SGX PCK Platform CA\";\n string constant PCK_PROCESSOR_CA_COMMON_NAME = \"Intel SGX PCK Processor CA\";\n string constant SIGNING_COMMON_NAME = \"Intel SGX TCB Signing\";\n string constant ROOT_CA_COMMON_NAME = \"Intel SGX Root CA\";\n\n // keccak256(hex\"0ba9c4c0c0c86193a3fe23d6b02cda10a8bbd4e88e48b4458561a36e705525f567918e2edc88e40d860bd0cc4ee26aacc988e505a953558c453f6b0904ae7394\")\n // the uncompressed (0x04) prefix is not included in the pubkey pre-image\n bytes32 constant ROOT_CA_PUBKEY_HASH = 0x89f72d7c488e5b53a77c23ebcb36970ef7eb5bcf6658e9b8292cfbe4703a8473;\n\n error Missing_Certificate(CA ca);\n error Invalid_PCK_CA(CA ca);\n error Invalid_Issuer_Name();\n error Invalid_Subject_Name();\n error Certificate_Expired();\n error Root_Key_Mismatch();\n error Certificate_Revoked(CA ca, uint256 serialNum);\n error Missing_Issuer();\n error Invalid_Signature();\n\n constructor(address _x509, address _crl) SigVerifyBase(_x509) {\n crlLib = X509CRLHelper(_crl);\n }\n\n modifier pckCACheck(CA ca) {\n if (ca == CA.ROOT || ca == CA.SIGNING) {\n revert Invalid_PCK_CA(ca);\n }\n _;\n }\n\n /**\n * @param ca see {Common.sol} for definition\n * @return cert - DER encoded certificate\n * @return crl - DER-encoded CRLs that is signed by the provided cert\n */\n function getCertificateById(CA ca) external view returns (bytes memory cert, bytes memory crl) {\n bytes32 pcsCertAttestationId = pcsCertAttestations[ca];\n if (pcsCertAttestationId == bytes32(0)) {\n revert Missing_Certificate(ca);\n }\n cert = getAttestedData(pcsCertAttestationId);\n\n bytes32 pcsCrlAttestationId = pcsCrlAttestations[ca];\n if (pcsCrlAttestationId != bytes32(0)) {\n crl = getAttestedData(pcsCrlAttestationId);\n }\n }\n\n /**\n * Section 4.2.6 (upsertPcsCertificates)\n * @param ca replaces the \"id\" value with the ca_id\n * @param cert the DER-encoded certificate\n */\n function upsertPcsCertificates(CA ca, bytes calldata cert) external returns (bytes32 attestationId) {\n bytes32 hash = _validatePcsCert(ca, cert);\n AttestationRequest memory req = _buildPcsAttestationRequest(false, ca, cert);\n attestationId = _attestPcs(req, hash);\n pcsCertAttestations[ca] = attestationId;\n }\n\n /**\n * Section 4.2.5 (upsertPckCrl)\n * @param ca either CA.PROCESSOR or CA.PLATFORM\n * @param crl the DER-encoded CRL\n */\n function upsertPckCrl(CA ca, bytes calldata crl) external pckCACheck(ca) returns (bytes32 attestationId) {\n attestationId = _upsertPcsCrl(ca, crl);\n }\n\n function upsertRootCACrl(bytes calldata rootcacrl) external returns (bytes32 attestationId) {\n attestationId = _upsertPcsCrl(CA.ROOT, rootcacrl);\n }\n\n function pcsCertSchemaID() public view virtual returns (bytes32 PCS_CERT_SCHEMA_ID);\n\n function pcsCrlSchemaID() public view virtual returns (bytes32 PCS_CRL_SCHEMA_ID);\n\n /**\n * @dev implement logic to validate and attest PCS Certificates or CRLs\n * @param req structure as defined by EAS\n * https://github.com/ethereum-attestation-service/eas-contracts/blob/52af661748bde9b40ae782907702f885852bc149/contracts/IEAS.sol#L9C1-L23C2\n * @return attestationId\n */\n function _attestPcs(AttestationRequest memory req, bytes32 hash) internal virtual returns (bytes32 attestationId);\n\n function _upsertPcsCrl(CA ca, bytes calldata crl) private returns (bytes32 attestationId) {\n bytes32 hash = _validatePcsCrl(ca, crl);\n AttestationRequest memory req = _buildPcsAttestationRequest(true, ca, crl);\n attestationId = _attestPcs(req, hash);\n pcsCrlAttestations[ca] = attestationId;\n }\n\n /**\n * @notice builds an EAS compliant attestation request\n * @param isCrl - true only if the attested data is a CRL\n * @param der - contains the DER encoded data, specified by isCrl and CA\n */\n function _buildPcsAttestationRequest(bool isCrl, CA ca, bytes calldata der)\n private\n view\n returns (AttestationRequest memory req)\n {\n bytes32 predecessorAttestationId = isCrl ? pcsCrlAttestations[ca] : pcsCertAttestations[ca];\n AttestationRequestData memory reqData = AttestationRequestData({\n recipient: msg.sender,\n expirationTime: 0, // assign zero here because this has already been checked\n revocable: true,\n refUID: predecessorAttestationId,\n data: der,\n value: 0\n });\n bytes32 schemaId = isCrl ? pcsCrlSchemaID() : pcsCertSchemaID();\n req = AttestationRequest({schema: schemaId, data: reqData});\n }\n\n function _validatePcsCert(CA ca, bytes calldata cert) private view returns (bytes32 hash) {\n X509Helper x509Lib = X509Helper(x509);\n\n // Step 1: Check whether cert has expired\n bool notExpired = x509Lib.certIsNotExpired(cert);\n if (!notExpired) {\n revert Certificate_Expired();\n }\n\n // Step 2: Check issuer and subject common names are valid\n string memory issuerName = x509Lib.getIssuerCommonName(cert);\n string memory subjectName = x509Lib.getSubjectCommonName(cert);\n string memory expectedIssuer = ROOT_CA_COMMON_NAME;\n string memory expectedSubject;\n if (ca == CA.PLATFORM) {\n expectedSubject = PCK_PLATFORM_CA_COMMON_NAME;\n } else if (ca == CA.PROCESSOR) {\n expectedSubject = PCK_PROCESSOR_CA_COMMON_NAME;\n } else if (ca == CA.SIGNING) {\n expectedSubject = SIGNING_COMMON_NAME;\n } else if (ca == CA.ROOT) {\n expectedSubject = ROOT_CA_COMMON_NAME;\n }\n\n if (!LibString.eq(issuerName, expectedIssuer)) {\n revert Invalid_Issuer_Name();\n }\n if (!LibString.eq(subjectName, expectedSubject)) {\n revert Invalid_Subject_Name();\n }\n\n // Step 3: Check Revocation Status\n bytes memory rootCrlData = getAttestedData(pcsCrlAttestations[CA.ROOT]);\n if (ca == CA.ROOT) {\n bytes memory pubKey = x509Lib.getSubjectPublicKey(cert);\n if (keccak256(pubKey) != ROOT_CA_PUBKEY_HASH) {\n revert Root_Key_Mismatch();\n }\n } else if (rootCrlData.length > 0) {\n uint256 serialNum = x509Lib.getSerialNumber(cert);\n bool revoked = crlLib.serialNumberIsRevoked(serialNum, rootCrlData);\n if (revoked) {\n revert Certificate_Revoked(ca, serialNum);\n }\n }\n\n // Step 4: Check signature\n bytes memory rootCert = _getIssuer(CA.ROOT);\n (bytes memory tbs, bytes memory signature) = x509Lib.getTbsAndSig(cert);\n bytes32 digest = sha256(tbs);\n bool sigVerified;\n if (ca == CA.ROOT) {\n // the root certificate is issued by its own key\n sigVerified = verifySignature(digest, signature, cert);\n } else if (rootCert.length > 0) {\n sigVerified = verifySignature(digest, signature, rootCert);\n } else {\n // all other certificates should already have an iusuer configured\n revert Missing_Issuer();\n }\n\n if (!sigVerified) {\n revert Invalid_Signature();\n }\n\n hash = keccak256(tbs);\n }\n\n function _validatePcsCrl(CA ca, bytes calldata crl) private view returns (bytes32 hash) {\n // Step 1: Check whether CRL has expired\n bool notExpired = crlLib.crlIsNotExpired(crl);\n if (!notExpired) {\n revert Certificate_Expired();\n }\n\n // Step 2: Check CRL issuer\n string memory issuerCommonName = crlLib.getIssuerCommonName(crl);\n string memory expectedIssuer;\n if (ca == CA.PLATFORM || ca == CA.PROCESSOR) {\n expectedIssuer = ca == CA.PLATFORM ? PCK_PLATFORM_CA_COMMON_NAME : PCK_PROCESSOR_CA_COMMON_NAME;\n } else {\n expectedIssuer = ROOT_CA_COMMON_NAME;\n }\n if (!LibString.eq(issuerCommonName, expectedIssuer)) {\n revert Invalid_Issuer_Name();\n }\n\n // Step 3: Verify signature\n (bytes memory tbs, bytes memory signature) = crlLib.getTbsAndSig(crl);\n bytes32 digest = sha256(tbs);\n bool sigVerified = verifySignature(digest, signature, _getIssuer(ca));\n if (!sigVerified) {\n revert Invalid_Signature();\n }\n\n hash = keccak256(tbs);\n }\n\n function _getIssuer(CA ca) private view returns (bytes memory issuerCert) {\n bytes32 intermediateCertAttestationId = pcsCertAttestations[ca];\n bytes32 rootCertAttestationId = pcsCertAttestations[CA.ROOT];\n if (ca == CA.PLATFORM || ca == CA.PROCESSOR) {\n // this is applicable to crls only\n // since all certs in the pcsdao are issued by the root\n issuerCert = getAttestedData(intermediateCertAttestationId);\n } else {\n issuerCert = getAttestedData(rootCertAttestationId);\n }\n }\n}\n"},"lib/solady/src/utils/JSONParserLib.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\n/// @notice Library for parsing JSONs.\n/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/JSONParserLib.sol)\nlibrary JSONParserLib {\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* CUSTOM ERRORS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev The input is invalid.\n error ParsingFailed();\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* CONSTANTS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n // There are 6 types of variables in JSON (excluding undefined).\n\n /// @dev For denoting that an item has not been initialized.\n /// A item returned from `parse` will never be of an undefined type.\n /// Parsing a invalid JSON string will simply revert.\n uint8 internal constant TYPE_UNDEFINED = 0;\n\n /// @dev Type representing an array (e.g. `[1,2,3]`).\n uint8 internal constant TYPE_ARRAY = 1;\n\n /// @dev Type representing an object (e.g. `{\"a\":\"A\",\"b\":\"B\"}`).\n uint8 internal constant TYPE_OBJECT = 2;\n\n /// @dev Type representing a number (e.g. `-1.23e+21`).\n uint8 internal constant TYPE_NUMBER = 3;\n\n /// @dev Type representing a string (e.g. `\"hello\"`).\n uint8 internal constant TYPE_STRING = 4;\n\n /// @dev Type representing a boolean (i.e. `true` or `false`).\n uint8 internal constant TYPE_BOOLEAN = 5;\n\n /// @dev Type representing null (i.e. `null`).\n uint8 internal constant TYPE_NULL = 6;\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* STRUCTS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev A pointer to a parsed JSON node.\n struct Item {\n // Do NOT modify the `_data` directly.\n uint256 _data;\n }\n\n // Private constants for packing `_data`.\n\n uint256 private constant _BITPOS_STRING = 32 * 7 - 8;\n uint256 private constant _BITPOS_KEY_LENGTH = 32 * 6 - 8;\n uint256 private constant _BITPOS_KEY = 32 * 5 - 8;\n uint256 private constant _BITPOS_VALUE_LENGTH = 32 * 4 - 8;\n uint256 private constant _BITPOS_VALUE = 32 * 3 - 8;\n uint256 private constant _BITPOS_CHILD = 32 * 2 - 8;\n uint256 private constant _BITPOS_SIBLING_OR_PARENT = 32 * 1 - 8;\n uint256 private constant _BITMASK_POINTER = 0xffffffff;\n uint256 private constant _BITMASK_TYPE = 7;\n uint256 private constant _KEY_INITED = 1 << 3;\n uint256 private constant _VALUE_INITED = 1 << 4;\n uint256 private constant _CHILDREN_INITED = 1 << 5;\n uint256 private constant _PARENT_IS_ARRAY = 1 << 6;\n uint256 private constant _PARENT_IS_OBJECT = 1 << 7;\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* JSON PARSING OPERATION */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev Parses the JSON string `s`, and returns the root.\n /// Reverts if `s` is not a valid JSON as specified in RFC 8259.\n /// Object items WILL simply contain all their children, inclusive of repeated keys,\n /// in the same order which they appear in the JSON string.\n ///\n /// Note: For efficiency, this function WILL NOT make a copy of `s`.\n /// The parsed tree WILL contain offsets to `s`.\n /// Do NOT pass in a string that WILL be modified later on.\n function parse(string memory s) internal pure returns (Item memory result) {\n /// @solidity memory-safe-assembly\n assembly {\n mstore(0x40, result) // We will use our own allocation instead.\n }\n bytes32 r = _query(_toInput(s), 255);\n /// @solidity memory-safe-assembly\n assembly {\n result := r\n }\n }\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* JSON ITEM OPERATIONS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n // Note:\n // - An item is a node in the JSON tree.\n // - The value of a string item WILL be double-quoted, JSON encoded.\n // - We make a distinction between `index` and `key`.\n // - Items in arrays are located by `index` (uint256).\n // - Items in objects are located by `key` (string).\n // - Keys are always strings, double-quoted, JSON encoded.\n //\n // These design choices are made to balance between efficiency and ease-of-use.\n\n /// @dev Returns the string value of the item.\n /// This is its exact string representation in the original JSON string.\n /// The returned string WILL have leading and trailing whitespace trimmed.\n /// All inner whitespace WILL be preserved, exactly as it is in the original JSON string.\n /// If the item's type is string, the returned string WILL be double-quoted, JSON encoded.\n ///\n /// Note: This function lazily instantiates and caches the returned string.\n /// Do NOT modify the returned string.\n function value(Item memory item) internal pure returns (string memory result) {\n bytes32 r = _query(_toInput(item), 0);\n /// @solidity memory-safe-assembly\n assembly {\n result := r\n }\n }\n\n /// @dev Returns the index of the item in the array.\n /// It the item's parent is not an array, returns 0.\n function index(Item memory item) internal pure returns (uint256 result) {\n /// @solidity memory-safe-assembly\n assembly {\n if and(mload(item), _PARENT_IS_ARRAY) {\n result := and(_BITMASK_POINTER, shr(_BITPOS_KEY, mload(item)))\n }\n }\n }\n\n /// @dev Returns the key of the item in the object.\n /// It the item's parent is not an object, returns an empty string.\n /// The returned string WILL be double-quoted, JSON encoded.\n ///\n /// Note: This function lazily instantiates and caches the returned string.\n /// Do NOT modify the returned string.\n function key(Item memory item) internal pure returns (string memory result) {\n if (item._data & _PARENT_IS_OBJECT != 0) {\n bytes32 r = _query(_toInput(item), 1);\n /// @solidity memory-safe-assembly\n assembly {\n result := r\n }\n }\n }\n\n /// @dev Returns the key of the item in the object.\n /// It the item is neither an array nor object, returns an empty array.\n ///\n /// Note: This function lazily instantiates and caches the returned array.\n /// Do NOT modify the returned array.\n function children(Item memory item) internal pure returns (Item[] memory result) {\n bytes32 r = _query(_toInput(item), 3);\n /// @solidity memory-safe-assembly\n assembly {\n result := r\n }\n }\n\n /// @dev Returns the number of children.\n /// It the item is neither an array nor object, returns zero.\n function size(Item memory item) internal pure returns (uint256 result) {\n bytes32 r = _query(_toInput(item), 3);\n /// @solidity memory-safe-assembly\n assembly {\n result := mload(r)\n }\n }\n\n /// @dev Returns the item at index `i` for (array).\n /// If `item` is not an array, the result's type WILL be undefined.\n /// If there is no item with the index, the result's type WILL be undefined.\n function at(Item memory item, uint256 i) internal pure returns (Item memory result) {\n /// @solidity memory-safe-assembly\n assembly {\n mstore(0x40, result) // Free the default allocation. We'll allocate manually.\n }\n bytes32 r = _query(_toInput(item), 3);\n /// @solidity memory-safe-assembly\n assembly {\n result := mload(add(add(r, 0x20), shl(5, i)))\n if iszero(and(lt(i, mload(r)), eq(and(mload(item), _BITMASK_TYPE), TYPE_ARRAY))) {\n result := 0x60 // Reset to the zero pointer.\n }\n }\n }\n\n /// @dev Returns the item at key `k` for (object).\n /// If `item` is not an object, the result's type WILL be undefined.\n /// The key MUST be double-quoted, JSON encoded. This is for efficiency reasons.\n /// - Correct : `item.at('\"k\"')`.\n /// - Wrong : `item.at(\"k\")`.\n /// For duplicated keys, the last item with the key WILL be returned.\n /// If there is no item with the key, the result's type WILL be undefined.\n function at(Item memory item, string memory k) internal pure returns (Item memory result) {\n /// @solidity memory-safe-assembly\n assembly {\n mstore(0x40, result) // Free the default allocation. We'll allocate manually.\n result := 0x60 // Initialize to the zero pointer.\n }\n if (isObject(item)) {\n bytes32 kHash = keccak256(bytes(k));\n Item[] memory r = children(item);\n // We'll just do a linear search. The alternatives are very bloated.\n for (uint256 i = r.length << 5; i != 0;) {\n /// @solidity memory-safe-assembly\n assembly {\n item := mload(add(r, i))\n i := sub(i, 0x20)\n }\n if (keccak256(bytes(key(item))) != kHash) continue;\n result = item;\n break;\n }\n }\n }\n\n /// @dev Returns the item's type.\n function getType(Item memory item) internal pure returns (uint8 result) {\n result = uint8(item._data & _BITMASK_TYPE);\n }\n\n /// Note: All types are mutually exclusive.\n\n /// @dev Returns whether the item is of type undefined.\n function isUndefined(Item memory item) internal pure returns (bool result) {\n result = item._data & _BITMASK_TYPE == TYPE_UNDEFINED;\n }\n\n /// @dev Returns whether the item is of type array.\n function isArray(Item memory item) internal pure returns (bool result) {\n result = item._data & _BITMASK_TYPE == TYPE_ARRAY;\n }\n\n /// @dev Returns whether the item is of type object.\n function isObject(Item memory item) internal pure returns (bool result) {\n result = item._data & _BITMASK_TYPE == TYPE_OBJECT;\n }\n\n /// @dev Returns whether the item is of type number.\n function isNumber(Item memory item) internal pure returns (bool result) {\n result = item._data & _BITMASK_TYPE == TYPE_NUMBER;\n }\n\n /// @dev Returns whether the item is of type string.\n function isString(Item memory item) internal pure returns (bool result) {\n result = item._data & _BITMASK_TYPE == TYPE_STRING;\n }\n\n /// @dev Returns whether the item is of type boolean.\n function isBoolean(Item memory item) internal pure returns (bool result) {\n result = item._data & _BITMASK_TYPE == TYPE_BOOLEAN;\n }\n\n /// @dev Returns whether the item is of type null.\n function isNull(Item memory item) internal pure returns (bool result) {\n result = item._data & _BITMASK_TYPE == TYPE_NULL;\n }\n\n /// @dev Returns the item's parent.\n /// If the item does not have a parent, the result's type will be undefined.\n function parent(Item memory item) internal pure returns (Item memory result) {\n /// @solidity memory-safe-assembly\n assembly {\n mstore(0x40, result) // Free the default allocation. We've already allocated.\n result := and(shr(_BITPOS_SIBLING_OR_PARENT, mload(item)), _BITMASK_POINTER)\n if iszero(result) { result := 0x60 } // Reset to the zero pointer.\n }\n }\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* UTILITY FUNCTIONS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev Parses an unsigned integer from a string (in decimal, i.e. base 10).\n /// Reverts if `s` is not a valid uint256 string matching the RegEx `^[0-9]+$`,\n /// or if the parsed number is too big for a uint256.\n function parseUint(string memory s) internal pure returns (uint256 result) {\n /// @solidity memory-safe-assembly\n assembly {\n let n := mload(s)\n let preMulOverflowThres := div(not(0), 10)\n for { let i := 0 } 1 {} {\n i := add(i, 1)\n let digit := sub(and(mload(add(s, i)), 0xff), 48)\n let mulOverflowed := gt(result, preMulOverflowThres)\n let product := mul(10, result)\n result := add(product, digit)\n n := mul(n, iszero(or(or(mulOverflowed, lt(result, product)), gt(digit, 9))))\n if iszero(lt(i, n)) { break }\n }\n if iszero(n) {\n mstore(0x00, 0x10182796) // `ParsingFailed()`.\n revert(0x1c, 0x04)\n }\n }\n }\n\n /// @dev Parses a signed integer from a string (in decimal, i.e. base 10).\n /// Reverts if `s` is not a valid int256 string matching the RegEx `^[+-]?[0-9]+$`,\n /// or if the parsed number is too big for a int256.\n function parseInt(string memory s) internal pure returns (int256 result) {\n uint256 n = bytes(s).length;\n uint256 sign;\n uint256 isNegative;\n /// @solidity memory-safe-assembly\n assembly {\n if n {\n let c := and(mload(add(s, 1)), 0xff)\n isNegative := eq(c, 45)\n if or(eq(c, 43), isNegative) {\n sign := c\n s := add(s, 1)\n mstore(s, sub(n, 1))\n }\n if iszero(or(sign, lt(sub(c, 48), 10))) { s := 0x60 }\n }\n }\n uint256 x = parseUint(s);\n /// @solidity memory-safe-assembly\n assembly {\n if shr(255, x) {\n mstore(0x00, 0x10182796) // `ParsingFailed()`.\n revert(0x1c, 0x04)\n }\n if sign {\n mstore(s, sign)\n s := sub(s, 1)\n mstore(s, n)\n }\n result := xor(x, mul(xor(x, add(not(x), 1)), isNegative))\n }\n }\n\n /// @dev Parses an unsigned integer from a string (in hexadecimal, i.e. base 16).\n /// Reverts if `s` is not a valid uint256 hex string matching the RegEx\n /// `^(0[xX])?[0-9a-fA-F]+$`, or if the parsed number is too big for a uint256.\n function parseUintFromHex(string memory s) internal pure returns (uint256 result) {\n /// @solidity memory-safe-assembly\n assembly {\n let n := mload(s)\n // Skip two if starts with '0x' or '0X'.\n let i := shl(1, and(eq(0x3078, or(shr(240, mload(add(s, 0x20))), 0x20)), gt(n, 1)))\n for {} 1 {} {\n i := add(i, 1)\n let c :=\n byte(\n and(0x1f, shr(and(mload(add(s, i)), 0xff), 0x3e4088843e41bac000000000000)),\n 0x3010a071000000b0104040208000c05090d060e0f\n )\n n := mul(n, iszero(or(iszero(c), shr(252, result))))\n result := add(shl(4, result), sub(c, 1))\n if iszero(lt(i, n)) { break }\n }\n if iszero(n) {\n mstore(0x00, 0x10182796) // `ParsingFailed()`.\n revert(0x1c, 0x04)\n }\n }\n }\n\n /// @dev Decodes a JSON encoded string.\n /// The string MUST be double-quoted, JSON encoded.\n /// Reverts if the string is invalid.\n /// As you can see, it's pretty complex for a deceptively simple looking task.\n function decodeString(string memory s) internal pure returns (string memory result) {\n /// @solidity memory-safe-assembly\n assembly {\n function fail() {\n mstore(0x00, 0x10182796) // `ParsingFailed()`.\n revert(0x1c, 0x04)\n }\n\n function decodeUnicodeEscapeSequence(pIn_, end_) -> _unicode, _pOut {\n _pOut := add(pIn_, 4)\n let b_ := iszero(gt(_pOut, end_))\n let t_ := mload(pIn_) // Load the whole word.\n for { let i_ := 0 } iszero(eq(i_, 4)) { i_ := add(i_, 1) } {\n let c_ := sub(byte(i_, t_), 48)\n if iszero(and(shr(c_, 0x7e0000007e03ff), b_)) { fail() } // Not hexadecimal.\n c_ := sub(c_, add(mul(gt(c_, 16), 7), shl(5, gt(c_, 48))))\n _unicode := add(shl(4, _unicode), c_)\n }\n }\n\n function decodeUnicodeCodePoint(pIn_, end_) -> _unicode, _pOut {\n _unicode, _pOut := decodeUnicodeEscapeSequence(pIn_, end_)\n if iszero(or(lt(_unicode, 0xd800), gt(_unicode, 0xdbff))) {\n let t_ := mload(_pOut) // Load the whole word.\n end_ := mul(end_, eq(shr(240, t_), 0x5c75)) // Fail if not starting with '\\\\u'.\n t_, _pOut := decodeUnicodeEscapeSequence(add(_pOut, 2), end_)\n _unicode := add(0x10000, add(shl(10, and(0x3ff, _unicode)), and(0x3ff, t_)))\n }\n }\n\n function appendCodePointAsUTF8(pIn_, c_) -> _pOut {\n if iszero(gt(c_, 0x7f)) {\n mstore8(pIn_, c_)\n _pOut := add(pIn_, 1)\n leave\n }\n mstore8(0x1f, c_)\n mstore8(0x1e, shr(6, c_))\n if iszero(gt(c_, 0x7ff)) {\n mstore(pIn_, shl(240, or(0xc080, and(0x1f3f, mload(0x00)))))\n _pOut := add(pIn_, 2)\n leave\n }\n mstore8(0x1d, shr(12, c_))\n if iszero(gt(c_, 0xffff)) {\n mstore(pIn_, shl(232, or(0xe08080, and(0x0f3f3f, mload(0x00)))))\n _pOut := add(pIn_, 3)\n leave\n }\n mstore8(0x1c, shr(18, c_))\n mstore(pIn_, shl(224, or(0xf0808080, and(0x073f3f3f, mload(0x00)))))\n _pOut := add(pIn_, shl(2, lt(c_, 0x110000)))\n }\n\n function chr(p_) -> _c {\n _c := byte(0, mload(p_))\n }\n\n let n := mload(s)\n let end := add(add(s, n), 0x1f)\n if iszero(and(gt(n, 1), eq(0x2222, or(and(0xff00, mload(add(s, 2))), chr(end))))) {\n fail() // Fail if not double-quoted.\n }\n let out := add(mload(0x40), 0x20)\n for { let curr := add(s, 0x21) } iszero(eq(curr, end)) {} {\n let c := chr(curr)\n curr := add(curr, 1)\n // Not '\\\\'.\n if iszero(eq(c, 92)) {\n // Not '\"'.\n if iszero(eq(c, 34)) {\n mstore8(out, c)\n out := add(out, 1)\n continue\n }\n curr := end\n }\n if iszero(eq(curr, end)) {\n let escape := chr(curr)\n curr := add(curr, 1)\n // '\"', '/', '\\\\'.\n if and(shr(escape, 0x100000000000800400000000), 1) {\n mstore8(out, escape)\n out := add(out, 1)\n continue\n }\n // 'u'.\n if eq(escape, 117) {\n escape, curr := decodeUnicodeCodePoint(curr, end)\n out := appendCodePointAsUTF8(out, escape)\n continue\n }\n // `{'b':'\\b', 'f':'\\f', 'n':'\\n', 'r':'\\r', 't':'\\t'}`.\n escape := byte(sub(escape, 85), 0x080000000c000000000000000a0000000d0009)\n if escape {\n mstore8(out, escape)\n out := add(out, 1)\n continue\n }\n }\n fail()\n break\n }\n mstore(out, 0) // Zeroize the last slot.\n result := mload(0x40)\n mstore(result, sub(out, add(result, 0x20))) // Store the length.\n mstore(0x40, add(out, 0x20)) // Allocate the memory.\n }\n }\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* PRIVATE HELPERS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev Performs a query on the input with the given mode.\n function _query(bytes32 input, uint256 mode) private pure returns (bytes32 result) {\n /// @solidity memory-safe-assembly\n assembly {\n function fail() {\n mstore(0x00, 0x10182796) // `ParsingFailed()`.\n revert(0x1c, 0x04)\n }\n\n function chr(p_) -> _c {\n _c := byte(0, mload(p_))\n }\n\n function skipWhitespace(pIn_, end_) -> _pOut {\n for { _pOut := pIn_ } 1 { _pOut := add(_pOut, 1) } {\n if iszero(and(shr(chr(_pOut), 0x100002600), 1)) { leave } // Not in ' \\n\\r\\t'.\n }\n }\n\n function setP(packed_, bitpos_, p_) -> _packed {\n // Perform an out-of-gas revert if `p_` exceeds `_BITMASK_POINTER`.\n returndatacopy(returndatasize(), returndatasize(), gt(p_, _BITMASK_POINTER))\n _packed := or(and(not(shl(bitpos_, _BITMASK_POINTER)), packed_), shl(bitpos_, p_))\n }\n\n function getP(packed_, bitpos_) -> _p {\n _p := and(_BITMASK_POINTER, shr(bitpos_, packed_))\n }\n\n function mallocItem(s_, packed_, pStart_, pCurr_, type_) -> _item {\n _item := mload(0x40)\n // forgefmt: disable-next-item\n packed_ := setP(setP(packed_, _BITPOS_VALUE, sub(pStart_, add(s_, 0x20))),\n _BITPOS_VALUE_LENGTH, sub(pCurr_, pStart_))\n mstore(_item, or(packed_, type_))\n mstore(0x40, add(_item, 0x20)) // Allocate memory.\n }\n\n function parseValue(s_, sibling_, pIn_, end_) -> _item, _pOut {\n let packed_ := setP(mload(0x00), _BITPOS_SIBLING_OR_PARENT, sibling_)\n _pOut := skipWhitespace(pIn_, end_)\n if iszero(lt(_pOut, end_)) { leave }\n for { let c_ := chr(_pOut) } 1 {} {\n // If starts with '\"'.\n if eq(c_, 34) {\n let pStart_ := _pOut\n _pOut := parseStringSub(s_, packed_, _pOut, end_)\n _item := mallocItem(s_, packed_, pStart_, _pOut, TYPE_STRING)\n break\n }\n // If starts with '['.\n if eq(c_, 91) {\n _item, _pOut := parseArray(s_, packed_, _pOut, end_)\n break\n }\n // If starts with '{'.\n if eq(c_, 123) {\n _item, _pOut := parseObject(s_, packed_, _pOut, end_)\n break\n }\n // If starts with any in '0123456789-'.\n if and(shr(c_, shl(45, 0x1ff9)), 1) {\n _item, _pOut := parseNumber(s_, packed_, _pOut, end_)\n break\n }\n if iszero(gt(add(_pOut, 4), end_)) {\n let pStart_ := _pOut\n let w_ := shr(224, mload(_pOut))\n // 'true' in hex format.\n if eq(w_, 0x74727565) {\n _pOut := add(_pOut, 4)\n _item := mallocItem(s_, packed_, pStart_, _pOut, TYPE_BOOLEAN)\n break\n }\n // 'null' in hex format.\n if eq(w_, 0x6e756c6c) {\n _pOut := add(_pOut, 4)\n _item := mallocItem(s_, packed_, pStart_, _pOut, TYPE_NULL)\n break\n }\n }\n if iszero(gt(add(_pOut, 5), end_)) {\n let pStart_ := _pOut\n let w_ := shr(216, mload(_pOut))\n // 'false' in hex format.\n if eq(w_, 0x66616c7365) {\n _pOut := add(_pOut, 5)\n _item := mallocItem(s_, packed_, pStart_, _pOut, TYPE_BOOLEAN)\n break\n }\n }\n fail()\n break\n }\n _pOut := skipWhitespace(_pOut, end_)\n }\n\n function parseArray(s_, packed_, pIn_, end_) -> _item, _pOut {\n let j_ := 0\n for { _pOut := add(pIn_, 1) } 1 { _pOut := add(_pOut, 1) } {\n if iszero(lt(_pOut, end_)) { fail() }\n if iszero(_item) {\n _pOut := skipWhitespace(_pOut, end_)\n if eq(chr(_pOut), 93) { break } // ']'.\n }\n _item, _pOut := parseValue(s_, _item, _pOut, end_)\n if _item {\n // forgefmt: disable-next-item\n mstore(_item, setP(or(_PARENT_IS_ARRAY, mload(_item)),\n _BITPOS_KEY, j_))\n j_ := add(j_, 1)\n let c_ := chr(_pOut)\n if eq(c_, 93) { break } // ']'.\n if eq(c_, 44) { continue } // ','.\n }\n _pOut := end_\n }\n _pOut := add(_pOut, 1)\n packed_ := setP(packed_, _BITPOS_CHILD, _item)\n _item := mallocItem(s_, packed_, pIn_, _pOut, TYPE_ARRAY)\n }\n\n function parseObject(s_, packed_, pIn_, end_) -> _item, _pOut {\n for { _pOut := add(pIn_, 1) } 1 { _pOut := add(_pOut, 1) } {\n if iszero(lt(_pOut, end_)) { fail() }\n if iszero(_item) {\n _pOut := skipWhitespace(_pOut, end_)\n if eq(chr(_pOut), 125) { break } // '}'.\n }\n _pOut := skipWhitespace(_pOut, end_)\n let pKeyStart_ := _pOut\n let pKeyEnd_ := parseStringSub(s_, _item, _pOut, end_)\n _pOut := skipWhitespace(pKeyEnd_, end_)\n // If ':'.\n if eq(chr(_pOut), 58) {\n _item, _pOut := parseValue(s_, _item, add(_pOut, 1), end_)\n if _item {\n // forgefmt: disable-next-item\n mstore(_item, setP(setP(or(_PARENT_IS_OBJECT, mload(_item)),\n _BITPOS_KEY_LENGTH, sub(pKeyEnd_, pKeyStart_)),\n _BITPOS_KEY, sub(pKeyStart_, add(s_, 0x20))))\n let c_ := chr(_pOut)\n if eq(c_, 125) { break } // '}'.\n if eq(c_, 44) { continue } // ','.\n }\n }\n _pOut := end_\n }\n _pOut := add(_pOut, 1)\n packed_ := setP(packed_, _BITPOS_CHILD, _item)\n _item := mallocItem(s_, packed_, pIn_, _pOut, TYPE_OBJECT)\n }\n\n function checkStringU(p_, o_) {\n // If not in '0123456789abcdefABCDEF', revert.\n if iszero(and(shr(sub(chr(add(p_, o_)), 48), 0x7e0000007e03ff), 1)) { fail() }\n if iszero(eq(o_, 5)) { checkStringU(p_, add(o_, 1)) }\n }\n\n function parseStringSub(s_, packed_, pIn_, end_) -> _pOut {\n if iszero(lt(pIn_, end_)) { fail() }\n for { _pOut := add(pIn_, 1) } 1 {} {\n let c_ := chr(_pOut)\n if eq(c_, 34) { break } // '\"'.\n // Not '\\'.\n if iszero(eq(c_, 92)) {\n _pOut := add(_pOut, 1)\n continue\n }\n c_ := chr(add(_pOut, 1))\n // '\"', '\\', '//', 'b', 'f', 'n', 'r', 't'.\n if and(shr(sub(c_, 34), 0x510110400000000002001), 1) {\n _pOut := add(_pOut, 2)\n continue\n }\n // 'u'.\n if eq(c_, 117) {\n checkStringU(_pOut, 2)\n _pOut := add(_pOut, 6)\n continue\n }\n _pOut := end_\n break\n }\n if iszero(lt(_pOut, end_)) { fail() }\n _pOut := add(_pOut, 1)\n }\n\n function skip0To9s(pIn_, end_, atLeastOne_) -> _pOut {\n for { _pOut := pIn_ } 1 { _pOut := add(_pOut, 1) } {\n if iszero(lt(sub(chr(_pOut), 48), 10)) { break } // Not '0'..'9'.\n }\n if and(atLeastOne_, eq(pIn_, _pOut)) { fail() }\n }\n\n function parseNumber(s_, packed_, pIn_, end_) -> _item, _pOut {\n _pOut := pIn_\n if eq(chr(_pOut), 45) { _pOut := add(_pOut, 1) } // '-'.\n if iszero(lt(sub(chr(_pOut), 48), 10)) { fail() } // Not '0'..'9'.\n let c_ := chr(_pOut)\n _pOut := add(_pOut, 1)\n if iszero(eq(c_, 48)) { _pOut := skip0To9s(_pOut, end_, 0) } // Not '0'.\n if eq(chr(_pOut), 46) { _pOut := skip0To9s(add(_pOut, 1), end_, 1) } // '.'.\n let t_ := mload(_pOut)\n // 'E', 'e'.\n if eq(or(0x20, byte(0, t_)), 101) {\n // forgefmt: disable-next-item\n _pOut := skip0To9s(add(byte(sub(byte(1, t_), 14), 0x010001), // '+', '-'.\n add(_pOut, 1)), end_, 1)\n }\n _item := mallocItem(s_, packed_, pIn_, _pOut, TYPE_NUMBER)\n }\n\n function copyStr(s_, offset_, len_) -> _sCopy {\n _sCopy := mload(0x40)\n s_ := add(s_, offset_)\n let w_ := not(0x1f)\n for { let i_ := and(add(len_, 0x1f), w_) } 1 {} {\n mstore(add(_sCopy, i_), mload(add(s_, i_)))\n i_ := add(i_, w_) // `sub(i_, 0x20)`.\n if iszero(i_) { break }\n }\n mstore(_sCopy, len_) // Copy the length.\n mstore(add(add(_sCopy, 0x20), len_), 0) // Zeroize the last slot.\n mstore(0x40, add(add(_sCopy, 0x40), len_)) // Allocate memory.\n }\n\n function value(item_) -> _value {\n let packed_ := mload(item_)\n _value := getP(packed_, _BITPOS_VALUE) // The offset in the string.\n if iszero(and(_VALUE_INITED, packed_)) {\n let s_ := getP(packed_, _BITPOS_STRING)\n _value := copyStr(s_, _value, getP(packed_, _BITPOS_VALUE_LENGTH))\n packed_ := setP(packed_, _BITPOS_VALUE, _value)\n mstore(s_, or(_VALUE_INITED, packed_))\n }\n }\n\n function children(item_) -> _arr {\n _arr := 0x60 // Initialize to the zero pointer.\n let packed_ := mload(item_)\n for {} iszero(gt(and(_BITMASK_TYPE, packed_), TYPE_OBJECT)) {} {\n if or(iszero(packed_), iszero(item_)) { break }\n if and(packed_, _CHILDREN_INITED) {\n _arr := getP(packed_, _BITPOS_CHILD)\n break\n }\n _arr := mload(0x40)\n let o_ := add(_arr, 0x20)\n for { let h_ := getP(packed_, _BITPOS_CHILD) } h_ {} {\n mstore(o_, h_)\n let q_ := mload(h_)\n let y_ := getP(q_, _BITPOS_SIBLING_OR_PARENT)\n mstore(h_, setP(q_, _BITPOS_SIBLING_OR_PARENT, item_))\n h_ := y_\n o_ := add(o_, 0x20)\n }\n let w_ := not(0x1f)\n let n_ := add(w_, sub(o_, _arr))\n mstore(_arr, shr(5, n_))\n mstore(0x40, o_) // Allocate memory.\n packed_ := setP(packed_, _BITPOS_CHILD, _arr)\n mstore(item_, or(_CHILDREN_INITED, packed_))\n // Reverse the array.\n if iszero(lt(n_, 0x40)) {\n let lo_ := add(_arr, 0x20)\n let hi_ := add(_arr, n_)\n for {} 1 {} {\n let temp_ := mload(lo_)\n mstore(lo_, mload(hi_))\n mstore(hi_, temp_)\n hi_ := add(hi_, w_)\n lo_ := add(lo_, 0x20)\n if iszero(lt(lo_, hi_)) { break }\n }\n }\n break\n }\n }\n\n function getStr(item_, bitpos_, bitposLength_, bitmaskInited_) -> _result {\n _result := 0x60 // Initialize to the zero pointer.\n let packed_ := mload(item_)\n if or(iszero(item_), iszero(packed_)) { leave }\n _result := getP(packed_, bitpos_)\n if iszero(and(bitmaskInited_, packed_)) {\n let s_ := getP(packed_, _BITPOS_STRING)\n _result := copyStr(s_, _result, getP(packed_, bitposLength_))\n mstore(item_, or(bitmaskInited_, setP(packed_, bitpos_, _result)))\n }\n }\n\n switch mode\n // Get value.\n case 0 { result := getStr(input, _BITPOS_VALUE, _BITPOS_VALUE_LENGTH, _VALUE_INITED) }\n // Get key.\n case 1 { result := getStr(input, _BITPOS_KEY, _BITPOS_KEY_LENGTH, _KEY_INITED) }\n // Get children.\n case 3 { result := children(input) }\n // Parse.\n default {\n let p := add(input, 0x20)\n let e := add(p, mload(input))\n if iszero(eq(p, e)) {\n let c := chr(e)\n mstore8(e, 34) // Place a '\"' at the end to speed up parsing.\n // The `34 << 248` makes `mallocItem` preserve '\"' at the end.\n mstore(0x00, setP(shl(248, 34), _BITPOS_STRING, input))\n result, p := parseValue(input, 0, p, e)\n mstore8(e, c) // Restore the original char at the end.\n }\n if or(lt(p, e), iszero(result)) { fail() }\n }\n }\n }\n\n /// @dev Casts the input to a bytes32.\n function _toInput(string memory input) private pure returns (bytes32 result) {\n /// @solidity memory-safe-assembly\n assembly {\n result := input\n }\n }\n\n /// @dev Casts the input to a bytes32.\n function _toInput(Item memory input) private pure returns (bytes32 result) {\n /// @solidity memory-safe-assembly\n assembly {\n result := input\n }\n }\n}\n"},"lib/solady/src/utils/LibString.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\n/// @notice Library for converting numbers into strings and other string operations.\n/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibString.sol)\n/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/LibString.sol)\n///\n/// Note:\n/// For performance and bytecode compactness, most of the string operations are restricted to\n/// byte strings (7-bit ASCII), except where otherwise specified.\n/// Usage of byte string operations on charsets with runes spanning two or more bytes\n/// can lead to undefined behavior.\nlibrary LibString {\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* CUSTOM ERRORS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev The length of the output is too small to contain all the hex digits.\n error HexLengthInsufficient();\n\n /// @dev The length of the string is more than 32 bytes.\n error TooBigForSmallString();\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* CONSTANTS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev The constant returned when the `search` is not found in the string.\n uint256 internal constant NOT_FOUND = type(uint256).max;\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* DECIMAL OPERATIONS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev Returns the base 10 decimal representation of `value`.\n function toString(uint256 value) internal pure returns (string memory str) {\n /// @solidity memory-safe-assembly\n assembly {\n // The maximum value of a uint256 contains 78 digits (1 byte per digit), but\n // we allocate 0xa0 bytes to keep the free memory pointer 32-byte word aligned.\n // We will need 1 word for the trailing zeros padding, 1 word for the length,\n // and 3 words for a maximum of 78 digits.\n str := add(mload(0x40), 0x80)\n // Update the free memory pointer to allocate.\n mstore(0x40, add(str, 0x20))\n // Zeroize the slot after the string.\n mstore(str, 0)\n\n // Cache the end of the memory to calculate the length later.\n let end := str\n\n let w := not(0) // Tsk.\n // We write the string from rightmost digit to leftmost digit.\n // The following is essentially a do-while loop that also handles the zero case.\n for { let temp := value } 1 {} {\n str := add(str, w) // `sub(str, 1)`.\n // Write the character to the pointer.\n // The ASCII index of the '0' character is 48.\n mstore8(str, add(48, mod(temp, 10)))\n // Keep dividing `temp` until zero.\n temp := div(temp, 10)\n if iszero(temp) { break }\n }\n\n let length := sub(end, str)\n // Move the pointer 32 bytes leftwards to make room for the length.\n str := sub(str, 0x20)\n // Store the length.\n mstore(str, length)\n }\n }\n\n /// @dev Returns the base 10 decimal representation of `value`.\n function toString(int256 value) internal pure returns (string memory str) {\n if (value >= 0) {\n return toString(uint256(value));\n }\n unchecked {\n str = toString(uint256(-value));\n }\n /// @solidity memory-safe-assembly\n assembly {\n // We still have some spare memory space on the left,\n // as we have allocated 3 words (96 bytes) for up to 78 digits.\n let length := mload(str) // Load the string length.\n mstore(str, 0x2d) // Store the '-' character.\n str := sub(str, 1) // Move back the string pointer by a byte.\n mstore(str, add(length, 1)) // Update the string length.\n }\n }\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* HEXADECIMAL OPERATIONS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev Returns the hexadecimal representation of `value`,\n /// left-padded to an input length of `length` bytes.\n /// The output is prefixed with \"0x\" encoded using 2 hexadecimal digits per byte,\n /// giving a total length of `length * 2 + 2` bytes.\n /// Reverts if `length` is too small for the output to contain all the digits.\n function toHexString(uint256 value, uint256 length) internal pure returns (string memory str) {\n str = toHexStringNoPrefix(value, length);\n /// @solidity memory-safe-assembly\n assembly {\n let strLength := add(mload(str), 2) // Compute the length.\n mstore(str, 0x3078) // Write the \"0x\" prefix.\n str := sub(str, 2) // Move the pointer.\n mstore(str, strLength) // Write the length.\n }\n }\n\n /// @dev Returns the hexadecimal representation of `value`,\n /// left-padded to an input length of `length` bytes.\n /// The output is prefixed with \"0x\" encoded using 2 hexadecimal digits per byte,\n /// giving a total length of `length * 2` bytes.\n /// Reverts if `length` is too small for the output to contain all the digits.\n function toHexStringNoPrefix(uint256 value, uint256 length)\n internal\n pure\n returns (string memory str)\n {\n /// @solidity memory-safe-assembly\n assembly {\n // We need 0x20 bytes for the trailing zeros padding, `length * 2` bytes\n // for the digits, 0x02 bytes for the prefix, and 0x20 bytes for the length.\n // We add 0x20 to the total and round down to a multiple of 0x20.\n // (0x20 + 0x20 + 0x02 + 0x20) = 0x62.\n str := add(mload(0x40), and(add(shl(1, length), 0x42), not(0x1f)))\n // Allocate the memory.\n mstore(0x40, add(str, 0x20))\n // Zeroize the slot after the string.\n mstore(str, 0)\n\n // Cache the end to calculate the length later.\n let end := str\n // Store \"0123456789abcdef\" in scratch space.\n mstore(0x0f, 0x30313233343536373839616263646566)\n\n let start := sub(str, add(length, length))\n let w := not(1) // Tsk.\n let temp := value\n // We write the string from rightmost digit to leftmost digit.\n // The following is essentially a do-while loop that also handles the zero case.\n for {} 1 {} {\n str := add(str, w) // `sub(str, 2)`.\n mstore8(add(str, 1), mload(and(temp, 15)))\n mstore8(str, mload(and(shr(4, temp), 15)))\n temp := shr(8, temp)\n if iszero(xor(str, start)) { break }\n }\n\n if temp {\n mstore(0x00, 0x2194895a) // `HexLengthInsufficient()`.\n revert(0x1c, 0x04)\n }\n\n // Compute the string's length.\n let strLength := sub(end, str)\n // Move the pointer and write the length.\n str := sub(str, 0x20)\n mstore(str, strLength)\n }\n }\n\n /// @dev Returns the hexadecimal representation of `value`.\n /// The output is prefixed with \"0x\" and encoded using 2 hexadecimal digits per byte.\n /// As address are 20 bytes long, the output will left-padded to have\n /// a length of `20 * 2 + 2` bytes.\n function toHexString(uint256 value) internal pure returns (string memory str) {\n str = toHexStringNoPrefix(value);\n /// @solidity memory-safe-assembly\n assembly {\n let strLength := add(mload(str), 2) // Compute the length.\n mstore(str, 0x3078) // Write the \"0x\" prefix.\n str := sub(str, 2) // Move the pointer.\n mstore(str, strLength) // Write the length.\n }\n }\n\n /// @dev Returns the hexadecimal representation of `value`.\n /// The output is prefixed with \"0x\".\n /// The output excludes leading \"0\" from the `toHexString` output.\n /// `0x00: \"0x0\", 0x01: \"0x1\", 0x12: \"0x12\", 0x123: \"0x123\"`.\n function toMinimalHexString(uint256 value) internal pure returns (string memory str) {\n str = toHexStringNoPrefix(value);\n /// @solidity memory-safe-assembly\n assembly {\n let o := eq(byte(0, mload(add(str, 0x20))), 0x30) // Whether leading zero is present.\n let strLength := add(mload(str), 2) // Compute the length.\n mstore(add(str, o), 0x3078) // Write the \"0x\" prefix, accounting for leading zero.\n str := sub(add(str, o), 2) // Move the pointer, accounting for leading zero.\n mstore(str, sub(strLength, o)) // Write the length, accounting for leading zero.\n }\n }\n\n /// @dev Returns the hexadecimal representation of `value`.\n /// The output excludes leading \"0\" from the `toHexStringNoPrefix` output.\n /// `0x00: \"0\", 0x01: \"1\", 0x12: \"12\", 0x123: \"123\"`.\n function toMinimalHexStringNoPrefix(uint256 value) internal pure returns (string memory str) {\n str = toHexStringNoPrefix(value);\n /// @solidity memory-safe-assembly\n assembly {\n let o := eq(byte(0, mload(add(str, 0x20))), 0x30) // Whether leading zero is present.\n let strLength := mload(str) // Get the length.\n str := add(str, o) // Move the pointer, accounting for leading zero.\n mstore(str, sub(strLength, o)) // Write the length, accounting for leading zero.\n }\n }\n\n /// @dev Returns the hexadecimal representation of `value`.\n /// The output is encoded using 2 hexadecimal digits per byte.\n /// As address are 20 bytes long, the output will left-padded to have\n /// a length of `20 * 2` bytes.\n function toHexStringNoPrefix(uint256 value) internal pure returns (string memory str) {\n /// @solidity memory-safe-assembly\n assembly {\n // We need 0x20 bytes for the trailing zeros padding, 0x20 bytes for the length,\n // 0x02 bytes for the prefix, and 0x40 bytes for the digits.\n // The next multiple of 0x20 above (0x20 + 0x20 + 0x02 + 0x40) is 0xa0.\n str := add(mload(0x40), 0x80)\n // Allocate the memory.\n mstore(0x40, add(str, 0x20))\n // Zeroize the slot after the string.\n mstore(str, 0)\n\n // Cache the end to calculate the length later.\n let end := str\n // Store \"0123456789abcdef\" in scratch space.\n mstore(0x0f, 0x30313233343536373839616263646566)\n\n let w := not(1) // Tsk.\n // We write the string from rightmost digit to leftmost digit.\n // The following is essentially a do-while loop that also handles the zero case.\n for { let temp := value } 1 {} {\n str := add(str, w) // `sub(str, 2)`.\n mstore8(add(str, 1), mload(and(temp, 15)))\n mstore8(str, mload(and(shr(4, temp), 15)))\n temp := shr(8, temp)\n if iszero(temp) { break }\n }\n\n // Compute the string's length.\n let strLength := sub(end, str)\n // Move the pointer and write the length.\n str := sub(str, 0x20)\n mstore(str, strLength)\n }\n }\n\n /// @dev Returns the hexadecimal representation of `value`.\n /// The output is prefixed with \"0x\", encoded using 2 hexadecimal digits per byte,\n /// and the alphabets are capitalized conditionally according to\n /// https://eips.ethereum.org/EIPS/eip-55\n function toHexStringChecksummed(address value) internal pure returns (string memory str) {\n str = toHexString(value);\n /// @solidity memory-safe-assembly\n assembly {\n let mask := shl(6, div(not(0), 255)) // `0b010000000100000000 ...`\n let o := add(str, 0x22)\n let hashed := and(keccak256(o, 40), mul(34, mask)) // `0b10001000 ... `\n let t := shl(240, 136) // `0b10001000 << 240`\n for { let i := 0 } 1 {} {\n mstore(add(i, i), mul(t, byte(i, hashed)))\n i := add(i, 1)\n if eq(i, 20) { break }\n }\n mstore(o, xor(mload(o), shr(1, and(mload(0x00), and(mload(o), mask)))))\n o := add(o, 0x20)\n mstore(o, xor(mload(o), shr(1, and(mload(0x20), and(mload(o), mask)))))\n }\n }\n\n /// @dev Returns the hexadecimal representation of `value`.\n /// The output is prefixed with \"0x\" and encoded using 2 hexadecimal digits per byte.\n function toHexString(address value) internal pure returns (string memory str) {\n str = toHexStringNoPrefix(value);\n /// @solidity memory-safe-assembly\n assembly {\n let strLength := add(mload(str), 2) // Compute the length.\n mstore(str, 0x3078) // Write the \"0x\" prefix.\n str := sub(str, 2) // Move the pointer.\n mstore(str, strLength) // Write the length.\n }\n }\n\n /// @dev Returns the hexadecimal representation of `value`.\n /// The output is encoded using 2 hexadecimal digits per byte.\n function toHexStringNoPrefix(address value) internal pure returns (string memory str) {\n /// @solidity memory-safe-assembly\n assembly {\n str := mload(0x40)\n\n // Allocate the memory.\n // We need 0x20 bytes for the trailing zeros padding, 0x20 bytes for the length,\n // 0x02 bytes for the prefix, and 0x28 bytes for the digits.\n // The next multiple of 0x20 above (0x20 + 0x20 + 0x02 + 0x28) is 0x80.\n mstore(0x40, add(str, 0x80))\n\n // Store \"0123456789abcdef\" in scratch space.\n mstore(0x0f, 0x30313233343536373839616263646566)\n\n str := add(str, 2)\n mstore(str, 40)\n\n let o := add(str, 0x20)\n mstore(add(o, 40), 0)\n\n value := shl(96, value)\n\n // We write the string from rightmost digit to leftmost digit.\n // The following is essentially a do-while loop that also handles the zero case.\n for { let i := 0 } 1 {} {\n let p := add(o, add(i, i))\n let temp := byte(i, value)\n mstore8(add(p, 1), mload(and(temp, 15)))\n mstore8(p, mload(shr(4, temp)))\n i := add(i, 1)\n if eq(i, 20) { break }\n }\n }\n }\n\n /// @dev Returns the hex encoded string from the raw bytes.\n /// The output is encoded using 2 hexadecimal digits per byte.\n function toHexString(bytes memory raw) internal pure returns (string memory str) {\n str = toHexStringNoPrefix(raw);\n /// @solidity memory-safe-assembly\n assembly {\n let strLength := add(mload(str), 2) // Compute the length.\n mstore(str, 0x3078) // Write the \"0x\" prefix.\n str := sub(str, 2) // Move the pointer.\n mstore(str, strLength) // Write the length.\n }\n }\n\n /// @dev Returns the hex encoded string from the raw bytes.\n /// The output is encoded using 2 hexadecimal digits per byte.\n function toHexStringNoPrefix(bytes memory raw) internal pure returns (string memory str) {\n /// @solidity memory-safe-assembly\n assembly {\n let length := mload(raw)\n str := add(mload(0x40), 2) // Skip 2 bytes for the optional prefix.\n mstore(str, add(length, length)) // Store the length of the output.\n\n // Store \"0123456789abcdef\" in scratch space.\n mstore(0x0f, 0x30313233343536373839616263646566)\n\n let o := add(str, 0x20)\n let end := add(raw, length)\n\n for {} iszero(eq(raw, end)) {} {\n raw := add(raw, 1)\n mstore8(add(o, 1), mload(and(mload(raw), 15)))\n mstore8(o, mload(and(shr(4, mload(raw)), 15)))\n o := add(o, 2)\n }\n mstore(o, 0) // Zeroize the slot after the string.\n mstore(0x40, add(o, 0x20)) // Allocate the memory.\n }\n }\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* RUNE STRING OPERATIONS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev Returns the number of UTF characters in the string.\n function runeCount(string memory s) internal pure returns (uint256 result) {\n /// @solidity memory-safe-assembly\n assembly {\n if mload(s) {\n mstore(0x00, div(not(0), 255))\n mstore(0x20, 0x0202020202020202020202020202020202020202020202020303030304040506)\n let o := add(s, 0x20)\n let end := add(o, mload(s))\n for { result := 1 } 1 { result := add(result, 1) } {\n o := add(o, byte(0, mload(shr(250, mload(o)))))\n if iszero(lt(o, end)) { break }\n }\n }\n }\n }\n\n /// @dev Returns if this string is a 7-bit ASCII string.\n /// (i.e. all characters codes are in [0..127])\n function is7BitASCII(string memory s) internal pure returns (bool result) {\n /// @solidity memory-safe-assembly\n assembly {\n let mask := shl(7, div(not(0), 255))\n result := 1\n let n := mload(s)\n if n {\n let o := add(s, 0x20)\n let end := add(o, n)\n let last := mload(end)\n mstore(end, 0)\n for {} 1 {} {\n if and(mask, mload(o)) {\n result := 0\n break\n }\n o := add(o, 0x20)\n if iszero(lt(o, end)) { break }\n }\n mstore(end, last)\n }\n }\n }\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* BYTE STRING OPERATIONS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n // For performance and bytecode compactness, byte string operations are restricted\n // to 7-bit ASCII strings. All offsets are byte offsets, not UTF character offsets.\n // Usage of byte string operations on charsets with runes spanning two or more bytes\n // can lead to undefined behavior.\n\n /// @dev Returns `subject` all occurrences of `search` replaced with `replacement`.\n function replace(string memory subject, string memory search, string memory replacement)\n internal\n pure\n returns (string memory result)\n {\n /// @solidity memory-safe-assembly\n assembly {\n let subjectLength := mload(subject)\n let searchLength := mload(search)\n let replacementLength := mload(replacement)\n\n subject := add(subject, 0x20)\n search := add(search, 0x20)\n replacement := add(replacement, 0x20)\n result := add(mload(0x40), 0x20)\n\n let subjectEnd := add(subject, subjectLength)\n if iszero(gt(searchLength, subjectLength)) {\n let subjectSearchEnd := add(sub(subjectEnd, searchLength), 1)\n let h := 0\n if iszero(lt(searchLength, 0x20)) { h := keccak256(search, searchLength) }\n let m := shl(3, sub(0x20, and(searchLength, 0x1f)))\n let s := mload(search)\n for {} 1 {} {\n let t := mload(subject)\n // Whether the first `searchLength % 32` bytes of\n // `subject` and `search` matches.\n if iszero(shr(m, xor(t, s))) {\n if h {\n if iszero(eq(keccak256(subject, searchLength), h)) {\n mstore(result, t)\n result := add(result, 1)\n subject := add(subject, 1)\n if iszero(lt(subject, subjectSearchEnd)) { break }\n continue\n }\n }\n // Copy the `replacement` one word at a time.\n for { let o := 0 } 1 {} {\n mstore(add(result, o), mload(add(replacement, o)))\n o := add(o, 0x20)\n if iszero(lt(o, replacementLength)) { break }\n }\n result := add(result, replacementLength)\n subject := add(subject, searchLength)\n if searchLength {\n if iszero(lt(subject, subjectSearchEnd)) { break }\n continue\n }\n }\n mstore(result, t)\n result := add(result, 1)\n subject := add(subject, 1)\n if iszero(lt(subject, subjectSearchEnd)) { break }\n }\n }\n\n let resultRemainder := result\n result := add(mload(0x40), 0x20)\n let k := add(sub(resultRemainder, result), sub(subjectEnd, subject))\n // Copy the rest of the string one word at a time.\n for {} lt(subject, subjectEnd) {} {\n mstore(resultRemainder, mload(subject))\n resultRemainder := add(resultRemainder, 0x20)\n subject := add(subject, 0x20)\n }\n result := sub(result, 0x20)\n let last := add(add(result, 0x20), k) // Zeroize the slot after the string.\n mstore(last, 0)\n mstore(0x40, add(last, 0x20)) // Allocate the memory.\n mstore(result, k) // Store the length.\n }\n }\n\n /// @dev Returns the byte index of the first location of `search` in `subject`,\n /// searching from left to right, starting from `from`.\n /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.\n function indexOf(string memory subject, string memory search, uint256 from)\n internal\n pure\n returns (uint256 result)\n {\n /// @solidity memory-safe-assembly\n assembly {\n for { let subjectLength := mload(subject) } 1 {} {\n if iszero(mload(search)) {\n if iszero(gt(from, subjectLength)) {\n result := from\n break\n }\n result := subjectLength\n break\n }\n let searchLength := mload(search)\n let subjectStart := add(subject, 0x20)\n\n result := not(0) // Initialize to `NOT_FOUND`.\n\n subject := add(subjectStart, from)\n let end := add(sub(add(subjectStart, subjectLength), searchLength), 1)\n\n let m := shl(3, sub(0x20, and(searchLength, 0x1f)))\n let s := mload(add(search, 0x20))\n\n if iszero(and(lt(subject, end), lt(from, subjectLength))) { break }\n\n if iszero(lt(searchLength, 0x20)) {\n for { let h := keccak256(add(search, 0x20), searchLength) } 1 {} {\n if iszero(shr(m, xor(mload(subject), s))) {\n if eq(keccak256(subject, searchLength), h) {\n result := sub(subject, subjectStart)\n break\n }\n }\n subject := add(subject, 1)\n if iszero(lt(subject, end)) { break }\n }\n break\n }\n for {} 1 {} {\n if iszero(shr(m, xor(mload(subject), s))) {\n result := sub(subject, subjectStart)\n break\n }\n subject := add(subject, 1)\n if iszero(lt(subject, end)) { break }\n }\n break\n }\n }\n }\n\n /// @dev Returns the byte index of the first location of `search` in `subject`,\n /// searching from left to right.\n /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.\n function indexOf(string memory subject, string memory search)\n internal\n pure\n returns (uint256 result)\n {\n result = indexOf(subject, search, 0);\n }\n\n /// @dev Returns the byte index of the first location of `search` in `subject`,\n /// searching from right to left, starting from `from`.\n /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.\n function lastIndexOf(string memory subject, string memory search, uint256 from)\n internal\n pure\n returns (uint256 result)\n {\n /// @solidity memory-safe-assembly\n assembly {\n for {} 1 {} {\n result := not(0) // Initialize to `NOT_FOUND`.\n let searchLength := mload(search)\n if gt(searchLength, mload(subject)) { break }\n let w := result\n\n let fromMax := sub(mload(subject), searchLength)\n if iszero(gt(fromMax, from)) { from := fromMax }\n\n let end := add(add(subject, 0x20), w)\n subject := add(add(subject, 0x20), from)\n if iszero(gt(subject, end)) { break }\n // As this function is not too often used,\n // we shall simply use keccak256 for smaller bytecode size.\n for { let h := keccak256(add(search, 0x20), searchLength) } 1 {} {\n if eq(keccak256(subject, searchLength), h) {\n result := sub(subject, add(end, 1))\n break\n }\n subject := add(subject, w) // `sub(subject, 1)`.\n if iszero(gt(subject, end)) { break }\n }\n break\n }\n }\n }\n\n /// @dev Returns the byte index of the first location of `search` in `subject`,\n /// searching from right to left.\n /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.\n function lastIndexOf(string memory subject, string memory search)\n internal\n pure\n returns (uint256 result)\n {\n result = lastIndexOf(subject, search, uint256(int256(-1)));\n }\n\n /// @dev Returns true if `search` is found in `subject`, false otherwise.\n function contains(string memory subject, string memory search) internal pure returns (bool) {\n return indexOf(subject, search) != NOT_FOUND;\n }\n\n /// @dev Returns whether `subject` starts with `search`.\n function startsWith(string memory subject, string memory search)\n internal\n pure\n returns (bool result)\n {\n /// @solidity memory-safe-assembly\n assembly {\n let searchLength := mload(search)\n // Just using keccak256 directly is actually cheaper.\n // forgefmt: disable-next-item\n result := and(\n iszero(gt(searchLength, mload(subject))),\n eq(\n keccak256(add(subject, 0x20), searchLength),\n keccak256(add(search, 0x20), searchLength)\n )\n )\n }\n }\n\n /// @dev Returns whether `subject` ends with `search`.\n function endsWith(string memory subject, string memory search)\n internal\n pure\n returns (bool result)\n {\n /// @solidity memory-safe-assembly\n assembly {\n let searchLength := mload(search)\n let subjectLength := mload(subject)\n // Whether `search` is not longer than `subject`.\n let withinRange := iszero(gt(searchLength, subjectLength))\n // Just using keccak256 directly is actually cheaper.\n // forgefmt: disable-next-item\n result := and(\n withinRange,\n eq(\n keccak256(\n // `subject + 0x20 + max(subjectLength - searchLength, 0)`.\n add(add(subject, 0x20), mul(withinRange, sub(subjectLength, searchLength))),\n searchLength\n ),\n keccak256(add(search, 0x20), searchLength)\n )\n )\n }\n }\n\n /// @dev Returns `subject` repeated `times`.\n function repeat(string memory subject, uint256 times)\n internal\n pure\n returns (string memory result)\n {\n /// @solidity memory-safe-assembly\n assembly {\n let subjectLength := mload(subject)\n if iszero(or(iszero(times), iszero(subjectLength))) {\n subject := add(subject, 0x20)\n result := mload(0x40)\n let output := add(result, 0x20)\n for {} 1 {} {\n // Copy the `subject` one word at a time.\n for { let o := 0 } 1 {} {\n mstore(add(output, o), mload(add(subject, o)))\n o := add(o, 0x20)\n if iszero(lt(o, subjectLength)) { break }\n }\n output := add(output, subjectLength)\n times := sub(times, 1)\n if iszero(times) { break }\n }\n mstore(output, 0) // Zeroize the slot after the string.\n let resultLength := sub(output, add(result, 0x20))\n mstore(result, resultLength) // Store the length.\n // Allocate the memory.\n mstore(0x40, add(result, add(resultLength, 0x20)))\n }\n }\n }\n\n /// @dev Returns a copy of `subject` sliced from `start` to `end` (exclusive).\n /// `start` and `end` are byte offsets.\n function slice(string memory subject, uint256 start, uint256 end)\n internal\n pure\n returns (string memory result)\n {\n /// @solidity memory-safe-assembly\n assembly {\n let subjectLength := mload(subject)\n if iszero(gt(subjectLength, end)) { end := subjectLength }\n if iszero(gt(subjectLength, start)) { start := subjectLength }\n if lt(start, end) {\n result := mload(0x40)\n let resultLength := sub(end, start)\n mstore(result, resultLength)\n subject := add(subject, start)\n let w := not(0x1f)\n // Copy the `subject` one word at a time, backwards.\n for { let o := and(add(resultLength, 0x1f), w) } 1 {} {\n mstore(add(result, o), mload(add(subject, o)))\n o := add(o, w) // `sub(o, 0x20)`.\n if iszero(o) { break }\n }\n // Zeroize the slot after the string.\n mstore(add(add(result, 0x20), resultLength), 0)\n // Allocate memory for the length and the bytes,\n // rounded up to a multiple of 32.\n mstore(0x40, add(result, and(add(resultLength, 0x3f), w)))\n }\n }\n }\n\n /// @dev Returns a copy of `subject` sliced from `start` to the end of the string.\n /// `start` is a byte offset.\n function slice(string memory subject, uint256 start)\n internal\n pure\n returns (string memory result)\n {\n result = slice(subject, start, uint256(int256(-1)));\n }\n\n /// @dev Returns all the indices of `search` in `subject`.\n /// The indices are byte offsets.\n function indicesOf(string memory subject, string memory search)\n internal\n pure\n returns (uint256[] memory result)\n {\n /// @solidity memory-safe-assembly\n assembly {\n let subjectLength := mload(subject)\n let searchLength := mload(search)\n\n if iszero(gt(searchLength, subjectLength)) {\n subject := add(subject, 0x20)\n search := add(search, 0x20)\n result := add(mload(0x40), 0x20)\n\n let subjectStart := subject\n let subjectSearchEnd := add(sub(add(subject, subjectLength), searchLength), 1)\n let h := 0\n if iszero(lt(searchLength, 0x20)) { h := keccak256(search, searchLength) }\n let m := shl(3, sub(0x20, and(searchLength, 0x1f)))\n let s := mload(search)\n for {} 1 {} {\n let t := mload(subject)\n // Whether the first `searchLength % 32` bytes of\n // `subject` and `search` matches.\n if iszero(shr(m, xor(t, s))) {\n if h {\n if iszero(eq(keccak256(subject, searchLength), h)) {\n subject := add(subject, 1)\n if iszero(lt(subject, subjectSearchEnd)) { break }\n continue\n }\n }\n // Append to `result`.\n mstore(result, sub(subject, subjectStart))\n result := add(result, 0x20)\n // Advance `subject` by `searchLength`.\n subject := add(subject, searchLength)\n if searchLength {\n if iszero(lt(subject, subjectSearchEnd)) { break }\n continue\n }\n }\n subject := add(subject, 1)\n if iszero(lt(subject, subjectSearchEnd)) { break }\n }\n let resultEnd := result\n // Assign `result` to the free memory pointer.\n result := mload(0x40)\n // Store the length of `result`.\n mstore(result, shr(5, sub(resultEnd, add(result, 0x20))))\n // Allocate memory for result.\n // We allocate one more word, so this array can be recycled for {split}.\n mstore(0x40, add(resultEnd, 0x20))\n }\n }\n }\n\n /// @dev Returns a arrays of strings based on the `delimiter` inside of the `subject` string.\n function split(string memory subject, string memory delimiter)\n internal\n pure\n returns (string[] memory result)\n {\n uint256[] memory indices = indicesOf(subject, delimiter);\n /// @solidity memory-safe-assembly\n assembly {\n let w := not(0x1f)\n let indexPtr := add(indices, 0x20)\n let indicesEnd := add(indexPtr, shl(5, add(mload(indices), 1)))\n mstore(add(indicesEnd, w), mload(subject))\n mstore(indices, add(mload(indices), 1))\n let prevIndex := 0\n for {} 1 {} {\n let index := mload(indexPtr)\n mstore(indexPtr, 0x60)\n if iszero(eq(index, prevIndex)) {\n let element := mload(0x40)\n let elementLength := sub(index, prevIndex)\n mstore(element, elementLength)\n // Copy the `subject` one word at a time, backwards.\n for { let o := and(add(elementLength, 0x1f), w) } 1 {} {\n mstore(add(element, o), mload(add(add(subject, prevIndex), o)))\n o := add(o, w) // `sub(o, 0x20)`.\n if iszero(o) { break }\n }\n // Zeroize the slot after the string.\n mstore(add(add(element, 0x20), elementLength), 0)\n // Allocate memory for the length and the bytes,\n // rounded up to a multiple of 32.\n mstore(0x40, add(element, and(add(elementLength, 0x3f), w)))\n // Store the `element` into the array.\n mstore(indexPtr, element)\n }\n prevIndex := add(index, mload(delimiter))\n indexPtr := add(indexPtr, 0x20)\n if iszero(lt(indexPtr, indicesEnd)) { break }\n }\n result := indices\n if iszero(mload(delimiter)) {\n result := add(indices, 0x20)\n mstore(result, sub(mload(indices), 2))\n }\n }\n }\n\n /// @dev Returns a concatenated string of `a` and `b`.\n /// Cheaper than `string.concat()` and does not de-align the free memory pointer.\n function concat(string memory a, string memory b)\n internal\n pure\n returns (string memory result)\n {\n /// @solidity memory-safe-assembly\n assembly {\n let w := not(0x1f)\n result := mload(0x40)\n let aLength := mload(a)\n // Copy `a` one word at a time, backwards.\n for { let o := and(add(aLength, 0x20), w) } 1 {} {\n mstore(add(result, o), mload(add(a, o)))\n o := add(o, w) // `sub(o, 0x20)`.\n if iszero(o) { break }\n }\n let bLength := mload(b)\n let output := add(result, aLength)\n // Copy `b` one word at a time, backwards.\n for { let o := and(add(bLength, 0x20), w) } 1 {} {\n mstore(add(output, o), mload(add(b, o)))\n o := add(o, w) // `sub(o, 0x20)`.\n if iszero(o) { break }\n }\n let totalLength := add(aLength, bLength)\n let last := add(add(result, 0x20), totalLength)\n // Zeroize the slot after the string.\n mstore(last, 0)\n // Stores the length.\n mstore(result, totalLength)\n // Allocate memory for the length and the bytes,\n // rounded up to a multiple of 32.\n mstore(0x40, and(add(last, 0x1f), w))\n }\n }\n\n /// @dev Returns a copy of the string in either lowercase or UPPERCASE.\n /// WARNING! This function is only compatible with 7-bit ASCII strings.\n function toCase(string memory subject, bool toUpper)\n internal\n pure\n returns (string memory result)\n {\n /// @solidity memory-safe-assembly\n assembly {\n let length := mload(subject)\n if length {\n result := add(mload(0x40), 0x20)\n subject := add(subject, 1)\n let flags := shl(add(70, shl(5, toUpper)), 0x3ffffff)\n let w := not(0)\n for { let o := length } 1 {} {\n o := add(o, w)\n let b := and(0xff, mload(add(subject, o)))\n mstore8(add(result, o), xor(b, and(shr(b, flags), 0x20)))\n if iszero(o) { break }\n }\n result := mload(0x40)\n mstore(result, length) // Store the length.\n let last := add(add(result, 0x20), length)\n mstore(last, 0) // Zeroize the slot after the string.\n mstore(0x40, add(last, 0x20)) // Allocate the memory.\n }\n }\n }\n\n /// @dev Returns a string from a small bytes32 string.\n /// `s` must be null-terminated, or behavior will be undefined.\n function fromSmallString(bytes32 s) internal pure returns (string memory result) {\n /// @solidity memory-safe-assembly\n assembly {\n result := mload(0x40)\n let n := 0\n for {} byte(n, s) { n := add(n, 1) } {} // Scan for '\\0'.\n mstore(result, n)\n let o := add(result, 0x20)\n mstore(o, s)\n mstore(add(o, n), 0)\n mstore(0x40, add(result, 0x40))\n }\n }\n\n /// @dev Returns the small string, with all bytes after the first null byte zeroized.\n function normalizeSmallString(bytes32 s) internal pure returns (bytes32 result) {\n /// @solidity memory-safe-assembly\n assembly {\n for {} byte(result, s) { result := add(result, 1) } {} // Scan for '\\0'.\n mstore(0x00, s)\n mstore(result, 0x00)\n result := mload(0x00)\n }\n }\n\n /// @dev Returns the string as a normalized null-terminated small string.\n function toSmallString(string memory s) internal pure returns (bytes32 result) {\n /// @solidity memory-safe-assembly\n assembly {\n result := mload(s)\n if iszero(lt(result, 33)) {\n mstore(0x00, 0xec92f9a3) // `TooBigForSmallString()`.\n revert(0x1c, 0x04)\n }\n result := shl(shl(3, sub(32, result)), mload(add(s, result)))\n }\n }\n\n /// @dev Returns a lowercased copy of the string.\n /// WARNING! This function is only compatible with 7-bit ASCII strings.\n function lower(string memory subject) internal pure returns (string memory result) {\n result = toCase(subject, false);\n }\n\n /// @dev Returns an UPPERCASED copy of the string.\n /// WARNING! This function is only compatible with 7-bit ASCII strings.\n function upper(string memory subject) internal pure returns (string memory result) {\n result = toCase(subject, true);\n }\n\n /// @dev Escapes the string to be used within HTML tags.\n function escapeHTML(string memory s) internal pure returns (string memory result) {\n /// @solidity memory-safe-assembly\n assembly {\n let end := add(s, mload(s))\n result := add(mload(0x40), 0x20)\n // Store the bytes of the packed offsets and strides into the scratch space.\n // `packed = (stride << 5) | offset`. Max offset is 20. Max stride is 6.\n mstore(0x1f, 0x900094)\n mstore(0x08, 0xc0000000a6ab)\n // Store \""&'<>\" into the scratch space.\n mstore(0x00, shl(64, 0x2671756f743b26616d703b262333393b266c743b2667743b))\n for {} iszero(eq(s, end)) {} {\n s := add(s, 1)\n let c := and(mload(s), 0xff)\n // Not in `[\"\\\"\",\"'\",\"&\",\"<\",\">\"]`.\n if iszero(and(shl(c, 1), 0x500000c400000000)) {\n mstore8(result, c)\n result := add(result, 1)\n continue\n }\n let t := shr(248, mload(c))\n mstore(result, mload(and(t, 0x1f)))\n result := add(result, shr(5, t))\n }\n let last := result\n mstore(last, 0) // Zeroize the slot after the string.\n result := mload(0x40)\n mstore(result, sub(last, add(result, 0x20))) // Store the length.\n mstore(0x40, add(last, 0x20)) // Allocate the memory.\n }\n }\n\n /// @dev Escapes the string to be used within double-quotes in a JSON.\n /// If `addDoubleQuotes` is true, the result will be enclosed in double-quotes.\n function escapeJSON(string memory s, bool addDoubleQuotes)\n internal\n pure\n returns (string memory result)\n {\n /// @solidity memory-safe-assembly\n assembly {\n let end := add(s, mload(s))\n result := add(mload(0x40), 0x20)\n if addDoubleQuotes {\n mstore8(result, 34)\n result := add(1, result)\n }\n // Store \"\\\\u0000\" in scratch space.\n // Store \"0123456789abcdef\" in scratch space.\n // Also, store `{0x08:\"b\", 0x09:\"t\", 0x0a:\"n\", 0x0c:\"f\", 0x0d:\"r\"}`.\n // into the scratch space.\n mstore(0x15, 0x5c75303030303031323334353637383961626364656662746e006672)\n // Bitmask for detecting `[\"\\\"\",\"\\\\\"]`.\n let e := or(shl(0x22, 1), shl(0x5c, 1))\n for {} iszero(eq(s, end)) {} {\n s := add(s, 1)\n let c := and(mload(s), 0xff)\n if iszero(lt(c, 0x20)) {\n if iszero(and(shl(c, 1), e)) {\n // Not in `[\"\\\"\",\"\\\\\"]`.\n mstore8(result, c)\n result := add(result, 1)\n continue\n }\n mstore8(result, 0x5c) // \"\\\\\".\n mstore8(add(result, 1), c)\n result := add(result, 2)\n continue\n }\n if iszero(and(shl(c, 1), 0x3700)) {\n // Not in `[\"\\b\",\"\\t\",\"\\n\",\"\\f\",\"\\d\"]`.\n mstore8(0x1d, mload(shr(4, c))) // Hex value.\n mstore8(0x1e, mload(and(c, 15))) // Hex value.\n mstore(result, mload(0x19)) // \"\\\\u00XX\".\n result := add(result, 6)\n continue\n }\n mstore8(result, 0x5c) // \"\\\\\".\n mstore8(add(result, 1), mload(add(c, 8)))\n result := add(result, 2)\n }\n if addDoubleQuotes {\n mstore8(result, 34)\n result := add(1, result)\n }\n let last := result\n mstore(last, 0) // Zeroize the slot after the string.\n result := mload(0x40)\n mstore(result, sub(last, add(result, 0x20))) // Store the length.\n mstore(0x40, add(last, 0x20)) // Allocate the memory.\n }\n }\n\n /// @dev Escapes the string to be used within double-quotes in a JSON.\n function escapeJSON(string memory s) internal pure returns (string memory result) {\n result = escapeJSON(s, false);\n }\n\n /// @dev Returns whether `a` equals `b`.\n function eq(string memory a, string memory b) internal pure returns (bool result) {\n /// @solidity memory-safe-assembly\n assembly {\n result := eq(keccak256(add(a, 0x20), mload(a)), keccak256(add(b, 0x20), mload(b)))\n }\n }\n\n /// @dev Returns whether `a` equals `b`, where `b` is a null-terminated small string.\n function eqs(string memory a, bytes32 b) internal pure returns (bool result) {\n /// @solidity memory-safe-assembly\n assembly {\n // These should be evaluated on compile time, as far as possible.\n let m := not(shl(7, div(not(iszero(b)), 255))) // `0x7f7f ...`.\n let x := not(or(m, or(b, add(m, and(b, m)))))\n let r := shl(7, iszero(iszero(shr(128, x))))\n r := or(r, shl(6, iszero(iszero(shr(64, shr(r, x))))))\n r := or(r, shl(5, lt(0xffffffff, shr(r, x))))\n r := or(r, shl(4, lt(0xffff, shr(r, x))))\n r := or(r, shl(3, lt(0xff, shr(r, x))))\n // forgefmt: disable-next-item\n result := gt(eq(mload(a), add(iszero(x), xor(31, shr(3, r)))),\n xor(shr(add(8, r), b), shr(add(8, r), mload(add(a, 0x20)))))\n }\n }\n\n /// @dev Packs a single string with its length into a single word.\n /// Returns `bytes32(0)` if the length is zero or greater than 31.\n function packOne(string memory a) internal pure returns (bytes32 result) {\n /// @solidity memory-safe-assembly\n assembly {\n // We don't need to zero right pad the string,\n // since this is our own custom non-standard packing scheme.\n result :=\n mul(\n // Load the length and the bytes.\n mload(add(a, 0x1f)),\n // `length != 0 && length < 32`. Abuses underflow.\n // Assumes that the length is valid and within the block gas limit.\n lt(sub(mload(a), 1), 0x1f)\n )\n }\n }\n\n /// @dev Unpacks a string packed using {packOne}.\n /// Returns the empty string if `packed` is `bytes32(0)`.\n /// If `packed` is not an output of {packOne}, the output behavior is undefined.\n function unpackOne(bytes32 packed) internal pure returns (string memory result) {\n /// @solidity memory-safe-assembly\n assembly {\n // Grab the free memory pointer.\n result := mload(0x40)\n // Allocate 2 words (1 for the length, 1 for the bytes).\n mstore(0x40, add(result, 0x40))\n // Zeroize the length slot.\n mstore(result, 0)\n // Store the length and bytes.\n mstore(add(result, 0x1f), packed)\n // Right pad with zeroes.\n mstore(add(add(result, 0x20), mload(result)), 0)\n }\n }\n\n /// @dev Packs two strings with their lengths into a single word.\n /// Returns `bytes32(0)` if combined length is zero or greater than 30.\n function packTwo(string memory a, string memory b) internal pure returns (bytes32 result) {\n /// @solidity memory-safe-assembly\n assembly {\n let aLength := mload(a)\n // We don't need to zero right pad the strings,\n // since this is our own custom non-standard packing scheme.\n result :=\n mul(\n // Load the length and the bytes of `a` and `b`.\n or(\n shl(shl(3, sub(0x1f, aLength)), mload(add(a, aLength))),\n mload(sub(add(b, 0x1e), aLength))\n ),\n // `totalLength != 0 && totalLength < 31`. Abuses underflow.\n // Assumes that the lengths are valid and within the block gas limit.\n lt(sub(add(aLength, mload(b)), 1), 0x1e)\n )\n }\n }\n\n /// @dev Unpacks strings packed using {packTwo}.\n /// Returns the empty strings if `packed` is `bytes32(0)`.\n /// If `packed` is not an output of {packTwo}, the output behavior is undefined.\n function unpackTwo(bytes32 packed)\n internal\n pure\n returns (string memory resultA, string memory resultB)\n {\n /// @solidity memory-safe-assembly\n assembly {\n // Grab the free memory pointer.\n resultA := mload(0x40)\n resultB := add(resultA, 0x40)\n // Allocate 2 words for each string (1 for the length, 1 for the byte). Total 4 words.\n mstore(0x40, add(resultB, 0x40))\n // Zeroize the length slots.\n mstore(resultA, 0)\n mstore(resultB, 0)\n // Store the lengths and bytes.\n mstore(add(resultA, 0x1f), packed)\n mstore(add(resultB, 0x1f), mload(add(add(resultA, 0x20), mload(resultA))))\n // Right pad with zeroes.\n mstore(add(add(resultA, 0x20), mload(resultA)), 0)\n mstore(add(add(resultB, 0x20), mload(resultB)), 0)\n }\n }\n\n /// @dev Directly returns `a` without copying.\n function directReturn(string memory a) internal pure {\n assembly {\n // Assumes that the string does not start from the scratch space.\n let retStart := sub(a, 0x20)\n let retSize := add(mload(a), 0x40)\n // Right pad with zeroes. Just in case the string is produced\n // by a method that doesn't zero right pad.\n mstore(add(retStart, retSize), 0)\n // Store the return offset.\n mstore(retStart, 0x20)\n // End the transaction, returning the string.\n return(retStart, retSize)\n }\n }\n}\n"},"src/utils/DateTimeUtils.sol":{"content":"// SPDX-License-Identifier: GPL-3.0\npragma solidity ^0.8.0;\n\nimport {DateTimeLib} from \"solady/utils/DateTimeLib.sol\";\nimport {LibString} from \"solady/utils/LibString.sol\";\n\nlibrary DateTimeUtils {\n using LibString for string;\n\n /*\n * @dev Convert a DER-encoded time to a unix timestamp\n * @param x509Time The DER-encoded time\n * @return The unix timestamp\n */\n function fromDERToTimestamp(bytes memory x509Time) internal pure returns (uint256) {\n uint16 yrs;\n uint8 mnths;\n uint8 dys;\n uint8 hrs;\n uint8 mins;\n uint8 secs;\n uint8 offset;\n\n if (x509Time.length == 13) {\n if (uint8(x509Time[0]) - 48 < 5) yrs += 2000;\n else yrs += 1900;\n } else {\n yrs += (uint8(x509Time[0]) - 48) * 1000 + (uint8(x509Time[1]) - 48) * 100;\n offset = 2;\n }\n yrs += (uint8(x509Time[offset + 0]) - 48) * 10 + uint8(x509Time[offset + 1]) - 48;\n mnths = (uint8(x509Time[offset + 2]) - 48) * 10 + uint8(x509Time[offset + 3]) - 48;\n dys += (uint8(x509Time[offset + 4]) - 48) * 10 + uint8(x509Time[offset + 5]) - 48;\n hrs += (uint8(x509Time[offset + 6]) - 48) * 10 + uint8(x509Time[offset + 7]) - 48;\n mins += (uint8(x509Time[offset + 8]) - 48) * 10 + uint8(x509Time[offset + 9]) - 48;\n secs += (uint8(x509Time[offset + 10]) - 48) * 10 + uint8(x509Time[offset + 11]) - 48;\n\n return DateTimeLib.dateTimeToTimestamp(yrs, mnths, dys, hrs, mins, secs);\n }\n\n /// @dev iso follows pattern: \"YYYY-MM-DDTHH:mm:ssZ\"\n function fromISOToTimestamp(string memory iso) internal pure returns (uint256) {\n require(bytes(iso).length == 20, \"invalid iso string length\");\n uint256 y = stringToUint(iso.slice(0, 4));\n uint256 m = stringToUint(iso.slice(5, 7));\n uint256 d = stringToUint(iso.slice(8, 10));\n uint256 h = stringToUint(iso.slice(11, 13));\n uint256 min = stringToUint(iso.slice(14, 16));\n uint256 s = stringToUint(iso.slice(17, 19));\n\n return DateTimeLib.dateTimeToTimestamp(y, m, d, h, min, s);\n }\n\n // https://ethereum.stackexchange.com/questions/10932/how-to-convert-string-to-int\n function stringToUint(string memory s) private pure returns (uint256 result) {\n bytes memory b = bytes(s);\n result = 0;\n for (uint256 i = 0; i < b.length; i++) {\n uint256 c = uint256(uint8(b[i]));\n if (c >= 48 && c <= 57) {\n result = result * 10 + (c - 48);\n }\n }\n }\n}\n"},"src/utils/BytesUtils.sol":{"content":"// SPDX-License-Identifier: BSD 2-Clause License\npragma solidity ^0.8.0;\n\n// Inspired by ensdomains/dnssec-oracle - BSD-2-Clause license\n// https://github.com/ensdomains/dnssec-oracle/blob/master/contracts/BytesUtils.sol\n\nlibrary BytesUtils {\n /*\n * @dev Returns the keccak-256 hash of a byte range.\n * @param self The byte string to hash.\n * @param offset The position to start hashing at.\n * @param len The number of bytes to hash.\n * @return The hash of the byte range.\n */\n function keccak(bytes memory self, uint256 offset, uint256 len) internal pure returns (bytes32 ret) {\n require(offset + len <= self.length);\n assembly {\n ret := keccak256(add(add(self, 32), offset), len)\n }\n }\n\n /*\n * @dev Returns a positive number if `other` comes lexicographically after\n * `self`, a negative number if it comes before, or zero if the\n * contents of the two bytes are equal.\n * @param self The first bytes to compare.\n * @param other The second bytes to compare.\n * @return The result of the comparison.\n */\n function compare(bytes memory self, bytes memory other) internal pure returns (int256) {\n return compare(self, 0, self.length, other, 0, other.length);\n }\n\n /*\n * @dev Returns a positive number if `other` comes lexicographically after\n * `self`, a negative number if it comes before, or zero if the\n * contents of the two bytes are equal. Comparison is done per-rune,\n * on unicode codepoints.\n * @param self The first bytes to compare.\n * @param offset The offset of self.\n * @param len The length of self.\n * @param other The second bytes to compare.\n * @param otheroffset The offset of the other string.\n * @param otherlen The length of the other string.\n * @return The result of the comparison.\n */\n function compare(\n bytes memory self,\n uint256 offset,\n uint256 len,\n bytes memory other,\n uint256 otheroffset,\n uint256 otherlen\n ) internal pure returns (int256) {\n uint256 shortest = len;\n if (otherlen < len) {\n shortest = otherlen;\n }\n\n uint256 selfptr;\n uint256 otherptr;\n\n assembly {\n selfptr := add(self, add(offset, 32))\n otherptr := add(other, add(otheroffset, 32))\n }\n for (uint256 idx = 0; idx < shortest; idx += 32) {\n uint256 a;\n uint256 b;\n assembly {\n a := mload(selfptr)\n b := mload(otherptr)\n }\n if (a != b) {\n // Mask out irrelevant bytes and check again\n uint256 mask;\n if (shortest > 32) {\n mask = type(uint256).max; // aka 0xffffff....\n } else {\n mask = ~(2 ** (8 * (32 - shortest + idx)) - 1);\n }\n uint256 diff = (a & mask) - (b & mask);\n if (diff != 0) {\n return int256(diff);\n }\n }\n selfptr += 32;\n otherptr += 32;\n }\n\n return int256(len) - int256(otherlen);\n }\n\n /*\n * @dev Returns true if the two byte ranges are equal.\n * @param self The first byte range to compare.\n * @param offset The offset into the first byte range.\n * @param other The second byte range to compare.\n * @param otherOffset The offset into the second byte range.\n * @param len The number of bytes to compare\n * @return True if the byte ranges are equal, false otherwise.\n */\n function equals(bytes memory self, uint256 offset, bytes memory other, uint256 otherOffset, uint256 len)\n internal\n pure\n returns (bool)\n {\n return keccak(self, offset, len) == keccak(other, otherOffset, len);\n }\n\n /*\n * @dev Returns true if the two byte ranges are equal with offsets.\n * @param self The first byte range to compare.\n * @param offset The offset into the first byte range.\n * @param other The second byte range to compare.\n * @param otherOffset The offset into the second byte range.\n * @return True if the byte ranges are equal, false otherwise.\n */\n function equals(bytes memory self, uint256 offset, bytes memory other, uint256 otherOffset)\n internal\n pure\n returns (bool)\n {\n return keccak(self, offset, self.length - offset) == keccak(other, otherOffset, other.length - otherOffset);\n }\n\n /*\n * @dev Compares a range of 'self' to all of 'other' and returns True iff\n * they are equal.\n * @param self The first byte range to compare.\n * @param offset The offset into the first byte range.\n * @param other The second byte range to compare.\n * @return True if the byte ranges are equal, false otherwise.\n */\n function equals(bytes memory self, uint256 offset, bytes memory other) internal pure returns (bool) {\n return self.length >= offset + other.length && equals(self, offset, other, 0, other.length);\n }\n\n /*\n * @dev Returns true if the two byte ranges are equal.\n * @param self The first byte range to compare.\n * @param other The second byte range to compare.\n * @return True if the byte ranges are equal, false otherwise.\n */\n function equals(bytes memory self, bytes memory other) internal pure returns (bool) {\n return self.length == other.length && equals(self, 0, other, 0, self.length);\n }\n\n /*\n * @dev Returns the 8-bit number at the specified index of self.\n * @param self The byte string.\n * @param idx The index into the bytes\n * @return The specified 8 bits of the string, interpreted as an integer.\n */\n function readUint8(bytes memory self, uint256 idx) internal pure returns (uint8 ret) {\n return uint8(self[idx]);\n }\n\n /*\n * @dev Returns the 16-bit number at the specified index of self.\n * @param self The byte string.\n * @param idx The index into the bytes\n * @return The specified 16 bits of the string, interpreted as an integer.\n */\n function readUint16(bytes memory self, uint256 idx) internal pure returns (uint16 ret) {\n require(idx + 2 <= self.length);\n assembly {\n ret := and(mload(add(add(self, 2), idx)), 0xFFFF)\n }\n }\n\n /*\n * @dev Returns the 32-bit number at the specified index of self.\n * @param self The byte string.\n * @param idx The index into the bytes\n * @return The specified 32 bits of the string, interpreted as an integer.\n */\n function readUint32(bytes memory self, uint256 idx) internal pure returns (uint32 ret) {\n require(idx + 4 <= self.length);\n assembly {\n ret := and(mload(add(add(self, 4), idx)), 0xFFFFFFFF)\n }\n }\n\n /*\n * @dev Returns the 32 byte value at the specified index of self.\n * @param self The byte string.\n * @param idx The index into the bytes\n * @return The specified 32 bytes of the string.\n */\n function readBytes32(bytes memory self, uint256 idx) internal pure returns (bytes32 ret) {\n require(idx + 32 <= self.length);\n assembly {\n ret := mload(add(add(self, 32), idx))\n }\n }\n\n /*\n * @dev Returns the 32 byte value at the specified index of self.\n * @param self The byte string.\n * @param idx The index into the bytes\n * @return The specified 32 bytes of the string.\n */\n function readBytes20(bytes memory self, uint256 idx) internal pure returns (bytes20 ret) {\n require(idx + 20 <= self.length);\n assembly {\n ret :=\n and(mload(add(add(self, 32), idx)), 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000000)\n }\n }\n\n /*\n * @dev Returns the n byte value at the specified index of self.\n * @param self The byte string.\n * @param idx The index into the bytes.\n * @param len The number of bytes.\n * @return The specified 32 bytes of the string.\n */\n function readBytesN(bytes memory self, uint256 idx, uint256 len) internal pure returns (bytes32 ret) {\n require(len <= 32);\n require(idx + len <= self.length);\n assembly {\n let mask := not(sub(exp(256, sub(32, len)), 1))\n ret := and(mload(add(add(self, 32), idx)), mask)\n }\n }\n\n function memcpy(uint256 dest, uint256 src, uint256 len) private pure {\n // Copy word-length chunks while possible\n for (; len >= 32; len -= 32) {\n assembly {\n mstore(dest, mload(src))\n }\n dest += 32;\n src += 32;\n }\n\n // Copy remaining bytes\n uint256 mask;\n if (len == 0) {\n mask = type(uint256).max; // Set to maximum value of uint256\n } else {\n mask = 256 ** (32 - len) - 1;\n }\n\n assembly {\n let srcpart := and(mload(src), not(mask))\n let destpart := and(mload(dest), mask)\n mstore(dest, or(destpart, srcpart))\n }\n }\n\n /*\n * @dev Copies a substring into a new byte string.\n * @param self The byte string to copy from.\n * @param offset The offset to start copying at.\n * @param len The number of bytes to copy.\n */\n function substring(bytes memory self, uint256 offset, uint256 len) internal pure returns (bytes memory) {\n require(offset + len <= self.length);\n\n bytes memory ret = new bytes(len);\n uint256 dest;\n uint256 src;\n\n assembly {\n dest := add(ret, 32)\n src := add(add(self, 32), offset)\n }\n memcpy(dest, src, len);\n\n return ret;\n }\n\n // Maps characters from 0x30 to 0x7A to their base32 values.\n // 0xFF represents invalid characters in that range.\n bytes constant base32HexTable =\n hex\"00010203040506070809FFFFFFFFFFFFFF0A0B0C0D0E0F101112131415161718191A1B1C1D1E1FFFFFFFFFFFFFFFFFFFFF0A0B0C0D0E0F101112131415161718191A1B1C1D1E1F\";\n\n /**\n * @dev Decodes unpadded base32 data of up to one word in length.\n * @param self The data to decode.\n * @param off Offset into the string to start at.\n * @param len Number of characters to decode.\n * @return The decoded data, left aligned.\n */\n function base32HexDecodeWord(bytes memory self, uint256 off, uint256 len) internal pure returns (bytes32) {\n require(len <= 52);\n\n uint256 ret = 0;\n uint8 decoded;\n for (uint256 i = 0; i < len; i++) {\n bytes1 char = self[off + i];\n require(char >= 0x30 && char <= 0x7A);\n decoded = uint8(base32HexTable[uint256(uint8(char)) - 0x30]);\n require(decoded <= 0x20);\n if (i == len - 1) {\n break;\n }\n ret = (ret << 5) | decoded;\n }\n\n uint256 bitlen = len * 5;\n if (len % 8 == 0) {\n // Multiple of 8 characters, no padding\n ret = (ret << 5) | decoded;\n } else if (len % 8 == 2) {\n // Two extra characters - 1 byte\n ret = (ret << 3) | (decoded >> 2);\n bitlen -= 2;\n } else if (len % 8 == 4) {\n // Four extra characters - 2 bytes\n ret = (ret << 1) | (decoded >> 4);\n bitlen -= 4;\n } else if (len % 8 == 5) {\n // Five extra characters - 3 bytes\n ret = (ret << 4) | (decoded >> 1);\n bitlen -= 1;\n } else if (len % 8 == 7) {\n // Seven extra characters - 4 bytes\n ret = (ret << 2) | (decoded >> 3);\n bitlen -= 3;\n } else {\n revert();\n }\n\n return bytes32(ret << (256 - bitlen));\n }\n\n function compareBytes(bytes memory a, bytes memory b) internal pure returns (bool) {\n if (a.length != b.length) {\n return false;\n }\n for (uint256 i = 0; i < a.length; i++) {\n if (a[i] != b[i]) {\n return false;\n }\n }\n return true;\n }\n}\n"},"src/utils/P256Verifier.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport \"./BytesUtils.sol\";\n\n/**\n * @notice modified from https://github.com/daimo-eth/p256-verifier/\n */\nlibrary P256Verifier {\n using BytesUtils for bytes;\n\n address internal constant P256_VERIFIER = 0xc2b78104907F722DABAc4C69f826a522B2754De4;\n\n function ecdsaVerify(bytes32 messageHash, bytes memory signature, bytes memory key)\n internal\n view\n returns (bool verified)\n {\n bytes memory args = abi.encode(\n messageHash,\n uint256(bytes32(signature.substring(0, 32))),\n uint256(bytes32(signature.substring(32, 32))),\n uint256(bytes32(key.substring(0, 32))),\n uint256(bytes32(key.substring(32, 32)))\n );\n (bool success, bytes memory ret) = P256_VERIFIER.staticcall(args);\n assert(success); // never reverts, always returns 0 or 1\n\n verified = abi.decode(ret, (uint256)) == 1;\n }\n}\n"},"src/helpers/X509Helper.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport {Asn1Decode, NodePtr} from \"../utils/Asn1Decode.sol\";\nimport {BytesUtils} from \"../utils/BytesUtils.sol\";\nimport {DateTimeUtils} from \"../utils/DateTimeUtils.sol\";\n\n/**\n * @title Solidity Structure representing X509 Certificates\n * @notice This is a simplified structure of a DER-decoded X509 Certificate\n */\nstruct X509CertObj {\n uint256 serialNumber;\n string issuerCommonName;\n uint256 validityNotBefore;\n uint256 validityNotAfter;\n string subjectCommonName;\n bytes subjectPublicKey;\n // the extension needs to be parsed further for PCK Certificates\n uint256 extensionPtr;\n // for signature verification in the cert chain\n bytes signature;\n bytes tbs;\n}\n\n/**\n * @title X509 Certificates Helper Contract\n * @notice This is a standalone contract that can be used by off-chain applications and smart contracts\n * to parse DER-encoded X509 certificates.\n * @dev The Extension sequence in Intel PCK Certificates is a custom ASN.1 Sequence that needs to be\n * @dev parsed further in a more specialized PCKHelper contract.\n */\ncontract X509Helper {\n using Asn1Decode for bytes;\n using NodePtr for uint256;\n using BytesUtils for bytes;\n\n /// =================================================================================\n /// USE THE GETTERS BELOW IF YOU DON'T WANT TO PARSE THE ENTIRE X509 CERTIFICATE\n /// =================================================================================\n\n function getTbsAndSig(bytes calldata der) external pure returns (bytes memory tbs, bytes memory sig) {\n uint256 root = der.root();\n uint256 tbsParentPtr = der.firstChildOf(root);\n uint256 sigPtr = der.nextSiblingOf(tbsParentPtr);\n sigPtr = der.nextSiblingOf(sigPtr);\n\n tbs = der.allBytesAt(tbsParentPtr);\n sig = _getSignature(der, sigPtr);\n }\n\n function getSerialNumber(bytes calldata der) external pure returns (uint256 serialNum) {\n uint256 root = der.root();\n uint256 tbsParentPtr = der.firstChildOf(root);\n uint256 tbsPtr = der.firstChildOf(tbsParentPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n serialNum = _parseSerialNumber(der.bytesAt(tbsPtr));\n }\n\n function getIssuerCommonName(bytes calldata der) external pure returns (string memory issuerCommonName) {\n uint256 root = der.root();\n uint256 tbsParentPtr = der.firstChildOf(root);\n uint256 tbsPtr = der.firstChildOf(tbsParentPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n issuerCommonName = _getCommonName(der, der.firstChildOf(tbsPtr));\n }\n\n function certIsNotExpired(bytes calldata der) external view returns (bool isValid) {\n uint256 root = der.root();\n uint256 tbsParentPtr = der.firstChildOf(root);\n uint256 tbsPtr = der.firstChildOf(tbsParentPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n (uint256 validityNotBefore, uint256 validityNotAfter) = _getValidity(der, tbsPtr);\n isValid = block.timestamp > validityNotBefore && block.timestamp < validityNotAfter;\n }\n\n function getSubjectCommonName(bytes calldata der) external pure returns (string memory subjectCommonName) {\n uint256 root = der.root();\n uint256 tbsParentPtr = der.firstChildOf(root);\n uint256 tbsPtr = der.firstChildOf(tbsParentPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n subjectCommonName = _getCommonName(der, der.firstChildOf(tbsPtr));\n }\n\n function getSubjectPublicKey(bytes calldata der) external pure returns (bytes memory pubKey) {\n uint256 root = der.root();\n uint256 tbsParentPtr = der.firstChildOf(root);\n uint256 tbsPtr = der.firstChildOf(tbsParentPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n pubKey = _getSubjectPublicKey(der, der.firstChildOf(tbsPtr));\n }\n\n /// x509 Certificates generally contain a sequence of elements in the following order:\n /// 1. tbs\n /// - 1a. version\n /// - 1b. serial number\n /// - 1c. siganture algorithm\n /// - 1d. issuer\n /// - - 1d(a). common name\n /// - - 1d(b). organization name\n /// - - 1d(c). locality name\n /// - - 1d(d). state or province name\n /// - - 1d(e). country name\n /// - 1e. validity\n /// - - 1e(a) notBefore\n /// - - 1e(b) notAfter\n /// - 1f. subject\n /// - - contains the same set of elements as 1d\n /// - 1g. subject public key info\n /// - - 1g(a). algorithm\n /// - - 1g(b). subject public key\n /// - 1h. Extensions\n /// 2. Signature Algorithm\n /// 3. Signature\n /// - 3a. X value\n /// - 3b. Y value\n function parseX509DER(bytes calldata der) external pure returns (X509CertObj memory cert) {\n uint256 root = der.root();\n\n uint256 tbsParentPtr = der.firstChildOf(root);\n cert.tbs = der.allBytesAt(tbsParentPtr);\n\n uint256 tbsPtr = der.firstChildOf(tbsParentPtr);\n\n tbsPtr = der.nextSiblingOf(tbsPtr);\n\n cert.serialNumber = _parseSerialNumber(der.bytesAt(tbsPtr));\n\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n\n cert.issuerCommonName = _getCommonName(der, der.firstChildOf(tbsPtr));\n\n tbsPtr = der.nextSiblingOf(tbsPtr);\n (cert.validityNotBefore, cert.validityNotAfter) = _getValidity(der, tbsPtr);\n\n tbsPtr = der.nextSiblingOf(tbsPtr);\n\n cert.subjectCommonName = _getCommonName(der, der.firstChildOf(tbsPtr));\n\n tbsPtr = der.nextSiblingOf(tbsPtr);\n cert.subjectPublicKey = _getSubjectPublicKey(der, der.firstChildOf(tbsPtr));\n\n cert.extensionPtr = der.nextSiblingOf(tbsPtr);\n\n // tbs iteration completed\n // now we just need to look for the signature\n\n uint256 sigPtr = der.nextSiblingOf(tbsParentPtr);\n sigPtr = der.nextSiblingOf(sigPtr);\n cert.signature = _getSignature(der, sigPtr);\n }\n\n function _getCommonName(bytes calldata der, uint256 commonNameParentPtr)\n private\n pure\n returns (string memory commonName)\n {\n commonNameParentPtr = der.firstChildOf(commonNameParentPtr);\n commonNameParentPtr = der.firstChildOf(commonNameParentPtr);\n commonNameParentPtr = der.nextSiblingOf(commonNameParentPtr);\n commonName = string(der.bytesAt(commonNameParentPtr));\n }\n\n function _getValidity(bytes calldata der, uint256 validityPtr)\n private\n pure\n returns (uint256 notBefore, uint256 notAfter)\n {\n uint256 notBeforePtr = der.firstChildOf(validityPtr);\n uint256 notAfterPtr = der.nextSiblingOf(notBeforePtr);\n notBefore = DateTimeUtils.fromDERToTimestamp(der.bytesAt(notBeforePtr));\n notAfter = DateTimeUtils.fromDERToTimestamp(der.bytesAt(notAfterPtr));\n }\n\n function _getSubjectPublicKey(bytes calldata der, uint256 subjectPublicKeyInfoPtr)\n private\n pure\n returns (bytes memory pubKey)\n {\n subjectPublicKeyInfoPtr = der.nextSiblingOf(subjectPublicKeyInfoPtr);\n pubKey = der.bitstringAt(subjectPublicKeyInfoPtr);\n if (pubKey.length != 65) {\n // TODO: we need to figure out how to handle key with prefix byte 0x02 or 0x03\n revert(\"compressed public key not supported\");\n }\n pubKey = _trimBytes(pubKey, 64);\n }\n\n function _parseSerialNumber(bytes memory serialBytes) private pure returns (uint256 serial) {\n uint256 shift = 8 * (32 - serialBytes.length);\n serial = uint256(bytes32(serialBytes) >> shift);\n }\n\n function _getSignature(bytes calldata der, uint256 sigPtr) private pure returns (bytes memory sig) {\n sigPtr = der.rootOfBitStringAt(sigPtr);\n\n sigPtr = der.firstChildOf(sigPtr);\n bytes memory sigX = _trimBytes(der.bytesAt(sigPtr), 32);\n\n sigPtr = der.nextSiblingOf(sigPtr);\n bytes memory sigY = _trimBytes(der.bytesAt(sigPtr), 32);\n\n sig = abi.encodePacked(sigX, sigY);\n }\n\n /// @dev remove unnecessary prefix from the input\n function _trimBytes(bytes memory input, uint256 expectedLength) private pure returns (bytes memory output) {\n uint256 n = input.length;\n if (n == expectedLength) {\n output = input;\n } else if (n < expectedLength) {\n output = new bytes(expectedLength);\n uint256 padLength = expectedLength - n;\n for (uint256 i = 0; i < n; i++) {\n output[padLength + i] = input[i];\n }\n } else {\n uint256 lengthDiff = n - expectedLength;\n output = input.substring(lengthDiff, expectedLength);\n }\n }\n}\n"},"src/helpers/X509CRLHelper.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport {Asn1Decode, NodePtr} from \"../utils/Asn1Decode.sol\";\nimport {BytesUtils} from \"../utils/BytesUtils.sol\";\nimport {DateTimeUtils} from \"../utils/DateTimeUtils.sol\";\n\n/**\n * @title Solidity Structure representing X509 CRL\n * @notice This is a simplified structure of a DER-decoded X509 CRL\n */\nstruct X509CRLObj {\n uint256 serialNumber;\n string issuerCommonName;\n uint256 validityNotBefore;\n uint256 validityNotAfter;\n uint256[] serialNumbersRevoked;\n // for signature verification in the cert chain\n bytes signature;\n bytes tbs;\n}\n\n/**\n * @title X509 CRL Helper Contract\n * @notice This is a standalone contract that can be used by off-chain applications and smart contracts\n * to parse DER-encoded CRLs.\n */\ncontract X509CRLHelper {\n using Asn1Decode for bytes;\n using NodePtr for uint256;\n using BytesUtils for bytes;\n\n // 2.5.29.20\n bytes constant CRL_NUMBER_OID = hex\"551d14\";\n\n /// =================================================================================\n /// USE THE GETTERS BELOW IF YOU DON'T WANT TO PARSE THE ENTIRE X509 CRL\n /// =================================================================================\n\n function getTbsAndSig(bytes calldata der) external pure returns (bytes memory tbs, bytes memory sig) {\n uint256 root = der.root();\n uint256 tbsParentPtr = der.firstChildOf(root);\n uint256 sigPtr = der.nextSiblingOf(tbsParentPtr);\n sigPtr = der.nextSiblingOf(sigPtr);\n\n tbs = der.allBytesAt(tbsParentPtr);\n sig = _getSignature(der, sigPtr);\n }\n\n function getSerialNumber(bytes calldata der) external pure returns (uint256 serialNum) {\n uint256 root = der.root();\n uint256 tbsParentPtr = der.firstChildOf(root);\n uint256 tbsPtr = der.firstChildOf(tbsParentPtr);\n serialNum = _parseSerialNumber(der.bytesAt(tbsPtr));\n }\n\n function getIssuerCommonName(bytes calldata der) external pure returns (string memory issuerCommonName) {\n uint256 root = der.root();\n uint256 tbsParentPtr = der.firstChildOf(root);\n uint256 tbsPtr = der.firstChildOf(tbsParentPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n issuerCommonName = _getCommonName(der, der.firstChildOf(tbsPtr));\n }\n\n function crlIsNotExpired(bytes calldata der) external view returns (bool isValid) {\n uint256 root = der.root();\n uint256 tbsParentPtr = der.firstChildOf(root);\n uint256 tbsPtr = der.firstChildOf(tbsParentPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n (uint256 validityNotBefore, uint256 validityNotAfter) = _getValidity(der, tbsPtr);\n isValid = block.timestamp > validityNotBefore && block.timestamp < validityNotAfter;\n }\n\n function serialNumberIsRevoked(uint256 serialNumber, bytes calldata der) external pure returns (bool revoked) {\n uint256 root = der.root();\n uint256 tbsParentPtr = der.firstChildOf(root);\n uint256 tbsPtr = der.firstChildOf(tbsParentPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n uint256[] memory ret = _getRevokedSerialNumbers(der, tbsPtr, true, serialNumber);\n revoked = ret[0] == serialNumber;\n }\n\n /// x509 CRL generally contain a sequence of elements in the following order:\n /// 1. tbs\n /// - 1a. serial number\n /// - 1b. signature algorithm\n /// - 1c. issuer\n /// - - 1c(a). common name\n /// - - 1c(b). organization name\n /// - - 1c(c). locality name\n /// - - 1c(d). state or province name\n /// - - 1c(e). country name\n /// - 1d. not before\n /// - 1e. not after\n /// - 1f. revoked certificates\n /// - - A list consists of revoked serial numbers and reasons.\n /// - 1g. CRL extensions\n /// - - 1g(a) CRL number\n /// - - 1g(b) Authority Key Identifier\n /// 2. Signature Algorithm\n /// 3. Signature\n /// - 3a. X value\n /// - 3b. Y value\n function parseCRLDER(bytes calldata der) external pure returns (X509CRLObj memory crl) {\n uint256 root = der.root();\n\n uint256 tbsParentPtr = der.firstChildOf(root);\n\n uint256 tbsPtr = der.firstChildOf(tbsParentPtr);\n\n crl.serialNumber = uint256(bytes32(der.bytesAt(tbsPtr)));\n\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n\n crl.issuerCommonName = _getCommonName(der, der.firstChildOf(tbsPtr));\n\n tbsPtr = der.nextSiblingOf(tbsPtr);\n (crl.validityNotBefore, crl.validityNotAfter) = _getValidity(der, tbsPtr);\n\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n\n crl.serialNumbersRevoked = _getRevokedSerialNumbers(der, tbsPtr, false, 0);\n\n // tbs iteration completed\n // now we just need to look for the signature\n\n uint256 sigPtr = der.nextSiblingOf(tbsParentPtr);\n sigPtr = der.nextSiblingOf(sigPtr);\n crl.signature = _getSignature(der, sigPtr);\n }\n\n function _getCommonName(bytes calldata der, uint256 commonNameParentPtr)\n private\n pure\n returns (string memory commonName)\n {\n commonNameParentPtr = der.firstChildOf(commonNameParentPtr);\n commonNameParentPtr = der.firstChildOf(commonNameParentPtr);\n commonNameParentPtr = der.nextSiblingOf(commonNameParentPtr);\n commonName = string(der.bytesAt(commonNameParentPtr));\n }\n\n function _getValidity(bytes calldata der, uint256 validityPtr)\n private\n pure\n returns (uint256 notBefore, uint256 notAfter)\n {\n uint256 notBeforePtr = validityPtr;\n uint256 notAfterPtr = der.nextSiblingOf(notBeforePtr);\n notBefore = DateTimeUtils.fromDERToTimestamp(der.bytesAt(notBeforePtr));\n notAfter = DateTimeUtils.fromDERToTimestamp(der.bytesAt(notAfterPtr));\n }\n\n function _getRevokedSerialNumbers(bytes calldata der, uint256 revokedParentPtr, bool breakIfFound, uint256 filter)\n private\n pure\n returns (uint256[] memory serialNumbers)\n {\n uint256 revokedPtr = der.firstChildOf(revokedParentPtr);\n\n if (der[revokedPtr.ixs()] == 0xA0) {\n uint256 crlExtensionPtr = der.firstChildOf(revokedPtr);\n require(BytesUtils.compareBytes(der.bytesAt(crlExtensionPtr), CRL_NUMBER_OID), \"invalid CRL\");\n } else {\n bytes memory serials;\n while (revokedPtr.ixl() <= revokedParentPtr.ixl()) {\n uint256 serialPtr = der.firstChildOf(revokedPtr);\n bytes memory serialBytes = der.bytesAt(serialPtr);\n uint256 serialNumber = _parseSerialNumber(serialBytes);\n serials = abi.encodePacked(serials, serialNumber);\n if (breakIfFound && filter == serialNumber) {\n serialNumbers = new uint256[](1);\n serialNumbers[0] = filter;\n return serialNumbers;\n }\n revokedPtr = der.nextSiblingOf(revokedPtr);\n }\n uint256 count = serials.length / 32;\n // ABI encoding format for a dynamic uint256[] value\n serials = abi.encodePacked(abi.encode(0x20), abi.encode(count), serials);\n serialNumbers = new uint256[](count);\n serialNumbers = abi.decode(serials, (uint256[]));\n }\n }\n\n function _parseSerialNumber(bytes memory serialBytes) private pure returns (uint256 serial) {\n uint256 shift = 8 * (32 - serialBytes.length);\n serial = uint256(bytes32(serialBytes) >> shift);\n }\n\n function _getSignature(bytes calldata der, uint256 sigPtr) private pure returns (bytes memory sig) {\n sigPtr = der.rootOfBitStringAt(sigPtr);\n\n sigPtr = der.firstChildOf(sigPtr);\n bytes memory sigX = _trimBytes(der.bytesAt(sigPtr), 32);\n\n sigPtr = der.nextSiblingOf(sigPtr);\n bytes memory sigY = _trimBytes(der.bytesAt(sigPtr), 32);\n\n sig = abi.encodePacked(sigX, sigY);\n }\n\n /// @dev remove unnecessary prefix from the input\n /// @dev remove unnecessary prefix from the input\n function _trimBytes(bytes memory input, uint256 expectedLength) private pure returns (bytes memory output) {\n uint256 n = input.length;\n if (n == expectedLength) {\n output = input;\n } else if (n < expectedLength) {\n output = new bytes(expectedLength);\n uint256 padLength = expectedLength - n;\n for (uint256 i = 0; i < n; i++) {\n output[padLength + i] = input[i];\n }\n } else {\n uint256 lengthDiff = n - expectedLength;\n output = input.substring(lengthDiff, expectedLength);\n }\n }\n}\n"},"lib/solady/src/utils/DateTimeLib.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\n/// @notice Library for date time operations.\n/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/DateTimeLib.sol)\n///\n/// Conventions:\n/// --------------------------------------------------------------------+\n/// Unit | Range | Notes |\n/// --------------------------------------------------------------------|\n/// timestamp | 0..0x1e18549868c76ff | Unix timestamp. |\n/// epochDay | 0..0x16d3e098039 | Days since 1970-01-01. |\n/// year | 1970..0xffffffff | Gregorian calendar year. |\n/// month | 1..12 | Gregorian calendar month. |\n/// day | 1..31 | Gregorian calendar day of month. |\n/// weekday | 1..7 | The day of the week (1-indexed). |\n/// --------------------------------------------------------------------+\n/// All timestamps of days are rounded down to 00:00:00 UTC.\nlibrary DateTimeLib {\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* CONSTANTS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n // Weekdays are 1-indexed for a traditional rustic feel.\n\n // \"And on the seventh day God finished his work that he had done,\n // and he rested on the seventh day from all his work that he had done.\"\n // -- Genesis 2:2\n\n uint256 internal constant MON = 1;\n uint256 internal constant TUE = 2;\n uint256 internal constant WED = 3;\n uint256 internal constant THU = 4;\n uint256 internal constant FRI = 5;\n uint256 internal constant SAT = 6;\n uint256 internal constant SUN = 7;\n\n // Months and days of months are 1-indexed for ease of use.\n\n uint256 internal constant JAN = 1;\n uint256 internal constant FEB = 2;\n uint256 internal constant MAR = 3;\n uint256 internal constant APR = 4;\n uint256 internal constant MAY = 5;\n uint256 internal constant JUN = 6;\n uint256 internal constant JUL = 7;\n uint256 internal constant AUG = 8;\n uint256 internal constant SEP = 9;\n uint256 internal constant OCT = 10;\n uint256 internal constant NOV = 11;\n uint256 internal constant DEC = 12;\n\n // These limits are large enough for most practical purposes.\n // Inputs that exceed these limits result in undefined behavior.\n\n uint256 internal constant MAX_SUPPORTED_YEAR = 0xffffffff;\n uint256 internal constant MAX_SUPPORTED_EPOCH_DAY = 0x16d3e098039;\n uint256 internal constant MAX_SUPPORTED_TIMESTAMP = 0x1e18549868c76ff;\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* DATE TIME OPERATIONS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev Returns the number of days since 1970-01-01 from (`year`,`month`,`day`).\n /// See: https://howardhinnant.github.io/date_algorithms.html\n /// Note: Inputs outside the supported ranges result in undefined behavior.\n /// Use {isSupportedDate} to check if the inputs are supported.\n function dateToEpochDay(uint256 year, uint256 month, uint256 day)\n internal\n pure\n returns (uint256 epochDay)\n {\n /// @solidity memory-safe-assembly\n assembly {\n year := sub(year, lt(month, 3))\n let doy := add(shr(11, add(mul(62719, mod(add(month, 9), 12)), 769)), day)\n let yoe := mod(year, 400)\n let doe := sub(add(add(mul(yoe, 365), shr(2, yoe)), doy), div(yoe, 100))\n epochDay := sub(add(mul(div(year, 400), 146097), doe), 719469)\n }\n }\n\n /// @dev Returns (`year`,`month`,`day`) from the number of days since 1970-01-01.\n /// Note: Inputs outside the supported ranges result in undefined behavior.\n /// Use {isSupportedDays} to check if the inputs is supported.\n function epochDayToDate(uint256 epochDay)\n internal\n pure\n returns (uint256 year, uint256 month, uint256 day)\n {\n /// @solidity memory-safe-assembly\n assembly {\n epochDay := add(epochDay, 719468)\n let doe := mod(epochDay, 146097)\n let yoe :=\n div(sub(sub(add(doe, div(doe, 36524)), div(doe, 1460)), eq(doe, 146096)), 365)\n let doy := sub(doe, sub(add(mul(365, yoe), shr(2, yoe)), div(yoe, 100)))\n let mp := div(add(mul(5, doy), 2), 153)\n day := add(sub(doy, shr(11, add(mul(mp, 62719), 769))), 1)\n month := byte(mp, shl(160, 0x030405060708090a0b0c0102))\n year := add(add(yoe, mul(div(epochDay, 146097), 400)), lt(month, 3))\n }\n }\n\n /// @dev Returns the unix timestamp from (`year`,`month`,`day`).\n /// Note: Inputs outside the supported ranges result in undefined behavior.\n /// Use {isSupportedDate} to check if the inputs are supported.\n function dateToTimestamp(uint256 year, uint256 month, uint256 day)\n internal\n pure\n returns (uint256 result)\n {\n unchecked {\n result = dateToEpochDay(year, month, day) * 86400;\n }\n }\n\n /// @dev Returns (`year`,`month`,`day`) from the given unix timestamp.\n /// Note: Inputs outside the supported ranges result in undefined behavior.\n /// Use {isSupportedTimestamp} to check if the inputs are supported.\n function timestampToDate(uint256 timestamp)\n internal\n pure\n returns (uint256 year, uint256 month, uint256 day)\n {\n (year, month, day) = epochDayToDate(timestamp / 86400);\n }\n\n /// @dev Returns the unix timestamp from\n /// (`year`,`month`,`day`,`hour`,`minute`,`second`).\n /// Note: Inputs outside the supported ranges result in undefined behavior.\n /// Use {isSupportedDateTime} to check if the inputs are supported.\n function dateTimeToTimestamp(\n uint256 year,\n uint256 month,\n uint256 day,\n uint256 hour,\n uint256 minute,\n uint256 second\n ) internal pure returns (uint256 result) {\n unchecked {\n result = dateToEpochDay(year, month, day) * 86400 + hour * 3600 + minute * 60 + second;\n }\n }\n\n /// @dev Returns (`year`,`month`,`day`,`hour`,`minute`,`second`)\n /// from the given unix timestamp.\n /// Note: Inputs outside the supported ranges result in undefined behavior.\n /// Use {isSupportedTimestamp} to check if the inputs are supported.\n function timestampToDateTime(uint256 timestamp)\n internal\n pure\n returns (\n uint256 year,\n uint256 month,\n uint256 day,\n uint256 hour,\n uint256 minute,\n uint256 second\n )\n {\n unchecked {\n (year, month, day) = epochDayToDate(timestamp / 86400);\n uint256 secs = timestamp % 86400;\n hour = secs / 3600;\n secs = secs % 3600;\n minute = secs / 60;\n second = secs % 60;\n }\n }\n\n /// @dev Returns if the `year` is leap.\n function isLeapYear(uint256 year) internal pure returns (bool leap) {\n /// @solidity memory-safe-assembly\n assembly {\n leap := iszero(and(add(mul(iszero(mod(year, 25)), 12), 3), year))\n }\n }\n\n /// @dev Returns number of days in given `month` of `year`.\n function daysInMonth(uint256 year, uint256 month) internal pure returns (uint256 result) {\n bool flag = isLeapYear(year);\n /// @solidity memory-safe-assembly\n assembly {\n // `daysInMonths = [31,28,31,30,31,30,31,31,30,31,30,31]`.\n // `result = daysInMonths[month - 1] + isLeapYear(year)`.\n result :=\n add(byte(month, shl(152, 0x1F1C1F1E1F1E1F1F1E1F1E1F)), and(eq(month, 2), flag))\n }\n }\n\n /// @dev Returns the weekday from the unix timestamp.\n /// Monday: 1, Tuesday: 2, ....., Sunday: 7.\n function weekday(uint256 timestamp) internal pure returns (uint256 result) {\n unchecked {\n result = ((timestamp / 86400 + 3) % 7) + 1;\n }\n }\n\n /// @dev Returns if (`year`,`month`,`day`) is a supported date.\n /// - `1970 <= year <= MAX_SUPPORTED_YEAR`.\n /// - `1 <= month <= 12`.\n /// - `1 <= day <= daysInMonth(year, month)`.\n function isSupportedDate(uint256 year, uint256 month, uint256 day)\n internal\n pure\n returns (bool result)\n {\n uint256 md = daysInMonth(year, month);\n /// @solidity memory-safe-assembly\n assembly {\n let w := not(0)\n result :=\n and(\n lt(sub(year, 1970), sub(MAX_SUPPORTED_YEAR, 1969)),\n and(lt(add(month, w), 12), lt(add(day, w), md))\n )\n }\n }\n\n /// @dev Returns if (`year`,`month`,`day`,`hour`,`minute`,`second`) is a supported date time.\n /// - `1970 <= year <= MAX_SUPPORTED_YEAR`.\n /// - `1 <= month <= 12`.\n /// - `1 <= day <= daysInMonth(year, month)`.\n /// - `hour < 24`.\n /// - `minute < 60`.\n /// - `second < 60`.\n function isSupportedDateTime(\n uint256 year,\n uint256 month,\n uint256 day,\n uint256 hour,\n uint256 minute,\n uint256 second\n ) internal pure returns (bool result) {\n if (isSupportedDate(year, month, day)) {\n /// @solidity memory-safe-assembly\n assembly {\n result := and(lt(hour, 24), and(lt(minute, 60), lt(second, 60)))\n }\n }\n }\n\n /// @dev Returns if `epochDay` is a supported unix epoch day.\n function isSupportedEpochDay(uint256 epochDay) internal pure returns (bool result) {\n unchecked {\n result = epochDay < MAX_SUPPORTED_EPOCH_DAY + 1;\n }\n }\n\n /// @dev Returns if `timestamp` is a supported unix timestamp.\n function isSupportedTimestamp(uint256 timestamp) internal pure returns (bool result) {\n unchecked {\n result = timestamp < MAX_SUPPORTED_TIMESTAMP + 1;\n }\n }\n\n /// @dev Returns the unix timestamp of the given `n`th weekday `wd`, in `month` of `year`.\n /// Example: 3rd Friday of Feb 2022 is `nthWeekdayInMonthOfYearTimestamp(2022, 2, 3, 5)`\n /// Note: `n` is 1-indexed for traditional consistency.\n /// Invalid weekdays (i.e. `wd == 0 || wd > 7`) result in undefined behavior.\n function nthWeekdayInMonthOfYearTimestamp(uint256 year, uint256 month, uint256 n, uint256 wd)\n internal\n pure\n returns (uint256 result)\n {\n uint256 d = dateToEpochDay(year, month, 1);\n uint256 md = daysInMonth(year, month);\n /// @solidity memory-safe-assembly\n assembly {\n let diff := sub(wd, add(mod(add(d, 3), 7), 1))\n let date := add(mul(sub(n, 1), 7), add(mul(gt(diff, 6), 7), diff))\n result := mul(mul(86400, add(date, d)), and(lt(date, md), iszero(iszero(n))))\n }\n }\n\n /// @dev Returns the unix timestamp of the most recent Monday.\n function mondayTimestamp(uint256 timestamp) internal pure returns (uint256 result) {\n uint256 t = timestamp;\n /// @solidity memory-safe-assembly\n assembly {\n let day := div(t, 86400)\n result := mul(mul(sub(day, mod(add(day, 3), 7)), 86400), gt(t, 345599))\n }\n }\n\n /// @dev Returns whether the unix timestamp falls on a Saturday or Sunday.\n /// To check whether it is a week day, just take the negation of the result.\n function isWeekEnd(uint256 timestamp) internal pure returns (bool result) {\n result = weekday(timestamp) > FRI;\n }\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* DATE TIME ARITHMETIC OPERATIONS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev Adds `numYears` to the unix timestamp, and returns the result.\n /// Note: The result will share the same Gregorian calendar month,\n /// but different Gregorian calendar years for non-zero `numYears`.\n /// If the Gregorian calendar month of the result has less days\n /// than the Gregorian calendar month day of the `timestamp`,\n /// the result's month day will be the maximum possible value for the month.\n /// (e.g. from 29th Feb to 28th Feb)\n function addYears(uint256 timestamp, uint256 numYears) internal pure returns (uint256 result) {\n (uint256 year, uint256 month, uint256 day) = epochDayToDate(timestamp / 86400);\n result = _offsetted(year + numYears, month, day, timestamp);\n }\n\n /// @dev Adds `numMonths` to the unix timestamp, and returns the result.\n /// Note: If the Gregorian calendar month of the result has less days\n /// than the Gregorian calendar month day of the `timestamp`,\n /// the result's month day will be the maximum possible value for the month.\n /// (e.g. from 29th Feb to 28th Feb)\n function addMonths(uint256 timestamp, uint256 numMonths)\n internal\n pure\n returns (uint256 result)\n {\n (uint256 year, uint256 month, uint256 day) = epochDayToDate(timestamp / 86400);\n month = _sub(month + numMonths, 1);\n result = _offsetted(year + month / 12, _add(month % 12, 1), day, timestamp);\n }\n\n /// @dev Adds `numDays` to the unix timestamp, and returns the result.\n function addDays(uint256 timestamp, uint256 numDays) internal pure returns (uint256 result) {\n result = timestamp + numDays * 86400;\n }\n\n /// @dev Adds `numHours` to the unix timestamp, and returns the result.\n function addHours(uint256 timestamp, uint256 numHours) internal pure returns (uint256 result) {\n result = timestamp + numHours * 3600;\n }\n\n /// @dev Adds `numMinutes` to the unix timestamp, and returns the result.\n function addMinutes(uint256 timestamp, uint256 numMinutes)\n internal\n pure\n returns (uint256 result)\n {\n result = timestamp + numMinutes * 60;\n }\n\n /// @dev Adds `numSeconds` to the unix timestamp, and returns the result.\n function addSeconds(uint256 timestamp, uint256 numSeconds)\n internal\n pure\n returns (uint256 result)\n {\n result = timestamp + numSeconds;\n }\n\n /// @dev Subtracts `numYears` from the unix timestamp, and returns the result.\n /// Note: The result will share the same Gregorian calendar month,\n /// but different Gregorian calendar years for non-zero `numYears`.\n /// If the Gregorian calendar month of the result has less days\n /// than the Gregorian calendar month day of the `timestamp`,\n /// the result's month day will be the maximum possible value for the month.\n /// (e.g. from 29th Feb to 28th Feb)\n function subYears(uint256 timestamp, uint256 numYears) internal pure returns (uint256 result) {\n (uint256 year, uint256 month, uint256 day) = epochDayToDate(timestamp / 86400);\n result = _offsetted(year - numYears, month, day, timestamp);\n }\n\n /// @dev Subtracts `numYears` from the unix timestamp, and returns the result.\n /// Note: If the Gregorian calendar month of the result has less days\n /// than the Gregorian calendar month day of the `timestamp`,\n /// the result's month day will be the maximum possible value for the month.\n /// (e.g. from 29th Feb to 28th Feb)\n function subMonths(uint256 timestamp, uint256 numMonths)\n internal\n pure\n returns (uint256 result)\n {\n (uint256 year, uint256 month, uint256 day) = epochDayToDate(timestamp / 86400);\n uint256 yearMonth = _totalMonths(year, month) - _add(numMonths, 1);\n result = _offsetted(yearMonth / 12, _add(yearMonth % 12, 1), day, timestamp);\n }\n\n /// @dev Subtracts `numDays` from the unix timestamp, and returns the result.\n function subDays(uint256 timestamp, uint256 numDays) internal pure returns (uint256 result) {\n result = timestamp - numDays * 86400;\n }\n\n /// @dev Subtracts `numHours` from the unix timestamp, and returns the result.\n function subHours(uint256 timestamp, uint256 numHours) internal pure returns (uint256 result) {\n result = timestamp - numHours * 3600;\n }\n\n /// @dev Subtracts `numMinutes` from the unix timestamp, and returns the result.\n function subMinutes(uint256 timestamp, uint256 numMinutes)\n internal\n pure\n returns (uint256 result)\n {\n result = timestamp - numMinutes * 60;\n }\n\n /// @dev Subtracts `numSeconds` from the unix timestamp, and returns the result.\n function subSeconds(uint256 timestamp, uint256 numSeconds)\n internal\n pure\n returns (uint256 result)\n {\n result = timestamp - numSeconds;\n }\n\n /// @dev Returns the difference in Gregorian calendar years\n /// between `fromTimestamp` and `toTimestamp`.\n /// Note: Even if the true time difference is less than a year,\n /// the difference can be non-zero is the timestamps are\n /// from different Gregorian calendar years\n function diffYears(uint256 fromTimestamp, uint256 toTimestamp)\n internal\n pure\n returns (uint256 result)\n {\n toTimestamp - fromTimestamp;\n (uint256 fromYear,,) = epochDayToDate(fromTimestamp / 86400);\n (uint256 toYear,,) = epochDayToDate(toTimestamp / 86400);\n result = _sub(toYear, fromYear);\n }\n\n /// @dev Returns the difference in Gregorian calendar months\n /// between `fromTimestamp` and `toTimestamp`.\n /// Note: Even if the true time difference is less than a month,\n /// the difference can be non-zero is the timestamps are\n /// from different Gregorian calendar months.\n function diffMonths(uint256 fromTimestamp, uint256 toTimestamp)\n internal\n pure\n returns (uint256 result)\n {\n toTimestamp - fromTimestamp;\n (uint256 fromYear, uint256 fromMonth,) = epochDayToDate(fromTimestamp / 86400);\n (uint256 toYear, uint256 toMonth,) = epochDayToDate(toTimestamp / 86400);\n result = _sub(_totalMonths(toYear, toMonth), _totalMonths(fromYear, fromMonth));\n }\n\n /// @dev Returns the difference in days between `fromTimestamp` and `toTimestamp`.\n function diffDays(uint256 fromTimestamp, uint256 toTimestamp)\n internal\n pure\n returns (uint256 result)\n {\n result = (toTimestamp - fromTimestamp) / 86400;\n }\n\n /// @dev Returns the difference in hours between `fromTimestamp` and `toTimestamp`.\n function diffHours(uint256 fromTimestamp, uint256 toTimestamp)\n internal\n pure\n returns (uint256 result)\n {\n result = (toTimestamp - fromTimestamp) / 3600;\n }\n\n /// @dev Returns the difference in minutes between `fromTimestamp` and `toTimestamp`.\n function diffMinutes(uint256 fromTimestamp, uint256 toTimestamp)\n internal\n pure\n returns (uint256 result)\n {\n result = (toTimestamp - fromTimestamp) / 60;\n }\n\n /// @dev Returns the difference in seconds between `fromTimestamp` and `toTimestamp`.\n function diffSeconds(uint256 fromTimestamp, uint256 toTimestamp)\n internal\n pure\n returns (uint256 result)\n {\n result = toTimestamp - fromTimestamp;\n }\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* PRIVATE HELPERS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev Unchecked arithmetic for computing the total number of months.\n function _totalMonths(uint256 numYears, uint256 numMonths)\n private\n pure\n returns (uint256 total)\n {\n unchecked {\n total = numYears * 12 + numMonths;\n }\n }\n\n /// @dev Unchecked arithmetic for adding two numbers.\n function _add(uint256 a, uint256 b) private pure returns (uint256 c) {\n unchecked {\n c = a + b;\n }\n }\n\n /// @dev Unchecked arithmetic for subtracting two numbers.\n function _sub(uint256 a, uint256 b) private pure returns (uint256 c) {\n unchecked {\n c = a - b;\n }\n }\n\n /// @dev Returns the offsetted timestamp.\n function _offsetted(uint256 year, uint256 month, uint256 day, uint256 timestamp)\n private\n pure\n returns (uint256 result)\n {\n uint256 dm = daysInMonth(year, month);\n if (day >= dm) {\n day = dm;\n }\n result = dateToEpochDay(year, month, day) * 86400 + (timestamp % 86400);\n }\n}\n"},"src/utils/Asn1Decode.sol":{"content":"// SPDX-License-Identifier: MIT\n// Original source: https://github.com/JonahGroendal/asn1-decode\npragma solidity ^0.8.0;\n\n// Inspired by PufferFinance/rave - Apache-2.0 license\n// https://github.com/JonahGroendal/asn1-decode/blob/5c2d1469fc678513753786acb441e597969192ec/contracts/Asn1Decode.sol\n\nimport \"./BytesUtils.sol\";\n\nlibrary NodePtr {\n // Unpack first byte index\n function ixs(uint256 self) internal pure returns (uint256) {\n return uint80(self);\n }\n // Unpack first content byte index\n\n function ixf(uint256 self) internal pure returns (uint256) {\n return uint80(self >> 80);\n }\n // Unpack last content byte index\n\n function ixl(uint256 self) internal pure returns (uint256) {\n return uint80(self >> 160);\n }\n // Pack 3 uint80s into a uint256\n\n function getPtr(uint256 _ixs, uint256 _ixf, uint256 _ixl) internal pure returns (uint256) {\n _ixs |= _ixf << 80;\n _ixs |= _ixl << 160;\n return _ixs;\n }\n}\n\nlibrary Asn1Decode {\n using NodePtr for uint256;\n using BytesUtils for bytes;\n\n /*\n * @dev Get the root node. First step in traversing an ASN1 structure\n * @param der The DER-encoded ASN1 structure\n * @return A pointer to the outermost node\n */\n function root(bytes memory der) internal pure returns (uint256) {\n return readNodeLength(der, 0);\n }\n\n /*\n * @dev Get the root node of an ASN1 structure that's within a bit string value\n * @param der The DER-encoded ASN1 structure\n * @return A pointer to the outermost node\n */\n function rootOfBitStringAt(bytes memory der, uint256 ptr) internal pure returns (uint256) {\n require(der[ptr.ixs()] == 0x03, \"Not type BIT STRING\");\n return readNodeLength(der, ptr.ixf() + 1);\n }\n\n /*\n * @dev Get the root node of an ASN1 structure that's within an octet string value\n * @param der The DER-encoded ASN1 structure\n * @return A pointer to the outermost node\n */\n function rootOfOctetStringAt(bytes memory der, uint256 ptr) internal pure returns (uint256) {\n require(der[ptr.ixs()] == 0x04, \"Not type OCTET STRING\");\n return readNodeLength(der, ptr.ixf());\n }\n\n /*\n * @dev Get the next sibling node\n * @param der The DER-encoded ASN1 structure\n * @param ptr Points to the indices of the current node\n * @return A pointer to the next sibling node\n */\n function nextSiblingOf(bytes memory der, uint256 ptr) internal pure returns (uint256) {\n return readNodeLength(der, ptr.ixl() + 1);\n }\n\n /*\n * @dev Get the first child node of the current node\n * @param der The DER-encoded ASN1 structure\n * @param ptr Points to the indices of the current node\n * @return A pointer to the first child node\n */\n function firstChildOf(bytes memory der, uint256 ptr) internal pure returns (uint256) {\n require(der[ptr.ixs()] & 0x20 == 0x20, \"Not a constructed type\");\n return readNodeLength(der, ptr.ixf());\n }\n\n /*\n * @dev Use for looping through children of a node (either i or j).\n * @param i Pointer to an ASN1 node\n * @param j Pointer to another ASN1 node of the same ASN1 structure\n * @return True iff j is child of i or i is child of j.\n */\n function isChildOf(uint256 i, uint256 j) internal pure returns (bool) {\n return (((i.ixf() <= j.ixs()) && (j.ixl() <= i.ixl())) || ((j.ixf() <= i.ixs()) && (i.ixl() <= j.ixl())));\n }\n\n /*\n * @dev Extract value of node from DER-encoded structure\n * @param der The der-encoded ASN1 structure\n * @param ptr Points to the indices of the current node\n * @return Value bytes of node\n */\n function bytesAt(bytes memory der, uint256 ptr) internal pure returns (bytes memory) {\n return der.substring(ptr.ixf(), ptr.ixl() + 1 - ptr.ixf());\n }\n\n /*\n * @dev Extract entire node from DER-encoded structure\n * @param der The DER-encoded ASN1 structure\n * @param ptr Points to the indices of the current node\n * @return All bytes of node\n */\n function allBytesAt(bytes memory der, uint256 ptr) internal pure returns (bytes memory) {\n return der.substring(ptr.ixs(), ptr.ixl() + 1 - ptr.ixs());\n }\n\n /*\n * @dev Extract value of node from DER-encoded structure\n * @param der The DER-encoded ASN1 structure\n * @param ptr Points to the indices of the current node\n * @return Value bytes of node as bytes32\n */\n function bytes32At(bytes memory der, uint256 ptr) internal pure returns (bytes32) {\n return der.readBytesN(ptr.ixf(), ptr.ixl() + 1 - ptr.ixf());\n }\n\n /*\n * @dev Extract value of node from DER-encoded structure\n * @param der The der-encoded ASN1 structure\n * @param ptr Points to the indices of the current node\n * @return Uint value of node\n */\n function uintAt(bytes memory der, uint256 ptr) internal pure returns (uint256) {\n require(der[ptr.ixs()] == 0x02, \"Not type INTEGER\");\n require(der[ptr.ixf()] & 0x80 == 0, \"Not positive\");\n uint256 len = ptr.ixl() + 1 - ptr.ixf();\n return uint256(der.readBytesN(ptr.ixf(), len) >> (32 - len) * 8);\n }\n\n /*\n * @dev Extract value of a positive integer node from DER-encoded structure\n * @param der The DER-encoded ASN1 structure\n * @param ptr Points to the indices of the current node\n * @return Value bytes of a positive integer node\n */\n function uintBytesAt(bytes memory der, uint256 ptr) internal pure returns (bytes memory) {\n require(der[ptr.ixs()] == 0x02, \"Not type INTEGER\");\n require(der[ptr.ixf()] & 0x80 == 0, \"Not positive\");\n uint256 valueLength = ptr.ixl() + 1 - ptr.ixf();\n if (der[ptr.ixf()] == 0) {\n return der.substring(ptr.ixf() + 1, valueLength - 1);\n } else {\n return der.substring(ptr.ixf(), valueLength);\n }\n }\n\n function keccakOfBytesAt(bytes memory der, uint256 ptr) internal pure returns (bytes32) {\n return der.keccak(ptr.ixf(), ptr.ixl() + 1 - ptr.ixf());\n }\n\n function keccakOfAllBytesAt(bytes memory der, uint256 ptr) internal pure returns (bytes32) {\n return der.keccak(ptr.ixs(), ptr.ixl() + 1 - ptr.ixs());\n }\n\n /*\n * @dev Extract value of bitstring node from DER-encoded structure\n * @param der The DER-encoded ASN1 structure\n * @param ptr Points to the indices of the current node\n * @return Value of bitstring converted to bytes\n */\n function bitstringAt(bytes memory der, uint256 ptr) internal pure returns (bytes memory) {\n require(der[ptr.ixs()] == 0x03, \"Not type BIT STRING\");\n // Only 00 padded bitstr can be converted to bytestr!\n require(der[ptr.ixf()] == 0x00);\n uint256 valueLength = ptr.ixl() + 1 - ptr.ixf();\n return der.substring(ptr.ixf() + 1, valueLength - 1);\n }\n\n function readNodeLength(bytes memory der, uint256 ix) private pure returns (uint256) {\n uint256 length;\n uint80 ixFirstContentByte;\n uint80 ixLastContentByte;\n if ((der[ix + 1] & 0x80) == 0) {\n length = uint8(der[ix + 1]);\n ixFirstContentByte = uint80(ix + 2);\n ixLastContentByte = uint80(ixFirstContentByte + length - 1);\n } else {\n uint8 lengthbytesLength = uint8(der[ix + 1] & 0x7F);\n if (lengthbytesLength == 1) {\n length = der.readUint8(ix + 2);\n } else if (lengthbytesLength == 2) {\n length = der.readUint16(ix + 2);\n } else {\n length = uint256(der.readBytesN(ix + 2, lengthbytesLength) >> (32 - lengthbytesLength) * 8);\n }\n ixFirstContentByte = uint80(ix + 2 + lengthbytesLength);\n ixLastContentByte = uint80(ixFirstContentByte + length - 1);\n }\n return NodePtr.getPtr(ix, ixFirstContentByte, ixLastContentByte);\n }\n}\n"}},"settings":{"remappings":["solady/=lib/solady/src/","@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/","ds-test/=lib/forge-std/lib/ds-test/src/","erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/","forge-std/=lib/forge-std/src/","halmos-cheatcodes/=lib/openzeppelin-contracts/lib/halmos-cheatcodes/src/","openzeppelin-contracts/=lib/openzeppelin-contracts/"],"optimizer":{"enabled":true,"runs":999999},"metadata":{"useLiteralContent":false,"bytecodeHash":"ipfs","appendCBOR":true},"outputSelection":{"*":{"*":["abi","evm.bytecode","evm.deployedBytecode","evm.methodIdentifiers","metadata"]}},"evmVersion":"paris","viaIR":true,"libraries":{}}} diff --git a/standard-json-input/tcb.json b/standard-json-input/tcb.json deleted file mode 100644 index cd93661..0000000 --- a/standard-json-input/tcb.json +++ /dev/null @@ -1 +0,0 @@ -{"language":"Solidity","sources":{"src/automata_pccs/AutomataFmspcTcbDao.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport {AutomataDaoBase} from \"./shared/AutomataDaoBase.sol\";\nimport {FmspcTcbDao, AttestationRequest, PcsDao} from \"../bases/FmspcTcbDao.sol\";\n\nimport {Ownable} from \"solady/auth/Ownable.sol\";\n\ncontract AutomataFmspcTcbDao is Ownable, AutomataDaoBase, FmspcTcbDao {\n constructor(address _storage, address _pcs, address _fmspcHelper, address _x509Helper)\n AutomataDaoBase(_storage)\n FmspcTcbDao(_pcs, _fmspcHelper, _x509Helper)\n {\n _initializeOwner(msg.sender);\n }\n\n function setPcs(address _pcs) external onlyOwner {\n Pcs = PcsDao(_pcs);\n }\n\n function fmpscTcbV2SchemaID() public pure override returns (bytes32) {\n // NOT-APPLICABLE FOR OUR USE CASE\n // but this is required by most attestation services, such as EAS, Verax etc\n return bytes32(0);\n }\n\n function fmpscTcbV3SchemaID() public pure override returns (bytes32) {\n // NOT-APPLICABLE FOR OUR USE CASE\n // but this is required by most attestation services, such as EAS, Verax etc\n return bytes32(0);\n }\n\n function _attestTcb(AttestationRequest memory req, bytes32 hash)\n internal\n override\n returns (bytes32 attestationId)\n {\n // delete the predecessor if replacing\n _deletePredecessor(req.data.refUID);\n _attestCollateral(hash, req.data.data);\n attestationId = hash;\n }\n}\n"},"src/automata_pccs/shared/AutomataDaoBase.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport {DaoBase} from \"../../bases/DaoBase.sol\";\nimport {CA} from \"../../Common.sol\";\n\ninterface IAutomataDaoStorage {\n function writeToPccs(bytes32 attId, bytes memory attData) external;\n\n function readPccs(bytes32 attId) external view returns (bytes memory attData);\n\n function deleteData(bytes32 attId) external;\n}\n\nabstract contract AutomataDaoBase is DaoBase {\n IAutomataDaoStorage pccsStorage;\n\n constructor(address _storage) {\n pccsStorage = IAutomataDaoStorage(_storage);\n }\n\n function getAttestedData(bytes32 attestationId) public view override returns (bytes memory attestationData) {\n attestationData = pccsStorage.readPccs(attestationId);\n }\n\n /// @dev we simply map the collateral hash to the data itself in our use case\n /// @dev however, this may not be the case when the dao integrates an attestation service, such as EAS\n /// @dev it is recommended to store the hash of the collateral as a separate attestation from the collateral\n /// to reduce the size of data read\n function getCollateralHash(bytes32 attestationId) public pure override returns (bytes32) {\n return attestationId;\n }\n\n function _attestCollateral(bytes32 collateralHash, bytes memory data) internal {\n pccsStorage.writeToPccs(collateralHash, data);\n }\n\n function _deletePredecessor(bytes32 predecessor) internal {\n if (getAttestedData(predecessor).length > 0) {\n pccsStorage.deleteData(predecessor);\n }\n }\n}\n"},"src/bases/FmspcTcbDao.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport {PcsDao} from \"./PcsDao.sol\";\nimport {DaoBase} from \"./DaoBase.sol\";\nimport {SigVerifyBase} from \"./SigVerifyBase.sol\";\n\nimport {CA, AttestationRequestData, AttestationRequest} from \"../Common.sol\";\nimport {\n FmspcTcbHelper,\n TcbInfoJsonObj,\n TcbId,\n TcbInfoBasic,\n TCBLevelsObj,\n TDXModule,\n TDXModuleIdentity\n} from \"../helpers/FmspcTcbHelper.sol\";\n\n/**\n * @title FMSPC TCB Data Access Object\n * @notice This contract is heavily inspired by Section 4.2.3 in the Intel SGX PCCS Design Guidelines\n * https://download.01.org/intel-sgx/sgx-dcap/1.19/linux/docs/SGX_DCAP_Caching_Service_Design_Guide.pdf\n * @dev should extends this contract and use the provided read/write methods to interact with TCBInfo JSON\n * data published on-chain.\n */\nabstract contract FmspcTcbDao is DaoBase, SigVerifyBase {\n PcsDao public Pcs;\n FmspcTcbHelper public FmspcTcbLib;\n\n /// @notice retrieves the attestationId of the attested FMSPC TCBInfo from the registry\n /// key: keccak256(type ++ FMSPC ++ version)\n /// @notice the schema of the attested data is dependent on the version of TCBInfo:\n /// For TCBInfoV2, it consists of the ABI-encoded tuple of:\n /// (TcbInfoBasic, TCBLevelsObj[], string tcbInfo, bytes signature)\n /// For TCBInfoV3, it consists of the abi-encoded tuple of:\n /// (TcbInfoBasic, TDXModule, TDXModuleIdentity[], TCBLevelsObj, string tcbInfo, bytes signature)\n /// See {{ FmspcTcbHelper.sol }} to learn more about FMSPC TCB related struct definitions.\n mapping(bytes32 => bytes32) public fmspcTcbInfoAttestations;\n\n constructor(address _pcs, address _fmspcHelper, address _x509Helper) SigVerifyBase(_x509Helper) {\n Pcs = PcsDao(_pcs);\n FmspcTcbLib = FmspcTcbHelper(_fmspcHelper);\n }\n\n error Invalid_TCB_Cert_Signature();\n error TCB_Expired();\n\n /**\n * @dev overwrite this method to define the schemaID for the attestation of TCBInfo\n */\n function fmpscTcbV2SchemaID() public view virtual returns (bytes32 FMSPC_TCB_V2_SCHEMA_ID);\n\n /**\n * @dev overwrite this method to define the schemaID for the attestation of TCBInfo\n */\n function fmpscTcbV3SchemaID() public view virtual returns (bytes32 FMSPC_TCB_V3_SCHEMA_ID);\n\n /**\n * @dev implement logic to validate and attest TCBInfo\n * @param req structure as defined by EAS\n * https://github.com/ethereum-attestation-service/eas-contracts/blob/52af661748bde9b40ae782907702f885852bc149/contracts/IEAS.sol#L9C1-L23C2\n * @return attestationId\n */\n function _attestTcb(AttestationRequest memory req, bytes32 hash) internal virtual returns (bytes32 attestationId);\n\n /**\n * @notice Section 4.2.3 (getTcbInfo)\n * @notice Queries TCB Info for the given FMSPC\n * @param tcbType 0: SGX, 1: TDX\n * https://github.com/intel/SGXDataCenterAttestationPrimitives/blob/39989a42bbbb0c968153a47254b6de79a27eb603/QuoteVerification/QVL/Src/AttestationParsers/src/Json/TcbInfo.cpp#L46-L47\n * @param fmspc FMSPC\n * @param version v2 or v3\n * https://github.com/intel/SGXDataCenterAttestationPrimitives/blob/39989a42bbbb0c968153a47254b6de79a27eb603/QuoteVerification/QVL/Src/AttestationParsers/include/SgxEcdsaAttestation/AttestationParsers.h#L241-L248\n * @return tcbObj See {FmspcTcbHelper.sol} to learn more about the structure definition\n */\n function getTcbInfo(uint256 tcbType, string calldata fmspc, uint256 version)\n external\n view\n returns (TcbInfoJsonObj memory tcbObj)\n {\n bytes6 fmspcBytes = bytes6(uint48(_parseUintFromHex(fmspc)));\n bytes32 attestationId = _getAttestationId(tcbType, fmspcBytes, version);\n if (attestationId != bytes32(0)) {\n bytes memory attestedTcbData = getAttestedData(attestationId);\n if (version < 3) {\n (,, tcbObj.tcbInfoStr, tcbObj.signature) =\n abi.decode(attestedTcbData, (TcbInfoBasic, TCBLevelsObj[], string, bytes));\n } else {\n (,,,, tcbObj.tcbInfoStr, tcbObj.signature) = abi.decode(\n attestedTcbData, (TcbInfoBasic, TDXModule, TDXModuleIdentity[], TCBLevelsObj[], string, bytes)\n );\n }\n }\n }\n\n /**\n * @notice Section 4.2.9 (upsertEnclaveIdentity)\n * @dev Attestation Registry Entrypoint Contracts, such as Portals on Verax are responsible\n * @dev for performing ECDSA verification on the provided TCBInfo\n * against the Signing CA key prior to attestations\n * @param tcbInfoObj See {FmspcTcbHelper.sol} to learn more about the structure definition\n */\n function upsertFmspcTcb(TcbInfoJsonObj calldata tcbInfoObj) external returns (bytes32 attestationId) {\n _validateTcbInfo(tcbInfoObj);\n (AttestationRequest memory req, TcbInfoBasic memory tcbInfo) = _buildTcbAttestationRequest(tcbInfoObj);\n bytes32 hash = sha256(bytes(tcbInfoObj.tcbInfoStr));\n attestationId = _attestTcb(req, hash);\n fmspcTcbInfoAttestations[keccak256(abi.encodePacked(uint8(tcbInfo.id), tcbInfo.fmspc, tcbInfo.version))] =\n attestationId;\n }\n\n /**\n * @notice Fetches the TCBInfo Issuer Chain\n * @return signingCert - DER encoded Intel TCB Signing Certificate\n * @return rootCert - DER encoded Intel SGX Root CA\n */\n function getTcbIssuerChain() public view returns (bytes memory signingCert, bytes memory rootCert) {\n bytes32 signingCertAttestationId = Pcs.pcsCertAttestations(CA.SIGNING);\n bytes32 rootCertAttestationId = Pcs.pcsCertAttestations(CA.ROOT);\n signingCert = getAttestedData(signingCertAttestationId);\n rootCert = getAttestedData(rootCertAttestationId);\n }\n\n /**\n * @notice computes the key that maps to the corresponding attestation ID\n * @dev once again I am reminding you that the argument tcbType is to indicate the TEE type for the\n * particular TCBInfo. i.e. 0: SGX, 1: TDX\n */\n function _getAttestationId(uint256 tcbType, bytes6 fmspc, uint256 version)\n private\n view\n returns (bytes32 attestationId)\n {\n attestationId = fmspcTcbInfoAttestations[keccak256(abi.encodePacked(uint8(tcbType), fmspc, uint32(version)))];\n }\n\n /**\n * @notice builds an EAS compliant attestation request\n */\n function _buildTcbAttestationRequest(TcbInfoJsonObj calldata tcbInfoObj)\n private\n view\n returns (AttestationRequest memory req, TcbInfoBasic memory tcbInfo)\n {\n bytes memory attestationData;\n (attestationData, tcbInfo) = _buildAttestationData(tcbInfoObj.tcbInfoStr, tcbInfoObj.signature);\n bytes32 predecessorAttestationId = _getAttestationId(uint8(tcbInfo.id), tcbInfo.fmspc, tcbInfo.version);\n if (block.timestamp < tcbInfo.issueDate || block.timestamp > tcbInfo.nextUpdate) {\n revert TCB_Expired();\n }\n AttestationRequestData memory reqData = AttestationRequestData({\n recipient: msg.sender,\n expirationTime: uint64(tcbInfo.nextUpdate),\n revocable: true,\n refUID: predecessorAttestationId,\n data: attestationData,\n value: 0\n });\n bytes32 schemaId = tcbInfo.version < 3 ? fmpscTcbV2SchemaID() : fmpscTcbV3SchemaID();\n req = AttestationRequest({schema: schemaId, data: reqData});\n }\n\n function _buildAttestationData(string memory tcbInfoStr, bytes memory signature)\n private\n view\n returns (bytes memory attestationData, TcbInfoBasic memory tcbInfo)\n {\n (, TCBLevelsObj[] memory tcbLevels) = FmspcTcbLib.parseTcbLevels(tcbInfoStr);\n tcbInfo = FmspcTcbLib.parseTcbString(tcbInfoStr);\n if (tcbInfo.version < 3) {\n attestationData = abi.encode(tcbInfo, tcbLevels, tcbInfoStr, signature);\n } else {\n TDXModule memory module;\n TDXModuleIdentity[] memory moduleIdentities;\n if (tcbInfo.id == TcbId.TDX) {\n (module, moduleIdentities) = FmspcTcbLib.parseTcbTdxModules(tcbInfoStr);\n }\n attestationData = abi.encode(tcbInfo, module, moduleIdentities, tcbLevels, tcbInfoStr, signature);\n }\n }\n\n function _validateTcbInfo(TcbInfoJsonObj calldata tcbInfoObj) private view {\n // Get TCB Signing Cert\n bytes32 tcbSigningAttestationId = Pcs.pcsCertAttestations(CA.SIGNING);\n bytes memory signingDer = getAttestedData(tcbSigningAttestationId);\n\n // Validate signature\n bool sigVerified = verifySignature(sha256(bytes(tcbInfoObj.tcbInfoStr)), tcbInfoObj.signature, signingDer);\n\n if (!sigVerified) {\n revert Invalid_TCB_Cert_Signature();\n }\n }\n}\n"},"lib/solady/src/auth/Ownable.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\n/// @notice Simple single owner authorization mixin.\n/// @author Solady (https://github.com/vectorized/solady/blob/main/src/auth/Ownable.sol)\n///\n/// @dev Note:\n/// This implementation does NOT auto-initialize the owner to `msg.sender`.\n/// You MUST call the `_initializeOwner` in the constructor / initializer.\n///\n/// While the ownable portion follows\n/// [EIP-173](https://eips.ethereum.org/EIPS/eip-173) for compatibility,\n/// the nomenclature for the 2-step ownership handover may be unique to this codebase.\nabstract contract Ownable {\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* CUSTOM ERRORS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev The caller is not authorized to call the function.\n error Unauthorized();\n\n /// @dev The `newOwner` cannot be the zero address.\n error NewOwnerIsZeroAddress();\n\n /// @dev The `pendingOwner` does not have a valid handover request.\n error NoHandoverRequest();\n\n /// @dev Cannot double-initialize.\n error AlreadyInitialized();\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* EVENTS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev The ownership is transferred from `oldOwner` to `newOwner`.\n /// This event is intentionally kept the same as OpenZeppelin's Ownable to be\n /// compatible with indexers and [EIP-173](https://eips.ethereum.org/EIPS/eip-173),\n /// despite it not being as lightweight as a single argument event.\n event OwnershipTransferred(address indexed oldOwner, address indexed newOwner);\n\n /// @dev An ownership handover to `pendingOwner` has been requested.\n event OwnershipHandoverRequested(address indexed pendingOwner);\n\n /// @dev The ownership handover to `pendingOwner` has been canceled.\n event OwnershipHandoverCanceled(address indexed pendingOwner);\n\n /// @dev `keccak256(bytes(\"OwnershipTransferred(address,address)\"))`.\n uint256 private constant _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE =\n 0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0;\n\n /// @dev `keccak256(bytes(\"OwnershipHandoverRequested(address)\"))`.\n uint256 private constant _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE =\n 0xdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d;\n\n /// @dev `keccak256(bytes(\"OwnershipHandoverCanceled(address)\"))`.\n uint256 private constant _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE =\n 0xfa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92;\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* STORAGE */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev The owner slot is given by:\n /// `bytes32(~uint256(uint32(bytes4(keccak256(\"_OWNER_SLOT_NOT\")))))`.\n /// It is intentionally chosen to be a high value\n /// to avoid collision with lower slots.\n /// The choice of manual storage layout is to enable compatibility\n /// with both regular and upgradeable contracts.\n bytes32 internal constant _OWNER_SLOT =\n 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff74873927;\n\n /// The ownership handover slot of `newOwner` is given by:\n /// ```\n /// mstore(0x00, or(shl(96, user), _HANDOVER_SLOT_SEED))\n /// let handoverSlot := keccak256(0x00, 0x20)\n /// ```\n /// It stores the expiry timestamp of the two-step ownership handover.\n uint256 private constant _HANDOVER_SLOT_SEED = 0x389a75e1;\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* INTERNAL FUNCTIONS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev Override to return true to make `_initializeOwner` prevent double-initialization.\n function _guardInitializeOwner() internal pure virtual returns (bool guard) {}\n\n /// @dev Initializes the owner directly without authorization guard.\n /// This function must be called upon initialization,\n /// regardless of whether the contract is upgradeable or not.\n /// This is to enable generalization to both regular and upgradeable contracts,\n /// and to save gas in case the initial owner is not the caller.\n /// For performance reasons, this function will not check if there\n /// is an existing owner.\n function _initializeOwner(address newOwner) internal virtual {\n if (_guardInitializeOwner()) {\n /// @solidity memory-safe-assembly\n assembly {\n let ownerSlot := _OWNER_SLOT\n if sload(ownerSlot) {\n mstore(0x00, 0x0dc149f0) // `AlreadyInitialized()`.\n revert(0x1c, 0x04)\n }\n // Clean the upper 96 bits.\n newOwner := shr(96, shl(96, newOwner))\n // Store the new value.\n sstore(ownerSlot, or(newOwner, shl(255, iszero(newOwner))))\n // Emit the {OwnershipTransferred} event.\n log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner)\n }\n } else {\n /// @solidity memory-safe-assembly\n assembly {\n // Clean the upper 96 bits.\n newOwner := shr(96, shl(96, newOwner))\n // Store the new value.\n sstore(_OWNER_SLOT, newOwner)\n // Emit the {OwnershipTransferred} event.\n log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner)\n }\n }\n }\n\n /// @dev Sets the owner directly without authorization guard.\n function _setOwner(address newOwner) internal virtual {\n if (_guardInitializeOwner()) {\n /// @solidity memory-safe-assembly\n assembly {\n let ownerSlot := _OWNER_SLOT\n // Clean the upper 96 bits.\n newOwner := shr(96, shl(96, newOwner))\n // Emit the {OwnershipTransferred} event.\n log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner)\n // Store the new value.\n sstore(ownerSlot, or(newOwner, shl(255, iszero(newOwner))))\n }\n } else {\n /// @solidity memory-safe-assembly\n assembly {\n let ownerSlot := _OWNER_SLOT\n // Clean the upper 96 bits.\n newOwner := shr(96, shl(96, newOwner))\n // Emit the {OwnershipTransferred} event.\n log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner)\n // Store the new value.\n sstore(ownerSlot, newOwner)\n }\n }\n }\n\n /// @dev Throws if the sender is not the owner.\n function _checkOwner() internal view virtual {\n /// @solidity memory-safe-assembly\n assembly {\n // If the caller is not the stored owner, revert.\n if iszero(eq(caller(), sload(_OWNER_SLOT))) {\n mstore(0x00, 0x82b42900) // `Unauthorized()`.\n revert(0x1c, 0x04)\n }\n }\n }\n\n /// @dev Returns how long a two-step ownership handover is valid for in seconds.\n /// Override to return a different value if needed.\n /// Made internal to conserve bytecode. Wrap it in a public function if needed.\n function _ownershipHandoverValidFor() internal view virtual returns (uint64) {\n return 48 * 3600;\n }\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* PUBLIC UPDATE FUNCTIONS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev Allows the owner to transfer the ownership to `newOwner`.\n function transferOwnership(address newOwner) public payable virtual onlyOwner {\n /// @solidity memory-safe-assembly\n assembly {\n if iszero(shl(96, newOwner)) {\n mstore(0x00, 0x7448fbae) // `NewOwnerIsZeroAddress()`.\n revert(0x1c, 0x04)\n }\n }\n _setOwner(newOwner);\n }\n\n /// @dev Allows the owner to renounce their ownership.\n function renounceOwnership() public payable virtual onlyOwner {\n _setOwner(address(0));\n }\n\n /// @dev Request a two-step ownership handover to the caller.\n /// The request will automatically expire in 48 hours (172800 seconds) by default.\n function requestOwnershipHandover() public payable virtual {\n unchecked {\n uint256 expires = block.timestamp + _ownershipHandoverValidFor();\n /// @solidity memory-safe-assembly\n assembly {\n // Compute and set the handover slot to `expires`.\n mstore(0x0c, _HANDOVER_SLOT_SEED)\n mstore(0x00, caller())\n sstore(keccak256(0x0c, 0x20), expires)\n // Emit the {OwnershipHandoverRequested} event.\n log2(0, 0, _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE, caller())\n }\n }\n }\n\n /// @dev Cancels the two-step ownership handover to the caller, if any.\n function cancelOwnershipHandover() public payable virtual {\n /// @solidity memory-safe-assembly\n assembly {\n // Compute and set the handover slot to 0.\n mstore(0x0c, _HANDOVER_SLOT_SEED)\n mstore(0x00, caller())\n sstore(keccak256(0x0c, 0x20), 0)\n // Emit the {OwnershipHandoverCanceled} event.\n log2(0, 0, _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE, caller())\n }\n }\n\n /// @dev Allows the owner to complete the two-step ownership handover to `pendingOwner`.\n /// Reverts if there is no existing ownership handover requested by `pendingOwner`.\n function completeOwnershipHandover(address pendingOwner) public payable virtual onlyOwner {\n /// @solidity memory-safe-assembly\n assembly {\n // Compute and set the handover slot to 0.\n mstore(0x0c, _HANDOVER_SLOT_SEED)\n mstore(0x00, pendingOwner)\n let handoverSlot := keccak256(0x0c, 0x20)\n // If the handover does not exist, or has expired.\n if gt(timestamp(), sload(handoverSlot)) {\n mstore(0x00, 0x6f5e8818) // `NoHandoverRequest()`.\n revert(0x1c, 0x04)\n }\n // Set the handover slot to 0.\n sstore(handoverSlot, 0)\n }\n _setOwner(pendingOwner);\n }\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* PUBLIC READ FUNCTIONS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev Returns the owner of the contract.\n function owner() public view virtual returns (address result) {\n /// @solidity memory-safe-assembly\n assembly {\n result := sload(_OWNER_SLOT)\n }\n }\n\n /// @dev Returns the expiry timestamp for the two-step ownership handover to `pendingOwner`.\n function ownershipHandoverExpiresAt(address pendingOwner)\n public\n view\n virtual\n returns (uint256 result)\n {\n /// @solidity memory-safe-assembly\n assembly {\n // Compute the handover slot.\n mstore(0x0c, _HANDOVER_SLOT_SEED)\n mstore(0x00, pendingOwner)\n // Load the handover slot.\n result := sload(keccak256(0x0c, 0x20))\n }\n }\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* MODIFIERS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev Marks a function as only callable by the owner.\n modifier onlyOwner() virtual {\n _checkOwner();\n _;\n }\n}\n"},"src/bases/DaoBase.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity >=0.8.0;\n\nabstract contract DaoBase {\n /**\n * @dev implement getter logic to retrieve attested data\n * @param attestationId maps to the data\n */\n function getAttestedData(bytes32 attestationId) public view virtual returns (bytes memory attestationData);\n\n /**\n * @dev must store the hash of a collateral (e.g. X509 Cert, TCBInfo JSON etc) in the attestation registry\n * @dev it is recommended to store hash as a separate attestation from the actual collateral\n * @dev this getter can be useful for checking the correctness of the queried attested collateral\n *\n * @dev may link the hash attestation with the attestation of the collateral\n * For example, the content of a hash attestation can be a tuple of bytes32 values consisting of:\n * (bytes32 collateralHash, bytes32 collateralAttestationId)\n * @param attestationId - the attestationId pointing to the hash attestation, or the collateral attestation\n * itself, if the hash is included as part of the attestation data, this varies by how you define the schema.\n */\n function getCollateralHash(bytes32 attestationId) public view virtual returns (bytes32 collateralHash);\n\n /// @dev https://github.com/Vectorized/solady/blob/4964e3e2da1bc86b0394f63a90821f51d60a260b/src/utils/JSONParserLib.sol#L339-L364\n /// @dev Parses an unsigned integer from a string (in hexadecimal, i.e. base 16).\n /// Reverts if `s` is not a valid uint256 hex string matching the RegEx\n /// `^(0[xX])?[0-9a-fA-F]+$`, or if the parsed number is too big for a uint256.\n function _parseUintFromHex(string memory s) internal pure returns (uint256 result) {\n /// @solidity memory-safe-assembly\n assembly {\n let n := mload(s)\n // Skip two if starts with '0x' or '0X'.\n let i := shl(1, and(eq(0x3078, or(shr(240, mload(add(s, 0x20))), 0x20)), gt(n, 1)))\n for {} 1 {} {\n i := add(i, 1)\n let c :=\n byte(\n and(0x1f, shr(and(mload(add(s, i)), 0xff), 0x3e4088843e41bac000000000000)),\n 0x3010a071000000b0104040208000c05090d060e0f\n )\n n := mul(n, iszero(or(iszero(c), shr(252, result))))\n result := add(shl(4, result), sub(c, 1))\n if iszero(lt(i, n)) { break }\n }\n if iszero(n) {\n mstore(0x00, 0x10182796) // `ParsingFailed()`.\n revert(0x1c, 0x04)\n }\n }\n }\n}\n"},"src/Common.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nenum CA {\n ROOT,\n PROCESSOR,\n PLATFORM,\n SIGNING\n}\n\n/// @notice Attestation Definition is taken from https://github.com/ethereum-attestation-service/eas-contracts/blob/52af661748bde9b40ae782907702f885852bc149/contracts/IEAS.sol#L9C1-L23C2\n/// @notice We opted for EAS Attestation Request Definition to ensure interoperability between Verax and EAS\n\nstruct AttestationRequestData {\n address recipient; // The recipient of the attestation.\n uint64 expirationTime; // The time when the attestation expires (Unix timestamp).\n bool revocable; // Whether the attestation is revocable.\n bytes32 refUID; // The UID of the related attestation.\n bytes data; // Custom attestation data.\n uint256 value; // An explicit ETH amount to send to the resolver. This is important to prevent accidental user errors.\n}\n\nstruct AttestationRequest {\n bytes32 schema; // The unique identifier of the schema.\n AttestationRequestData data; // The arguments of the attestation request.\n}\n\n/// @notice A struct representing a single attestation.\n/// https://github.com/ethereum-attestation-service/eas-contracts/blob/52af661748bde9b40ae782907702f885852bc149/contracts/Common.sol#L25C1-L37C2\nstruct Attestation {\n bytes32 uid; // A unique identifier of the attestation.\n bytes32 schema; // The unique identifier of the schema.\n uint64 time; // The time when the attestation was created (Unix timestamp).\n uint64 expirationTime; // The time when the attestation expires (Unix timestamp).\n uint64 revocationTime; // The time when the attestation was revoked (Unix timestamp).\n bytes32 refUID; // The UID of the related attestation.\n address recipient; // The recipient of the attestation.\n address attester; // The attester/sender of the attestation.\n bool revocable; // Whether the attestation is revocable.\n bytes data; // Custom attestation data.\n}\n"},"src/bases/PcsDao.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport {CA, AttestationRequestData, AttestationRequest} from \"../Common.sol\";\nimport {X509Helper, X509CertObj} from \"../helpers/X509Helper.sol\";\nimport {X509CRLHelper, X509CRLObj} from \"../helpers/X509CRLHelper.sol\";\n\nimport {DaoBase} from \"./DaoBase.sol\";\nimport {SigVerifyBase} from \"./SigVerifyBase.sol\";\n\nimport {LibString} from \"solady/utils/LibString.sol\";\n\n/**\n * @title Intel PCS Data Access Object\n * @notice This is a core contract of our on-chain PCCS implementation as it provides methods\n * @notice to read/write essential collaterals such as the RootCA, Intermediate CAs and CRLs.\n * @notice All other DAOs are expected to configure and make external calls to this contract to fetch those collaterals.\n * @notice This contract is heavily inspired by Sections 4.2.5 and 4.2.6 in the Intel SGX PCCS Design Guideline\n * https://download.01.org/intel-sgx/sgx-dcap/1.19/linux/docs/SGX_DCAP_Caching_Service_Design_Guide.pdf\n */\nabstract contract PcsDao is DaoBase, SigVerifyBase {\n using LibString for string;\n\n X509CRLHelper public crlLib;\n\n /// @notice Fetches the attestationId of the attested PCS Certificate\n ///\n /// @dev Must ensure that the public key for the configured Intel Root CA matches with\n /// @dev the Intel source code at: https://github.com/intel/SGXDataCenterAttestationPrimitives/blob/39989a42bbbb0c968153a47254b6de79a27eb603/QuoteVerification/QvE/Enclave/qve.cpp#L92-L100\n ///\n /// @notice the schema of the attested data is the following:\n /// - bytes pcsCert\n mapping(CA => bytes32) public pcsCertAttestations;\n\n /// @notice Fetches the attestationId of the attested PCS CRLs\n ///\n /// @dev Verification of CRLs are conducted as part of the PCS attestation process\n ///\n /// @notice the schema of the attested data is the following:\n /// - bytes pcsCrl\n mapping(CA => bytes32) public pcsCrlAttestations;\n\n string constant PCK_PLATFORM_CA_COMMON_NAME = \"Intel SGX PCK Platform CA\";\n string constant PCK_PROCESSOR_CA_COMMON_NAME = \"Intel SGX PCK Processor CA\";\n string constant SIGNING_COMMON_NAME = \"Intel SGX TCB Signing\";\n string constant ROOT_CA_COMMON_NAME = \"Intel SGX Root CA\";\n\n // keccak256(hex\"0ba9c4c0c0c86193a3fe23d6b02cda10a8bbd4e88e48b4458561a36e705525f567918e2edc88e40d860bd0cc4ee26aacc988e505a953558c453f6b0904ae7394\")\n // the uncompressed (0x04) prefix is not included in the pubkey pre-image\n bytes32 constant ROOT_CA_PUBKEY_HASH = 0x89f72d7c488e5b53a77c23ebcb36970ef7eb5bcf6658e9b8292cfbe4703a8473;\n\n error Missing_Certificate(CA ca);\n error Invalid_PCK_CA(CA ca);\n error Invalid_Issuer_Name();\n error Invalid_Subject_Name();\n error Certificate_Expired();\n error Root_Key_Mismatch();\n error Certificate_Revoked(CA ca, uint256 serialNum);\n error Missing_Issuer();\n error Invalid_Signature();\n\n constructor(address _x509, address _crl) SigVerifyBase(_x509) {\n crlLib = X509CRLHelper(_crl);\n }\n\n modifier pckCACheck(CA ca) {\n if (ca == CA.ROOT || ca == CA.SIGNING) {\n revert Invalid_PCK_CA(ca);\n }\n _;\n }\n\n /**\n * @param ca see {Common.sol} for definition\n * @return cert - DER encoded certificate\n * @return crl - DER-encoded CRLs that is signed by the provided cert\n */\n function getCertificateById(CA ca) external view returns (bytes memory cert, bytes memory crl) {\n bytes32 pcsCertAttestationId = pcsCertAttestations[ca];\n if (pcsCertAttestationId == bytes32(0)) {\n revert Missing_Certificate(ca);\n }\n cert = getAttestedData(pcsCertAttestationId);\n\n bytes32 pcsCrlAttestationId = pcsCrlAttestations[ca];\n if (pcsCrlAttestationId != bytes32(0)) {\n crl = getAttestedData(pcsCrlAttestationId);\n }\n }\n\n /**\n * Section 4.2.6 (upsertPcsCertificates)\n * @param ca replaces the \"id\" value with the ca_id\n * @param cert the DER-encoded certificate\n */\n function upsertPcsCertificates(CA ca, bytes calldata cert) external returns (bytes32 attestationId) {\n bytes32 hash = _validatePcsCert(ca, cert);\n AttestationRequest memory req = _buildPcsAttestationRequest(false, ca, cert);\n attestationId = _attestPcs(req, hash);\n pcsCertAttestations[ca] = attestationId;\n }\n\n /**\n * Section 4.2.5 (upsertPckCrl)\n * @param ca either CA.PROCESSOR or CA.PLATFORM\n * @param crl the DER-encoded CRL\n */\n function upsertPckCrl(CA ca, bytes calldata crl) external pckCACheck(ca) returns (bytes32 attestationId) {\n attestationId = _upsertPcsCrl(ca, crl);\n }\n\n function upsertRootCACrl(bytes calldata rootcacrl) external returns (bytes32 attestationId) {\n attestationId = _upsertPcsCrl(CA.ROOT, rootcacrl);\n }\n\n function pcsCertSchemaID() public view virtual returns (bytes32 PCS_CERT_SCHEMA_ID);\n\n function pcsCrlSchemaID() public view virtual returns (bytes32 PCS_CRL_SCHEMA_ID);\n\n /**\n * @dev implement logic to validate and attest PCS Certificates or CRLs\n * @param req structure as defined by EAS\n * https://github.com/ethereum-attestation-service/eas-contracts/blob/52af661748bde9b40ae782907702f885852bc149/contracts/IEAS.sol#L9C1-L23C2\n * @return attestationId\n */\n function _attestPcs(AttestationRequest memory req, bytes32 hash) internal virtual returns (bytes32 attestationId);\n\n function _upsertPcsCrl(CA ca, bytes calldata crl) private returns (bytes32 attestationId) {\n bytes32 hash = _validatePcsCrl(ca, crl);\n AttestationRequest memory req = _buildPcsAttestationRequest(true, ca, crl);\n attestationId = _attestPcs(req, hash);\n pcsCrlAttestations[ca] = attestationId;\n }\n\n /**\n * @notice builds an EAS compliant attestation request\n * @param isCrl - true only if the attested data is a CRL\n * @param der - contains the DER encoded data, specified by isCrl and CA\n */\n function _buildPcsAttestationRequest(bool isCrl, CA ca, bytes calldata der)\n private\n view\n returns (AttestationRequest memory req)\n {\n bytes32 predecessorAttestationId = isCrl ? pcsCrlAttestations[ca] : pcsCertAttestations[ca];\n AttestationRequestData memory reqData = AttestationRequestData({\n recipient: msg.sender,\n expirationTime: 0, // assign zero here because this has already been checked\n revocable: true,\n refUID: predecessorAttestationId,\n data: der,\n value: 0\n });\n bytes32 schemaId = isCrl ? pcsCrlSchemaID() : pcsCertSchemaID();\n req = AttestationRequest({schema: schemaId, data: reqData});\n }\n\n function _validatePcsCert(CA ca, bytes calldata cert) private view returns (bytes32 hash) {\n X509Helper x509Lib = X509Helper(x509);\n\n // Step 1: Check whether cert has expired\n bool notExpired = x509Lib.certIsNotExpired(cert);\n if (!notExpired) {\n revert Certificate_Expired();\n }\n\n // Step 2: Check issuer and subject common names are valid\n string memory issuerName = x509Lib.getIssuerCommonName(cert);\n string memory subjectName = x509Lib.getSubjectCommonName(cert);\n string memory expectedIssuer = ROOT_CA_COMMON_NAME;\n string memory expectedSubject;\n if (ca == CA.PLATFORM) {\n expectedSubject = PCK_PLATFORM_CA_COMMON_NAME;\n } else if (ca == CA.PROCESSOR) {\n expectedSubject = PCK_PROCESSOR_CA_COMMON_NAME;\n } else if (ca == CA.SIGNING) {\n expectedSubject = SIGNING_COMMON_NAME;\n } else if (ca == CA.ROOT) {\n expectedSubject = ROOT_CA_COMMON_NAME;\n }\n\n if (!LibString.eq(issuerName, expectedIssuer)) {\n revert Invalid_Issuer_Name();\n }\n if (!LibString.eq(subjectName, expectedSubject)) {\n revert Invalid_Subject_Name();\n }\n\n // Step 3: Check Revocation Status\n bytes memory rootCrlData = getAttestedData(pcsCrlAttestations[CA.ROOT]);\n if (ca == CA.ROOT) {\n bytes memory pubKey = x509Lib.getSubjectPublicKey(cert);\n if (keccak256(pubKey) != ROOT_CA_PUBKEY_HASH) {\n revert Root_Key_Mismatch();\n }\n } else if (rootCrlData.length > 0) {\n uint256 serialNum = x509Lib.getSerialNumber(cert);\n bool revoked = crlLib.serialNumberIsRevoked(serialNum, rootCrlData);\n if (revoked) {\n revert Certificate_Revoked(ca, serialNum);\n }\n }\n\n // Step 4: Check signature\n bytes memory rootCert = _getIssuer(CA.ROOT);\n (bytes memory tbs, bytes memory signature) = x509Lib.getTbsAndSig(cert);\n bytes32 digest = sha256(tbs);\n bool sigVerified;\n if (ca == CA.ROOT) {\n // the root certificate is issued by its own key\n sigVerified = verifySignature(digest, signature, cert);\n } else if (rootCert.length > 0) {\n sigVerified = verifySignature(digest, signature, rootCert);\n } else {\n // all other certificates should already have an iusuer configured\n revert Missing_Issuer();\n }\n\n if (!sigVerified) {\n revert Invalid_Signature();\n }\n\n hash = keccak256(tbs);\n }\n\n function _validatePcsCrl(CA ca, bytes calldata crl) private view returns (bytes32 hash) {\n // Step 1: Check whether CRL has expired\n bool notExpired = crlLib.crlIsNotExpired(crl);\n if (!notExpired) {\n revert Certificate_Expired();\n }\n\n // Step 2: Check CRL issuer\n string memory issuerCommonName = crlLib.getIssuerCommonName(crl);\n string memory expectedIssuer;\n if (ca == CA.PLATFORM || ca == CA.PROCESSOR) {\n expectedIssuer = ca == CA.PLATFORM ? PCK_PLATFORM_CA_COMMON_NAME : PCK_PROCESSOR_CA_COMMON_NAME;\n } else {\n expectedIssuer = ROOT_CA_COMMON_NAME;\n }\n if (!LibString.eq(issuerCommonName, expectedIssuer)) {\n revert Invalid_Issuer_Name();\n }\n\n // Step 3: Verify signature\n (bytes memory tbs, bytes memory signature) = crlLib.getTbsAndSig(crl);\n bytes32 digest = sha256(tbs);\n bool sigVerified = verifySignature(digest, signature, _getIssuer(ca));\n if (!sigVerified) {\n revert Invalid_Signature();\n }\n\n hash = keccak256(tbs);\n }\n\n function _getIssuer(CA ca) private view returns (bytes memory issuerCert) {\n bytes32 intermediateCertAttestationId = pcsCertAttestations[ca];\n bytes32 rootCertAttestationId = pcsCertAttestations[CA.ROOT];\n if (ca == CA.PLATFORM || ca == CA.PROCESSOR) {\n // this is applicable to crls only\n // since all certs in the pcsdao are issued by the root\n issuerCert = getAttestedData(intermediateCertAttestationId);\n } else {\n issuerCert = getAttestedData(rootCertAttestationId);\n }\n }\n}\n"},"src/bases/SigVerifyBase.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport {BytesUtils} from \"../utils/BytesUtils.sol\";\nimport {P256Verifier} from \"../utils/P256Verifier.sol\";\n\ninterface IX509 {\n function getSubjectPublicKey(bytes memory der) external pure returns (bytes memory pubKey);\n}\n\nabstract contract SigVerifyBase {\n address public x509;\n\n using BytesUtils for bytes;\n\n constructor(address _x509helper) {\n x509 = _x509helper;\n }\n\n function verifySignature(bytes32 digest, bytes memory signature, bytes memory signingCertBlob)\n internal\n view\n returns (bool verified)\n {\n if (signature.length != 64) {\n return false;\n }\n\n bytes memory pubKey = IX509(x509).getSubjectPublicKey(signingCertBlob);\n if (pubKey.length != 64) {\n return false;\n }\n\n verified = P256Verifier.ecdsaVerify(digest, signature, pubKey);\n }\n}\n"},"src/helpers/FmspcTcbHelper.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport {JSONParserLib} from \"solady/utils/JSONParserLib.sol\";\nimport {LibString} from \"solady/utils/LibString.sol\";\nimport {DateTimeUtils} from \"../utils/DateTimeUtils.sol\";\n\n// https://github.com/intel/SGXDataCenterAttestationPrimitives/blob/e7604e02331b3377f3766ed3653250e03af72d45/QuoteVerification/QVL/Src/AttestationLibrary/src/CertVerification/X509Constants.h#L64\nuint256 constant TCB_CPUSVN_SIZE = 16;\n\nenum TcbId {\n /// the \"id\" field is absent from TCBInfo V2\n /// which defaults TcbId to SGX\n /// since TDX TCBInfos are only included in V3 or above\n SGX,\n TDX\n}\n\n/**\n * @title Solidity Object representing the TCBInfo JSON\n * @param tcbInfo: tcbInfoJson.tcbInfo string object body\n * @param signature The signature to be passed as bytes array\n */\nstruct TcbInfoJsonObj {\n string tcbInfoStr;\n bytes signature;\n}\n\nstruct TcbInfoBasic {\n /// the name \"tcbType\" can be confusing/misleading\n /// as the tcbType referred here in this struct is the type\n /// of TCB level composition that determines TCB level comparison logic\n /// It is not the same as the \"type\" parameter passed as an argument to the\n /// getTcbInfo() API method described in Section 4.2.3 of the Intel PCCS Design Document\n /// Instead, getTcbInfo() \"type\" argument should be checked against the \"id\" value of this struct\n /// which represents the TEE type for the given TCBInfo\n uint8 tcbType;\n TcbId id;\n uint32 version;\n uint64 issueDate;\n uint64 nextUpdate;\n uint32 evaluationDataNumber;\n bytes6 fmspc;\n bytes2 pceid;\n}\n\nstruct TCBLevelsObj {\n uint16 pcesvn;\n uint8[] sgxComponentCpuSvns;\n uint8[] tdxSvns;\n uint64 tcbDateTimestamp;\n TCBStatus status;\n}\n\nstruct TDXModule {\n bytes mrsigner; // 48 bytes\n bytes8 attributes;\n bytes8 attributesMask;\n}\n\nstruct TDXModuleIdentity {\n string id;\n bytes8 attributes;\n bytes8 attributesMask;\n bytes mrsigner; // 48 bytes\n TDXModuleTCBLevelsObj[] tcbLevels;\n}\n\nstruct TDXModuleTCBLevelsObj {\n uint8 isvsvn;\n uint64 tcbDateTimestamp;\n TCBStatus status;\n}\n\nenum TCBStatus {\n OK,\n TCB_SW_HARDENING_NEEDED,\n TCB_CONFIGURATION_AND_SW_HARDENING_NEEDED,\n TCB_CONFIGURATION_NEEDED,\n TCB_OUT_OF_DATE,\n TCB_OUT_OF_DATE_CONFIGURATION_NEEDED,\n TCB_REVOKED,\n TCB_UNRECOGNIZED\n}\n\n/**\n * @title FMSPC TCB Helper Contract\n * @notice This is a standalone contract that can be used by off-chain applications and smart contracts\n * to parse TCBInfo data\n * @notice The TCBInfo Object itself may vary by their version and type.\n * @notice This contract only provides a simple parser that could only extract basic info about the TCBInfo\n * such as, its version, type, fmspc, issue date and next update.\n * @dev should consider extending this contract to implement parsers that could extract detailed TCBInfo\n * using logic that complies to the specific version and type.\n */\ncontract FmspcTcbHelper {\n using JSONParserLib for JSONParserLib.Item;\n using LibString for string;\n\n error TCBInfo_Invalid();\n error TCB_TDX_Version_Invalid();\n error TCB_TDX_ID_Invalid();\n\n // 544k gas\n function parseTcbString(string calldata tcbInfoStr) external pure returns (TcbInfoBasic memory tcbInfo) {\n JSONParserLib.Item memory root = JSONParserLib.parse(tcbInfoStr);\n JSONParserLib.Item[] memory tcbInfoObj = root.children();\n\n bool tcbTypeFound;\n bool fmspcFound;\n bool versionFound;\n bool issueDateFound;\n bool nextUpdateFound;\n bool pceidFound;\n bool evaluationFound;\n bool idFound;\n bool allFound;\n\n for (uint256 y = 0; y < root.size(); y++) {\n JSONParserLib.Item memory current = tcbInfoObj[y];\n string memory decodedKey = JSONParserLib.decodeString(current.key());\n string memory val = current.value();\n if (decodedKey.eq(\"tcbType\")) {\n tcbInfo.tcbType = uint8(JSONParserLib.parseUint(val));\n tcbTypeFound = true;\n } else if (decodedKey.eq(\"id\")) {\n string memory idStr = JSONParserLib.decodeString(val);\n if (idStr.eq(\"SGX\")) {\n tcbInfo.id = TcbId.SGX;\n } else if (idStr.eq(\"TDX\")) {\n tcbInfo.id = TcbId.TDX;\n } else {\n revert TCBInfo_Invalid();\n }\n idFound = true;\n } else if (decodedKey.eq(\"fmspc\")) {\n tcbInfo.fmspc = bytes6(uint48(JSONParserLib.parseUintFromHex(JSONParserLib.decodeString(val))));\n fmspcFound = true;\n } else if (decodedKey.eq(\"version\")) {\n tcbInfo.version = uint32(JSONParserLib.parseUint(val));\n versionFound = true;\n } else if (decodedKey.eq(\"issueDate\")) {\n tcbInfo.issueDate = uint64(DateTimeUtils.fromISOToTimestamp(JSONParserLib.decodeString(val)));\n issueDateFound = true;\n } else if (decodedKey.eq(\"nextUpdate\")) {\n tcbInfo.nextUpdate = uint64(DateTimeUtils.fromISOToTimestamp(JSONParserLib.decodeString(val)));\n nextUpdateFound = true;\n } else if (decodedKey.eq(\"pceId\")) {\n tcbInfo.pceid = bytes2(uint16(JSONParserLib.parseUintFromHex(JSONParserLib.decodeString(val))));\n pceidFound = true;\n } else if (decodedKey.eq(\"tcbEvaluationDataNumber\")) {\n tcbInfo.evaluationDataNumber = uint32(JSONParserLib.parseUint(val));\n evaluationFound = true;\n }\n if (versionFound) {\n allFound =\n (tcbTypeFound && fmspcFound && issueDateFound && nextUpdateFound && pceidFound && evaluationFound);\n if (tcbInfo.version >= 3) {\n allFound = allFound && idFound;\n }\n if (allFound) {\n break;\n }\n }\n }\n\n if (!allFound) {\n revert TCBInfo_Invalid();\n }\n }\n\n // 1.4M gas\n function parseTcbLevels(string calldata tcbInfoStr)\n external\n pure\n returns (uint256 version, TCBLevelsObj[] memory tcbLevels)\n {\n JSONParserLib.Item memory root = JSONParserLib.parse(tcbInfoStr);\n JSONParserLib.Item[] memory tcbInfoObj = root.children();\n\n bool versionFound;\n bool tcbLevelsFound;\n JSONParserLib.Item[] memory tcbLevelsObj;\n\n for (uint256 i = 0; i < root.size(); i++) {\n JSONParserLib.Item memory current = tcbInfoObj[i];\n string memory decodedKey = JSONParserLib.decodeString(current.key());\n if (decodedKey.eq(\"version\")) {\n version = JSONParserLib.parseUint(current.value());\n versionFound = true;\n }\n if (decodedKey.eq(\"tcbLevels\")) {\n tcbLevelsObj = current.children();\n tcbLevelsFound = true;\n }\n if (versionFound && tcbLevelsFound) {\n break;\n }\n }\n\n if (versionFound && tcbLevelsFound) {\n tcbLevels = _parseTCBLevels(version, tcbLevelsObj);\n } else {\n revert TCBInfo_Invalid();\n }\n }\n\n // 684k gas\n function parseTcbTdxModules(string calldata tcbInfoStr)\n external\n pure\n returns (TDXModule memory module, TDXModuleIdentity[] memory moduleIdentities)\n {\n JSONParserLib.Item memory root = JSONParserLib.parse(tcbInfoStr);\n JSONParserLib.Item[] memory tcbInfoObj = root.children();\n\n bool versionFound;\n bool idFound;\n bool tdxModuleFound;\n bool tdxModuleIdentitiesFound;\n bool allFound;\n\n for (uint256 i = 0; i < root.size(); i++) {\n JSONParserLib.Item memory current = tcbInfoObj[i];\n string memory decodedKey = JSONParserLib.decodeString(current.key());\n if (decodedKey.eq(\"version\")) {\n uint256 version = JSONParserLib.parseUint(current.value());\n if (version < 3) {\n revert TCB_TDX_Version_Invalid();\n }\n versionFound = true;\n }\n if (decodedKey.eq(\"id\")) {\n string memory id = JSONParserLib.decodeString(current.value());\n if (!id.eq(\"TDX\")) {\n revert TCB_TDX_ID_Invalid();\n }\n idFound = true;\n }\n if (decodedKey.eq(\"tdxModule\")) {\n module = _parseTdxModule(current.children());\n tdxModuleFound = true;\n }\n if (decodedKey.eq(\"tdxModuleIdentities\")) {\n moduleIdentities = _parseTdxModuleIdentities(current.children());\n tdxModuleIdentitiesFound = true;\n }\n allFound = versionFound && idFound && tdxModuleFound && tdxModuleIdentitiesFound;\n if (allFound) {\n break;\n }\n }\n\n if (!allFound) {\n revert TCBInfo_Invalid();\n }\n }\n\n /// ====== INTERNAL METHODS BELOW ======\n\n function _parseTCBLevels(uint256 version, JSONParserLib.Item[] memory tcbLevelsObj)\n private\n pure\n returns (TCBLevelsObj[] memory tcbLevels)\n {\n uint256 tcbLevelsSize = tcbLevelsObj.length;\n tcbLevels = new TCBLevelsObj[](tcbLevelsSize);\n\n // iterating through the array\n for (uint256 i = 0; i < tcbLevelsSize; i++) {\n JSONParserLib.Item[] memory tcbObj = tcbLevelsObj[i].children();\n // iterating through individual tcb objects\n for (uint256 j = 0; j < tcbLevelsObj[i].size(); j++) {\n string memory tcbKey = JSONParserLib.decodeString(tcbObj[j].key());\n if (tcbKey.eq(\"tcb\")) {\n string memory tcbStr = tcbObj[j].value();\n JSONParserLib.Item memory tcbParent = JSONParserLib.parse(tcbStr);\n JSONParserLib.Item[] memory tcbComponents = tcbParent.children();\n if (version == 2) {\n (tcbLevels[i].sgxComponentCpuSvns, tcbLevels[i].pcesvn) = _parseV2Tcb(tcbComponents);\n } else if (version == 3) {\n (tcbLevels[i].sgxComponentCpuSvns, tcbLevels[i].tdxSvns, tcbLevels[i].pcesvn) =\n _parseV3Tcb(tcbComponents);\n } else {\n revert TCBInfo_Invalid();\n }\n } else if (tcbKey.eq(\"tcbDate\")) {\n tcbLevels[i].tcbDateTimestamp =\n uint64(DateTimeUtils.fromISOToTimestamp(JSONParserLib.decodeString(tcbObj[j].value())));\n } else if (tcbKey.eq(\"tcbStatus\")) {\n tcbLevels[i].status = _getTcbStatus(JSONParserLib.decodeString(tcbObj[j].value()));\n }\n }\n }\n }\n\n function _getTcbStatus(string memory statusStr) private pure returns (TCBStatus status) {\n if (statusStr.eq(\"UpToDate\")) {\n status = TCBStatus.OK;\n } else if (statusStr.eq(\"OutOfDate\")) {\n status = TCBStatus.TCB_OUT_OF_DATE;\n } else if (statusStr.eq(\"OutOfDateConfigurationNeeded\")) {\n status = TCBStatus.TCB_OUT_OF_DATE_CONFIGURATION_NEEDED;\n } else if (statusStr.eq(\"ConfigurationNeeded\")) {\n status = TCBStatus.TCB_CONFIGURATION_NEEDED;\n } else if (statusStr.eq(\"ConfigurationAndSWHardeningNeeded\")) {\n status = TCBStatus.TCB_CONFIGURATION_AND_SW_HARDENING_NEEDED;\n } else if (statusStr.eq(\"SWHardeningNeeded\")) {\n status = TCBStatus.TCB_SW_HARDENING_NEEDED;\n } else if (statusStr.eq(\"Revoked\")) {\n status = TCBStatus.TCB_REVOKED;\n } else {\n status = TCBStatus.TCB_UNRECOGNIZED;\n }\n }\n\n function _parseV2Tcb(JSONParserLib.Item[] memory tcbComponents)\n private\n pure\n returns (uint8[] memory sgxComponentCpuSvns, uint16 pcesvn)\n {\n sgxComponentCpuSvns = new uint8[](TCB_CPUSVN_SIZE);\n uint256 cpusvnCounter = 0;\n for (uint256 i = 0; i < tcbComponents.length; i++) {\n string memory key = JSONParserLib.decodeString(tcbComponents[i].key());\n uint256 value = JSONParserLib.parseUint(tcbComponents[i].value());\n if (key.eq(\"pcesvn\")) {\n pcesvn = uint16(value);\n } else {\n sgxComponentCpuSvns[cpusvnCounter++] = uint8(value);\n }\n }\n if (cpusvnCounter != TCB_CPUSVN_SIZE) {\n revert TCBInfo_Invalid();\n }\n }\n\n function _parseV3Tcb(JSONParserLib.Item[] memory tcbComponents)\n private\n pure\n returns (uint8[] memory sgxComponentCpuSvns, uint8[] memory tdxSvns, uint16 pcesvn)\n {\n sgxComponentCpuSvns = new uint8[](TCB_CPUSVN_SIZE);\n tdxSvns = new uint8[](TCB_CPUSVN_SIZE);\n for (uint256 i = 0; i < tcbComponents.length; i++) {\n string memory key = JSONParserLib.decodeString(tcbComponents[i].key());\n if (key.eq(\"pcesvn\")) {\n pcesvn = uint16(JSONParserLib.parseUint(tcbComponents[i].value()));\n } else {\n string memory componentKey = key;\n JSONParserLib.Item[] memory componentArr = tcbComponents[i].children();\n uint256 cpusvnCounter = 0;\n for (uint256 j = 0; j < tcbComponents[i].size(); j++) {\n JSONParserLib.Item[] memory component = componentArr[j].children();\n for (uint256 k = 0; k < componentArr[j].size(); k++) {\n key = JSONParserLib.decodeString(component[k].key());\n if (key.eq(\"svn\")) {\n if (componentKey.eq(\"tdxtcbcomponents\")) {\n tdxSvns[cpusvnCounter++] = uint8(JSONParserLib.parseUint(component[k].value()));\n } else {\n sgxComponentCpuSvns[cpusvnCounter++] =\n uint8(JSONParserLib.parseUint(component[k].value()));\n }\n }\n }\n }\n if (cpusvnCounter != TCB_CPUSVN_SIZE) {\n revert TCBInfo_Invalid();\n }\n }\n }\n }\n\n function _parseTdxModule(JSONParserLib.Item[] memory tdxModuleObj) private pure returns (TDXModule memory module) {\n for (uint256 i = 0; i < tdxModuleObj.length; i++) {\n string memory key = JSONParserLib.decodeString(tdxModuleObj[i].key());\n string memory val = JSONParserLib.decodeString(tdxModuleObj[i].value());\n if (key.eq(\"attributes\")) {\n module.attributes = bytes8(uint64(JSONParserLib.parseUintFromHex(val)));\n }\n if (key.eq(\"attributesMask\")) {\n module.attributesMask = bytes8(uint64(JSONParserLib.parseUintFromHex(val)));\n }\n if (key.eq(\"mrsigner\")) {\n module.mrsigner = _getMrSignerHex(val);\n }\n }\n }\n\n function _parseTdxModuleIdentities(JSONParserLib.Item[] memory tdxModuleIdentitiesArr)\n private\n pure\n returns (TDXModuleIdentity[] memory identities)\n {\n uint256 n = tdxModuleIdentitiesArr.length;\n identities = new TDXModuleIdentity[](n);\n for (uint256 i = 0; i < n; i++) {\n JSONParserLib.Item[] memory currIdentity = tdxModuleIdentitiesArr[i].children();\n for (uint256 j = 0; j < tdxModuleIdentitiesArr[i].size(); j++) {\n string memory key = JSONParserLib.decodeString(currIdentity[j].key());\n if (key.eq(\"id\")) {\n string memory val = JSONParserLib.decodeString(currIdentity[j].value());\n identities[i].id = val;\n }\n if (key.eq(\"mrsigner\")) {\n string memory val = JSONParserLib.decodeString(currIdentity[j].value());\n identities[i].mrsigner = _getMrSignerHex(val);\n }\n if (key.eq(\"attributes\")) {\n string memory val = JSONParserLib.decodeString(currIdentity[j].value());\n identities[i].attributes = bytes8(uint64(JSONParserLib.parseUintFromHex(val)));\n }\n if (key.eq(\"attributesMask\")) {\n string memory val = JSONParserLib.decodeString(currIdentity[j].value());\n identities[i].attributesMask = bytes8(uint64(JSONParserLib.parseUintFromHex(val)));\n }\n if (key.eq(\"tcbLevels\")) {\n JSONParserLib.Item[] memory tcbLevelsArr = currIdentity[j].children();\n uint256 x = tcbLevelsArr.length;\n identities[i].tcbLevels = new TDXModuleTCBLevelsObj[](x);\n for (uint256 k = 0; k < x; k++) {\n JSONParserLib.Item[] memory tcb = tcbLevelsArr[k].children();\n for (uint256 l = 0; l < tcb.length; l++) {\n key = JSONParserLib.decodeString(tcb[l].key());\n if (key.eq(\"tcb\")) {\n JSONParserLib.Item[] memory isvsvnObj = tcb[l].children();\n key = JSONParserLib.decodeString(isvsvnObj[0].key());\n if (key.eq(\"isvsvn\")) {\n identities[i].tcbLevels[k].isvsvn =\n uint8(JSONParserLib.parseUint(isvsvnObj[0].value()));\n } else {\n revert TCBInfo_Invalid();\n }\n }\n if (key.eq(\"tcbDate\")) {\n identities[i].tcbLevels[k].tcbDateTimestamp =\n uint64(DateTimeUtils.fromISOToTimestamp(JSONParserLib.decodeString(tcb[l].value())));\n }\n if (key.eq(\"tcbStatus\")) {\n identities[i].tcbLevels[k].status =\n _getTcbStatus(JSONParserLib.decodeString(tcb[l].value()));\n }\n }\n }\n }\n }\n }\n }\n\n function _getMrSignerHex(string memory mrSignerStr) private pure returns (bytes memory mrSignerBytes) {\n string memory mrSignerUpper16BytesStr = mrSignerStr.slice(0, 16);\n string memory mrSignerLower32BytesStr = mrSignerStr.slice(16, 48);\n uint256 mrSignerUpperBytes = JSONParserLib.parseUintFromHex(mrSignerUpper16BytesStr);\n uint256 mrSignerLowerBytes = JSONParserLib.parseUintFromHex(mrSignerLower32BytesStr);\n mrSignerBytes = abi.encodePacked(uint128(mrSignerUpperBytes), mrSignerLowerBytes);\n }\n}\n"},"src/helpers/X509Helper.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport {Asn1Decode, NodePtr} from \"../utils/Asn1Decode.sol\";\nimport {BytesUtils} from \"../utils/BytesUtils.sol\";\nimport {DateTimeUtils} from \"../utils/DateTimeUtils.sol\";\n\n/**\n * @title Solidity Structure representing X509 Certificates\n * @notice This is a simplified structure of a DER-decoded X509 Certificate\n */\nstruct X509CertObj {\n uint256 serialNumber;\n string issuerCommonName;\n uint256 validityNotBefore;\n uint256 validityNotAfter;\n string subjectCommonName;\n bytes subjectPublicKey;\n // the extension needs to be parsed further for PCK Certificates\n uint256 extensionPtr;\n // for signature verification in the cert chain\n bytes signature;\n bytes tbs;\n}\n\n/**\n * @title X509 Certificates Helper Contract\n * @notice This is a standalone contract that can be used by off-chain applications and smart contracts\n * to parse DER-encoded X509 certificates.\n * @dev The Extension sequence in Intel PCK Certificates is a custom ASN.1 Sequence that needs to be\n * @dev parsed further in a more specialized PCKHelper contract.\n */\ncontract X509Helper {\n using Asn1Decode for bytes;\n using NodePtr for uint256;\n using BytesUtils for bytes;\n\n /// =================================================================================\n /// USE THE GETTERS BELOW IF YOU DON'T WANT TO PARSE THE ENTIRE X509 CERTIFICATE\n /// =================================================================================\n\n function getTbsAndSig(bytes calldata der) external pure returns (bytes memory tbs, bytes memory sig) {\n uint256 root = der.root();\n uint256 tbsParentPtr = der.firstChildOf(root);\n uint256 sigPtr = der.nextSiblingOf(tbsParentPtr);\n sigPtr = der.nextSiblingOf(sigPtr);\n\n tbs = der.allBytesAt(tbsParentPtr);\n sig = _getSignature(der, sigPtr);\n }\n\n function getSerialNumber(bytes calldata der) external pure returns (uint256 serialNum) {\n uint256 root = der.root();\n uint256 tbsParentPtr = der.firstChildOf(root);\n uint256 tbsPtr = der.firstChildOf(tbsParentPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n serialNum = _parseSerialNumber(der.bytesAt(tbsPtr));\n }\n\n function getIssuerCommonName(bytes calldata der) external pure returns (string memory issuerCommonName) {\n uint256 root = der.root();\n uint256 tbsParentPtr = der.firstChildOf(root);\n uint256 tbsPtr = der.firstChildOf(tbsParentPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n issuerCommonName = _getCommonName(der, der.firstChildOf(tbsPtr));\n }\n\n function certIsNotExpired(bytes calldata der) external view returns (bool isValid) {\n uint256 root = der.root();\n uint256 tbsParentPtr = der.firstChildOf(root);\n uint256 tbsPtr = der.firstChildOf(tbsParentPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n (uint256 validityNotBefore, uint256 validityNotAfter) = _getValidity(der, tbsPtr);\n isValid = block.timestamp > validityNotBefore && block.timestamp < validityNotAfter;\n }\n\n function getSubjectCommonName(bytes calldata der) external pure returns (string memory subjectCommonName) {\n uint256 root = der.root();\n uint256 tbsParentPtr = der.firstChildOf(root);\n uint256 tbsPtr = der.firstChildOf(tbsParentPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n subjectCommonName = _getCommonName(der, der.firstChildOf(tbsPtr));\n }\n\n function getSubjectPublicKey(bytes calldata der) external pure returns (bytes memory pubKey) {\n uint256 root = der.root();\n uint256 tbsParentPtr = der.firstChildOf(root);\n uint256 tbsPtr = der.firstChildOf(tbsParentPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n pubKey = _getSubjectPublicKey(der, der.firstChildOf(tbsPtr));\n }\n\n /// x509 Certificates generally contain a sequence of elements in the following order:\n /// 1. tbs\n /// - 1a. version\n /// - 1b. serial number\n /// - 1c. siganture algorithm\n /// - 1d. issuer\n /// - - 1d(a). common name\n /// - - 1d(b). organization name\n /// - - 1d(c). locality name\n /// - - 1d(d). state or province name\n /// - - 1d(e). country name\n /// - 1e. validity\n /// - - 1e(a) notBefore\n /// - - 1e(b) notAfter\n /// - 1f. subject\n /// - - contains the same set of elements as 1d\n /// - 1g. subject public key info\n /// - - 1g(a). algorithm\n /// - - 1g(b). subject public key\n /// - 1h. Extensions\n /// 2. Signature Algorithm\n /// 3. Signature\n /// - 3a. X value\n /// - 3b. Y value\n function parseX509DER(bytes calldata der) external pure returns (X509CertObj memory cert) {\n uint256 root = der.root();\n\n uint256 tbsParentPtr = der.firstChildOf(root);\n cert.tbs = der.allBytesAt(tbsParentPtr);\n\n uint256 tbsPtr = der.firstChildOf(tbsParentPtr);\n\n tbsPtr = der.nextSiblingOf(tbsPtr);\n\n cert.serialNumber = _parseSerialNumber(der.bytesAt(tbsPtr));\n\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n\n cert.issuerCommonName = _getCommonName(der, der.firstChildOf(tbsPtr));\n\n tbsPtr = der.nextSiblingOf(tbsPtr);\n (cert.validityNotBefore, cert.validityNotAfter) = _getValidity(der, tbsPtr);\n\n tbsPtr = der.nextSiblingOf(tbsPtr);\n\n cert.subjectCommonName = _getCommonName(der, der.firstChildOf(tbsPtr));\n\n tbsPtr = der.nextSiblingOf(tbsPtr);\n cert.subjectPublicKey = _getSubjectPublicKey(der, der.firstChildOf(tbsPtr));\n\n cert.extensionPtr = der.nextSiblingOf(tbsPtr);\n\n // tbs iteration completed\n // now we just need to look for the signature\n\n uint256 sigPtr = der.nextSiblingOf(tbsParentPtr);\n sigPtr = der.nextSiblingOf(sigPtr);\n cert.signature = _getSignature(der, sigPtr);\n }\n\n function _getCommonName(bytes calldata der, uint256 commonNameParentPtr)\n private\n pure\n returns (string memory commonName)\n {\n commonNameParentPtr = der.firstChildOf(commonNameParentPtr);\n commonNameParentPtr = der.firstChildOf(commonNameParentPtr);\n commonNameParentPtr = der.nextSiblingOf(commonNameParentPtr);\n commonName = string(der.bytesAt(commonNameParentPtr));\n }\n\n function _getValidity(bytes calldata der, uint256 validityPtr)\n private\n pure\n returns (uint256 notBefore, uint256 notAfter)\n {\n uint256 notBeforePtr = der.firstChildOf(validityPtr);\n uint256 notAfterPtr = der.nextSiblingOf(notBeforePtr);\n notBefore = DateTimeUtils.fromDERToTimestamp(der.bytesAt(notBeforePtr));\n notAfter = DateTimeUtils.fromDERToTimestamp(der.bytesAt(notAfterPtr));\n }\n\n function _getSubjectPublicKey(bytes calldata der, uint256 subjectPublicKeyInfoPtr)\n private\n pure\n returns (bytes memory pubKey)\n {\n subjectPublicKeyInfoPtr = der.nextSiblingOf(subjectPublicKeyInfoPtr);\n pubKey = der.bitstringAt(subjectPublicKeyInfoPtr);\n if (pubKey.length != 65) {\n // TODO: we need to figure out how to handle key with prefix byte 0x02 or 0x03\n revert(\"compressed public key not supported\");\n }\n pubKey = _trimBytes(pubKey, 64);\n }\n\n function _parseSerialNumber(bytes memory serialBytes) private pure returns (uint256 serial) {\n uint256 shift = 8 * (32 - serialBytes.length);\n serial = uint256(bytes32(serialBytes) >> shift);\n }\n\n function _getSignature(bytes calldata der, uint256 sigPtr) private pure returns (bytes memory sig) {\n sigPtr = der.rootOfBitStringAt(sigPtr);\n\n sigPtr = der.firstChildOf(sigPtr);\n bytes memory sigX = _trimBytes(der.bytesAt(sigPtr), 32);\n\n sigPtr = der.nextSiblingOf(sigPtr);\n bytes memory sigY = _trimBytes(der.bytesAt(sigPtr), 32);\n\n sig = abi.encodePacked(sigX, sigY);\n }\n\n /// @dev remove unnecessary prefix from the input\n function _trimBytes(bytes memory input, uint256 expectedLength) private pure returns (bytes memory output) {\n uint256 n = input.length;\n if (n == expectedLength) {\n output = input;\n } else if (n < expectedLength) {\n output = new bytes(expectedLength);\n uint256 padLength = expectedLength - n;\n for (uint256 i = 0; i < n; i++) {\n output[padLength + i] = input[i];\n }\n } else {\n uint256 lengthDiff = n - expectedLength;\n output = input.substring(lengthDiff, expectedLength);\n }\n }\n}\n"},"src/helpers/X509CRLHelper.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport {Asn1Decode, NodePtr} from \"../utils/Asn1Decode.sol\";\nimport {BytesUtils} from \"../utils/BytesUtils.sol\";\nimport {DateTimeUtils} from \"../utils/DateTimeUtils.sol\";\n\n/**\n * @title Solidity Structure representing X509 CRL\n * @notice This is a simplified structure of a DER-decoded X509 CRL\n */\nstruct X509CRLObj {\n uint256 serialNumber;\n string issuerCommonName;\n uint256 validityNotBefore;\n uint256 validityNotAfter;\n uint256[] serialNumbersRevoked;\n // for signature verification in the cert chain\n bytes signature;\n bytes tbs;\n}\n\n/**\n * @title X509 CRL Helper Contract\n * @notice This is a standalone contract that can be used by off-chain applications and smart contracts\n * to parse DER-encoded CRLs.\n */\ncontract X509CRLHelper {\n using Asn1Decode for bytes;\n using NodePtr for uint256;\n using BytesUtils for bytes;\n\n // 2.5.29.20\n bytes constant CRL_NUMBER_OID = hex\"551d14\";\n\n /// =================================================================================\n /// USE THE GETTERS BELOW IF YOU DON'T WANT TO PARSE THE ENTIRE X509 CRL\n /// =================================================================================\n\n function getTbsAndSig(bytes calldata der) external pure returns (bytes memory tbs, bytes memory sig) {\n uint256 root = der.root();\n uint256 tbsParentPtr = der.firstChildOf(root);\n uint256 sigPtr = der.nextSiblingOf(tbsParentPtr);\n sigPtr = der.nextSiblingOf(sigPtr);\n\n tbs = der.allBytesAt(tbsParentPtr);\n sig = _getSignature(der, sigPtr);\n }\n\n function getSerialNumber(bytes calldata der) external pure returns (uint256 serialNum) {\n uint256 root = der.root();\n uint256 tbsParentPtr = der.firstChildOf(root);\n uint256 tbsPtr = der.firstChildOf(tbsParentPtr);\n serialNum = _parseSerialNumber(der.bytesAt(tbsPtr));\n }\n\n function getIssuerCommonName(bytes calldata der) external pure returns (string memory issuerCommonName) {\n uint256 root = der.root();\n uint256 tbsParentPtr = der.firstChildOf(root);\n uint256 tbsPtr = der.firstChildOf(tbsParentPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n issuerCommonName = _getCommonName(der, der.firstChildOf(tbsPtr));\n }\n\n function crlIsNotExpired(bytes calldata der) external view returns (bool isValid) {\n uint256 root = der.root();\n uint256 tbsParentPtr = der.firstChildOf(root);\n uint256 tbsPtr = der.firstChildOf(tbsParentPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n (uint256 validityNotBefore, uint256 validityNotAfter) = _getValidity(der, tbsPtr);\n isValid = block.timestamp > validityNotBefore && block.timestamp < validityNotAfter;\n }\n\n function serialNumberIsRevoked(uint256 serialNumber, bytes calldata der) external pure returns (bool revoked) {\n uint256 root = der.root();\n uint256 tbsParentPtr = der.firstChildOf(root);\n uint256 tbsPtr = der.firstChildOf(tbsParentPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n uint256[] memory ret = _getRevokedSerialNumbers(der, tbsPtr, true, serialNumber);\n revoked = ret[0] == serialNumber;\n }\n\n /// x509 CRL generally contain a sequence of elements in the following order:\n /// 1. tbs\n /// - 1a. serial number\n /// - 1b. signature algorithm\n /// - 1c. issuer\n /// - - 1c(a). common name\n /// - - 1c(b). organization name\n /// - - 1c(c). locality name\n /// - - 1c(d). state or province name\n /// - - 1c(e). country name\n /// - 1d. not before\n /// - 1e. not after\n /// - 1f. revoked certificates\n /// - - A list consists of revoked serial numbers and reasons.\n /// - 1g. CRL extensions\n /// - - 1g(a) CRL number\n /// - - 1g(b) Authority Key Identifier\n /// 2. Signature Algorithm\n /// 3. Signature\n /// - 3a. X value\n /// - 3b. Y value\n function parseCRLDER(bytes calldata der) external pure returns (X509CRLObj memory crl) {\n uint256 root = der.root();\n\n uint256 tbsParentPtr = der.firstChildOf(root);\n\n uint256 tbsPtr = der.firstChildOf(tbsParentPtr);\n\n crl.serialNumber = uint256(bytes32(der.bytesAt(tbsPtr)));\n\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n\n crl.issuerCommonName = _getCommonName(der, der.firstChildOf(tbsPtr));\n\n tbsPtr = der.nextSiblingOf(tbsPtr);\n (crl.validityNotBefore, crl.validityNotAfter) = _getValidity(der, tbsPtr);\n\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n\n crl.serialNumbersRevoked = _getRevokedSerialNumbers(der, tbsPtr, false, 0);\n\n // tbs iteration completed\n // now we just need to look for the signature\n\n uint256 sigPtr = der.nextSiblingOf(tbsParentPtr);\n sigPtr = der.nextSiblingOf(sigPtr);\n crl.signature = _getSignature(der, sigPtr);\n }\n\n function _getCommonName(bytes calldata der, uint256 commonNameParentPtr)\n private\n pure\n returns (string memory commonName)\n {\n commonNameParentPtr = der.firstChildOf(commonNameParentPtr);\n commonNameParentPtr = der.firstChildOf(commonNameParentPtr);\n commonNameParentPtr = der.nextSiblingOf(commonNameParentPtr);\n commonName = string(der.bytesAt(commonNameParentPtr));\n }\n\n function _getValidity(bytes calldata der, uint256 validityPtr)\n private\n pure\n returns (uint256 notBefore, uint256 notAfter)\n {\n uint256 notBeforePtr = validityPtr;\n uint256 notAfterPtr = der.nextSiblingOf(notBeforePtr);\n notBefore = DateTimeUtils.fromDERToTimestamp(der.bytesAt(notBeforePtr));\n notAfter = DateTimeUtils.fromDERToTimestamp(der.bytesAt(notAfterPtr));\n }\n\n function _getRevokedSerialNumbers(bytes calldata der, uint256 revokedParentPtr, bool breakIfFound, uint256 filter)\n private\n pure\n returns (uint256[] memory serialNumbers)\n {\n uint256 revokedPtr = der.firstChildOf(revokedParentPtr);\n\n if (der[revokedPtr.ixs()] == 0xA0) {\n uint256 crlExtensionPtr = der.firstChildOf(revokedPtr);\n require(BytesUtils.compareBytes(der.bytesAt(crlExtensionPtr), CRL_NUMBER_OID), \"invalid CRL\");\n } else {\n bytes memory serials;\n while (revokedPtr.ixl() <= revokedParentPtr.ixl()) {\n uint256 serialPtr = der.firstChildOf(revokedPtr);\n bytes memory serialBytes = der.bytesAt(serialPtr);\n uint256 serialNumber = _parseSerialNumber(serialBytes);\n serials = abi.encodePacked(serials, serialNumber);\n if (breakIfFound && filter == serialNumber) {\n serialNumbers = new uint256[](1);\n serialNumbers[0] = filter;\n return serialNumbers;\n }\n revokedPtr = der.nextSiblingOf(revokedPtr);\n }\n uint256 count = serials.length / 32;\n // ABI encoding format for a dynamic uint256[] value\n serials = abi.encodePacked(abi.encode(0x20), abi.encode(count), serials);\n serialNumbers = new uint256[](count);\n serialNumbers = abi.decode(serials, (uint256[]));\n }\n }\n\n function _parseSerialNumber(bytes memory serialBytes) private pure returns (uint256 serial) {\n uint256 shift = 8 * (32 - serialBytes.length);\n serial = uint256(bytes32(serialBytes) >> shift);\n }\n\n function _getSignature(bytes calldata der, uint256 sigPtr) private pure returns (bytes memory sig) {\n sigPtr = der.rootOfBitStringAt(sigPtr);\n\n sigPtr = der.firstChildOf(sigPtr);\n bytes memory sigX = _trimBytes(der.bytesAt(sigPtr), 32);\n\n sigPtr = der.nextSiblingOf(sigPtr);\n bytes memory sigY = _trimBytes(der.bytesAt(sigPtr), 32);\n\n sig = abi.encodePacked(sigX, sigY);\n }\n\n /// @dev remove unnecessary prefix from the input\n /// @dev remove unnecessary prefix from the input\n function _trimBytes(bytes memory input, uint256 expectedLength) private pure returns (bytes memory output) {\n uint256 n = input.length;\n if (n == expectedLength) {\n output = input;\n } else if (n < expectedLength) {\n output = new bytes(expectedLength);\n uint256 padLength = expectedLength - n;\n for (uint256 i = 0; i < n; i++) {\n output[padLength + i] = input[i];\n }\n } else {\n uint256 lengthDiff = n - expectedLength;\n output = input.substring(lengthDiff, expectedLength);\n }\n }\n}\n"},"lib/solady/src/utils/LibString.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\n/// @notice Library for converting numbers into strings and other string operations.\n/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibString.sol)\n/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/LibString.sol)\n///\n/// Note:\n/// For performance and bytecode compactness, most of the string operations are restricted to\n/// byte strings (7-bit ASCII), except where otherwise specified.\n/// Usage of byte string operations on charsets with runes spanning two or more bytes\n/// can lead to undefined behavior.\nlibrary LibString {\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* CUSTOM ERRORS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev The length of the output is too small to contain all the hex digits.\n error HexLengthInsufficient();\n\n /// @dev The length of the string is more than 32 bytes.\n error TooBigForSmallString();\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* CONSTANTS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev The constant returned when the `search` is not found in the string.\n uint256 internal constant NOT_FOUND = type(uint256).max;\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* DECIMAL OPERATIONS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev Returns the base 10 decimal representation of `value`.\n function toString(uint256 value) internal pure returns (string memory str) {\n /// @solidity memory-safe-assembly\n assembly {\n // The maximum value of a uint256 contains 78 digits (1 byte per digit), but\n // we allocate 0xa0 bytes to keep the free memory pointer 32-byte word aligned.\n // We will need 1 word for the trailing zeros padding, 1 word for the length,\n // and 3 words for a maximum of 78 digits.\n str := add(mload(0x40), 0x80)\n // Update the free memory pointer to allocate.\n mstore(0x40, add(str, 0x20))\n // Zeroize the slot after the string.\n mstore(str, 0)\n\n // Cache the end of the memory to calculate the length later.\n let end := str\n\n let w := not(0) // Tsk.\n // We write the string from rightmost digit to leftmost digit.\n // The following is essentially a do-while loop that also handles the zero case.\n for { let temp := value } 1 {} {\n str := add(str, w) // `sub(str, 1)`.\n // Write the character to the pointer.\n // The ASCII index of the '0' character is 48.\n mstore8(str, add(48, mod(temp, 10)))\n // Keep dividing `temp` until zero.\n temp := div(temp, 10)\n if iszero(temp) { break }\n }\n\n let length := sub(end, str)\n // Move the pointer 32 bytes leftwards to make room for the length.\n str := sub(str, 0x20)\n // Store the length.\n mstore(str, length)\n }\n }\n\n /// @dev Returns the base 10 decimal representation of `value`.\n function toString(int256 value) internal pure returns (string memory str) {\n if (value >= 0) {\n return toString(uint256(value));\n }\n unchecked {\n str = toString(uint256(-value));\n }\n /// @solidity memory-safe-assembly\n assembly {\n // We still have some spare memory space on the left,\n // as we have allocated 3 words (96 bytes) for up to 78 digits.\n let length := mload(str) // Load the string length.\n mstore(str, 0x2d) // Store the '-' character.\n str := sub(str, 1) // Move back the string pointer by a byte.\n mstore(str, add(length, 1)) // Update the string length.\n }\n }\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* HEXADECIMAL OPERATIONS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev Returns the hexadecimal representation of `value`,\n /// left-padded to an input length of `length` bytes.\n /// The output is prefixed with \"0x\" encoded using 2 hexadecimal digits per byte,\n /// giving a total length of `length * 2 + 2` bytes.\n /// Reverts if `length` is too small for the output to contain all the digits.\n function toHexString(uint256 value, uint256 length) internal pure returns (string memory str) {\n str = toHexStringNoPrefix(value, length);\n /// @solidity memory-safe-assembly\n assembly {\n let strLength := add(mload(str), 2) // Compute the length.\n mstore(str, 0x3078) // Write the \"0x\" prefix.\n str := sub(str, 2) // Move the pointer.\n mstore(str, strLength) // Write the length.\n }\n }\n\n /// @dev Returns the hexadecimal representation of `value`,\n /// left-padded to an input length of `length` bytes.\n /// The output is prefixed with \"0x\" encoded using 2 hexadecimal digits per byte,\n /// giving a total length of `length * 2` bytes.\n /// Reverts if `length` is too small for the output to contain all the digits.\n function toHexStringNoPrefix(uint256 value, uint256 length)\n internal\n pure\n returns (string memory str)\n {\n /// @solidity memory-safe-assembly\n assembly {\n // We need 0x20 bytes for the trailing zeros padding, `length * 2` bytes\n // for the digits, 0x02 bytes for the prefix, and 0x20 bytes for the length.\n // We add 0x20 to the total and round down to a multiple of 0x20.\n // (0x20 + 0x20 + 0x02 + 0x20) = 0x62.\n str := add(mload(0x40), and(add(shl(1, length), 0x42), not(0x1f)))\n // Allocate the memory.\n mstore(0x40, add(str, 0x20))\n // Zeroize the slot after the string.\n mstore(str, 0)\n\n // Cache the end to calculate the length later.\n let end := str\n // Store \"0123456789abcdef\" in scratch space.\n mstore(0x0f, 0x30313233343536373839616263646566)\n\n let start := sub(str, add(length, length))\n let w := not(1) // Tsk.\n let temp := value\n // We write the string from rightmost digit to leftmost digit.\n // The following is essentially a do-while loop that also handles the zero case.\n for {} 1 {} {\n str := add(str, w) // `sub(str, 2)`.\n mstore8(add(str, 1), mload(and(temp, 15)))\n mstore8(str, mload(and(shr(4, temp), 15)))\n temp := shr(8, temp)\n if iszero(xor(str, start)) { break }\n }\n\n if temp {\n mstore(0x00, 0x2194895a) // `HexLengthInsufficient()`.\n revert(0x1c, 0x04)\n }\n\n // Compute the string's length.\n let strLength := sub(end, str)\n // Move the pointer and write the length.\n str := sub(str, 0x20)\n mstore(str, strLength)\n }\n }\n\n /// @dev Returns the hexadecimal representation of `value`.\n /// The output is prefixed with \"0x\" and encoded using 2 hexadecimal digits per byte.\n /// As address are 20 bytes long, the output will left-padded to have\n /// a length of `20 * 2 + 2` bytes.\n function toHexString(uint256 value) internal pure returns (string memory str) {\n str = toHexStringNoPrefix(value);\n /// @solidity memory-safe-assembly\n assembly {\n let strLength := add(mload(str), 2) // Compute the length.\n mstore(str, 0x3078) // Write the \"0x\" prefix.\n str := sub(str, 2) // Move the pointer.\n mstore(str, strLength) // Write the length.\n }\n }\n\n /// @dev Returns the hexadecimal representation of `value`.\n /// The output is prefixed with \"0x\".\n /// The output excludes leading \"0\" from the `toHexString` output.\n /// `0x00: \"0x0\", 0x01: \"0x1\", 0x12: \"0x12\", 0x123: \"0x123\"`.\n function toMinimalHexString(uint256 value) internal pure returns (string memory str) {\n str = toHexStringNoPrefix(value);\n /// @solidity memory-safe-assembly\n assembly {\n let o := eq(byte(0, mload(add(str, 0x20))), 0x30) // Whether leading zero is present.\n let strLength := add(mload(str), 2) // Compute the length.\n mstore(add(str, o), 0x3078) // Write the \"0x\" prefix, accounting for leading zero.\n str := sub(add(str, o), 2) // Move the pointer, accounting for leading zero.\n mstore(str, sub(strLength, o)) // Write the length, accounting for leading zero.\n }\n }\n\n /// @dev Returns the hexadecimal representation of `value`.\n /// The output excludes leading \"0\" from the `toHexStringNoPrefix` output.\n /// `0x00: \"0\", 0x01: \"1\", 0x12: \"12\", 0x123: \"123\"`.\n function toMinimalHexStringNoPrefix(uint256 value) internal pure returns (string memory str) {\n str = toHexStringNoPrefix(value);\n /// @solidity memory-safe-assembly\n assembly {\n let o := eq(byte(0, mload(add(str, 0x20))), 0x30) // Whether leading zero is present.\n let strLength := mload(str) // Get the length.\n str := add(str, o) // Move the pointer, accounting for leading zero.\n mstore(str, sub(strLength, o)) // Write the length, accounting for leading zero.\n }\n }\n\n /// @dev Returns the hexadecimal representation of `value`.\n /// The output is encoded using 2 hexadecimal digits per byte.\n /// As address are 20 bytes long, the output will left-padded to have\n /// a length of `20 * 2` bytes.\n function toHexStringNoPrefix(uint256 value) internal pure returns (string memory str) {\n /// @solidity memory-safe-assembly\n assembly {\n // We need 0x20 bytes for the trailing zeros padding, 0x20 bytes for the length,\n // 0x02 bytes for the prefix, and 0x40 bytes for the digits.\n // The next multiple of 0x20 above (0x20 + 0x20 + 0x02 + 0x40) is 0xa0.\n str := add(mload(0x40), 0x80)\n // Allocate the memory.\n mstore(0x40, add(str, 0x20))\n // Zeroize the slot after the string.\n mstore(str, 0)\n\n // Cache the end to calculate the length later.\n let end := str\n // Store \"0123456789abcdef\" in scratch space.\n mstore(0x0f, 0x30313233343536373839616263646566)\n\n let w := not(1) // Tsk.\n // We write the string from rightmost digit to leftmost digit.\n // The following is essentially a do-while loop that also handles the zero case.\n for { let temp := value } 1 {} {\n str := add(str, w) // `sub(str, 2)`.\n mstore8(add(str, 1), mload(and(temp, 15)))\n mstore8(str, mload(and(shr(4, temp), 15)))\n temp := shr(8, temp)\n if iszero(temp) { break }\n }\n\n // Compute the string's length.\n let strLength := sub(end, str)\n // Move the pointer and write the length.\n str := sub(str, 0x20)\n mstore(str, strLength)\n }\n }\n\n /// @dev Returns the hexadecimal representation of `value`.\n /// The output is prefixed with \"0x\", encoded using 2 hexadecimal digits per byte,\n /// and the alphabets are capitalized conditionally according to\n /// https://eips.ethereum.org/EIPS/eip-55\n function toHexStringChecksummed(address value) internal pure returns (string memory str) {\n str = toHexString(value);\n /// @solidity memory-safe-assembly\n assembly {\n let mask := shl(6, div(not(0), 255)) // `0b010000000100000000 ...`\n let o := add(str, 0x22)\n let hashed := and(keccak256(o, 40), mul(34, mask)) // `0b10001000 ... `\n let t := shl(240, 136) // `0b10001000 << 240`\n for { let i := 0 } 1 {} {\n mstore(add(i, i), mul(t, byte(i, hashed)))\n i := add(i, 1)\n if eq(i, 20) { break }\n }\n mstore(o, xor(mload(o), shr(1, and(mload(0x00), and(mload(o), mask)))))\n o := add(o, 0x20)\n mstore(o, xor(mload(o), shr(1, and(mload(0x20), and(mload(o), mask)))))\n }\n }\n\n /// @dev Returns the hexadecimal representation of `value`.\n /// The output is prefixed with \"0x\" and encoded using 2 hexadecimal digits per byte.\n function toHexString(address value) internal pure returns (string memory str) {\n str = toHexStringNoPrefix(value);\n /// @solidity memory-safe-assembly\n assembly {\n let strLength := add(mload(str), 2) // Compute the length.\n mstore(str, 0x3078) // Write the \"0x\" prefix.\n str := sub(str, 2) // Move the pointer.\n mstore(str, strLength) // Write the length.\n }\n }\n\n /// @dev Returns the hexadecimal representation of `value`.\n /// The output is encoded using 2 hexadecimal digits per byte.\n function toHexStringNoPrefix(address value) internal pure returns (string memory str) {\n /// @solidity memory-safe-assembly\n assembly {\n str := mload(0x40)\n\n // Allocate the memory.\n // We need 0x20 bytes for the trailing zeros padding, 0x20 bytes for the length,\n // 0x02 bytes for the prefix, and 0x28 bytes for the digits.\n // The next multiple of 0x20 above (0x20 + 0x20 + 0x02 + 0x28) is 0x80.\n mstore(0x40, add(str, 0x80))\n\n // Store \"0123456789abcdef\" in scratch space.\n mstore(0x0f, 0x30313233343536373839616263646566)\n\n str := add(str, 2)\n mstore(str, 40)\n\n let o := add(str, 0x20)\n mstore(add(o, 40), 0)\n\n value := shl(96, value)\n\n // We write the string from rightmost digit to leftmost digit.\n // The following is essentially a do-while loop that also handles the zero case.\n for { let i := 0 } 1 {} {\n let p := add(o, add(i, i))\n let temp := byte(i, value)\n mstore8(add(p, 1), mload(and(temp, 15)))\n mstore8(p, mload(shr(4, temp)))\n i := add(i, 1)\n if eq(i, 20) { break }\n }\n }\n }\n\n /// @dev Returns the hex encoded string from the raw bytes.\n /// The output is encoded using 2 hexadecimal digits per byte.\n function toHexString(bytes memory raw) internal pure returns (string memory str) {\n str = toHexStringNoPrefix(raw);\n /// @solidity memory-safe-assembly\n assembly {\n let strLength := add(mload(str), 2) // Compute the length.\n mstore(str, 0x3078) // Write the \"0x\" prefix.\n str := sub(str, 2) // Move the pointer.\n mstore(str, strLength) // Write the length.\n }\n }\n\n /// @dev Returns the hex encoded string from the raw bytes.\n /// The output is encoded using 2 hexadecimal digits per byte.\n function toHexStringNoPrefix(bytes memory raw) internal pure returns (string memory str) {\n /// @solidity memory-safe-assembly\n assembly {\n let length := mload(raw)\n str := add(mload(0x40), 2) // Skip 2 bytes for the optional prefix.\n mstore(str, add(length, length)) // Store the length of the output.\n\n // Store \"0123456789abcdef\" in scratch space.\n mstore(0x0f, 0x30313233343536373839616263646566)\n\n let o := add(str, 0x20)\n let end := add(raw, length)\n\n for {} iszero(eq(raw, end)) {} {\n raw := add(raw, 1)\n mstore8(add(o, 1), mload(and(mload(raw), 15)))\n mstore8(o, mload(and(shr(4, mload(raw)), 15)))\n o := add(o, 2)\n }\n mstore(o, 0) // Zeroize the slot after the string.\n mstore(0x40, add(o, 0x20)) // Allocate the memory.\n }\n }\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* RUNE STRING OPERATIONS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev Returns the number of UTF characters in the string.\n function runeCount(string memory s) internal pure returns (uint256 result) {\n /// @solidity memory-safe-assembly\n assembly {\n if mload(s) {\n mstore(0x00, div(not(0), 255))\n mstore(0x20, 0x0202020202020202020202020202020202020202020202020303030304040506)\n let o := add(s, 0x20)\n let end := add(o, mload(s))\n for { result := 1 } 1 { result := add(result, 1) } {\n o := add(o, byte(0, mload(shr(250, mload(o)))))\n if iszero(lt(o, end)) { break }\n }\n }\n }\n }\n\n /// @dev Returns if this string is a 7-bit ASCII string.\n /// (i.e. all characters codes are in [0..127])\n function is7BitASCII(string memory s) internal pure returns (bool result) {\n /// @solidity memory-safe-assembly\n assembly {\n let mask := shl(7, div(not(0), 255))\n result := 1\n let n := mload(s)\n if n {\n let o := add(s, 0x20)\n let end := add(o, n)\n let last := mload(end)\n mstore(end, 0)\n for {} 1 {} {\n if and(mask, mload(o)) {\n result := 0\n break\n }\n o := add(o, 0x20)\n if iszero(lt(o, end)) { break }\n }\n mstore(end, last)\n }\n }\n }\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* BYTE STRING OPERATIONS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n // For performance and bytecode compactness, byte string operations are restricted\n // to 7-bit ASCII strings. All offsets are byte offsets, not UTF character offsets.\n // Usage of byte string operations on charsets with runes spanning two or more bytes\n // can lead to undefined behavior.\n\n /// @dev Returns `subject` all occurrences of `search` replaced with `replacement`.\n function replace(string memory subject, string memory search, string memory replacement)\n internal\n pure\n returns (string memory result)\n {\n /// @solidity memory-safe-assembly\n assembly {\n let subjectLength := mload(subject)\n let searchLength := mload(search)\n let replacementLength := mload(replacement)\n\n subject := add(subject, 0x20)\n search := add(search, 0x20)\n replacement := add(replacement, 0x20)\n result := add(mload(0x40), 0x20)\n\n let subjectEnd := add(subject, subjectLength)\n if iszero(gt(searchLength, subjectLength)) {\n let subjectSearchEnd := add(sub(subjectEnd, searchLength), 1)\n let h := 0\n if iszero(lt(searchLength, 0x20)) { h := keccak256(search, searchLength) }\n let m := shl(3, sub(0x20, and(searchLength, 0x1f)))\n let s := mload(search)\n for {} 1 {} {\n let t := mload(subject)\n // Whether the first `searchLength % 32` bytes of\n // `subject` and `search` matches.\n if iszero(shr(m, xor(t, s))) {\n if h {\n if iszero(eq(keccak256(subject, searchLength), h)) {\n mstore(result, t)\n result := add(result, 1)\n subject := add(subject, 1)\n if iszero(lt(subject, subjectSearchEnd)) { break }\n continue\n }\n }\n // Copy the `replacement` one word at a time.\n for { let o := 0 } 1 {} {\n mstore(add(result, o), mload(add(replacement, o)))\n o := add(o, 0x20)\n if iszero(lt(o, replacementLength)) { break }\n }\n result := add(result, replacementLength)\n subject := add(subject, searchLength)\n if searchLength {\n if iszero(lt(subject, subjectSearchEnd)) { break }\n continue\n }\n }\n mstore(result, t)\n result := add(result, 1)\n subject := add(subject, 1)\n if iszero(lt(subject, subjectSearchEnd)) { break }\n }\n }\n\n let resultRemainder := result\n result := add(mload(0x40), 0x20)\n let k := add(sub(resultRemainder, result), sub(subjectEnd, subject))\n // Copy the rest of the string one word at a time.\n for {} lt(subject, subjectEnd) {} {\n mstore(resultRemainder, mload(subject))\n resultRemainder := add(resultRemainder, 0x20)\n subject := add(subject, 0x20)\n }\n result := sub(result, 0x20)\n let last := add(add(result, 0x20), k) // Zeroize the slot after the string.\n mstore(last, 0)\n mstore(0x40, add(last, 0x20)) // Allocate the memory.\n mstore(result, k) // Store the length.\n }\n }\n\n /// @dev Returns the byte index of the first location of `search` in `subject`,\n /// searching from left to right, starting from `from`.\n /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.\n function indexOf(string memory subject, string memory search, uint256 from)\n internal\n pure\n returns (uint256 result)\n {\n /// @solidity memory-safe-assembly\n assembly {\n for { let subjectLength := mload(subject) } 1 {} {\n if iszero(mload(search)) {\n if iszero(gt(from, subjectLength)) {\n result := from\n break\n }\n result := subjectLength\n break\n }\n let searchLength := mload(search)\n let subjectStart := add(subject, 0x20)\n\n result := not(0) // Initialize to `NOT_FOUND`.\n\n subject := add(subjectStart, from)\n let end := add(sub(add(subjectStart, subjectLength), searchLength), 1)\n\n let m := shl(3, sub(0x20, and(searchLength, 0x1f)))\n let s := mload(add(search, 0x20))\n\n if iszero(and(lt(subject, end), lt(from, subjectLength))) { break }\n\n if iszero(lt(searchLength, 0x20)) {\n for { let h := keccak256(add(search, 0x20), searchLength) } 1 {} {\n if iszero(shr(m, xor(mload(subject), s))) {\n if eq(keccak256(subject, searchLength), h) {\n result := sub(subject, subjectStart)\n break\n }\n }\n subject := add(subject, 1)\n if iszero(lt(subject, end)) { break }\n }\n break\n }\n for {} 1 {} {\n if iszero(shr(m, xor(mload(subject), s))) {\n result := sub(subject, subjectStart)\n break\n }\n subject := add(subject, 1)\n if iszero(lt(subject, end)) { break }\n }\n break\n }\n }\n }\n\n /// @dev Returns the byte index of the first location of `search` in `subject`,\n /// searching from left to right.\n /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.\n function indexOf(string memory subject, string memory search)\n internal\n pure\n returns (uint256 result)\n {\n result = indexOf(subject, search, 0);\n }\n\n /// @dev Returns the byte index of the first location of `search` in `subject`,\n /// searching from right to left, starting from `from`.\n /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.\n function lastIndexOf(string memory subject, string memory search, uint256 from)\n internal\n pure\n returns (uint256 result)\n {\n /// @solidity memory-safe-assembly\n assembly {\n for {} 1 {} {\n result := not(0) // Initialize to `NOT_FOUND`.\n let searchLength := mload(search)\n if gt(searchLength, mload(subject)) { break }\n let w := result\n\n let fromMax := sub(mload(subject), searchLength)\n if iszero(gt(fromMax, from)) { from := fromMax }\n\n let end := add(add(subject, 0x20), w)\n subject := add(add(subject, 0x20), from)\n if iszero(gt(subject, end)) { break }\n // As this function is not too often used,\n // we shall simply use keccak256 for smaller bytecode size.\n for { let h := keccak256(add(search, 0x20), searchLength) } 1 {} {\n if eq(keccak256(subject, searchLength), h) {\n result := sub(subject, add(end, 1))\n break\n }\n subject := add(subject, w) // `sub(subject, 1)`.\n if iszero(gt(subject, end)) { break }\n }\n break\n }\n }\n }\n\n /// @dev Returns the byte index of the first location of `search` in `subject`,\n /// searching from right to left.\n /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.\n function lastIndexOf(string memory subject, string memory search)\n internal\n pure\n returns (uint256 result)\n {\n result = lastIndexOf(subject, search, uint256(int256(-1)));\n }\n\n /// @dev Returns true if `search` is found in `subject`, false otherwise.\n function contains(string memory subject, string memory search) internal pure returns (bool) {\n return indexOf(subject, search) != NOT_FOUND;\n }\n\n /// @dev Returns whether `subject` starts with `search`.\n function startsWith(string memory subject, string memory search)\n internal\n pure\n returns (bool result)\n {\n /// @solidity memory-safe-assembly\n assembly {\n let searchLength := mload(search)\n // Just using keccak256 directly is actually cheaper.\n // forgefmt: disable-next-item\n result := and(\n iszero(gt(searchLength, mload(subject))),\n eq(\n keccak256(add(subject, 0x20), searchLength),\n keccak256(add(search, 0x20), searchLength)\n )\n )\n }\n }\n\n /// @dev Returns whether `subject` ends with `search`.\n function endsWith(string memory subject, string memory search)\n internal\n pure\n returns (bool result)\n {\n /// @solidity memory-safe-assembly\n assembly {\n let searchLength := mload(search)\n let subjectLength := mload(subject)\n // Whether `search` is not longer than `subject`.\n let withinRange := iszero(gt(searchLength, subjectLength))\n // Just using keccak256 directly is actually cheaper.\n // forgefmt: disable-next-item\n result := and(\n withinRange,\n eq(\n keccak256(\n // `subject + 0x20 + max(subjectLength - searchLength, 0)`.\n add(add(subject, 0x20), mul(withinRange, sub(subjectLength, searchLength))),\n searchLength\n ),\n keccak256(add(search, 0x20), searchLength)\n )\n )\n }\n }\n\n /// @dev Returns `subject` repeated `times`.\n function repeat(string memory subject, uint256 times)\n internal\n pure\n returns (string memory result)\n {\n /// @solidity memory-safe-assembly\n assembly {\n let subjectLength := mload(subject)\n if iszero(or(iszero(times), iszero(subjectLength))) {\n subject := add(subject, 0x20)\n result := mload(0x40)\n let output := add(result, 0x20)\n for {} 1 {} {\n // Copy the `subject` one word at a time.\n for { let o := 0 } 1 {} {\n mstore(add(output, o), mload(add(subject, o)))\n o := add(o, 0x20)\n if iszero(lt(o, subjectLength)) { break }\n }\n output := add(output, subjectLength)\n times := sub(times, 1)\n if iszero(times) { break }\n }\n mstore(output, 0) // Zeroize the slot after the string.\n let resultLength := sub(output, add(result, 0x20))\n mstore(result, resultLength) // Store the length.\n // Allocate the memory.\n mstore(0x40, add(result, add(resultLength, 0x20)))\n }\n }\n }\n\n /// @dev Returns a copy of `subject` sliced from `start` to `end` (exclusive).\n /// `start` and `end` are byte offsets.\n function slice(string memory subject, uint256 start, uint256 end)\n internal\n pure\n returns (string memory result)\n {\n /// @solidity memory-safe-assembly\n assembly {\n let subjectLength := mload(subject)\n if iszero(gt(subjectLength, end)) { end := subjectLength }\n if iszero(gt(subjectLength, start)) { start := subjectLength }\n if lt(start, end) {\n result := mload(0x40)\n let resultLength := sub(end, start)\n mstore(result, resultLength)\n subject := add(subject, start)\n let w := not(0x1f)\n // Copy the `subject` one word at a time, backwards.\n for { let o := and(add(resultLength, 0x1f), w) } 1 {} {\n mstore(add(result, o), mload(add(subject, o)))\n o := add(o, w) // `sub(o, 0x20)`.\n if iszero(o) { break }\n }\n // Zeroize the slot after the string.\n mstore(add(add(result, 0x20), resultLength), 0)\n // Allocate memory for the length and the bytes,\n // rounded up to a multiple of 32.\n mstore(0x40, add(result, and(add(resultLength, 0x3f), w)))\n }\n }\n }\n\n /// @dev Returns a copy of `subject` sliced from `start` to the end of the string.\n /// `start` is a byte offset.\n function slice(string memory subject, uint256 start)\n internal\n pure\n returns (string memory result)\n {\n result = slice(subject, start, uint256(int256(-1)));\n }\n\n /// @dev Returns all the indices of `search` in `subject`.\n /// The indices are byte offsets.\n function indicesOf(string memory subject, string memory search)\n internal\n pure\n returns (uint256[] memory result)\n {\n /// @solidity memory-safe-assembly\n assembly {\n let subjectLength := mload(subject)\n let searchLength := mload(search)\n\n if iszero(gt(searchLength, subjectLength)) {\n subject := add(subject, 0x20)\n search := add(search, 0x20)\n result := add(mload(0x40), 0x20)\n\n let subjectStart := subject\n let subjectSearchEnd := add(sub(add(subject, subjectLength), searchLength), 1)\n let h := 0\n if iszero(lt(searchLength, 0x20)) { h := keccak256(search, searchLength) }\n let m := shl(3, sub(0x20, and(searchLength, 0x1f)))\n let s := mload(search)\n for {} 1 {} {\n let t := mload(subject)\n // Whether the first `searchLength % 32` bytes of\n // `subject` and `search` matches.\n if iszero(shr(m, xor(t, s))) {\n if h {\n if iszero(eq(keccak256(subject, searchLength), h)) {\n subject := add(subject, 1)\n if iszero(lt(subject, subjectSearchEnd)) { break }\n continue\n }\n }\n // Append to `result`.\n mstore(result, sub(subject, subjectStart))\n result := add(result, 0x20)\n // Advance `subject` by `searchLength`.\n subject := add(subject, searchLength)\n if searchLength {\n if iszero(lt(subject, subjectSearchEnd)) { break }\n continue\n }\n }\n subject := add(subject, 1)\n if iszero(lt(subject, subjectSearchEnd)) { break }\n }\n let resultEnd := result\n // Assign `result` to the free memory pointer.\n result := mload(0x40)\n // Store the length of `result`.\n mstore(result, shr(5, sub(resultEnd, add(result, 0x20))))\n // Allocate memory for result.\n // We allocate one more word, so this array can be recycled for {split}.\n mstore(0x40, add(resultEnd, 0x20))\n }\n }\n }\n\n /// @dev Returns a arrays of strings based on the `delimiter` inside of the `subject` string.\n function split(string memory subject, string memory delimiter)\n internal\n pure\n returns (string[] memory result)\n {\n uint256[] memory indices = indicesOf(subject, delimiter);\n /// @solidity memory-safe-assembly\n assembly {\n let w := not(0x1f)\n let indexPtr := add(indices, 0x20)\n let indicesEnd := add(indexPtr, shl(5, add(mload(indices), 1)))\n mstore(add(indicesEnd, w), mload(subject))\n mstore(indices, add(mload(indices), 1))\n let prevIndex := 0\n for {} 1 {} {\n let index := mload(indexPtr)\n mstore(indexPtr, 0x60)\n if iszero(eq(index, prevIndex)) {\n let element := mload(0x40)\n let elementLength := sub(index, prevIndex)\n mstore(element, elementLength)\n // Copy the `subject` one word at a time, backwards.\n for { let o := and(add(elementLength, 0x1f), w) } 1 {} {\n mstore(add(element, o), mload(add(add(subject, prevIndex), o)))\n o := add(o, w) // `sub(o, 0x20)`.\n if iszero(o) { break }\n }\n // Zeroize the slot after the string.\n mstore(add(add(element, 0x20), elementLength), 0)\n // Allocate memory for the length and the bytes,\n // rounded up to a multiple of 32.\n mstore(0x40, add(element, and(add(elementLength, 0x3f), w)))\n // Store the `element` into the array.\n mstore(indexPtr, element)\n }\n prevIndex := add(index, mload(delimiter))\n indexPtr := add(indexPtr, 0x20)\n if iszero(lt(indexPtr, indicesEnd)) { break }\n }\n result := indices\n if iszero(mload(delimiter)) {\n result := add(indices, 0x20)\n mstore(result, sub(mload(indices), 2))\n }\n }\n }\n\n /// @dev Returns a concatenated string of `a` and `b`.\n /// Cheaper than `string.concat()` and does not de-align the free memory pointer.\n function concat(string memory a, string memory b)\n internal\n pure\n returns (string memory result)\n {\n /// @solidity memory-safe-assembly\n assembly {\n let w := not(0x1f)\n result := mload(0x40)\n let aLength := mload(a)\n // Copy `a` one word at a time, backwards.\n for { let o := and(add(aLength, 0x20), w) } 1 {} {\n mstore(add(result, o), mload(add(a, o)))\n o := add(o, w) // `sub(o, 0x20)`.\n if iszero(o) { break }\n }\n let bLength := mload(b)\n let output := add(result, aLength)\n // Copy `b` one word at a time, backwards.\n for { let o := and(add(bLength, 0x20), w) } 1 {} {\n mstore(add(output, o), mload(add(b, o)))\n o := add(o, w) // `sub(o, 0x20)`.\n if iszero(o) { break }\n }\n let totalLength := add(aLength, bLength)\n let last := add(add(result, 0x20), totalLength)\n // Zeroize the slot after the string.\n mstore(last, 0)\n // Stores the length.\n mstore(result, totalLength)\n // Allocate memory for the length and the bytes,\n // rounded up to a multiple of 32.\n mstore(0x40, and(add(last, 0x1f), w))\n }\n }\n\n /// @dev Returns a copy of the string in either lowercase or UPPERCASE.\n /// WARNING! This function is only compatible with 7-bit ASCII strings.\n function toCase(string memory subject, bool toUpper)\n internal\n pure\n returns (string memory result)\n {\n /// @solidity memory-safe-assembly\n assembly {\n let length := mload(subject)\n if length {\n result := add(mload(0x40), 0x20)\n subject := add(subject, 1)\n let flags := shl(add(70, shl(5, toUpper)), 0x3ffffff)\n let w := not(0)\n for { let o := length } 1 {} {\n o := add(o, w)\n let b := and(0xff, mload(add(subject, o)))\n mstore8(add(result, o), xor(b, and(shr(b, flags), 0x20)))\n if iszero(o) { break }\n }\n result := mload(0x40)\n mstore(result, length) // Store the length.\n let last := add(add(result, 0x20), length)\n mstore(last, 0) // Zeroize the slot after the string.\n mstore(0x40, add(last, 0x20)) // Allocate the memory.\n }\n }\n }\n\n /// @dev Returns a string from a small bytes32 string.\n /// `s` must be null-terminated, or behavior will be undefined.\n function fromSmallString(bytes32 s) internal pure returns (string memory result) {\n /// @solidity memory-safe-assembly\n assembly {\n result := mload(0x40)\n let n := 0\n for {} byte(n, s) { n := add(n, 1) } {} // Scan for '\\0'.\n mstore(result, n)\n let o := add(result, 0x20)\n mstore(o, s)\n mstore(add(o, n), 0)\n mstore(0x40, add(result, 0x40))\n }\n }\n\n /// @dev Returns the small string, with all bytes after the first null byte zeroized.\n function normalizeSmallString(bytes32 s) internal pure returns (bytes32 result) {\n /// @solidity memory-safe-assembly\n assembly {\n for {} byte(result, s) { result := add(result, 1) } {} // Scan for '\\0'.\n mstore(0x00, s)\n mstore(result, 0x00)\n result := mload(0x00)\n }\n }\n\n /// @dev Returns the string as a normalized null-terminated small string.\n function toSmallString(string memory s) internal pure returns (bytes32 result) {\n /// @solidity memory-safe-assembly\n assembly {\n result := mload(s)\n if iszero(lt(result, 33)) {\n mstore(0x00, 0xec92f9a3) // `TooBigForSmallString()`.\n revert(0x1c, 0x04)\n }\n result := shl(shl(3, sub(32, result)), mload(add(s, result)))\n }\n }\n\n /// @dev Returns a lowercased copy of the string.\n /// WARNING! This function is only compatible with 7-bit ASCII strings.\n function lower(string memory subject) internal pure returns (string memory result) {\n result = toCase(subject, false);\n }\n\n /// @dev Returns an UPPERCASED copy of the string.\n /// WARNING! This function is only compatible with 7-bit ASCII strings.\n function upper(string memory subject) internal pure returns (string memory result) {\n result = toCase(subject, true);\n }\n\n /// @dev Escapes the string to be used within HTML tags.\n function escapeHTML(string memory s) internal pure returns (string memory result) {\n /// @solidity memory-safe-assembly\n assembly {\n let end := add(s, mload(s))\n result := add(mload(0x40), 0x20)\n // Store the bytes of the packed offsets and strides into the scratch space.\n // `packed = (stride << 5) | offset`. Max offset is 20. Max stride is 6.\n mstore(0x1f, 0x900094)\n mstore(0x08, 0xc0000000a6ab)\n // Store \""&'<>\" into the scratch space.\n mstore(0x00, shl(64, 0x2671756f743b26616d703b262333393b266c743b2667743b))\n for {} iszero(eq(s, end)) {} {\n s := add(s, 1)\n let c := and(mload(s), 0xff)\n // Not in `[\"\\\"\",\"'\",\"&\",\"<\",\">\"]`.\n if iszero(and(shl(c, 1), 0x500000c400000000)) {\n mstore8(result, c)\n result := add(result, 1)\n continue\n }\n let t := shr(248, mload(c))\n mstore(result, mload(and(t, 0x1f)))\n result := add(result, shr(5, t))\n }\n let last := result\n mstore(last, 0) // Zeroize the slot after the string.\n result := mload(0x40)\n mstore(result, sub(last, add(result, 0x20))) // Store the length.\n mstore(0x40, add(last, 0x20)) // Allocate the memory.\n }\n }\n\n /// @dev Escapes the string to be used within double-quotes in a JSON.\n /// If `addDoubleQuotes` is true, the result will be enclosed in double-quotes.\n function escapeJSON(string memory s, bool addDoubleQuotes)\n internal\n pure\n returns (string memory result)\n {\n /// @solidity memory-safe-assembly\n assembly {\n let end := add(s, mload(s))\n result := add(mload(0x40), 0x20)\n if addDoubleQuotes {\n mstore8(result, 34)\n result := add(1, result)\n }\n // Store \"\\\\u0000\" in scratch space.\n // Store \"0123456789abcdef\" in scratch space.\n // Also, store `{0x08:\"b\", 0x09:\"t\", 0x0a:\"n\", 0x0c:\"f\", 0x0d:\"r\"}`.\n // into the scratch space.\n mstore(0x15, 0x5c75303030303031323334353637383961626364656662746e006672)\n // Bitmask for detecting `[\"\\\"\",\"\\\\\"]`.\n let e := or(shl(0x22, 1), shl(0x5c, 1))\n for {} iszero(eq(s, end)) {} {\n s := add(s, 1)\n let c := and(mload(s), 0xff)\n if iszero(lt(c, 0x20)) {\n if iszero(and(shl(c, 1), e)) {\n // Not in `[\"\\\"\",\"\\\\\"]`.\n mstore8(result, c)\n result := add(result, 1)\n continue\n }\n mstore8(result, 0x5c) // \"\\\\\".\n mstore8(add(result, 1), c)\n result := add(result, 2)\n continue\n }\n if iszero(and(shl(c, 1), 0x3700)) {\n // Not in `[\"\\b\",\"\\t\",\"\\n\",\"\\f\",\"\\d\"]`.\n mstore8(0x1d, mload(shr(4, c))) // Hex value.\n mstore8(0x1e, mload(and(c, 15))) // Hex value.\n mstore(result, mload(0x19)) // \"\\\\u00XX\".\n result := add(result, 6)\n continue\n }\n mstore8(result, 0x5c) // \"\\\\\".\n mstore8(add(result, 1), mload(add(c, 8)))\n result := add(result, 2)\n }\n if addDoubleQuotes {\n mstore8(result, 34)\n result := add(1, result)\n }\n let last := result\n mstore(last, 0) // Zeroize the slot after the string.\n result := mload(0x40)\n mstore(result, sub(last, add(result, 0x20))) // Store the length.\n mstore(0x40, add(last, 0x20)) // Allocate the memory.\n }\n }\n\n /// @dev Escapes the string to be used within double-quotes in a JSON.\n function escapeJSON(string memory s) internal pure returns (string memory result) {\n result = escapeJSON(s, false);\n }\n\n /// @dev Returns whether `a` equals `b`.\n function eq(string memory a, string memory b) internal pure returns (bool result) {\n /// @solidity memory-safe-assembly\n assembly {\n result := eq(keccak256(add(a, 0x20), mload(a)), keccak256(add(b, 0x20), mload(b)))\n }\n }\n\n /// @dev Returns whether `a` equals `b`, where `b` is a null-terminated small string.\n function eqs(string memory a, bytes32 b) internal pure returns (bool result) {\n /// @solidity memory-safe-assembly\n assembly {\n // These should be evaluated on compile time, as far as possible.\n let m := not(shl(7, div(not(iszero(b)), 255))) // `0x7f7f ...`.\n let x := not(or(m, or(b, add(m, and(b, m)))))\n let r := shl(7, iszero(iszero(shr(128, x))))\n r := or(r, shl(6, iszero(iszero(shr(64, shr(r, x))))))\n r := or(r, shl(5, lt(0xffffffff, shr(r, x))))\n r := or(r, shl(4, lt(0xffff, shr(r, x))))\n r := or(r, shl(3, lt(0xff, shr(r, x))))\n // forgefmt: disable-next-item\n result := gt(eq(mload(a), add(iszero(x), xor(31, shr(3, r)))),\n xor(shr(add(8, r), b), shr(add(8, r), mload(add(a, 0x20)))))\n }\n }\n\n /// @dev Packs a single string with its length into a single word.\n /// Returns `bytes32(0)` if the length is zero or greater than 31.\n function packOne(string memory a) internal pure returns (bytes32 result) {\n /// @solidity memory-safe-assembly\n assembly {\n // We don't need to zero right pad the string,\n // since this is our own custom non-standard packing scheme.\n result :=\n mul(\n // Load the length and the bytes.\n mload(add(a, 0x1f)),\n // `length != 0 && length < 32`. Abuses underflow.\n // Assumes that the length is valid and within the block gas limit.\n lt(sub(mload(a), 1), 0x1f)\n )\n }\n }\n\n /// @dev Unpacks a string packed using {packOne}.\n /// Returns the empty string if `packed` is `bytes32(0)`.\n /// If `packed` is not an output of {packOne}, the output behavior is undefined.\n function unpackOne(bytes32 packed) internal pure returns (string memory result) {\n /// @solidity memory-safe-assembly\n assembly {\n // Grab the free memory pointer.\n result := mload(0x40)\n // Allocate 2 words (1 for the length, 1 for the bytes).\n mstore(0x40, add(result, 0x40))\n // Zeroize the length slot.\n mstore(result, 0)\n // Store the length and bytes.\n mstore(add(result, 0x1f), packed)\n // Right pad with zeroes.\n mstore(add(add(result, 0x20), mload(result)), 0)\n }\n }\n\n /// @dev Packs two strings with their lengths into a single word.\n /// Returns `bytes32(0)` if combined length is zero or greater than 30.\n function packTwo(string memory a, string memory b) internal pure returns (bytes32 result) {\n /// @solidity memory-safe-assembly\n assembly {\n let aLength := mload(a)\n // We don't need to zero right pad the strings,\n // since this is our own custom non-standard packing scheme.\n result :=\n mul(\n // Load the length and the bytes of `a` and `b`.\n or(\n shl(shl(3, sub(0x1f, aLength)), mload(add(a, aLength))),\n mload(sub(add(b, 0x1e), aLength))\n ),\n // `totalLength != 0 && totalLength < 31`. Abuses underflow.\n // Assumes that the lengths are valid and within the block gas limit.\n lt(sub(add(aLength, mload(b)), 1), 0x1e)\n )\n }\n }\n\n /// @dev Unpacks strings packed using {packTwo}.\n /// Returns the empty strings if `packed` is `bytes32(0)`.\n /// If `packed` is not an output of {packTwo}, the output behavior is undefined.\n function unpackTwo(bytes32 packed)\n internal\n pure\n returns (string memory resultA, string memory resultB)\n {\n /// @solidity memory-safe-assembly\n assembly {\n // Grab the free memory pointer.\n resultA := mload(0x40)\n resultB := add(resultA, 0x40)\n // Allocate 2 words for each string (1 for the length, 1 for the byte). Total 4 words.\n mstore(0x40, add(resultB, 0x40))\n // Zeroize the length slots.\n mstore(resultA, 0)\n mstore(resultB, 0)\n // Store the lengths and bytes.\n mstore(add(resultA, 0x1f), packed)\n mstore(add(resultB, 0x1f), mload(add(add(resultA, 0x20), mload(resultA))))\n // Right pad with zeroes.\n mstore(add(add(resultA, 0x20), mload(resultA)), 0)\n mstore(add(add(resultB, 0x20), mload(resultB)), 0)\n }\n }\n\n /// @dev Directly returns `a` without copying.\n function directReturn(string memory a) internal pure {\n assembly {\n // Assumes that the string does not start from the scratch space.\n let retStart := sub(a, 0x20)\n let retSize := add(mload(a), 0x40)\n // Right pad with zeroes. Just in case the string is produced\n // by a method that doesn't zero right pad.\n mstore(add(retStart, retSize), 0)\n // Store the return offset.\n mstore(retStart, 0x20)\n // End the transaction, returning the string.\n return(retStart, retSize)\n }\n }\n}\n"},"src/utils/BytesUtils.sol":{"content":"// SPDX-License-Identifier: BSD 2-Clause License\npragma solidity ^0.8.0;\n\n// Inspired by ensdomains/dnssec-oracle - BSD-2-Clause license\n// https://github.com/ensdomains/dnssec-oracle/blob/master/contracts/BytesUtils.sol\n\nlibrary BytesUtils {\n /*\n * @dev Returns the keccak-256 hash of a byte range.\n * @param self The byte string to hash.\n * @param offset The position to start hashing at.\n * @param len The number of bytes to hash.\n * @return The hash of the byte range.\n */\n function keccak(bytes memory self, uint256 offset, uint256 len) internal pure returns (bytes32 ret) {\n require(offset + len <= self.length);\n assembly {\n ret := keccak256(add(add(self, 32), offset), len)\n }\n }\n\n /*\n * @dev Returns a positive number if `other` comes lexicographically after\n * `self`, a negative number if it comes before, or zero if the\n * contents of the two bytes are equal.\n * @param self The first bytes to compare.\n * @param other The second bytes to compare.\n * @return The result of the comparison.\n */\n function compare(bytes memory self, bytes memory other) internal pure returns (int256) {\n return compare(self, 0, self.length, other, 0, other.length);\n }\n\n /*\n * @dev Returns a positive number if `other` comes lexicographically after\n * `self`, a negative number if it comes before, or zero if the\n * contents of the two bytes are equal. Comparison is done per-rune,\n * on unicode codepoints.\n * @param self The first bytes to compare.\n * @param offset The offset of self.\n * @param len The length of self.\n * @param other The second bytes to compare.\n * @param otheroffset The offset of the other string.\n * @param otherlen The length of the other string.\n * @return The result of the comparison.\n */\n function compare(\n bytes memory self,\n uint256 offset,\n uint256 len,\n bytes memory other,\n uint256 otheroffset,\n uint256 otherlen\n ) internal pure returns (int256) {\n uint256 shortest = len;\n if (otherlen < len) {\n shortest = otherlen;\n }\n\n uint256 selfptr;\n uint256 otherptr;\n\n assembly {\n selfptr := add(self, add(offset, 32))\n otherptr := add(other, add(otheroffset, 32))\n }\n for (uint256 idx = 0; idx < shortest; idx += 32) {\n uint256 a;\n uint256 b;\n assembly {\n a := mload(selfptr)\n b := mload(otherptr)\n }\n if (a != b) {\n // Mask out irrelevant bytes and check again\n uint256 mask;\n if (shortest > 32) {\n mask = type(uint256).max; // aka 0xffffff....\n } else {\n mask = ~(2 ** (8 * (32 - shortest + idx)) - 1);\n }\n uint256 diff = (a & mask) - (b & mask);\n if (diff != 0) {\n return int256(diff);\n }\n }\n selfptr += 32;\n otherptr += 32;\n }\n\n return int256(len) - int256(otherlen);\n }\n\n /*\n * @dev Returns true if the two byte ranges are equal.\n * @param self The first byte range to compare.\n * @param offset The offset into the first byte range.\n * @param other The second byte range to compare.\n * @param otherOffset The offset into the second byte range.\n * @param len The number of bytes to compare\n * @return True if the byte ranges are equal, false otherwise.\n */\n function equals(bytes memory self, uint256 offset, bytes memory other, uint256 otherOffset, uint256 len)\n internal\n pure\n returns (bool)\n {\n return keccak(self, offset, len) == keccak(other, otherOffset, len);\n }\n\n /*\n * @dev Returns true if the two byte ranges are equal with offsets.\n * @param self The first byte range to compare.\n * @param offset The offset into the first byte range.\n * @param other The second byte range to compare.\n * @param otherOffset The offset into the second byte range.\n * @return True if the byte ranges are equal, false otherwise.\n */\n function equals(bytes memory self, uint256 offset, bytes memory other, uint256 otherOffset)\n internal\n pure\n returns (bool)\n {\n return keccak(self, offset, self.length - offset) == keccak(other, otherOffset, other.length - otherOffset);\n }\n\n /*\n * @dev Compares a range of 'self' to all of 'other' and returns True iff\n * they are equal.\n * @param self The first byte range to compare.\n * @param offset The offset into the first byte range.\n * @param other The second byte range to compare.\n * @return True if the byte ranges are equal, false otherwise.\n */\n function equals(bytes memory self, uint256 offset, bytes memory other) internal pure returns (bool) {\n return self.length >= offset + other.length && equals(self, offset, other, 0, other.length);\n }\n\n /*\n * @dev Returns true if the two byte ranges are equal.\n * @param self The first byte range to compare.\n * @param other The second byte range to compare.\n * @return True if the byte ranges are equal, false otherwise.\n */\n function equals(bytes memory self, bytes memory other) internal pure returns (bool) {\n return self.length == other.length && equals(self, 0, other, 0, self.length);\n }\n\n /*\n * @dev Returns the 8-bit number at the specified index of self.\n * @param self The byte string.\n * @param idx The index into the bytes\n * @return The specified 8 bits of the string, interpreted as an integer.\n */\n function readUint8(bytes memory self, uint256 idx) internal pure returns (uint8 ret) {\n return uint8(self[idx]);\n }\n\n /*\n * @dev Returns the 16-bit number at the specified index of self.\n * @param self The byte string.\n * @param idx The index into the bytes\n * @return The specified 16 bits of the string, interpreted as an integer.\n */\n function readUint16(bytes memory self, uint256 idx) internal pure returns (uint16 ret) {\n require(idx + 2 <= self.length);\n assembly {\n ret := and(mload(add(add(self, 2), idx)), 0xFFFF)\n }\n }\n\n /*\n * @dev Returns the 32-bit number at the specified index of self.\n * @param self The byte string.\n * @param idx The index into the bytes\n * @return The specified 32 bits of the string, interpreted as an integer.\n */\n function readUint32(bytes memory self, uint256 idx) internal pure returns (uint32 ret) {\n require(idx + 4 <= self.length);\n assembly {\n ret := and(mload(add(add(self, 4), idx)), 0xFFFFFFFF)\n }\n }\n\n /*\n * @dev Returns the 32 byte value at the specified index of self.\n * @param self The byte string.\n * @param idx The index into the bytes\n * @return The specified 32 bytes of the string.\n */\n function readBytes32(bytes memory self, uint256 idx) internal pure returns (bytes32 ret) {\n require(idx + 32 <= self.length);\n assembly {\n ret := mload(add(add(self, 32), idx))\n }\n }\n\n /*\n * @dev Returns the 32 byte value at the specified index of self.\n * @param self The byte string.\n * @param idx The index into the bytes\n * @return The specified 32 bytes of the string.\n */\n function readBytes20(bytes memory self, uint256 idx) internal pure returns (bytes20 ret) {\n require(idx + 20 <= self.length);\n assembly {\n ret :=\n and(mload(add(add(self, 32), idx)), 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000000)\n }\n }\n\n /*\n * @dev Returns the n byte value at the specified index of self.\n * @param self The byte string.\n * @param idx The index into the bytes.\n * @param len The number of bytes.\n * @return The specified 32 bytes of the string.\n */\n function readBytesN(bytes memory self, uint256 idx, uint256 len) internal pure returns (bytes32 ret) {\n require(len <= 32);\n require(idx + len <= self.length);\n assembly {\n let mask := not(sub(exp(256, sub(32, len)), 1))\n ret := and(mload(add(add(self, 32), idx)), mask)\n }\n }\n\n function memcpy(uint256 dest, uint256 src, uint256 len) private pure {\n // Copy word-length chunks while possible\n for (; len >= 32; len -= 32) {\n assembly {\n mstore(dest, mload(src))\n }\n dest += 32;\n src += 32;\n }\n\n // Copy remaining bytes\n uint256 mask;\n if (len == 0) {\n mask = type(uint256).max; // Set to maximum value of uint256\n } else {\n mask = 256 ** (32 - len) - 1;\n }\n\n assembly {\n let srcpart := and(mload(src), not(mask))\n let destpart := and(mload(dest), mask)\n mstore(dest, or(destpart, srcpart))\n }\n }\n\n /*\n * @dev Copies a substring into a new byte string.\n * @param self The byte string to copy from.\n * @param offset The offset to start copying at.\n * @param len The number of bytes to copy.\n */\n function substring(bytes memory self, uint256 offset, uint256 len) internal pure returns (bytes memory) {\n require(offset + len <= self.length);\n\n bytes memory ret = new bytes(len);\n uint256 dest;\n uint256 src;\n\n assembly {\n dest := add(ret, 32)\n src := add(add(self, 32), offset)\n }\n memcpy(dest, src, len);\n\n return ret;\n }\n\n // Maps characters from 0x30 to 0x7A to their base32 values.\n // 0xFF represents invalid characters in that range.\n bytes constant base32HexTable =\n hex\"00010203040506070809FFFFFFFFFFFFFF0A0B0C0D0E0F101112131415161718191A1B1C1D1E1FFFFFFFFFFFFFFFFFFFFF0A0B0C0D0E0F101112131415161718191A1B1C1D1E1F\";\n\n /**\n * @dev Decodes unpadded base32 data of up to one word in length.\n * @param self The data to decode.\n * @param off Offset into the string to start at.\n * @param len Number of characters to decode.\n * @return The decoded data, left aligned.\n */\n function base32HexDecodeWord(bytes memory self, uint256 off, uint256 len) internal pure returns (bytes32) {\n require(len <= 52);\n\n uint256 ret = 0;\n uint8 decoded;\n for (uint256 i = 0; i < len; i++) {\n bytes1 char = self[off + i];\n require(char >= 0x30 && char <= 0x7A);\n decoded = uint8(base32HexTable[uint256(uint8(char)) - 0x30]);\n require(decoded <= 0x20);\n if (i == len - 1) {\n break;\n }\n ret = (ret << 5) | decoded;\n }\n\n uint256 bitlen = len * 5;\n if (len % 8 == 0) {\n // Multiple of 8 characters, no padding\n ret = (ret << 5) | decoded;\n } else if (len % 8 == 2) {\n // Two extra characters - 1 byte\n ret = (ret << 3) | (decoded >> 2);\n bitlen -= 2;\n } else if (len % 8 == 4) {\n // Four extra characters - 2 bytes\n ret = (ret << 1) | (decoded >> 4);\n bitlen -= 4;\n } else if (len % 8 == 5) {\n // Five extra characters - 3 bytes\n ret = (ret << 4) | (decoded >> 1);\n bitlen -= 1;\n } else if (len % 8 == 7) {\n // Seven extra characters - 4 bytes\n ret = (ret << 2) | (decoded >> 3);\n bitlen -= 3;\n } else {\n revert();\n }\n\n return bytes32(ret << (256 - bitlen));\n }\n\n function compareBytes(bytes memory a, bytes memory b) internal pure returns (bool) {\n if (a.length != b.length) {\n return false;\n }\n for (uint256 i = 0; i < a.length; i++) {\n if (a[i] != b[i]) {\n return false;\n }\n }\n return true;\n }\n}\n"},"src/utils/P256Verifier.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport \"./BytesUtils.sol\";\n\n/**\n * @notice modified from https://github.com/daimo-eth/p256-verifier/\n */\nlibrary P256Verifier {\n using BytesUtils for bytes;\n\n address internal constant P256_VERIFIER = 0xc2b78104907F722DABAc4C69f826a522B2754De4;\n\n function ecdsaVerify(bytes32 messageHash, bytes memory signature, bytes memory key)\n internal\n view\n returns (bool verified)\n {\n bytes memory args = abi.encode(\n messageHash,\n uint256(bytes32(signature.substring(0, 32))),\n uint256(bytes32(signature.substring(32, 32))),\n uint256(bytes32(key.substring(0, 32))),\n uint256(bytes32(key.substring(32, 32)))\n );\n (bool success, bytes memory ret) = P256_VERIFIER.staticcall(args);\n assert(success); // never reverts, always returns 0 or 1\n\n verified = abi.decode(ret, (uint256)) == 1;\n }\n}\n"},"lib/solady/src/utils/JSONParserLib.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\n/// @notice Library for parsing JSONs.\n/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/JSONParserLib.sol)\nlibrary JSONParserLib {\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* CUSTOM ERRORS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev The input is invalid.\n error ParsingFailed();\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* CONSTANTS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n // There are 6 types of variables in JSON (excluding undefined).\n\n /// @dev For denoting that an item has not been initialized.\n /// A item returned from `parse` will never be of an undefined type.\n /// Parsing a invalid JSON string will simply revert.\n uint8 internal constant TYPE_UNDEFINED = 0;\n\n /// @dev Type representing an array (e.g. `[1,2,3]`).\n uint8 internal constant TYPE_ARRAY = 1;\n\n /// @dev Type representing an object (e.g. `{\"a\":\"A\",\"b\":\"B\"}`).\n uint8 internal constant TYPE_OBJECT = 2;\n\n /// @dev Type representing a number (e.g. `-1.23e+21`).\n uint8 internal constant TYPE_NUMBER = 3;\n\n /// @dev Type representing a string (e.g. `\"hello\"`).\n uint8 internal constant TYPE_STRING = 4;\n\n /// @dev Type representing a boolean (i.e. `true` or `false`).\n uint8 internal constant TYPE_BOOLEAN = 5;\n\n /// @dev Type representing null (i.e. `null`).\n uint8 internal constant TYPE_NULL = 6;\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* STRUCTS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev A pointer to a parsed JSON node.\n struct Item {\n // Do NOT modify the `_data` directly.\n uint256 _data;\n }\n\n // Private constants for packing `_data`.\n\n uint256 private constant _BITPOS_STRING = 32 * 7 - 8;\n uint256 private constant _BITPOS_KEY_LENGTH = 32 * 6 - 8;\n uint256 private constant _BITPOS_KEY = 32 * 5 - 8;\n uint256 private constant _BITPOS_VALUE_LENGTH = 32 * 4 - 8;\n uint256 private constant _BITPOS_VALUE = 32 * 3 - 8;\n uint256 private constant _BITPOS_CHILD = 32 * 2 - 8;\n uint256 private constant _BITPOS_SIBLING_OR_PARENT = 32 * 1 - 8;\n uint256 private constant _BITMASK_POINTER = 0xffffffff;\n uint256 private constant _BITMASK_TYPE = 7;\n uint256 private constant _KEY_INITED = 1 << 3;\n uint256 private constant _VALUE_INITED = 1 << 4;\n uint256 private constant _CHILDREN_INITED = 1 << 5;\n uint256 private constant _PARENT_IS_ARRAY = 1 << 6;\n uint256 private constant _PARENT_IS_OBJECT = 1 << 7;\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* JSON PARSING OPERATION */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev Parses the JSON string `s`, and returns the root.\n /// Reverts if `s` is not a valid JSON as specified in RFC 8259.\n /// Object items WILL simply contain all their children, inclusive of repeated keys,\n /// in the same order which they appear in the JSON string.\n ///\n /// Note: For efficiency, this function WILL NOT make a copy of `s`.\n /// The parsed tree WILL contain offsets to `s`.\n /// Do NOT pass in a string that WILL be modified later on.\n function parse(string memory s) internal pure returns (Item memory result) {\n /// @solidity memory-safe-assembly\n assembly {\n mstore(0x40, result) // We will use our own allocation instead.\n }\n bytes32 r = _query(_toInput(s), 255);\n /// @solidity memory-safe-assembly\n assembly {\n result := r\n }\n }\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* JSON ITEM OPERATIONS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n // Note:\n // - An item is a node in the JSON tree.\n // - The value of a string item WILL be double-quoted, JSON encoded.\n // - We make a distinction between `index` and `key`.\n // - Items in arrays are located by `index` (uint256).\n // - Items in objects are located by `key` (string).\n // - Keys are always strings, double-quoted, JSON encoded.\n //\n // These design choices are made to balance between efficiency and ease-of-use.\n\n /// @dev Returns the string value of the item.\n /// This is its exact string representation in the original JSON string.\n /// The returned string WILL have leading and trailing whitespace trimmed.\n /// All inner whitespace WILL be preserved, exactly as it is in the original JSON string.\n /// If the item's type is string, the returned string WILL be double-quoted, JSON encoded.\n ///\n /// Note: This function lazily instantiates and caches the returned string.\n /// Do NOT modify the returned string.\n function value(Item memory item) internal pure returns (string memory result) {\n bytes32 r = _query(_toInput(item), 0);\n /// @solidity memory-safe-assembly\n assembly {\n result := r\n }\n }\n\n /// @dev Returns the index of the item in the array.\n /// It the item's parent is not an array, returns 0.\n function index(Item memory item) internal pure returns (uint256 result) {\n /// @solidity memory-safe-assembly\n assembly {\n if and(mload(item), _PARENT_IS_ARRAY) {\n result := and(_BITMASK_POINTER, shr(_BITPOS_KEY, mload(item)))\n }\n }\n }\n\n /// @dev Returns the key of the item in the object.\n /// It the item's parent is not an object, returns an empty string.\n /// The returned string WILL be double-quoted, JSON encoded.\n ///\n /// Note: This function lazily instantiates and caches the returned string.\n /// Do NOT modify the returned string.\n function key(Item memory item) internal pure returns (string memory result) {\n if (item._data & _PARENT_IS_OBJECT != 0) {\n bytes32 r = _query(_toInput(item), 1);\n /// @solidity memory-safe-assembly\n assembly {\n result := r\n }\n }\n }\n\n /// @dev Returns the key of the item in the object.\n /// It the item is neither an array nor object, returns an empty array.\n ///\n /// Note: This function lazily instantiates and caches the returned array.\n /// Do NOT modify the returned array.\n function children(Item memory item) internal pure returns (Item[] memory result) {\n bytes32 r = _query(_toInput(item), 3);\n /// @solidity memory-safe-assembly\n assembly {\n result := r\n }\n }\n\n /// @dev Returns the number of children.\n /// It the item is neither an array nor object, returns zero.\n function size(Item memory item) internal pure returns (uint256 result) {\n bytes32 r = _query(_toInput(item), 3);\n /// @solidity memory-safe-assembly\n assembly {\n result := mload(r)\n }\n }\n\n /// @dev Returns the item at index `i` for (array).\n /// If `item` is not an array, the result's type WILL be undefined.\n /// If there is no item with the index, the result's type WILL be undefined.\n function at(Item memory item, uint256 i) internal pure returns (Item memory result) {\n /// @solidity memory-safe-assembly\n assembly {\n mstore(0x40, result) // Free the default allocation. We'll allocate manually.\n }\n bytes32 r = _query(_toInput(item), 3);\n /// @solidity memory-safe-assembly\n assembly {\n result := mload(add(add(r, 0x20), shl(5, i)))\n if iszero(and(lt(i, mload(r)), eq(and(mload(item), _BITMASK_TYPE), TYPE_ARRAY))) {\n result := 0x60 // Reset to the zero pointer.\n }\n }\n }\n\n /// @dev Returns the item at key `k` for (object).\n /// If `item` is not an object, the result's type WILL be undefined.\n /// The key MUST be double-quoted, JSON encoded. This is for efficiency reasons.\n /// - Correct : `item.at('\"k\"')`.\n /// - Wrong : `item.at(\"k\")`.\n /// For duplicated keys, the last item with the key WILL be returned.\n /// If there is no item with the key, the result's type WILL be undefined.\n function at(Item memory item, string memory k) internal pure returns (Item memory result) {\n /// @solidity memory-safe-assembly\n assembly {\n mstore(0x40, result) // Free the default allocation. We'll allocate manually.\n result := 0x60 // Initialize to the zero pointer.\n }\n if (isObject(item)) {\n bytes32 kHash = keccak256(bytes(k));\n Item[] memory r = children(item);\n // We'll just do a linear search. The alternatives are very bloated.\n for (uint256 i = r.length << 5; i != 0;) {\n /// @solidity memory-safe-assembly\n assembly {\n item := mload(add(r, i))\n i := sub(i, 0x20)\n }\n if (keccak256(bytes(key(item))) != kHash) continue;\n result = item;\n break;\n }\n }\n }\n\n /// @dev Returns the item's type.\n function getType(Item memory item) internal pure returns (uint8 result) {\n result = uint8(item._data & _BITMASK_TYPE);\n }\n\n /// Note: All types are mutually exclusive.\n\n /// @dev Returns whether the item is of type undefined.\n function isUndefined(Item memory item) internal pure returns (bool result) {\n result = item._data & _BITMASK_TYPE == TYPE_UNDEFINED;\n }\n\n /// @dev Returns whether the item is of type array.\n function isArray(Item memory item) internal pure returns (bool result) {\n result = item._data & _BITMASK_TYPE == TYPE_ARRAY;\n }\n\n /// @dev Returns whether the item is of type object.\n function isObject(Item memory item) internal pure returns (bool result) {\n result = item._data & _BITMASK_TYPE == TYPE_OBJECT;\n }\n\n /// @dev Returns whether the item is of type number.\n function isNumber(Item memory item) internal pure returns (bool result) {\n result = item._data & _BITMASK_TYPE == TYPE_NUMBER;\n }\n\n /// @dev Returns whether the item is of type string.\n function isString(Item memory item) internal pure returns (bool result) {\n result = item._data & _BITMASK_TYPE == TYPE_STRING;\n }\n\n /// @dev Returns whether the item is of type boolean.\n function isBoolean(Item memory item) internal pure returns (bool result) {\n result = item._data & _BITMASK_TYPE == TYPE_BOOLEAN;\n }\n\n /// @dev Returns whether the item is of type null.\n function isNull(Item memory item) internal pure returns (bool result) {\n result = item._data & _BITMASK_TYPE == TYPE_NULL;\n }\n\n /// @dev Returns the item's parent.\n /// If the item does not have a parent, the result's type will be undefined.\n function parent(Item memory item) internal pure returns (Item memory result) {\n /// @solidity memory-safe-assembly\n assembly {\n mstore(0x40, result) // Free the default allocation. We've already allocated.\n result := and(shr(_BITPOS_SIBLING_OR_PARENT, mload(item)), _BITMASK_POINTER)\n if iszero(result) { result := 0x60 } // Reset to the zero pointer.\n }\n }\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* UTILITY FUNCTIONS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev Parses an unsigned integer from a string (in decimal, i.e. base 10).\n /// Reverts if `s` is not a valid uint256 string matching the RegEx `^[0-9]+$`,\n /// or if the parsed number is too big for a uint256.\n function parseUint(string memory s) internal pure returns (uint256 result) {\n /// @solidity memory-safe-assembly\n assembly {\n let n := mload(s)\n let preMulOverflowThres := div(not(0), 10)\n for { let i := 0 } 1 {} {\n i := add(i, 1)\n let digit := sub(and(mload(add(s, i)), 0xff), 48)\n let mulOverflowed := gt(result, preMulOverflowThres)\n let product := mul(10, result)\n result := add(product, digit)\n n := mul(n, iszero(or(or(mulOverflowed, lt(result, product)), gt(digit, 9))))\n if iszero(lt(i, n)) { break }\n }\n if iszero(n) {\n mstore(0x00, 0x10182796) // `ParsingFailed()`.\n revert(0x1c, 0x04)\n }\n }\n }\n\n /// @dev Parses a signed integer from a string (in decimal, i.e. base 10).\n /// Reverts if `s` is not a valid int256 string matching the RegEx `^[+-]?[0-9]+$`,\n /// or if the parsed number is too big for a int256.\n function parseInt(string memory s) internal pure returns (int256 result) {\n uint256 n = bytes(s).length;\n uint256 sign;\n uint256 isNegative;\n /// @solidity memory-safe-assembly\n assembly {\n if n {\n let c := and(mload(add(s, 1)), 0xff)\n isNegative := eq(c, 45)\n if or(eq(c, 43), isNegative) {\n sign := c\n s := add(s, 1)\n mstore(s, sub(n, 1))\n }\n if iszero(or(sign, lt(sub(c, 48), 10))) { s := 0x60 }\n }\n }\n uint256 x = parseUint(s);\n /// @solidity memory-safe-assembly\n assembly {\n if shr(255, x) {\n mstore(0x00, 0x10182796) // `ParsingFailed()`.\n revert(0x1c, 0x04)\n }\n if sign {\n mstore(s, sign)\n s := sub(s, 1)\n mstore(s, n)\n }\n result := xor(x, mul(xor(x, add(not(x), 1)), isNegative))\n }\n }\n\n /// @dev Parses an unsigned integer from a string (in hexadecimal, i.e. base 16).\n /// Reverts if `s` is not a valid uint256 hex string matching the RegEx\n /// `^(0[xX])?[0-9a-fA-F]+$`, or if the parsed number is too big for a uint256.\n function parseUintFromHex(string memory s) internal pure returns (uint256 result) {\n /// @solidity memory-safe-assembly\n assembly {\n let n := mload(s)\n // Skip two if starts with '0x' or '0X'.\n let i := shl(1, and(eq(0x3078, or(shr(240, mload(add(s, 0x20))), 0x20)), gt(n, 1)))\n for {} 1 {} {\n i := add(i, 1)\n let c :=\n byte(\n and(0x1f, shr(and(mload(add(s, i)), 0xff), 0x3e4088843e41bac000000000000)),\n 0x3010a071000000b0104040208000c05090d060e0f\n )\n n := mul(n, iszero(or(iszero(c), shr(252, result))))\n result := add(shl(4, result), sub(c, 1))\n if iszero(lt(i, n)) { break }\n }\n if iszero(n) {\n mstore(0x00, 0x10182796) // `ParsingFailed()`.\n revert(0x1c, 0x04)\n }\n }\n }\n\n /// @dev Decodes a JSON encoded string.\n /// The string MUST be double-quoted, JSON encoded.\n /// Reverts if the string is invalid.\n /// As you can see, it's pretty complex for a deceptively simple looking task.\n function decodeString(string memory s) internal pure returns (string memory result) {\n /// @solidity memory-safe-assembly\n assembly {\n function fail() {\n mstore(0x00, 0x10182796) // `ParsingFailed()`.\n revert(0x1c, 0x04)\n }\n\n function decodeUnicodeEscapeSequence(pIn_, end_) -> _unicode, _pOut {\n _pOut := add(pIn_, 4)\n let b_ := iszero(gt(_pOut, end_))\n let t_ := mload(pIn_) // Load the whole word.\n for { let i_ := 0 } iszero(eq(i_, 4)) { i_ := add(i_, 1) } {\n let c_ := sub(byte(i_, t_), 48)\n if iszero(and(shr(c_, 0x7e0000007e03ff), b_)) { fail() } // Not hexadecimal.\n c_ := sub(c_, add(mul(gt(c_, 16), 7), shl(5, gt(c_, 48))))\n _unicode := add(shl(4, _unicode), c_)\n }\n }\n\n function decodeUnicodeCodePoint(pIn_, end_) -> _unicode, _pOut {\n _unicode, _pOut := decodeUnicodeEscapeSequence(pIn_, end_)\n if iszero(or(lt(_unicode, 0xd800), gt(_unicode, 0xdbff))) {\n let t_ := mload(_pOut) // Load the whole word.\n end_ := mul(end_, eq(shr(240, t_), 0x5c75)) // Fail if not starting with '\\\\u'.\n t_, _pOut := decodeUnicodeEscapeSequence(add(_pOut, 2), end_)\n _unicode := add(0x10000, add(shl(10, and(0x3ff, _unicode)), and(0x3ff, t_)))\n }\n }\n\n function appendCodePointAsUTF8(pIn_, c_) -> _pOut {\n if iszero(gt(c_, 0x7f)) {\n mstore8(pIn_, c_)\n _pOut := add(pIn_, 1)\n leave\n }\n mstore8(0x1f, c_)\n mstore8(0x1e, shr(6, c_))\n if iszero(gt(c_, 0x7ff)) {\n mstore(pIn_, shl(240, or(0xc080, and(0x1f3f, mload(0x00)))))\n _pOut := add(pIn_, 2)\n leave\n }\n mstore8(0x1d, shr(12, c_))\n if iszero(gt(c_, 0xffff)) {\n mstore(pIn_, shl(232, or(0xe08080, and(0x0f3f3f, mload(0x00)))))\n _pOut := add(pIn_, 3)\n leave\n }\n mstore8(0x1c, shr(18, c_))\n mstore(pIn_, shl(224, or(0xf0808080, and(0x073f3f3f, mload(0x00)))))\n _pOut := add(pIn_, shl(2, lt(c_, 0x110000)))\n }\n\n function chr(p_) -> _c {\n _c := byte(0, mload(p_))\n }\n\n let n := mload(s)\n let end := add(add(s, n), 0x1f)\n if iszero(and(gt(n, 1), eq(0x2222, or(and(0xff00, mload(add(s, 2))), chr(end))))) {\n fail() // Fail if not double-quoted.\n }\n let out := add(mload(0x40), 0x20)\n for { let curr := add(s, 0x21) } iszero(eq(curr, end)) {} {\n let c := chr(curr)\n curr := add(curr, 1)\n // Not '\\\\'.\n if iszero(eq(c, 92)) {\n // Not '\"'.\n if iszero(eq(c, 34)) {\n mstore8(out, c)\n out := add(out, 1)\n continue\n }\n curr := end\n }\n if iszero(eq(curr, end)) {\n let escape := chr(curr)\n curr := add(curr, 1)\n // '\"', '/', '\\\\'.\n if and(shr(escape, 0x100000000000800400000000), 1) {\n mstore8(out, escape)\n out := add(out, 1)\n continue\n }\n // 'u'.\n if eq(escape, 117) {\n escape, curr := decodeUnicodeCodePoint(curr, end)\n out := appendCodePointAsUTF8(out, escape)\n continue\n }\n // `{'b':'\\b', 'f':'\\f', 'n':'\\n', 'r':'\\r', 't':'\\t'}`.\n escape := byte(sub(escape, 85), 0x080000000c000000000000000a0000000d0009)\n if escape {\n mstore8(out, escape)\n out := add(out, 1)\n continue\n }\n }\n fail()\n break\n }\n mstore(out, 0) // Zeroize the last slot.\n result := mload(0x40)\n mstore(result, sub(out, add(result, 0x20))) // Store the length.\n mstore(0x40, add(out, 0x20)) // Allocate the memory.\n }\n }\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* PRIVATE HELPERS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev Performs a query on the input with the given mode.\n function _query(bytes32 input, uint256 mode) private pure returns (bytes32 result) {\n /// @solidity memory-safe-assembly\n assembly {\n function fail() {\n mstore(0x00, 0x10182796) // `ParsingFailed()`.\n revert(0x1c, 0x04)\n }\n\n function chr(p_) -> _c {\n _c := byte(0, mload(p_))\n }\n\n function skipWhitespace(pIn_, end_) -> _pOut {\n for { _pOut := pIn_ } 1 { _pOut := add(_pOut, 1) } {\n if iszero(and(shr(chr(_pOut), 0x100002600), 1)) { leave } // Not in ' \\n\\r\\t'.\n }\n }\n\n function setP(packed_, bitpos_, p_) -> _packed {\n // Perform an out-of-gas revert if `p_` exceeds `_BITMASK_POINTER`.\n returndatacopy(returndatasize(), returndatasize(), gt(p_, _BITMASK_POINTER))\n _packed := or(and(not(shl(bitpos_, _BITMASK_POINTER)), packed_), shl(bitpos_, p_))\n }\n\n function getP(packed_, bitpos_) -> _p {\n _p := and(_BITMASK_POINTER, shr(bitpos_, packed_))\n }\n\n function mallocItem(s_, packed_, pStart_, pCurr_, type_) -> _item {\n _item := mload(0x40)\n // forgefmt: disable-next-item\n packed_ := setP(setP(packed_, _BITPOS_VALUE, sub(pStart_, add(s_, 0x20))),\n _BITPOS_VALUE_LENGTH, sub(pCurr_, pStart_))\n mstore(_item, or(packed_, type_))\n mstore(0x40, add(_item, 0x20)) // Allocate memory.\n }\n\n function parseValue(s_, sibling_, pIn_, end_) -> _item, _pOut {\n let packed_ := setP(mload(0x00), _BITPOS_SIBLING_OR_PARENT, sibling_)\n _pOut := skipWhitespace(pIn_, end_)\n if iszero(lt(_pOut, end_)) { leave }\n for { let c_ := chr(_pOut) } 1 {} {\n // If starts with '\"'.\n if eq(c_, 34) {\n let pStart_ := _pOut\n _pOut := parseStringSub(s_, packed_, _pOut, end_)\n _item := mallocItem(s_, packed_, pStart_, _pOut, TYPE_STRING)\n break\n }\n // If starts with '['.\n if eq(c_, 91) {\n _item, _pOut := parseArray(s_, packed_, _pOut, end_)\n break\n }\n // If starts with '{'.\n if eq(c_, 123) {\n _item, _pOut := parseObject(s_, packed_, _pOut, end_)\n break\n }\n // If starts with any in '0123456789-'.\n if and(shr(c_, shl(45, 0x1ff9)), 1) {\n _item, _pOut := parseNumber(s_, packed_, _pOut, end_)\n break\n }\n if iszero(gt(add(_pOut, 4), end_)) {\n let pStart_ := _pOut\n let w_ := shr(224, mload(_pOut))\n // 'true' in hex format.\n if eq(w_, 0x74727565) {\n _pOut := add(_pOut, 4)\n _item := mallocItem(s_, packed_, pStart_, _pOut, TYPE_BOOLEAN)\n break\n }\n // 'null' in hex format.\n if eq(w_, 0x6e756c6c) {\n _pOut := add(_pOut, 4)\n _item := mallocItem(s_, packed_, pStart_, _pOut, TYPE_NULL)\n break\n }\n }\n if iszero(gt(add(_pOut, 5), end_)) {\n let pStart_ := _pOut\n let w_ := shr(216, mload(_pOut))\n // 'false' in hex format.\n if eq(w_, 0x66616c7365) {\n _pOut := add(_pOut, 5)\n _item := mallocItem(s_, packed_, pStart_, _pOut, TYPE_BOOLEAN)\n break\n }\n }\n fail()\n break\n }\n _pOut := skipWhitespace(_pOut, end_)\n }\n\n function parseArray(s_, packed_, pIn_, end_) -> _item, _pOut {\n let j_ := 0\n for { _pOut := add(pIn_, 1) } 1 { _pOut := add(_pOut, 1) } {\n if iszero(lt(_pOut, end_)) { fail() }\n if iszero(_item) {\n _pOut := skipWhitespace(_pOut, end_)\n if eq(chr(_pOut), 93) { break } // ']'.\n }\n _item, _pOut := parseValue(s_, _item, _pOut, end_)\n if _item {\n // forgefmt: disable-next-item\n mstore(_item, setP(or(_PARENT_IS_ARRAY, mload(_item)),\n _BITPOS_KEY, j_))\n j_ := add(j_, 1)\n let c_ := chr(_pOut)\n if eq(c_, 93) { break } // ']'.\n if eq(c_, 44) { continue } // ','.\n }\n _pOut := end_\n }\n _pOut := add(_pOut, 1)\n packed_ := setP(packed_, _BITPOS_CHILD, _item)\n _item := mallocItem(s_, packed_, pIn_, _pOut, TYPE_ARRAY)\n }\n\n function parseObject(s_, packed_, pIn_, end_) -> _item, _pOut {\n for { _pOut := add(pIn_, 1) } 1 { _pOut := add(_pOut, 1) } {\n if iszero(lt(_pOut, end_)) { fail() }\n if iszero(_item) {\n _pOut := skipWhitespace(_pOut, end_)\n if eq(chr(_pOut), 125) { break } // '}'.\n }\n _pOut := skipWhitespace(_pOut, end_)\n let pKeyStart_ := _pOut\n let pKeyEnd_ := parseStringSub(s_, _item, _pOut, end_)\n _pOut := skipWhitespace(pKeyEnd_, end_)\n // If ':'.\n if eq(chr(_pOut), 58) {\n _item, _pOut := parseValue(s_, _item, add(_pOut, 1), end_)\n if _item {\n // forgefmt: disable-next-item\n mstore(_item, setP(setP(or(_PARENT_IS_OBJECT, mload(_item)),\n _BITPOS_KEY_LENGTH, sub(pKeyEnd_, pKeyStart_)),\n _BITPOS_KEY, sub(pKeyStart_, add(s_, 0x20))))\n let c_ := chr(_pOut)\n if eq(c_, 125) { break } // '}'.\n if eq(c_, 44) { continue } // ','.\n }\n }\n _pOut := end_\n }\n _pOut := add(_pOut, 1)\n packed_ := setP(packed_, _BITPOS_CHILD, _item)\n _item := mallocItem(s_, packed_, pIn_, _pOut, TYPE_OBJECT)\n }\n\n function checkStringU(p_, o_) {\n // If not in '0123456789abcdefABCDEF', revert.\n if iszero(and(shr(sub(chr(add(p_, o_)), 48), 0x7e0000007e03ff), 1)) { fail() }\n if iszero(eq(o_, 5)) { checkStringU(p_, add(o_, 1)) }\n }\n\n function parseStringSub(s_, packed_, pIn_, end_) -> _pOut {\n if iszero(lt(pIn_, end_)) { fail() }\n for { _pOut := add(pIn_, 1) } 1 {} {\n let c_ := chr(_pOut)\n if eq(c_, 34) { break } // '\"'.\n // Not '\\'.\n if iszero(eq(c_, 92)) {\n _pOut := add(_pOut, 1)\n continue\n }\n c_ := chr(add(_pOut, 1))\n // '\"', '\\', '//', 'b', 'f', 'n', 'r', 't'.\n if and(shr(sub(c_, 34), 0x510110400000000002001), 1) {\n _pOut := add(_pOut, 2)\n continue\n }\n // 'u'.\n if eq(c_, 117) {\n checkStringU(_pOut, 2)\n _pOut := add(_pOut, 6)\n continue\n }\n _pOut := end_\n break\n }\n if iszero(lt(_pOut, end_)) { fail() }\n _pOut := add(_pOut, 1)\n }\n\n function skip0To9s(pIn_, end_, atLeastOne_) -> _pOut {\n for { _pOut := pIn_ } 1 { _pOut := add(_pOut, 1) } {\n if iszero(lt(sub(chr(_pOut), 48), 10)) { break } // Not '0'..'9'.\n }\n if and(atLeastOne_, eq(pIn_, _pOut)) { fail() }\n }\n\n function parseNumber(s_, packed_, pIn_, end_) -> _item, _pOut {\n _pOut := pIn_\n if eq(chr(_pOut), 45) { _pOut := add(_pOut, 1) } // '-'.\n if iszero(lt(sub(chr(_pOut), 48), 10)) { fail() } // Not '0'..'9'.\n let c_ := chr(_pOut)\n _pOut := add(_pOut, 1)\n if iszero(eq(c_, 48)) { _pOut := skip0To9s(_pOut, end_, 0) } // Not '0'.\n if eq(chr(_pOut), 46) { _pOut := skip0To9s(add(_pOut, 1), end_, 1) } // '.'.\n let t_ := mload(_pOut)\n // 'E', 'e'.\n if eq(or(0x20, byte(0, t_)), 101) {\n // forgefmt: disable-next-item\n _pOut := skip0To9s(add(byte(sub(byte(1, t_), 14), 0x010001), // '+', '-'.\n add(_pOut, 1)), end_, 1)\n }\n _item := mallocItem(s_, packed_, pIn_, _pOut, TYPE_NUMBER)\n }\n\n function copyStr(s_, offset_, len_) -> _sCopy {\n _sCopy := mload(0x40)\n s_ := add(s_, offset_)\n let w_ := not(0x1f)\n for { let i_ := and(add(len_, 0x1f), w_) } 1 {} {\n mstore(add(_sCopy, i_), mload(add(s_, i_)))\n i_ := add(i_, w_) // `sub(i_, 0x20)`.\n if iszero(i_) { break }\n }\n mstore(_sCopy, len_) // Copy the length.\n mstore(add(add(_sCopy, 0x20), len_), 0) // Zeroize the last slot.\n mstore(0x40, add(add(_sCopy, 0x40), len_)) // Allocate memory.\n }\n\n function value(item_) -> _value {\n let packed_ := mload(item_)\n _value := getP(packed_, _BITPOS_VALUE) // The offset in the string.\n if iszero(and(_VALUE_INITED, packed_)) {\n let s_ := getP(packed_, _BITPOS_STRING)\n _value := copyStr(s_, _value, getP(packed_, _BITPOS_VALUE_LENGTH))\n packed_ := setP(packed_, _BITPOS_VALUE, _value)\n mstore(s_, or(_VALUE_INITED, packed_))\n }\n }\n\n function children(item_) -> _arr {\n _arr := 0x60 // Initialize to the zero pointer.\n let packed_ := mload(item_)\n for {} iszero(gt(and(_BITMASK_TYPE, packed_), TYPE_OBJECT)) {} {\n if or(iszero(packed_), iszero(item_)) { break }\n if and(packed_, _CHILDREN_INITED) {\n _arr := getP(packed_, _BITPOS_CHILD)\n break\n }\n _arr := mload(0x40)\n let o_ := add(_arr, 0x20)\n for { let h_ := getP(packed_, _BITPOS_CHILD) } h_ {} {\n mstore(o_, h_)\n let q_ := mload(h_)\n let y_ := getP(q_, _BITPOS_SIBLING_OR_PARENT)\n mstore(h_, setP(q_, _BITPOS_SIBLING_OR_PARENT, item_))\n h_ := y_\n o_ := add(o_, 0x20)\n }\n let w_ := not(0x1f)\n let n_ := add(w_, sub(o_, _arr))\n mstore(_arr, shr(5, n_))\n mstore(0x40, o_) // Allocate memory.\n packed_ := setP(packed_, _BITPOS_CHILD, _arr)\n mstore(item_, or(_CHILDREN_INITED, packed_))\n // Reverse the array.\n if iszero(lt(n_, 0x40)) {\n let lo_ := add(_arr, 0x20)\n let hi_ := add(_arr, n_)\n for {} 1 {} {\n let temp_ := mload(lo_)\n mstore(lo_, mload(hi_))\n mstore(hi_, temp_)\n hi_ := add(hi_, w_)\n lo_ := add(lo_, 0x20)\n if iszero(lt(lo_, hi_)) { break }\n }\n }\n break\n }\n }\n\n function getStr(item_, bitpos_, bitposLength_, bitmaskInited_) -> _result {\n _result := 0x60 // Initialize to the zero pointer.\n let packed_ := mload(item_)\n if or(iszero(item_), iszero(packed_)) { leave }\n _result := getP(packed_, bitpos_)\n if iszero(and(bitmaskInited_, packed_)) {\n let s_ := getP(packed_, _BITPOS_STRING)\n _result := copyStr(s_, _result, getP(packed_, bitposLength_))\n mstore(item_, or(bitmaskInited_, setP(packed_, bitpos_, _result)))\n }\n }\n\n switch mode\n // Get value.\n case 0 { result := getStr(input, _BITPOS_VALUE, _BITPOS_VALUE_LENGTH, _VALUE_INITED) }\n // Get key.\n case 1 { result := getStr(input, _BITPOS_KEY, _BITPOS_KEY_LENGTH, _KEY_INITED) }\n // Get children.\n case 3 { result := children(input) }\n // Parse.\n default {\n let p := add(input, 0x20)\n let e := add(p, mload(input))\n if iszero(eq(p, e)) {\n let c := chr(e)\n mstore8(e, 34) // Place a '\"' at the end to speed up parsing.\n // The `34 << 248` makes `mallocItem` preserve '\"' at the end.\n mstore(0x00, setP(shl(248, 34), _BITPOS_STRING, input))\n result, p := parseValue(input, 0, p, e)\n mstore8(e, c) // Restore the original char at the end.\n }\n if or(lt(p, e), iszero(result)) { fail() }\n }\n }\n }\n\n /// @dev Casts the input to a bytes32.\n function _toInput(string memory input) private pure returns (bytes32 result) {\n /// @solidity memory-safe-assembly\n assembly {\n result := input\n }\n }\n\n /// @dev Casts the input to a bytes32.\n function _toInput(Item memory input) private pure returns (bytes32 result) {\n /// @solidity memory-safe-assembly\n assembly {\n result := input\n }\n }\n}\n"},"src/utils/DateTimeUtils.sol":{"content":"// SPDX-License-Identifier: GPL-3.0\npragma solidity ^0.8.0;\n\nimport {DateTimeLib} from \"solady/utils/DateTimeLib.sol\";\nimport {LibString} from \"solady/utils/LibString.sol\";\n\nlibrary DateTimeUtils {\n using LibString for string;\n\n /*\n * @dev Convert a DER-encoded time to a unix timestamp\n * @param x509Time The DER-encoded time\n * @return The unix timestamp\n */\n function fromDERToTimestamp(bytes memory x509Time) internal pure returns (uint256) {\n uint16 yrs;\n uint8 mnths;\n uint8 dys;\n uint8 hrs;\n uint8 mins;\n uint8 secs;\n uint8 offset;\n\n if (x509Time.length == 13) {\n if (uint8(x509Time[0]) - 48 < 5) yrs += 2000;\n else yrs += 1900;\n } else {\n yrs += (uint8(x509Time[0]) - 48) * 1000 + (uint8(x509Time[1]) - 48) * 100;\n offset = 2;\n }\n yrs += (uint8(x509Time[offset + 0]) - 48) * 10 + uint8(x509Time[offset + 1]) - 48;\n mnths = (uint8(x509Time[offset + 2]) - 48) * 10 + uint8(x509Time[offset + 3]) - 48;\n dys += (uint8(x509Time[offset + 4]) - 48) * 10 + uint8(x509Time[offset + 5]) - 48;\n hrs += (uint8(x509Time[offset + 6]) - 48) * 10 + uint8(x509Time[offset + 7]) - 48;\n mins += (uint8(x509Time[offset + 8]) - 48) * 10 + uint8(x509Time[offset + 9]) - 48;\n secs += (uint8(x509Time[offset + 10]) - 48) * 10 + uint8(x509Time[offset + 11]) - 48;\n\n return DateTimeLib.dateTimeToTimestamp(yrs, mnths, dys, hrs, mins, secs);\n }\n\n /// @dev iso follows pattern: \"YYYY-MM-DDTHH:mm:ssZ\"\n function fromISOToTimestamp(string memory iso) internal pure returns (uint256) {\n require(bytes(iso).length == 20, \"invalid iso string length\");\n uint256 y = stringToUint(iso.slice(0, 4));\n uint256 m = stringToUint(iso.slice(5, 7));\n uint256 d = stringToUint(iso.slice(8, 10));\n uint256 h = stringToUint(iso.slice(11, 13));\n uint256 min = stringToUint(iso.slice(14, 16));\n uint256 s = stringToUint(iso.slice(17, 19));\n\n return DateTimeLib.dateTimeToTimestamp(y, m, d, h, min, s);\n }\n\n // https://ethereum.stackexchange.com/questions/10932/how-to-convert-string-to-int\n function stringToUint(string memory s) private pure returns (uint256 result) {\n bytes memory b = bytes(s);\n result = 0;\n for (uint256 i = 0; i < b.length; i++) {\n uint256 c = uint256(uint8(b[i]));\n if (c >= 48 && c <= 57) {\n result = result * 10 + (c - 48);\n }\n }\n }\n}\n"},"src/utils/Asn1Decode.sol":{"content":"// SPDX-License-Identifier: MIT\n// Original source: https://github.com/JonahGroendal/asn1-decode\npragma solidity ^0.8.0;\n\n// Inspired by PufferFinance/rave - Apache-2.0 license\n// https://github.com/JonahGroendal/asn1-decode/blob/5c2d1469fc678513753786acb441e597969192ec/contracts/Asn1Decode.sol\n\nimport \"./BytesUtils.sol\";\n\nlibrary NodePtr {\n // Unpack first byte index\n function ixs(uint256 self) internal pure returns (uint256) {\n return uint80(self);\n }\n // Unpack first content byte index\n\n function ixf(uint256 self) internal pure returns (uint256) {\n return uint80(self >> 80);\n }\n // Unpack last content byte index\n\n function ixl(uint256 self) internal pure returns (uint256) {\n return uint80(self >> 160);\n }\n // Pack 3 uint80s into a uint256\n\n function getPtr(uint256 _ixs, uint256 _ixf, uint256 _ixl) internal pure returns (uint256) {\n _ixs |= _ixf << 80;\n _ixs |= _ixl << 160;\n return _ixs;\n }\n}\n\nlibrary Asn1Decode {\n using NodePtr for uint256;\n using BytesUtils for bytes;\n\n /*\n * @dev Get the root node. First step in traversing an ASN1 structure\n * @param der The DER-encoded ASN1 structure\n * @return A pointer to the outermost node\n */\n function root(bytes memory der) internal pure returns (uint256) {\n return readNodeLength(der, 0);\n }\n\n /*\n * @dev Get the root node of an ASN1 structure that's within a bit string value\n * @param der The DER-encoded ASN1 structure\n * @return A pointer to the outermost node\n */\n function rootOfBitStringAt(bytes memory der, uint256 ptr) internal pure returns (uint256) {\n require(der[ptr.ixs()] == 0x03, \"Not type BIT STRING\");\n return readNodeLength(der, ptr.ixf() + 1);\n }\n\n /*\n * @dev Get the root node of an ASN1 structure that's within an octet string value\n * @param der The DER-encoded ASN1 structure\n * @return A pointer to the outermost node\n */\n function rootOfOctetStringAt(bytes memory der, uint256 ptr) internal pure returns (uint256) {\n require(der[ptr.ixs()] == 0x04, \"Not type OCTET STRING\");\n return readNodeLength(der, ptr.ixf());\n }\n\n /*\n * @dev Get the next sibling node\n * @param der The DER-encoded ASN1 structure\n * @param ptr Points to the indices of the current node\n * @return A pointer to the next sibling node\n */\n function nextSiblingOf(bytes memory der, uint256 ptr) internal pure returns (uint256) {\n return readNodeLength(der, ptr.ixl() + 1);\n }\n\n /*\n * @dev Get the first child node of the current node\n * @param der The DER-encoded ASN1 structure\n * @param ptr Points to the indices of the current node\n * @return A pointer to the first child node\n */\n function firstChildOf(bytes memory der, uint256 ptr) internal pure returns (uint256) {\n require(der[ptr.ixs()] & 0x20 == 0x20, \"Not a constructed type\");\n return readNodeLength(der, ptr.ixf());\n }\n\n /*\n * @dev Use for looping through children of a node (either i or j).\n * @param i Pointer to an ASN1 node\n * @param j Pointer to another ASN1 node of the same ASN1 structure\n * @return True iff j is child of i or i is child of j.\n */\n function isChildOf(uint256 i, uint256 j) internal pure returns (bool) {\n return (((i.ixf() <= j.ixs()) && (j.ixl() <= i.ixl())) || ((j.ixf() <= i.ixs()) && (i.ixl() <= j.ixl())));\n }\n\n /*\n * @dev Extract value of node from DER-encoded structure\n * @param der The der-encoded ASN1 structure\n * @param ptr Points to the indices of the current node\n * @return Value bytes of node\n */\n function bytesAt(bytes memory der, uint256 ptr) internal pure returns (bytes memory) {\n return der.substring(ptr.ixf(), ptr.ixl() + 1 - ptr.ixf());\n }\n\n /*\n * @dev Extract entire node from DER-encoded structure\n * @param der The DER-encoded ASN1 structure\n * @param ptr Points to the indices of the current node\n * @return All bytes of node\n */\n function allBytesAt(bytes memory der, uint256 ptr) internal pure returns (bytes memory) {\n return der.substring(ptr.ixs(), ptr.ixl() + 1 - ptr.ixs());\n }\n\n /*\n * @dev Extract value of node from DER-encoded structure\n * @param der The DER-encoded ASN1 structure\n * @param ptr Points to the indices of the current node\n * @return Value bytes of node as bytes32\n */\n function bytes32At(bytes memory der, uint256 ptr) internal pure returns (bytes32) {\n return der.readBytesN(ptr.ixf(), ptr.ixl() + 1 - ptr.ixf());\n }\n\n /*\n * @dev Extract value of node from DER-encoded structure\n * @param der The der-encoded ASN1 structure\n * @param ptr Points to the indices of the current node\n * @return Uint value of node\n */\n function uintAt(bytes memory der, uint256 ptr) internal pure returns (uint256) {\n require(der[ptr.ixs()] == 0x02, \"Not type INTEGER\");\n require(der[ptr.ixf()] & 0x80 == 0, \"Not positive\");\n uint256 len = ptr.ixl() + 1 - ptr.ixf();\n return uint256(der.readBytesN(ptr.ixf(), len) >> (32 - len) * 8);\n }\n\n /*\n * @dev Extract value of a positive integer node from DER-encoded structure\n * @param der The DER-encoded ASN1 structure\n * @param ptr Points to the indices of the current node\n * @return Value bytes of a positive integer node\n */\n function uintBytesAt(bytes memory der, uint256 ptr) internal pure returns (bytes memory) {\n require(der[ptr.ixs()] == 0x02, \"Not type INTEGER\");\n require(der[ptr.ixf()] & 0x80 == 0, \"Not positive\");\n uint256 valueLength = ptr.ixl() + 1 - ptr.ixf();\n if (der[ptr.ixf()] == 0) {\n return der.substring(ptr.ixf() + 1, valueLength - 1);\n } else {\n return der.substring(ptr.ixf(), valueLength);\n }\n }\n\n function keccakOfBytesAt(bytes memory der, uint256 ptr) internal pure returns (bytes32) {\n return der.keccak(ptr.ixf(), ptr.ixl() + 1 - ptr.ixf());\n }\n\n function keccakOfAllBytesAt(bytes memory der, uint256 ptr) internal pure returns (bytes32) {\n return der.keccak(ptr.ixs(), ptr.ixl() + 1 - ptr.ixs());\n }\n\n /*\n * @dev Extract value of bitstring node from DER-encoded structure\n * @param der The DER-encoded ASN1 structure\n * @param ptr Points to the indices of the current node\n * @return Value of bitstring converted to bytes\n */\n function bitstringAt(bytes memory der, uint256 ptr) internal pure returns (bytes memory) {\n require(der[ptr.ixs()] == 0x03, \"Not type BIT STRING\");\n // Only 00 padded bitstr can be converted to bytestr!\n require(der[ptr.ixf()] == 0x00);\n uint256 valueLength = ptr.ixl() + 1 - ptr.ixf();\n return der.substring(ptr.ixf() + 1, valueLength - 1);\n }\n\n function readNodeLength(bytes memory der, uint256 ix) private pure returns (uint256) {\n uint256 length;\n uint80 ixFirstContentByte;\n uint80 ixLastContentByte;\n if ((der[ix + 1] & 0x80) == 0) {\n length = uint8(der[ix + 1]);\n ixFirstContentByte = uint80(ix + 2);\n ixLastContentByte = uint80(ixFirstContentByte + length - 1);\n } else {\n uint8 lengthbytesLength = uint8(der[ix + 1] & 0x7F);\n if (lengthbytesLength == 1) {\n length = der.readUint8(ix + 2);\n } else if (lengthbytesLength == 2) {\n length = der.readUint16(ix + 2);\n } else {\n length = uint256(der.readBytesN(ix + 2, lengthbytesLength) >> (32 - lengthbytesLength) * 8);\n }\n ixFirstContentByte = uint80(ix + 2 + lengthbytesLength);\n ixLastContentByte = uint80(ixFirstContentByte + length - 1);\n }\n return NodePtr.getPtr(ix, ixFirstContentByte, ixLastContentByte);\n }\n}\n"},"lib/solady/src/utils/DateTimeLib.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\n/// @notice Library for date time operations.\n/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/DateTimeLib.sol)\n///\n/// Conventions:\n/// --------------------------------------------------------------------+\n/// Unit | Range | Notes |\n/// --------------------------------------------------------------------|\n/// timestamp | 0..0x1e18549868c76ff | Unix timestamp. |\n/// epochDay | 0..0x16d3e098039 | Days since 1970-01-01. |\n/// year | 1970..0xffffffff | Gregorian calendar year. |\n/// month | 1..12 | Gregorian calendar month. |\n/// day | 1..31 | Gregorian calendar day of month. |\n/// weekday | 1..7 | The day of the week (1-indexed). |\n/// --------------------------------------------------------------------+\n/// All timestamps of days are rounded down to 00:00:00 UTC.\nlibrary DateTimeLib {\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* CONSTANTS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n // Weekdays are 1-indexed for a traditional rustic feel.\n\n // \"And on the seventh day God finished his work that he had done,\n // and he rested on the seventh day from all his work that he had done.\"\n // -- Genesis 2:2\n\n uint256 internal constant MON = 1;\n uint256 internal constant TUE = 2;\n uint256 internal constant WED = 3;\n uint256 internal constant THU = 4;\n uint256 internal constant FRI = 5;\n uint256 internal constant SAT = 6;\n uint256 internal constant SUN = 7;\n\n // Months and days of months are 1-indexed for ease of use.\n\n uint256 internal constant JAN = 1;\n uint256 internal constant FEB = 2;\n uint256 internal constant MAR = 3;\n uint256 internal constant APR = 4;\n uint256 internal constant MAY = 5;\n uint256 internal constant JUN = 6;\n uint256 internal constant JUL = 7;\n uint256 internal constant AUG = 8;\n uint256 internal constant SEP = 9;\n uint256 internal constant OCT = 10;\n uint256 internal constant NOV = 11;\n uint256 internal constant DEC = 12;\n\n // These limits are large enough for most practical purposes.\n // Inputs that exceed these limits result in undefined behavior.\n\n uint256 internal constant MAX_SUPPORTED_YEAR = 0xffffffff;\n uint256 internal constant MAX_SUPPORTED_EPOCH_DAY = 0x16d3e098039;\n uint256 internal constant MAX_SUPPORTED_TIMESTAMP = 0x1e18549868c76ff;\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* DATE TIME OPERATIONS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev Returns the number of days since 1970-01-01 from (`year`,`month`,`day`).\n /// See: https://howardhinnant.github.io/date_algorithms.html\n /// Note: Inputs outside the supported ranges result in undefined behavior.\n /// Use {isSupportedDate} to check if the inputs are supported.\n function dateToEpochDay(uint256 year, uint256 month, uint256 day)\n internal\n pure\n returns (uint256 epochDay)\n {\n /// @solidity memory-safe-assembly\n assembly {\n year := sub(year, lt(month, 3))\n let doy := add(shr(11, add(mul(62719, mod(add(month, 9), 12)), 769)), day)\n let yoe := mod(year, 400)\n let doe := sub(add(add(mul(yoe, 365), shr(2, yoe)), doy), div(yoe, 100))\n epochDay := sub(add(mul(div(year, 400), 146097), doe), 719469)\n }\n }\n\n /// @dev Returns (`year`,`month`,`day`) from the number of days since 1970-01-01.\n /// Note: Inputs outside the supported ranges result in undefined behavior.\n /// Use {isSupportedDays} to check if the inputs is supported.\n function epochDayToDate(uint256 epochDay)\n internal\n pure\n returns (uint256 year, uint256 month, uint256 day)\n {\n /// @solidity memory-safe-assembly\n assembly {\n epochDay := add(epochDay, 719468)\n let doe := mod(epochDay, 146097)\n let yoe :=\n div(sub(sub(add(doe, div(doe, 36524)), div(doe, 1460)), eq(doe, 146096)), 365)\n let doy := sub(doe, sub(add(mul(365, yoe), shr(2, yoe)), div(yoe, 100)))\n let mp := div(add(mul(5, doy), 2), 153)\n day := add(sub(doy, shr(11, add(mul(mp, 62719), 769))), 1)\n month := byte(mp, shl(160, 0x030405060708090a0b0c0102))\n year := add(add(yoe, mul(div(epochDay, 146097), 400)), lt(month, 3))\n }\n }\n\n /// @dev Returns the unix timestamp from (`year`,`month`,`day`).\n /// Note: Inputs outside the supported ranges result in undefined behavior.\n /// Use {isSupportedDate} to check if the inputs are supported.\n function dateToTimestamp(uint256 year, uint256 month, uint256 day)\n internal\n pure\n returns (uint256 result)\n {\n unchecked {\n result = dateToEpochDay(year, month, day) * 86400;\n }\n }\n\n /// @dev Returns (`year`,`month`,`day`) from the given unix timestamp.\n /// Note: Inputs outside the supported ranges result in undefined behavior.\n /// Use {isSupportedTimestamp} to check if the inputs are supported.\n function timestampToDate(uint256 timestamp)\n internal\n pure\n returns (uint256 year, uint256 month, uint256 day)\n {\n (year, month, day) = epochDayToDate(timestamp / 86400);\n }\n\n /// @dev Returns the unix timestamp from\n /// (`year`,`month`,`day`,`hour`,`minute`,`second`).\n /// Note: Inputs outside the supported ranges result in undefined behavior.\n /// Use {isSupportedDateTime} to check if the inputs are supported.\n function dateTimeToTimestamp(\n uint256 year,\n uint256 month,\n uint256 day,\n uint256 hour,\n uint256 minute,\n uint256 second\n ) internal pure returns (uint256 result) {\n unchecked {\n result = dateToEpochDay(year, month, day) * 86400 + hour * 3600 + minute * 60 + second;\n }\n }\n\n /// @dev Returns (`year`,`month`,`day`,`hour`,`minute`,`second`)\n /// from the given unix timestamp.\n /// Note: Inputs outside the supported ranges result in undefined behavior.\n /// Use {isSupportedTimestamp} to check if the inputs are supported.\n function timestampToDateTime(uint256 timestamp)\n internal\n pure\n returns (\n uint256 year,\n uint256 month,\n uint256 day,\n uint256 hour,\n uint256 minute,\n uint256 second\n )\n {\n unchecked {\n (year, month, day) = epochDayToDate(timestamp / 86400);\n uint256 secs = timestamp % 86400;\n hour = secs / 3600;\n secs = secs % 3600;\n minute = secs / 60;\n second = secs % 60;\n }\n }\n\n /// @dev Returns if the `year` is leap.\n function isLeapYear(uint256 year) internal pure returns (bool leap) {\n /// @solidity memory-safe-assembly\n assembly {\n leap := iszero(and(add(mul(iszero(mod(year, 25)), 12), 3), year))\n }\n }\n\n /// @dev Returns number of days in given `month` of `year`.\n function daysInMonth(uint256 year, uint256 month) internal pure returns (uint256 result) {\n bool flag = isLeapYear(year);\n /// @solidity memory-safe-assembly\n assembly {\n // `daysInMonths = [31,28,31,30,31,30,31,31,30,31,30,31]`.\n // `result = daysInMonths[month - 1] + isLeapYear(year)`.\n result :=\n add(byte(month, shl(152, 0x1F1C1F1E1F1E1F1F1E1F1E1F)), and(eq(month, 2), flag))\n }\n }\n\n /// @dev Returns the weekday from the unix timestamp.\n /// Monday: 1, Tuesday: 2, ....., Sunday: 7.\n function weekday(uint256 timestamp) internal pure returns (uint256 result) {\n unchecked {\n result = ((timestamp / 86400 + 3) % 7) + 1;\n }\n }\n\n /// @dev Returns if (`year`,`month`,`day`) is a supported date.\n /// - `1970 <= year <= MAX_SUPPORTED_YEAR`.\n /// - `1 <= month <= 12`.\n /// - `1 <= day <= daysInMonth(year, month)`.\n function isSupportedDate(uint256 year, uint256 month, uint256 day)\n internal\n pure\n returns (bool result)\n {\n uint256 md = daysInMonth(year, month);\n /// @solidity memory-safe-assembly\n assembly {\n let w := not(0)\n result :=\n and(\n lt(sub(year, 1970), sub(MAX_SUPPORTED_YEAR, 1969)),\n and(lt(add(month, w), 12), lt(add(day, w), md))\n )\n }\n }\n\n /// @dev Returns if (`year`,`month`,`day`,`hour`,`minute`,`second`) is a supported date time.\n /// - `1970 <= year <= MAX_SUPPORTED_YEAR`.\n /// - `1 <= month <= 12`.\n /// - `1 <= day <= daysInMonth(year, month)`.\n /// - `hour < 24`.\n /// - `minute < 60`.\n /// - `second < 60`.\n function isSupportedDateTime(\n uint256 year,\n uint256 month,\n uint256 day,\n uint256 hour,\n uint256 minute,\n uint256 second\n ) internal pure returns (bool result) {\n if (isSupportedDate(year, month, day)) {\n /// @solidity memory-safe-assembly\n assembly {\n result := and(lt(hour, 24), and(lt(minute, 60), lt(second, 60)))\n }\n }\n }\n\n /// @dev Returns if `epochDay` is a supported unix epoch day.\n function isSupportedEpochDay(uint256 epochDay) internal pure returns (bool result) {\n unchecked {\n result = epochDay < MAX_SUPPORTED_EPOCH_DAY + 1;\n }\n }\n\n /// @dev Returns if `timestamp` is a supported unix timestamp.\n function isSupportedTimestamp(uint256 timestamp) internal pure returns (bool result) {\n unchecked {\n result = timestamp < MAX_SUPPORTED_TIMESTAMP + 1;\n }\n }\n\n /// @dev Returns the unix timestamp of the given `n`th weekday `wd`, in `month` of `year`.\n /// Example: 3rd Friday of Feb 2022 is `nthWeekdayInMonthOfYearTimestamp(2022, 2, 3, 5)`\n /// Note: `n` is 1-indexed for traditional consistency.\n /// Invalid weekdays (i.e. `wd == 0 || wd > 7`) result in undefined behavior.\n function nthWeekdayInMonthOfYearTimestamp(uint256 year, uint256 month, uint256 n, uint256 wd)\n internal\n pure\n returns (uint256 result)\n {\n uint256 d = dateToEpochDay(year, month, 1);\n uint256 md = daysInMonth(year, month);\n /// @solidity memory-safe-assembly\n assembly {\n let diff := sub(wd, add(mod(add(d, 3), 7), 1))\n let date := add(mul(sub(n, 1), 7), add(mul(gt(diff, 6), 7), diff))\n result := mul(mul(86400, add(date, d)), and(lt(date, md), iszero(iszero(n))))\n }\n }\n\n /// @dev Returns the unix timestamp of the most recent Monday.\n function mondayTimestamp(uint256 timestamp) internal pure returns (uint256 result) {\n uint256 t = timestamp;\n /// @solidity memory-safe-assembly\n assembly {\n let day := div(t, 86400)\n result := mul(mul(sub(day, mod(add(day, 3), 7)), 86400), gt(t, 345599))\n }\n }\n\n /// @dev Returns whether the unix timestamp falls on a Saturday or Sunday.\n /// To check whether it is a week day, just take the negation of the result.\n function isWeekEnd(uint256 timestamp) internal pure returns (bool result) {\n result = weekday(timestamp) > FRI;\n }\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* DATE TIME ARITHMETIC OPERATIONS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev Adds `numYears` to the unix timestamp, and returns the result.\n /// Note: The result will share the same Gregorian calendar month,\n /// but different Gregorian calendar years for non-zero `numYears`.\n /// If the Gregorian calendar month of the result has less days\n /// than the Gregorian calendar month day of the `timestamp`,\n /// the result's month day will be the maximum possible value for the month.\n /// (e.g. from 29th Feb to 28th Feb)\n function addYears(uint256 timestamp, uint256 numYears) internal pure returns (uint256 result) {\n (uint256 year, uint256 month, uint256 day) = epochDayToDate(timestamp / 86400);\n result = _offsetted(year + numYears, month, day, timestamp);\n }\n\n /// @dev Adds `numMonths` to the unix timestamp, and returns the result.\n /// Note: If the Gregorian calendar month of the result has less days\n /// than the Gregorian calendar month day of the `timestamp`,\n /// the result's month day will be the maximum possible value for the month.\n /// (e.g. from 29th Feb to 28th Feb)\n function addMonths(uint256 timestamp, uint256 numMonths)\n internal\n pure\n returns (uint256 result)\n {\n (uint256 year, uint256 month, uint256 day) = epochDayToDate(timestamp / 86400);\n month = _sub(month + numMonths, 1);\n result = _offsetted(year + month / 12, _add(month % 12, 1), day, timestamp);\n }\n\n /// @dev Adds `numDays` to the unix timestamp, and returns the result.\n function addDays(uint256 timestamp, uint256 numDays) internal pure returns (uint256 result) {\n result = timestamp + numDays * 86400;\n }\n\n /// @dev Adds `numHours` to the unix timestamp, and returns the result.\n function addHours(uint256 timestamp, uint256 numHours) internal pure returns (uint256 result) {\n result = timestamp + numHours * 3600;\n }\n\n /// @dev Adds `numMinutes` to the unix timestamp, and returns the result.\n function addMinutes(uint256 timestamp, uint256 numMinutes)\n internal\n pure\n returns (uint256 result)\n {\n result = timestamp + numMinutes * 60;\n }\n\n /// @dev Adds `numSeconds` to the unix timestamp, and returns the result.\n function addSeconds(uint256 timestamp, uint256 numSeconds)\n internal\n pure\n returns (uint256 result)\n {\n result = timestamp + numSeconds;\n }\n\n /// @dev Subtracts `numYears` from the unix timestamp, and returns the result.\n /// Note: The result will share the same Gregorian calendar month,\n /// but different Gregorian calendar years for non-zero `numYears`.\n /// If the Gregorian calendar month of the result has less days\n /// than the Gregorian calendar month day of the `timestamp`,\n /// the result's month day will be the maximum possible value for the month.\n /// (e.g. from 29th Feb to 28th Feb)\n function subYears(uint256 timestamp, uint256 numYears) internal pure returns (uint256 result) {\n (uint256 year, uint256 month, uint256 day) = epochDayToDate(timestamp / 86400);\n result = _offsetted(year - numYears, month, day, timestamp);\n }\n\n /// @dev Subtracts `numYears` from the unix timestamp, and returns the result.\n /// Note: If the Gregorian calendar month of the result has less days\n /// than the Gregorian calendar month day of the `timestamp`,\n /// the result's month day will be the maximum possible value for the month.\n /// (e.g. from 29th Feb to 28th Feb)\n function subMonths(uint256 timestamp, uint256 numMonths)\n internal\n pure\n returns (uint256 result)\n {\n (uint256 year, uint256 month, uint256 day) = epochDayToDate(timestamp / 86400);\n uint256 yearMonth = _totalMonths(year, month) - _add(numMonths, 1);\n result = _offsetted(yearMonth / 12, _add(yearMonth % 12, 1), day, timestamp);\n }\n\n /// @dev Subtracts `numDays` from the unix timestamp, and returns the result.\n function subDays(uint256 timestamp, uint256 numDays) internal pure returns (uint256 result) {\n result = timestamp - numDays * 86400;\n }\n\n /// @dev Subtracts `numHours` from the unix timestamp, and returns the result.\n function subHours(uint256 timestamp, uint256 numHours) internal pure returns (uint256 result) {\n result = timestamp - numHours * 3600;\n }\n\n /// @dev Subtracts `numMinutes` from the unix timestamp, and returns the result.\n function subMinutes(uint256 timestamp, uint256 numMinutes)\n internal\n pure\n returns (uint256 result)\n {\n result = timestamp - numMinutes * 60;\n }\n\n /// @dev Subtracts `numSeconds` from the unix timestamp, and returns the result.\n function subSeconds(uint256 timestamp, uint256 numSeconds)\n internal\n pure\n returns (uint256 result)\n {\n result = timestamp - numSeconds;\n }\n\n /// @dev Returns the difference in Gregorian calendar years\n /// between `fromTimestamp` and `toTimestamp`.\n /// Note: Even if the true time difference is less than a year,\n /// the difference can be non-zero is the timestamps are\n /// from different Gregorian calendar years\n function diffYears(uint256 fromTimestamp, uint256 toTimestamp)\n internal\n pure\n returns (uint256 result)\n {\n toTimestamp - fromTimestamp;\n (uint256 fromYear,,) = epochDayToDate(fromTimestamp / 86400);\n (uint256 toYear,,) = epochDayToDate(toTimestamp / 86400);\n result = _sub(toYear, fromYear);\n }\n\n /// @dev Returns the difference in Gregorian calendar months\n /// between `fromTimestamp` and `toTimestamp`.\n /// Note: Even if the true time difference is less than a month,\n /// the difference can be non-zero is the timestamps are\n /// from different Gregorian calendar months.\n function diffMonths(uint256 fromTimestamp, uint256 toTimestamp)\n internal\n pure\n returns (uint256 result)\n {\n toTimestamp - fromTimestamp;\n (uint256 fromYear, uint256 fromMonth,) = epochDayToDate(fromTimestamp / 86400);\n (uint256 toYear, uint256 toMonth,) = epochDayToDate(toTimestamp / 86400);\n result = _sub(_totalMonths(toYear, toMonth), _totalMonths(fromYear, fromMonth));\n }\n\n /// @dev Returns the difference in days between `fromTimestamp` and `toTimestamp`.\n function diffDays(uint256 fromTimestamp, uint256 toTimestamp)\n internal\n pure\n returns (uint256 result)\n {\n result = (toTimestamp - fromTimestamp) / 86400;\n }\n\n /// @dev Returns the difference in hours between `fromTimestamp` and `toTimestamp`.\n function diffHours(uint256 fromTimestamp, uint256 toTimestamp)\n internal\n pure\n returns (uint256 result)\n {\n result = (toTimestamp - fromTimestamp) / 3600;\n }\n\n /// @dev Returns the difference in minutes between `fromTimestamp` and `toTimestamp`.\n function diffMinutes(uint256 fromTimestamp, uint256 toTimestamp)\n internal\n pure\n returns (uint256 result)\n {\n result = (toTimestamp - fromTimestamp) / 60;\n }\n\n /// @dev Returns the difference in seconds between `fromTimestamp` and `toTimestamp`.\n function diffSeconds(uint256 fromTimestamp, uint256 toTimestamp)\n internal\n pure\n returns (uint256 result)\n {\n result = toTimestamp - fromTimestamp;\n }\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* PRIVATE HELPERS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev Unchecked arithmetic for computing the total number of months.\n function _totalMonths(uint256 numYears, uint256 numMonths)\n private\n pure\n returns (uint256 total)\n {\n unchecked {\n total = numYears * 12 + numMonths;\n }\n }\n\n /// @dev Unchecked arithmetic for adding two numbers.\n function _add(uint256 a, uint256 b) private pure returns (uint256 c) {\n unchecked {\n c = a + b;\n }\n }\n\n /// @dev Unchecked arithmetic for subtracting two numbers.\n function _sub(uint256 a, uint256 b) private pure returns (uint256 c) {\n unchecked {\n c = a - b;\n }\n }\n\n /// @dev Returns the offsetted timestamp.\n function _offsetted(uint256 year, uint256 month, uint256 day, uint256 timestamp)\n private\n pure\n returns (uint256 result)\n {\n uint256 dm = daysInMonth(year, month);\n if (day >= dm) {\n day = dm;\n }\n result = dateToEpochDay(year, month, day) * 86400 + (timestamp % 86400);\n }\n}\n"}},"settings":{"remappings":["solady/=lib/solady/src/","@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/","ds-test/=lib/forge-std/lib/ds-test/src/","erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/","forge-std/=lib/forge-std/src/","halmos-cheatcodes/=lib/openzeppelin-contracts/lib/halmos-cheatcodes/src/","openzeppelin-contracts/=lib/openzeppelin-contracts/"],"optimizer":{"enabled":true,"runs":999999},"metadata":{"useLiteralContent":false,"bytecodeHash":"ipfs","appendCBOR":true},"outputSelection":{"*":{"*":["abi","evm.bytecode","evm.deployedBytecode","evm.methodIdentifiers","metadata"]}},"evmVersion":"paris","viaIR":true,"libraries":{}}} diff --git a/standard-json-input/tcbhelper.json b/standard-json-input/tcbhelper.json deleted file mode 100644 index 05d743d..0000000 --- a/standard-json-input/tcbhelper.json +++ /dev/null @@ -1 +0,0 @@ -{"language":"Solidity","sources":{"src/helpers/FmspcTcbHelper.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport {JSONParserLib} from \"solady/utils/JSONParserLib.sol\";\nimport {LibString} from \"solady/utils/LibString.sol\";\nimport {DateTimeUtils} from \"../utils/DateTimeUtils.sol\";\n\n// https://github.com/intel/SGXDataCenterAttestationPrimitives/blob/e7604e02331b3377f3766ed3653250e03af72d45/QuoteVerification/QVL/Src/AttestationLibrary/src/CertVerification/X509Constants.h#L64\nuint256 constant TCB_CPUSVN_SIZE = 16;\n\nenum TcbId {\n /// the \"id\" field is absent from TCBInfo V2\n /// which defaults TcbId to SGX\n /// since TDX TCBInfos are only included in V3 or above\n SGX,\n TDX\n}\n\n/**\n * @title Solidity Object representing the TCBInfo JSON\n * @param tcbInfo: tcbInfoJson.tcbInfo string object body\n * @param signature The signature to be passed as bytes array\n */\nstruct TcbInfoJsonObj {\n string tcbInfoStr;\n bytes signature;\n}\n\nstruct TcbInfoBasic {\n /// the name \"tcbType\" can be confusing/misleading\n /// as the tcbType referred here in this struct is the type\n /// of TCB level composition that determines TCB level comparison logic\n /// It is not the same as the \"type\" parameter passed as an argument to the\n /// getTcbInfo() API method described in Section 4.2.3 of the Intel PCCS Design Document\n /// Instead, getTcbInfo() \"type\" argument should be checked against the \"id\" value of this struct\n /// which represents the TEE type for the given TCBInfo\n uint8 tcbType;\n TcbId id;\n uint32 version;\n uint64 issueDate;\n uint64 nextUpdate;\n uint32 evaluationDataNumber;\n bytes6 fmspc;\n bytes2 pceid;\n}\n\nstruct TCBLevelsObj {\n uint16 pcesvn;\n uint8[] sgxComponentCpuSvns;\n uint8[] tdxSvns;\n uint64 tcbDateTimestamp;\n TCBStatus status;\n}\n\nstruct TDXModule {\n bytes mrsigner; // 48 bytes\n bytes8 attributes;\n bytes8 attributesMask;\n}\n\nstruct TDXModuleIdentity {\n string id;\n bytes8 attributes;\n bytes8 attributesMask;\n bytes mrsigner; // 48 bytes\n TDXModuleTCBLevelsObj[] tcbLevels;\n}\n\nstruct TDXModuleTCBLevelsObj {\n uint8 isvsvn;\n uint64 tcbDateTimestamp;\n TCBStatus status;\n}\n\nenum TCBStatus {\n OK,\n TCB_SW_HARDENING_NEEDED,\n TCB_CONFIGURATION_AND_SW_HARDENING_NEEDED,\n TCB_CONFIGURATION_NEEDED,\n TCB_OUT_OF_DATE,\n TCB_OUT_OF_DATE_CONFIGURATION_NEEDED,\n TCB_REVOKED,\n TCB_UNRECOGNIZED\n}\n\n/**\n * @title FMSPC TCB Helper Contract\n * @notice This is a standalone contract that can be used by off-chain applications and smart contracts\n * to parse TCBInfo data\n * @notice The TCBInfo Object itself may vary by their version and type.\n * @notice This contract only provides a simple parser that could only extract basic info about the TCBInfo\n * such as, its version, type, fmspc, issue date and next update.\n * @dev should consider extending this contract to implement parsers that could extract detailed TCBInfo\n * using logic that complies to the specific version and type.\n */\ncontract FmspcTcbHelper {\n using JSONParserLib for JSONParserLib.Item;\n using LibString for string;\n\n error TCBInfo_Invalid();\n error TCB_TDX_Version_Invalid();\n error TCB_TDX_ID_Invalid();\n\n // 544k gas\n function parseTcbString(string calldata tcbInfoStr) external pure returns (TcbInfoBasic memory tcbInfo) {\n JSONParserLib.Item memory root = JSONParserLib.parse(tcbInfoStr);\n JSONParserLib.Item[] memory tcbInfoObj = root.children();\n\n bool tcbTypeFound;\n bool fmspcFound;\n bool versionFound;\n bool issueDateFound;\n bool nextUpdateFound;\n bool pceidFound;\n bool evaluationFound;\n bool idFound;\n bool allFound;\n\n for (uint256 y = 0; y < root.size(); y++) {\n JSONParserLib.Item memory current = tcbInfoObj[y];\n string memory decodedKey = JSONParserLib.decodeString(current.key());\n string memory val = current.value();\n if (decodedKey.eq(\"tcbType\")) {\n tcbInfo.tcbType = uint8(JSONParserLib.parseUint(val));\n tcbTypeFound = true;\n } else if (decodedKey.eq(\"id\")) {\n string memory idStr = JSONParserLib.decodeString(val);\n if (idStr.eq(\"SGX\")) {\n tcbInfo.id = TcbId.SGX;\n } else if (idStr.eq(\"TDX\")) {\n tcbInfo.id = TcbId.TDX;\n } else {\n revert TCBInfo_Invalid();\n }\n idFound = true;\n } else if (decodedKey.eq(\"fmspc\")) {\n tcbInfo.fmspc = bytes6(uint48(JSONParserLib.parseUintFromHex(JSONParserLib.decodeString(val))));\n fmspcFound = true;\n } else if (decodedKey.eq(\"version\")) {\n tcbInfo.version = uint32(JSONParserLib.parseUint(val));\n versionFound = true;\n } else if (decodedKey.eq(\"issueDate\")) {\n tcbInfo.issueDate = uint64(DateTimeUtils.fromISOToTimestamp(JSONParserLib.decodeString(val)));\n issueDateFound = true;\n } else if (decodedKey.eq(\"nextUpdate\")) {\n tcbInfo.nextUpdate = uint64(DateTimeUtils.fromISOToTimestamp(JSONParserLib.decodeString(val)));\n nextUpdateFound = true;\n } else if (decodedKey.eq(\"pceId\")) {\n tcbInfo.pceid = bytes2(uint16(JSONParserLib.parseUintFromHex(JSONParserLib.decodeString(val))));\n pceidFound = true;\n } else if (decodedKey.eq(\"tcbEvaluationDataNumber\")) {\n tcbInfo.evaluationDataNumber = uint32(JSONParserLib.parseUint(val));\n evaluationFound = true;\n }\n if (versionFound) {\n allFound =\n (tcbTypeFound && fmspcFound && issueDateFound && nextUpdateFound && pceidFound && evaluationFound);\n if (tcbInfo.version >= 3) {\n allFound = allFound && idFound;\n }\n if (allFound) {\n break;\n }\n }\n }\n\n if (!allFound) {\n revert TCBInfo_Invalid();\n }\n }\n\n // 1.4M gas\n function parseTcbLevels(string calldata tcbInfoStr)\n external\n pure\n returns (uint256 version, TCBLevelsObj[] memory tcbLevels)\n {\n JSONParserLib.Item memory root = JSONParserLib.parse(tcbInfoStr);\n JSONParserLib.Item[] memory tcbInfoObj = root.children();\n\n bool versionFound;\n bool tcbLevelsFound;\n JSONParserLib.Item[] memory tcbLevelsObj;\n\n for (uint256 i = 0; i < root.size(); i++) {\n JSONParserLib.Item memory current = tcbInfoObj[i];\n string memory decodedKey = JSONParserLib.decodeString(current.key());\n if (decodedKey.eq(\"version\")) {\n version = JSONParserLib.parseUint(current.value());\n versionFound = true;\n }\n if (decodedKey.eq(\"tcbLevels\")) {\n tcbLevelsObj = current.children();\n tcbLevelsFound = true;\n }\n if (versionFound && tcbLevelsFound) {\n break;\n }\n }\n\n if (versionFound && tcbLevelsFound) {\n tcbLevels = _parseTCBLevels(version, tcbLevelsObj);\n } else {\n revert TCBInfo_Invalid();\n }\n }\n\n // 684k gas\n function parseTcbTdxModules(string calldata tcbInfoStr)\n external\n pure\n returns (TDXModule memory module, TDXModuleIdentity[] memory moduleIdentities)\n {\n JSONParserLib.Item memory root = JSONParserLib.parse(tcbInfoStr);\n JSONParserLib.Item[] memory tcbInfoObj = root.children();\n\n bool versionFound;\n bool idFound;\n bool tdxModuleFound;\n bool tdxModuleIdentitiesFound;\n bool allFound;\n\n for (uint256 i = 0; i < root.size(); i++) {\n JSONParserLib.Item memory current = tcbInfoObj[i];\n string memory decodedKey = JSONParserLib.decodeString(current.key());\n if (decodedKey.eq(\"version\")) {\n uint256 version = JSONParserLib.parseUint(current.value());\n if (version < 3) {\n revert TCB_TDX_Version_Invalid();\n }\n versionFound = true;\n }\n if (decodedKey.eq(\"id\")) {\n string memory id = JSONParserLib.decodeString(current.value());\n if (!id.eq(\"TDX\")) {\n revert TCB_TDX_ID_Invalid();\n }\n idFound = true;\n }\n if (decodedKey.eq(\"tdxModule\")) {\n module = _parseTdxModule(current.children());\n tdxModuleFound = true;\n }\n if (decodedKey.eq(\"tdxModuleIdentities\")) {\n moduleIdentities = _parseTdxModuleIdentities(current.children());\n tdxModuleIdentitiesFound = true;\n }\n allFound = versionFound && idFound && tdxModuleFound && tdxModuleIdentitiesFound;\n if (allFound) {\n break;\n }\n }\n\n if (!allFound) {\n revert TCBInfo_Invalid();\n }\n }\n\n /// ====== INTERNAL METHODS BELOW ======\n\n function _parseTCBLevels(uint256 version, JSONParserLib.Item[] memory tcbLevelsObj)\n private\n pure\n returns (TCBLevelsObj[] memory tcbLevels)\n {\n uint256 tcbLevelsSize = tcbLevelsObj.length;\n tcbLevels = new TCBLevelsObj[](tcbLevelsSize);\n\n // iterating through the array\n for (uint256 i = 0; i < tcbLevelsSize; i++) {\n JSONParserLib.Item[] memory tcbObj = tcbLevelsObj[i].children();\n // iterating through individual tcb objects\n for (uint256 j = 0; j < tcbLevelsObj[i].size(); j++) {\n string memory tcbKey = JSONParserLib.decodeString(tcbObj[j].key());\n if (tcbKey.eq(\"tcb\")) {\n string memory tcbStr = tcbObj[j].value();\n JSONParserLib.Item memory tcbParent = JSONParserLib.parse(tcbStr);\n JSONParserLib.Item[] memory tcbComponents = tcbParent.children();\n if (version == 2) {\n (tcbLevels[i].sgxComponentCpuSvns, tcbLevels[i].pcesvn) = _parseV2Tcb(tcbComponents);\n } else if (version == 3) {\n (tcbLevels[i].sgxComponentCpuSvns, tcbLevels[i].tdxSvns, tcbLevels[i].pcesvn) =\n _parseV3Tcb(tcbComponents);\n } else {\n revert TCBInfo_Invalid();\n }\n } else if (tcbKey.eq(\"tcbDate\")) {\n tcbLevels[i].tcbDateTimestamp =\n uint64(DateTimeUtils.fromISOToTimestamp(JSONParserLib.decodeString(tcbObj[j].value())));\n } else if (tcbKey.eq(\"tcbStatus\")) {\n tcbLevels[i].status = _getTcbStatus(JSONParserLib.decodeString(tcbObj[j].value()));\n }\n }\n }\n }\n\n function _getTcbStatus(string memory statusStr) private pure returns (TCBStatus status) {\n if (statusStr.eq(\"UpToDate\")) {\n status = TCBStatus.OK;\n } else if (statusStr.eq(\"OutOfDate\")) {\n status = TCBStatus.TCB_OUT_OF_DATE;\n } else if (statusStr.eq(\"OutOfDateConfigurationNeeded\")) {\n status = TCBStatus.TCB_OUT_OF_DATE_CONFIGURATION_NEEDED;\n } else if (statusStr.eq(\"ConfigurationNeeded\")) {\n status = TCBStatus.TCB_CONFIGURATION_NEEDED;\n } else if (statusStr.eq(\"ConfigurationAndSWHardeningNeeded\")) {\n status = TCBStatus.TCB_CONFIGURATION_AND_SW_HARDENING_NEEDED;\n } else if (statusStr.eq(\"SWHardeningNeeded\")) {\n status = TCBStatus.TCB_SW_HARDENING_NEEDED;\n } else if (statusStr.eq(\"Revoked\")) {\n status = TCBStatus.TCB_REVOKED;\n } else {\n status = TCBStatus.TCB_UNRECOGNIZED;\n }\n }\n\n function _parseV2Tcb(JSONParserLib.Item[] memory tcbComponents)\n private\n pure\n returns (uint8[] memory sgxComponentCpuSvns, uint16 pcesvn)\n {\n sgxComponentCpuSvns = new uint8[](TCB_CPUSVN_SIZE);\n uint256 cpusvnCounter = 0;\n for (uint256 i = 0; i < tcbComponents.length; i++) {\n string memory key = JSONParserLib.decodeString(tcbComponents[i].key());\n uint256 value = JSONParserLib.parseUint(tcbComponents[i].value());\n if (key.eq(\"pcesvn\")) {\n pcesvn = uint16(value);\n } else {\n sgxComponentCpuSvns[cpusvnCounter++] = uint8(value);\n }\n }\n if (cpusvnCounter != TCB_CPUSVN_SIZE) {\n revert TCBInfo_Invalid();\n }\n }\n\n function _parseV3Tcb(JSONParserLib.Item[] memory tcbComponents)\n private\n pure\n returns (uint8[] memory sgxComponentCpuSvns, uint8[] memory tdxSvns, uint16 pcesvn)\n {\n sgxComponentCpuSvns = new uint8[](TCB_CPUSVN_SIZE);\n tdxSvns = new uint8[](TCB_CPUSVN_SIZE);\n for (uint256 i = 0; i < tcbComponents.length; i++) {\n string memory key = JSONParserLib.decodeString(tcbComponents[i].key());\n if (key.eq(\"pcesvn\")) {\n pcesvn = uint16(JSONParserLib.parseUint(tcbComponents[i].value()));\n } else {\n string memory componentKey = key;\n JSONParserLib.Item[] memory componentArr = tcbComponents[i].children();\n uint256 cpusvnCounter = 0;\n for (uint256 j = 0; j < tcbComponents[i].size(); j++) {\n JSONParserLib.Item[] memory component = componentArr[j].children();\n for (uint256 k = 0; k < componentArr[j].size(); k++) {\n key = JSONParserLib.decodeString(component[k].key());\n if (key.eq(\"svn\")) {\n if (componentKey.eq(\"tdxtcbcomponents\")) {\n tdxSvns[cpusvnCounter++] = uint8(JSONParserLib.parseUint(component[k].value()));\n } else {\n sgxComponentCpuSvns[cpusvnCounter++] =\n uint8(JSONParserLib.parseUint(component[k].value()));\n }\n }\n }\n }\n if (cpusvnCounter != TCB_CPUSVN_SIZE) {\n revert TCBInfo_Invalid();\n }\n }\n }\n }\n\n function _parseTdxModule(JSONParserLib.Item[] memory tdxModuleObj) private pure returns (TDXModule memory module) {\n for (uint256 i = 0; i < tdxModuleObj.length; i++) {\n string memory key = JSONParserLib.decodeString(tdxModuleObj[i].key());\n string memory val = JSONParserLib.decodeString(tdxModuleObj[i].value());\n if (key.eq(\"attributes\")) {\n module.attributes = bytes8(uint64(JSONParserLib.parseUintFromHex(val)));\n }\n if (key.eq(\"attributesMask\")) {\n module.attributesMask = bytes8(uint64(JSONParserLib.parseUintFromHex(val)));\n }\n if (key.eq(\"mrsigner\")) {\n module.mrsigner = _getMrSignerHex(val);\n }\n }\n }\n\n function _parseTdxModuleIdentities(JSONParserLib.Item[] memory tdxModuleIdentitiesArr)\n private\n pure\n returns (TDXModuleIdentity[] memory identities)\n {\n uint256 n = tdxModuleIdentitiesArr.length;\n identities = new TDXModuleIdentity[](n);\n for (uint256 i = 0; i < n; i++) {\n JSONParserLib.Item[] memory currIdentity = tdxModuleIdentitiesArr[i].children();\n for (uint256 j = 0; j < tdxModuleIdentitiesArr[i].size(); j++) {\n string memory key = JSONParserLib.decodeString(currIdentity[j].key());\n if (key.eq(\"id\")) {\n string memory val = JSONParserLib.decodeString(currIdentity[j].value());\n identities[i].id = val;\n }\n if (key.eq(\"mrsigner\")) {\n string memory val = JSONParserLib.decodeString(currIdentity[j].value());\n identities[i].mrsigner = _getMrSignerHex(val);\n }\n if (key.eq(\"attributes\")) {\n string memory val = JSONParserLib.decodeString(currIdentity[j].value());\n identities[i].attributes = bytes8(uint64(JSONParserLib.parseUintFromHex(val)));\n }\n if (key.eq(\"attributesMask\")) {\n string memory val = JSONParserLib.decodeString(currIdentity[j].value());\n identities[i].attributesMask = bytes8(uint64(JSONParserLib.parseUintFromHex(val)));\n }\n if (key.eq(\"tcbLevels\")) {\n JSONParserLib.Item[] memory tcbLevelsArr = currIdentity[j].children();\n uint256 x = tcbLevelsArr.length;\n identities[i].tcbLevels = new TDXModuleTCBLevelsObj[](x);\n for (uint256 k = 0; k < x; k++) {\n JSONParserLib.Item[] memory tcb = tcbLevelsArr[k].children();\n for (uint256 l = 0; l < tcb.length; l++) {\n key = JSONParserLib.decodeString(tcb[l].key());\n if (key.eq(\"tcb\")) {\n JSONParserLib.Item[] memory isvsvnObj = tcb[l].children();\n key = JSONParserLib.decodeString(isvsvnObj[0].key());\n if (key.eq(\"isvsvn\")) {\n identities[i].tcbLevels[k].isvsvn =\n uint8(JSONParserLib.parseUint(isvsvnObj[0].value()));\n } else {\n revert TCBInfo_Invalid();\n }\n }\n if (key.eq(\"tcbDate\")) {\n identities[i].tcbLevels[k].tcbDateTimestamp =\n uint64(DateTimeUtils.fromISOToTimestamp(JSONParserLib.decodeString(tcb[l].value())));\n }\n if (key.eq(\"tcbStatus\")) {\n identities[i].tcbLevels[k].status =\n _getTcbStatus(JSONParserLib.decodeString(tcb[l].value()));\n }\n }\n }\n }\n }\n }\n }\n\n function _getMrSignerHex(string memory mrSignerStr) private pure returns (bytes memory mrSignerBytes) {\n string memory mrSignerUpper16BytesStr = mrSignerStr.slice(0, 16);\n string memory mrSignerLower32BytesStr = mrSignerStr.slice(16, 48);\n uint256 mrSignerUpperBytes = JSONParserLib.parseUintFromHex(mrSignerUpper16BytesStr);\n uint256 mrSignerLowerBytes = JSONParserLib.parseUintFromHex(mrSignerLower32BytesStr);\n mrSignerBytes = abi.encodePacked(uint128(mrSignerUpperBytes), mrSignerLowerBytes);\n }\n}\n"},"lib/solady/src/utils/JSONParserLib.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\n/// @notice Library for parsing JSONs.\n/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/JSONParserLib.sol)\nlibrary JSONParserLib {\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* CUSTOM ERRORS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev The input is invalid.\n error ParsingFailed();\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* CONSTANTS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n // There are 6 types of variables in JSON (excluding undefined).\n\n /// @dev For denoting that an item has not been initialized.\n /// A item returned from `parse` will never be of an undefined type.\n /// Parsing a invalid JSON string will simply revert.\n uint8 internal constant TYPE_UNDEFINED = 0;\n\n /// @dev Type representing an array (e.g. `[1,2,3]`).\n uint8 internal constant TYPE_ARRAY = 1;\n\n /// @dev Type representing an object (e.g. `{\"a\":\"A\",\"b\":\"B\"}`).\n uint8 internal constant TYPE_OBJECT = 2;\n\n /// @dev Type representing a number (e.g. `-1.23e+21`).\n uint8 internal constant TYPE_NUMBER = 3;\n\n /// @dev Type representing a string (e.g. `\"hello\"`).\n uint8 internal constant TYPE_STRING = 4;\n\n /// @dev Type representing a boolean (i.e. `true` or `false`).\n uint8 internal constant TYPE_BOOLEAN = 5;\n\n /// @dev Type representing null (i.e. `null`).\n uint8 internal constant TYPE_NULL = 6;\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* STRUCTS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev A pointer to a parsed JSON node.\n struct Item {\n // Do NOT modify the `_data` directly.\n uint256 _data;\n }\n\n // Private constants for packing `_data`.\n\n uint256 private constant _BITPOS_STRING = 32 * 7 - 8;\n uint256 private constant _BITPOS_KEY_LENGTH = 32 * 6 - 8;\n uint256 private constant _BITPOS_KEY = 32 * 5 - 8;\n uint256 private constant _BITPOS_VALUE_LENGTH = 32 * 4 - 8;\n uint256 private constant _BITPOS_VALUE = 32 * 3 - 8;\n uint256 private constant _BITPOS_CHILD = 32 * 2 - 8;\n uint256 private constant _BITPOS_SIBLING_OR_PARENT = 32 * 1 - 8;\n uint256 private constant _BITMASK_POINTER = 0xffffffff;\n uint256 private constant _BITMASK_TYPE = 7;\n uint256 private constant _KEY_INITED = 1 << 3;\n uint256 private constant _VALUE_INITED = 1 << 4;\n uint256 private constant _CHILDREN_INITED = 1 << 5;\n uint256 private constant _PARENT_IS_ARRAY = 1 << 6;\n uint256 private constant _PARENT_IS_OBJECT = 1 << 7;\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* JSON PARSING OPERATION */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev Parses the JSON string `s`, and returns the root.\n /// Reverts if `s` is not a valid JSON as specified in RFC 8259.\n /// Object items WILL simply contain all their children, inclusive of repeated keys,\n /// in the same order which they appear in the JSON string.\n ///\n /// Note: For efficiency, this function WILL NOT make a copy of `s`.\n /// The parsed tree WILL contain offsets to `s`.\n /// Do NOT pass in a string that WILL be modified later on.\n function parse(string memory s) internal pure returns (Item memory result) {\n /// @solidity memory-safe-assembly\n assembly {\n mstore(0x40, result) // We will use our own allocation instead.\n }\n bytes32 r = _query(_toInput(s), 255);\n /// @solidity memory-safe-assembly\n assembly {\n result := r\n }\n }\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* JSON ITEM OPERATIONS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n // Note:\n // - An item is a node in the JSON tree.\n // - The value of a string item WILL be double-quoted, JSON encoded.\n // - We make a distinction between `index` and `key`.\n // - Items in arrays are located by `index` (uint256).\n // - Items in objects are located by `key` (string).\n // - Keys are always strings, double-quoted, JSON encoded.\n //\n // These design choices are made to balance between efficiency and ease-of-use.\n\n /// @dev Returns the string value of the item.\n /// This is its exact string representation in the original JSON string.\n /// The returned string WILL have leading and trailing whitespace trimmed.\n /// All inner whitespace WILL be preserved, exactly as it is in the original JSON string.\n /// If the item's type is string, the returned string WILL be double-quoted, JSON encoded.\n ///\n /// Note: This function lazily instantiates and caches the returned string.\n /// Do NOT modify the returned string.\n function value(Item memory item) internal pure returns (string memory result) {\n bytes32 r = _query(_toInput(item), 0);\n /// @solidity memory-safe-assembly\n assembly {\n result := r\n }\n }\n\n /// @dev Returns the index of the item in the array.\n /// It the item's parent is not an array, returns 0.\n function index(Item memory item) internal pure returns (uint256 result) {\n /// @solidity memory-safe-assembly\n assembly {\n if and(mload(item), _PARENT_IS_ARRAY) {\n result := and(_BITMASK_POINTER, shr(_BITPOS_KEY, mload(item)))\n }\n }\n }\n\n /// @dev Returns the key of the item in the object.\n /// It the item's parent is not an object, returns an empty string.\n /// The returned string WILL be double-quoted, JSON encoded.\n ///\n /// Note: This function lazily instantiates and caches the returned string.\n /// Do NOT modify the returned string.\n function key(Item memory item) internal pure returns (string memory result) {\n if (item._data & _PARENT_IS_OBJECT != 0) {\n bytes32 r = _query(_toInput(item), 1);\n /// @solidity memory-safe-assembly\n assembly {\n result := r\n }\n }\n }\n\n /// @dev Returns the key of the item in the object.\n /// It the item is neither an array nor object, returns an empty array.\n ///\n /// Note: This function lazily instantiates and caches the returned array.\n /// Do NOT modify the returned array.\n function children(Item memory item) internal pure returns (Item[] memory result) {\n bytes32 r = _query(_toInput(item), 3);\n /// @solidity memory-safe-assembly\n assembly {\n result := r\n }\n }\n\n /// @dev Returns the number of children.\n /// It the item is neither an array nor object, returns zero.\n function size(Item memory item) internal pure returns (uint256 result) {\n bytes32 r = _query(_toInput(item), 3);\n /// @solidity memory-safe-assembly\n assembly {\n result := mload(r)\n }\n }\n\n /// @dev Returns the item at index `i` for (array).\n /// If `item` is not an array, the result's type WILL be undefined.\n /// If there is no item with the index, the result's type WILL be undefined.\n function at(Item memory item, uint256 i) internal pure returns (Item memory result) {\n /// @solidity memory-safe-assembly\n assembly {\n mstore(0x40, result) // Free the default allocation. We'll allocate manually.\n }\n bytes32 r = _query(_toInput(item), 3);\n /// @solidity memory-safe-assembly\n assembly {\n result := mload(add(add(r, 0x20), shl(5, i)))\n if iszero(and(lt(i, mload(r)), eq(and(mload(item), _BITMASK_TYPE), TYPE_ARRAY))) {\n result := 0x60 // Reset to the zero pointer.\n }\n }\n }\n\n /// @dev Returns the item at key `k` for (object).\n /// If `item` is not an object, the result's type WILL be undefined.\n /// The key MUST be double-quoted, JSON encoded. This is for efficiency reasons.\n /// - Correct : `item.at('\"k\"')`.\n /// - Wrong : `item.at(\"k\")`.\n /// For duplicated keys, the last item with the key WILL be returned.\n /// If there is no item with the key, the result's type WILL be undefined.\n function at(Item memory item, string memory k) internal pure returns (Item memory result) {\n /// @solidity memory-safe-assembly\n assembly {\n mstore(0x40, result) // Free the default allocation. We'll allocate manually.\n result := 0x60 // Initialize to the zero pointer.\n }\n if (isObject(item)) {\n bytes32 kHash = keccak256(bytes(k));\n Item[] memory r = children(item);\n // We'll just do a linear search. The alternatives are very bloated.\n for (uint256 i = r.length << 5; i != 0;) {\n /// @solidity memory-safe-assembly\n assembly {\n item := mload(add(r, i))\n i := sub(i, 0x20)\n }\n if (keccak256(bytes(key(item))) != kHash) continue;\n result = item;\n break;\n }\n }\n }\n\n /// @dev Returns the item's type.\n function getType(Item memory item) internal pure returns (uint8 result) {\n result = uint8(item._data & _BITMASK_TYPE);\n }\n\n /// Note: All types are mutually exclusive.\n\n /// @dev Returns whether the item is of type undefined.\n function isUndefined(Item memory item) internal pure returns (bool result) {\n result = item._data & _BITMASK_TYPE == TYPE_UNDEFINED;\n }\n\n /// @dev Returns whether the item is of type array.\n function isArray(Item memory item) internal pure returns (bool result) {\n result = item._data & _BITMASK_TYPE == TYPE_ARRAY;\n }\n\n /// @dev Returns whether the item is of type object.\n function isObject(Item memory item) internal pure returns (bool result) {\n result = item._data & _BITMASK_TYPE == TYPE_OBJECT;\n }\n\n /// @dev Returns whether the item is of type number.\n function isNumber(Item memory item) internal pure returns (bool result) {\n result = item._data & _BITMASK_TYPE == TYPE_NUMBER;\n }\n\n /// @dev Returns whether the item is of type string.\n function isString(Item memory item) internal pure returns (bool result) {\n result = item._data & _BITMASK_TYPE == TYPE_STRING;\n }\n\n /// @dev Returns whether the item is of type boolean.\n function isBoolean(Item memory item) internal pure returns (bool result) {\n result = item._data & _BITMASK_TYPE == TYPE_BOOLEAN;\n }\n\n /// @dev Returns whether the item is of type null.\n function isNull(Item memory item) internal pure returns (bool result) {\n result = item._data & _BITMASK_TYPE == TYPE_NULL;\n }\n\n /// @dev Returns the item's parent.\n /// If the item does not have a parent, the result's type will be undefined.\n function parent(Item memory item) internal pure returns (Item memory result) {\n /// @solidity memory-safe-assembly\n assembly {\n mstore(0x40, result) // Free the default allocation. We've already allocated.\n result := and(shr(_BITPOS_SIBLING_OR_PARENT, mload(item)), _BITMASK_POINTER)\n if iszero(result) { result := 0x60 } // Reset to the zero pointer.\n }\n }\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* UTILITY FUNCTIONS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev Parses an unsigned integer from a string (in decimal, i.e. base 10).\n /// Reverts if `s` is not a valid uint256 string matching the RegEx `^[0-9]+$`,\n /// or if the parsed number is too big for a uint256.\n function parseUint(string memory s) internal pure returns (uint256 result) {\n /// @solidity memory-safe-assembly\n assembly {\n let n := mload(s)\n let preMulOverflowThres := div(not(0), 10)\n for { let i := 0 } 1 {} {\n i := add(i, 1)\n let digit := sub(and(mload(add(s, i)), 0xff), 48)\n let mulOverflowed := gt(result, preMulOverflowThres)\n let product := mul(10, result)\n result := add(product, digit)\n n := mul(n, iszero(or(or(mulOverflowed, lt(result, product)), gt(digit, 9))))\n if iszero(lt(i, n)) { break }\n }\n if iszero(n) {\n mstore(0x00, 0x10182796) // `ParsingFailed()`.\n revert(0x1c, 0x04)\n }\n }\n }\n\n /// @dev Parses a signed integer from a string (in decimal, i.e. base 10).\n /// Reverts if `s` is not a valid int256 string matching the RegEx `^[+-]?[0-9]+$`,\n /// or if the parsed number is too big for a int256.\n function parseInt(string memory s) internal pure returns (int256 result) {\n uint256 n = bytes(s).length;\n uint256 sign;\n uint256 isNegative;\n /// @solidity memory-safe-assembly\n assembly {\n if n {\n let c := and(mload(add(s, 1)), 0xff)\n isNegative := eq(c, 45)\n if or(eq(c, 43), isNegative) {\n sign := c\n s := add(s, 1)\n mstore(s, sub(n, 1))\n }\n if iszero(or(sign, lt(sub(c, 48), 10))) { s := 0x60 }\n }\n }\n uint256 x = parseUint(s);\n /// @solidity memory-safe-assembly\n assembly {\n if shr(255, x) {\n mstore(0x00, 0x10182796) // `ParsingFailed()`.\n revert(0x1c, 0x04)\n }\n if sign {\n mstore(s, sign)\n s := sub(s, 1)\n mstore(s, n)\n }\n result := xor(x, mul(xor(x, add(not(x), 1)), isNegative))\n }\n }\n\n /// @dev Parses an unsigned integer from a string (in hexadecimal, i.e. base 16).\n /// Reverts if `s` is not a valid uint256 hex string matching the RegEx\n /// `^(0[xX])?[0-9a-fA-F]+$`, or if the parsed number is too big for a uint256.\n function parseUintFromHex(string memory s) internal pure returns (uint256 result) {\n /// @solidity memory-safe-assembly\n assembly {\n let n := mload(s)\n // Skip two if starts with '0x' or '0X'.\n let i := shl(1, and(eq(0x3078, or(shr(240, mload(add(s, 0x20))), 0x20)), gt(n, 1)))\n for {} 1 {} {\n i := add(i, 1)\n let c :=\n byte(\n and(0x1f, shr(and(mload(add(s, i)), 0xff), 0x3e4088843e41bac000000000000)),\n 0x3010a071000000b0104040208000c05090d060e0f\n )\n n := mul(n, iszero(or(iszero(c), shr(252, result))))\n result := add(shl(4, result), sub(c, 1))\n if iszero(lt(i, n)) { break }\n }\n if iszero(n) {\n mstore(0x00, 0x10182796) // `ParsingFailed()`.\n revert(0x1c, 0x04)\n }\n }\n }\n\n /// @dev Decodes a JSON encoded string.\n /// The string MUST be double-quoted, JSON encoded.\n /// Reverts if the string is invalid.\n /// As you can see, it's pretty complex for a deceptively simple looking task.\n function decodeString(string memory s) internal pure returns (string memory result) {\n /// @solidity memory-safe-assembly\n assembly {\n function fail() {\n mstore(0x00, 0x10182796) // `ParsingFailed()`.\n revert(0x1c, 0x04)\n }\n\n function decodeUnicodeEscapeSequence(pIn_, end_) -> _unicode, _pOut {\n _pOut := add(pIn_, 4)\n let b_ := iszero(gt(_pOut, end_))\n let t_ := mload(pIn_) // Load the whole word.\n for { let i_ := 0 } iszero(eq(i_, 4)) { i_ := add(i_, 1) } {\n let c_ := sub(byte(i_, t_), 48)\n if iszero(and(shr(c_, 0x7e0000007e03ff), b_)) { fail() } // Not hexadecimal.\n c_ := sub(c_, add(mul(gt(c_, 16), 7), shl(5, gt(c_, 48))))\n _unicode := add(shl(4, _unicode), c_)\n }\n }\n\n function decodeUnicodeCodePoint(pIn_, end_) -> _unicode, _pOut {\n _unicode, _pOut := decodeUnicodeEscapeSequence(pIn_, end_)\n if iszero(or(lt(_unicode, 0xd800), gt(_unicode, 0xdbff))) {\n let t_ := mload(_pOut) // Load the whole word.\n end_ := mul(end_, eq(shr(240, t_), 0x5c75)) // Fail if not starting with '\\\\u'.\n t_, _pOut := decodeUnicodeEscapeSequence(add(_pOut, 2), end_)\n _unicode := add(0x10000, add(shl(10, and(0x3ff, _unicode)), and(0x3ff, t_)))\n }\n }\n\n function appendCodePointAsUTF8(pIn_, c_) -> _pOut {\n if iszero(gt(c_, 0x7f)) {\n mstore8(pIn_, c_)\n _pOut := add(pIn_, 1)\n leave\n }\n mstore8(0x1f, c_)\n mstore8(0x1e, shr(6, c_))\n if iszero(gt(c_, 0x7ff)) {\n mstore(pIn_, shl(240, or(0xc080, and(0x1f3f, mload(0x00)))))\n _pOut := add(pIn_, 2)\n leave\n }\n mstore8(0x1d, shr(12, c_))\n if iszero(gt(c_, 0xffff)) {\n mstore(pIn_, shl(232, or(0xe08080, and(0x0f3f3f, mload(0x00)))))\n _pOut := add(pIn_, 3)\n leave\n }\n mstore8(0x1c, shr(18, c_))\n mstore(pIn_, shl(224, or(0xf0808080, and(0x073f3f3f, mload(0x00)))))\n _pOut := add(pIn_, shl(2, lt(c_, 0x110000)))\n }\n\n function chr(p_) -> _c {\n _c := byte(0, mload(p_))\n }\n\n let n := mload(s)\n let end := add(add(s, n), 0x1f)\n if iszero(and(gt(n, 1), eq(0x2222, or(and(0xff00, mload(add(s, 2))), chr(end))))) {\n fail() // Fail if not double-quoted.\n }\n let out := add(mload(0x40), 0x20)\n for { let curr := add(s, 0x21) } iszero(eq(curr, end)) {} {\n let c := chr(curr)\n curr := add(curr, 1)\n // Not '\\\\'.\n if iszero(eq(c, 92)) {\n // Not '\"'.\n if iszero(eq(c, 34)) {\n mstore8(out, c)\n out := add(out, 1)\n continue\n }\n curr := end\n }\n if iszero(eq(curr, end)) {\n let escape := chr(curr)\n curr := add(curr, 1)\n // '\"', '/', '\\\\'.\n if and(shr(escape, 0x100000000000800400000000), 1) {\n mstore8(out, escape)\n out := add(out, 1)\n continue\n }\n // 'u'.\n if eq(escape, 117) {\n escape, curr := decodeUnicodeCodePoint(curr, end)\n out := appendCodePointAsUTF8(out, escape)\n continue\n }\n // `{'b':'\\b', 'f':'\\f', 'n':'\\n', 'r':'\\r', 't':'\\t'}`.\n escape := byte(sub(escape, 85), 0x080000000c000000000000000a0000000d0009)\n if escape {\n mstore8(out, escape)\n out := add(out, 1)\n continue\n }\n }\n fail()\n break\n }\n mstore(out, 0) // Zeroize the last slot.\n result := mload(0x40)\n mstore(result, sub(out, add(result, 0x20))) // Store the length.\n mstore(0x40, add(out, 0x20)) // Allocate the memory.\n }\n }\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* PRIVATE HELPERS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev Performs a query on the input with the given mode.\n function _query(bytes32 input, uint256 mode) private pure returns (bytes32 result) {\n /// @solidity memory-safe-assembly\n assembly {\n function fail() {\n mstore(0x00, 0x10182796) // `ParsingFailed()`.\n revert(0x1c, 0x04)\n }\n\n function chr(p_) -> _c {\n _c := byte(0, mload(p_))\n }\n\n function skipWhitespace(pIn_, end_) -> _pOut {\n for { _pOut := pIn_ } 1 { _pOut := add(_pOut, 1) } {\n if iszero(and(shr(chr(_pOut), 0x100002600), 1)) { leave } // Not in ' \\n\\r\\t'.\n }\n }\n\n function setP(packed_, bitpos_, p_) -> _packed {\n // Perform an out-of-gas revert if `p_` exceeds `_BITMASK_POINTER`.\n returndatacopy(returndatasize(), returndatasize(), gt(p_, _BITMASK_POINTER))\n _packed := or(and(not(shl(bitpos_, _BITMASK_POINTER)), packed_), shl(bitpos_, p_))\n }\n\n function getP(packed_, bitpos_) -> _p {\n _p := and(_BITMASK_POINTER, shr(bitpos_, packed_))\n }\n\n function mallocItem(s_, packed_, pStart_, pCurr_, type_) -> _item {\n _item := mload(0x40)\n // forgefmt: disable-next-item\n packed_ := setP(setP(packed_, _BITPOS_VALUE, sub(pStart_, add(s_, 0x20))),\n _BITPOS_VALUE_LENGTH, sub(pCurr_, pStart_))\n mstore(_item, or(packed_, type_))\n mstore(0x40, add(_item, 0x20)) // Allocate memory.\n }\n\n function parseValue(s_, sibling_, pIn_, end_) -> _item, _pOut {\n let packed_ := setP(mload(0x00), _BITPOS_SIBLING_OR_PARENT, sibling_)\n _pOut := skipWhitespace(pIn_, end_)\n if iszero(lt(_pOut, end_)) { leave }\n for { let c_ := chr(_pOut) } 1 {} {\n // If starts with '\"'.\n if eq(c_, 34) {\n let pStart_ := _pOut\n _pOut := parseStringSub(s_, packed_, _pOut, end_)\n _item := mallocItem(s_, packed_, pStart_, _pOut, TYPE_STRING)\n break\n }\n // If starts with '['.\n if eq(c_, 91) {\n _item, _pOut := parseArray(s_, packed_, _pOut, end_)\n break\n }\n // If starts with '{'.\n if eq(c_, 123) {\n _item, _pOut := parseObject(s_, packed_, _pOut, end_)\n break\n }\n // If starts with any in '0123456789-'.\n if and(shr(c_, shl(45, 0x1ff9)), 1) {\n _item, _pOut := parseNumber(s_, packed_, _pOut, end_)\n break\n }\n if iszero(gt(add(_pOut, 4), end_)) {\n let pStart_ := _pOut\n let w_ := shr(224, mload(_pOut))\n // 'true' in hex format.\n if eq(w_, 0x74727565) {\n _pOut := add(_pOut, 4)\n _item := mallocItem(s_, packed_, pStart_, _pOut, TYPE_BOOLEAN)\n break\n }\n // 'null' in hex format.\n if eq(w_, 0x6e756c6c) {\n _pOut := add(_pOut, 4)\n _item := mallocItem(s_, packed_, pStart_, _pOut, TYPE_NULL)\n break\n }\n }\n if iszero(gt(add(_pOut, 5), end_)) {\n let pStart_ := _pOut\n let w_ := shr(216, mload(_pOut))\n // 'false' in hex format.\n if eq(w_, 0x66616c7365) {\n _pOut := add(_pOut, 5)\n _item := mallocItem(s_, packed_, pStart_, _pOut, TYPE_BOOLEAN)\n break\n }\n }\n fail()\n break\n }\n _pOut := skipWhitespace(_pOut, end_)\n }\n\n function parseArray(s_, packed_, pIn_, end_) -> _item, _pOut {\n let j_ := 0\n for { _pOut := add(pIn_, 1) } 1 { _pOut := add(_pOut, 1) } {\n if iszero(lt(_pOut, end_)) { fail() }\n if iszero(_item) {\n _pOut := skipWhitespace(_pOut, end_)\n if eq(chr(_pOut), 93) { break } // ']'.\n }\n _item, _pOut := parseValue(s_, _item, _pOut, end_)\n if _item {\n // forgefmt: disable-next-item\n mstore(_item, setP(or(_PARENT_IS_ARRAY, mload(_item)),\n _BITPOS_KEY, j_))\n j_ := add(j_, 1)\n let c_ := chr(_pOut)\n if eq(c_, 93) { break } // ']'.\n if eq(c_, 44) { continue } // ','.\n }\n _pOut := end_\n }\n _pOut := add(_pOut, 1)\n packed_ := setP(packed_, _BITPOS_CHILD, _item)\n _item := mallocItem(s_, packed_, pIn_, _pOut, TYPE_ARRAY)\n }\n\n function parseObject(s_, packed_, pIn_, end_) -> _item, _pOut {\n for { _pOut := add(pIn_, 1) } 1 { _pOut := add(_pOut, 1) } {\n if iszero(lt(_pOut, end_)) { fail() }\n if iszero(_item) {\n _pOut := skipWhitespace(_pOut, end_)\n if eq(chr(_pOut), 125) { break } // '}'.\n }\n _pOut := skipWhitespace(_pOut, end_)\n let pKeyStart_ := _pOut\n let pKeyEnd_ := parseStringSub(s_, _item, _pOut, end_)\n _pOut := skipWhitespace(pKeyEnd_, end_)\n // If ':'.\n if eq(chr(_pOut), 58) {\n _item, _pOut := parseValue(s_, _item, add(_pOut, 1), end_)\n if _item {\n // forgefmt: disable-next-item\n mstore(_item, setP(setP(or(_PARENT_IS_OBJECT, mload(_item)),\n _BITPOS_KEY_LENGTH, sub(pKeyEnd_, pKeyStart_)),\n _BITPOS_KEY, sub(pKeyStart_, add(s_, 0x20))))\n let c_ := chr(_pOut)\n if eq(c_, 125) { break } // '}'.\n if eq(c_, 44) { continue } // ','.\n }\n }\n _pOut := end_\n }\n _pOut := add(_pOut, 1)\n packed_ := setP(packed_, _BITPOS_CHILD, _item)\n _item := mallocItem(s_, packed_, pIn_, _pOut, TYPE_OBJECT)\n }\n\n function checkStringU(p_, o_) {\n // If not in '0123456789abcdefABCDEF', revert.\n if iszero(and(shr(sub(chr(add(p_, o_)), 48), 0x7e0000007e03ff), 1)) { fail() }\n if iszero(eq(o_, 5)) { checkStringU(p_, add(o_, 1)) }\n }\n\n function parseStringSub(s_, packed_, pIn_, end_) -> _pOut {\n if iszero(lt(pIn_, end_)) { fail() }\n for { _pOut := add(pIn_, 1) } 1 {} {\n let c_ := chr(_pOut)\n if eq(c_, 34) { break } // '\"'.\n // Not '\\'.\n if iszero(eq(c_, 92)) {\n _pOut := add(_pOut, 1)\n continue\n }\n c_ := chr(add(_pOut, 1))\n // '\"', '\\', '//', 'b', 'f', 'n', 'r', 't'.\n if and(shr(sub(c_, 34), 0x510110400000000002001), 1) {\n _pOut := add(_pOut, 2)\n continue\n }\n // 'u'.\n if eq(c_, 117) {\n checkStringU(_pOut, 2)\n _pOut := add(_pOut, 6)\n continue\n }\n _pOut := end_\n break\n }\n if iszero(lt(_pOut, end_)) { fail() }\n _pOut := add(_pOut, 1)\n }\n\n function skip0To9s(pIn_, end_, atLeastOne_) -> _pOut {\n for { _pOut := pIn_ } 1 { _pOut := add(_pOut, 1) } {\n if iszero(lt(sub(chr(_pOut), 48), 10)) { break } // Not '0'..'9'.\n }\n if and(atLeastOne_, eq(pIn_, _pOut)) { fail() }\n }\n\n function parseNumber(s_, packed_, pIn_, end_) -> _item, _pOut {\n _pOut := pIn_\n if eq(chr(_pOut), 45) { _pOut := add(_pOut, 1) } // '-'.\n if iszero(lt(sub(chr(_pOut), 48), 10)) { fail() } // Not '0'..'9'.\n let c_ := chr(_pOut)\n _pOut := add(_pOut, 1)\n if iszero(eq(c_, 48)) { _pOut := skip0To9s(_pOut, end_, 0) } // Not '0'.\n if eq(chr(_pOut), 46) { _pOut := skip0To9s(add(_pOut, 1), end_, 1) } // '.'.\n let t_ := mload(_pOut)\n // 'E', 'e'.\n if eq(or(0x20, byte(0, t_)), 101) {\n // forgefmt: disable-next-item\n _pOut := skip0To9s(add(byte(sub(byte(1, t_), 14), 0x010001), // '+', '-'.\n add(_pOut, 1)), end_, 1)\n }\n _item := mallocItem(s_, packed_, pIn_, _pOut, TYPE_NUMBER)\n }\n\n function copyStr(s_, offset_, len_) -> _sCopy {\n _sCopy := mload(0x40)\n s_ := add(s_, offset_)\n let w_ := not(0x1f)\n for { let i_ := and(add(len_, 0x1f), w_) } 1 {} {\n mstore(add(_sCopy, i_), mload(add(s_, i_)))\n i_ := add(i_, w_) // `sub(i_, 0x20)`.\n if iszero(i_) { break }\n }\n mstore(_sCopy, len_) // Copy the length.\n mstore(add(add(_sCopy, 0x20), len_), 0) // Zeroize the last slot.\n mstore(0x40, add(add(_sCopy, 0x40), len_)) // Allocate memory.\n }\n\n function value(item_) -> _value {\n let packed_ := mload(item_)\n _value := getP(packed_, _BITPOS_VALUE) // The offset in the string.\n if iszero(and(_VALUE_INITED, packed_)) {\n let s_ := getP(packed_, _BITPOS_STRING)\n _value := copyStr(s_, _value, getP(packed_, _BITPOS_VALUE_LENGTH))\n packed_ := setP(packed_, _BITPOS_VALUE, _value)\n mstore(s_, or(_VALUE_INITED, packed_))\n }\n }\n\n function children(item_) -> _arr {\n _arr := 0x60 // Initialize to the zero pointer.\n let packed_ := mload(item_)\n for {} iszero(gt(and(_BITMASK_TYPE, packed_), TYPE_OBJECT)) {} {\n if or(iszero(packed_), iszero(item_)) { break }\n if and(packed_, _CHILDREN_INITED) {\n _arr := getP(packed_, _BITPOS_CHILD)\n break\n }\n _arr := mload(0x40)\n let o_ := add(_arr, 0x20)\n for { let h_ := getP(packed_, _BITPOS_CHILD) } h_ {} {\n mstore(o_, h_)\n let q_ := mload(h_)\n let y_ := getP(q_, _BITPOS_SIBLING_OR_PARENT)\n mstore(h_, setP(q_, _BITPOS_SIBLING_OR_PARENT, item_))\n h_ := y_\n o_ := add(o_, 0x20)\n }\n let w_ := not(0x1f)\n let n_ := add(w_, sub(o_, _arr))\n mstore(_arr, shr(5, n_))\n mstore(0x40, o_) // Allocate memory.\n packed_ := setP(packed_, _BITPOS_CHILD, _arr)\n mstore(item_, or(_CHILDREN_INITED, packed_))\n // Reverse the array.\n if iszero(lt(n_, 0x40)) {\n let lo_ := add(_arr, 0x20)\n let hi_ := add(_arr, n_)\n for {} 1 {} {\n let temp_ := mload(lo_)\n mstore(lo_, mload(hi_))\n mstore(hi_, temp_)\n hi_ := add(hi_, w_)\n lo_ := add(lo_, 0x20)\n if iszero(lt(lo_, hi_)) { break }\n }\n }\n break\n }\n }\n\n function getStr(item_, bitpos_, bitposLength_, bitmaskInited_) -> _result {\n _result := 0x60 // Initialize to the zero pointer.\n let packed_ := mload(item_)\n if or(iszero(item_), iszero(packed_)) { leave }\n _result := getP(packed_, bitpos_)\n if iszero(and(bitmaskInited_, packed_)) {\n let s_ := getP(packed_, _BITPOS_STRING)\n _result := copyStr(s_, _result, getP(packed_, bitposLength_))\n mstore(item_, or(bitmaskInited_, setP(packed_, bitpos_, _result)))\n }\n }\n\n switch mode\n // Get value.\n case 0 { result := getStr(input, _BITPOS_VALUE, _BITPOS_VALUE_LENGTH, _VALUE_INITED) }\n // Get key.\n case 1 { result := getStr(input, _BITPOS_KEY, _BITPOS_KEY_LENGTH, _KEY_INITED) }\n // Get children.\n case 3 { result := children(input) }\n // Parse.\n default {\n let p := add(input, 0x20)\n let e := add(p, mload(input))\n if iszero(eq(p, e)) {\n let c := chr(e)\n mstore8(e, 34) // Place a '\"' at the end to speed up parsing.\n // The `34 << 248` makes `mallocItem` preserve '\"' at the end.\n mstore(0x00, setP(shl(248, 34), _BITPOS_STRING, input))\n result, p := parseValue(input, 0, p, e)\n mstore8(e, c) // Restore the original char at the end.\n }\n if or(lt(p, e), iszero(result)) { fail() }\n }\n }\n }\n\n /// @dev Casts the input to a bytes32.\n function _toInput(string memory input) private pure returns (bytes32 result) {\n /// @solidity memory-safe-assembly\n assembly {\n result := input\n }\n }\n\n /// @dev Casts the input to a bytes32.\n function _toInput(Item memory input) private pure returns (bytes32 result) {\n /// @solidity memory-safe-assembly\n assembly {\n result := input\n }\n }\n}\n"},"lib/solady/src/utils/LibString.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\n/// @notice Library for converting numbers into strings and other string operations.\n/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibString.sol)\n/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/LibString.sol)\n///\n/// Note:\n/// For performance and bytecode compactness, most of the string operations are restricted to\n/// byte strings (7-bit ASCII), except where otherwise specified.\n/// Usage of byte string operations on charsets with runes spanning two or more bytes\n/// can lead to undefined behavior.\nlibrary LibString {\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* CUSTOM ERRORS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev The length of the output is too small to contain all the hex digits.\n error HexLengthInsufficient();\n\n /// @dev The length of the string is more than 32 bytes.\n error TooBigForSmallString();\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* CONSTANTS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev The constant returned when the `search` is not found in the string.\n uint256 internal constant NOT_FOUND = type(uint256).max;\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* DECIMAL OPERATIONS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev Returns the base 10 decimal representation of `value`.\n function toString(uint256 value) internal pure returns (string memory str) {\n /// @solidity memory-safe-assembly\n assembly {\n // The maximum value of a uint256 contains 78 digits (1 byte per digit), but\n // we allocate 0xa0 bytes to keep the free memory pointer 32-byte word aligned.\n // We will need 1 word for the trailing zeros padding, 1 word for the length,\n // and 3 words for a maximum of 78 digits.\n str := add(mload(0x40), 0x80)\n // Update the free memory pointer to allocate.\n mstore(0x40, add(str, 0x20))\n // Zeroize the slot after the string.\n mstore(str, 0)\n\n // Cache the end of the memory to calculate the length later.\n let end := str\n\n let w := not(0) // Tsk.\n // We write the string from rightmost digit to leftmost digit.\n // The following is essentially a do-while loop that also handles the zero case.\n for { let temp := value } 1 {} {\n str := add(str, w) // `sub(str, 1)`.\n // Write the character to the pointer.\n // The ASCII index of the '0' character is 48.\n mstore8(str, add(48, mod(temp, 10)))\n // Keep dividing `temp` until zero.\n temp := div(temp, 10)\n if iszero(temp) { break }\n }\n\n let length := sub(end, str)\n // Move the pointer 32 bytes leftwards to make room for the length.\n str := sub(str, 0x20)\n // Store the length.\n mstore(str, length)\n }\n }\n\n /// @dev Returns the base 10 decimal representation of `value`.\n function toString(int256 value) internal pure returns (string memory str) {\n if (value >= 0) {\n return toString(uint256(value));\n }\n unchecked {\n str = toString(uint256(-value));\n }\n /// @solidity memory-safe-assembly\n assembly {\n // We still have some spare memory space on the left,\n // as we have allocated 3 words (96 bytes) for up to 78 digits.\n let length := mload(str) // Load the string length.\n mstore(str, 0x2d) // Store the '-' character.\n str := sub(str, 1) // Move back the string pointer by a byte.\n mstore(str, add(length, 1)) // Update the string length.\n }\n }\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* HEXADECIMAL OPERATIONS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev Returns the hexadecimal representation of `value`,\n /// left-padded to an input length of `length` bytes.\n /// The output is prefixed with \"0x\" encoded using 2 hexadecimal digits per byte,\n /// giving a total length of `length * 2 + 2` bytes.\n /// Reverts if `length` is too small for the output to contain all the digits.\n function toHexString(uint256 value, uint256 length) internal pure returns (string memory str) {\n str = toHexStringNoPrefix(value, length);\n /// @solidity memory-safe-assembly\n assembly {\n let strLength := add(mload(str), 2) // Compute the length.\n mstore(str, 0x3078) // Write the \"0x\" prefix.\n str := sub(str, 2) // Move the pointer.\n mstore(str, strLength) // Write the length.\n }\n }\n\n /// @dev Returns the hexadecimal representation of `value`,\n /// left-padded to an input length of `length` bytes.\n /// The output is prefixed with \"0x\" encoded using 2 hexadecimal digits per byte,\n /// giving a total length of `length * 2` bytes.\n /// Reverts if `length` is too small for the output to contain all the digits.\n function toHexStringNoPrefix(uint256 value, uint256 length)\n internal\n pure\n returns (string memory str)\n {\n /// @solidity memory-safe-assembly\n assembly {\n // We need 0x20 bytes for the trailing zeros padding, `length * 2` bytes\n // for the digits, 0x02 bytes for the prefix, and 0x20 bytes for the length.\n // We add 0x20 to the total and round down to a multiple of 0x20.\n // (0x20 + 0x20 + 0x02 + 0x20) = 0x62.\n str := add(mload(0x40), and(add(shl(1, length), 0x42), not(0x1f)))\n // Allocate the memory.\n mstore(0x40, add(str, 0x20))\n // Zeroize the slot after the string.\n mstore(str, 0)\n\n // Cache the end to calculate the length later.\n let end := str\n // Store \"0123456789abcdef\" in scratch space.\n mstore(0x0f, 0x30313233343536373839616263646566)\n\n let start := sub(str, add(length, length))\n let w := not(1) // Tsk.\n let temp := value\n // We write the string from rightmost digit to leftmost digit.\n // The following is essentially a do-while loop that also handles the zero case.\n for {} 1 {} {\n str := add(str, w) // `sub(str, 2)`.\n mstore8(add(str, 1), mload(and(temp, 15)))\n mstore8(str, mload(and(shr(4, temp), 15)))\n temp := shr(8, temp)\n if iszero(xor(str, start)) { break }\n }\n\n if temp {\n mstore(0x00, 0x2194895a) // `HexLengthInsufficient()`.\n revert(0x1c, 0x04)\n }\n\n // Compute the string's length.\n let strLength := sub(end, str)\n // Move the pointer and write the length.\n str := sub(str, 0x20)\n mstore(str, strLength)\n }\n }\n\n /// @dev Returns the hexadecimal representation of `value`.\n /// The output is prefixed with \"0x\" and encoded using 2 hexadecimal digits per byte.\n /// As address are 20 bytes long, the output will left-padded to have\n /// a length of `20 * 2 + 2` bytes.\n function toHexString(uint256 value) internal pure returns (string memory str) {\n str = toHexStringNoPrefix(value);\n /// @solidity memory-safe-assembly\n assembly {\n let strLength := add(mload(str), 2) // Compute the length.\n mstore(str, 0x3078) // Write the \"0x\" prefix.\n str := sub(str, 2) // Move the pointer.\n mstore(str, strLength) // Write the length.\n }\n }\n\n /// @dev Returns the hexadecimal representation of `value`.\n /// The output is prefixed with \"0x\".\n /// The output excludes leading \"0\" from the `toHexString` output.\n /// `0x00: \"0x0\", 0x01: \"0x1\", 0x12: \"0x12\", 0x123: \"0x123\"`.\n function toMinimalHexString(uint256 value) internal pure returns (string memory str) {\n str = toHexStringNoPrefix(value);\n /// @solidity memory-safe-assembly\n assembly {\n let o := eq(byte(0, mload(add(str, 0x20))), 0x30) // Whether leading zero is present.\n let strLength := add(mload(str), 2) // Compute the length.\n mstore(add(str, o), 0x3078) // Write the \"0x\" prefix, accounting for leading zero.\n str := sub(add(str, o), 2) // Move the pointer, accounting for leading zero.\n mstore(str, sub(strLength, o)) // Write the length, accounting for leading zero.\n }\n }\n\n /// @dev Returns the hexadecimal representation of `value`.\n /// The output excludes leading \"0\" from the `toHexStringNoPrefix` output.\n /// `0x00: \"0\", 0x01: \"1\", 0x12: \"12\", 0x123: \"123\"`.\n function toMinimalHexStringNoPrefix(uint256 value) internal pure returns (string memory str) {\n str = toHexStringNoPrefix(value);\n /// @solidity memory-safe-assembly\n assembly {\n let o := eq(byte(0, mload(add(str, 0x20))), 0x30) // Whether leading zero is present.\n let strLength := mload(str) // Get the length.\n str := add(str, o) // Move the pointer, accounting for leading zero.\n mstore(str, sub(strLength, o)) // Write the length, accounting for leading zero.\n }\n }\n\n /// @dev Returns the hexadecimal representation of `value`.\n /// The output is encoded using 2 hexadecimal digits per byte.\n /// As address are 20 bytes long, the output will left-padded to have\n /// a length of `20 * 2` bytes.\n function toHexStringNoPrefix(uint256 value) internal pure returns (string memory str) {\n /// @solidity memory-safe-assembly\n assembly {\n // We need 0x20 bytes for the trailing zeros padding, 0x20 bytes for the length,\n // 0x02 bytes for the prefix, and 0x40 bytes for the digits.\n // The next multiple of 0x20 above (0x20 + 0x20 + 0x02 + 0x40) is 0xa0.\n str := add(mload(0x40), 0x80)\n // Allocate the memory.\n mstore(0x40, add(str, 0x20))\n // Zeroize the slot after the string.\n mstore(str, 0)\n\n // Cache the end to calculate the length later.\n let end := str\n // Store \"0123456789abcdef\" in scratch space.\n mstore(0x0f, 0x30313233343536373839616263646566)\n\n let w := not(1) // Tsk.\n // We write the string from rightmost digit to leftmost digit.\n // The following is essentially a do-while loop that also handles the zero case.\n for { let temp := value } 1 {} {\n str := add(str, w) // `sub(str, 2)`.\n mstore8(add(str, 1), mload(and(temp, 15)))\n mstore8(str, mload(and(shr(4, temp), 15)))\n temp := shr(8, temp)\n if iszero(temp) { break }\n }\n\n // Compute the string's length.\n let strLength := sub(end, str)\n // Move the pointer and write the length.\n str := sub(str, 0x20)\n mstore(str, strLength)\n }\n }\n\n /// @dev Returns the hexadecimal representation of `value`.\n /// The output is prefixed with \"0x\", encoded using 2 hexadecimal digits per byte,\n /// and the alphabets are capitalized conditionally according to\n /// https://eips.ethereum.org/EIPS/eip-55\n function toHexStringChecksummed(address value) internal pure returns (string memory str) {\n str = toHexString(value);\n /// @solidity memory-safe-assembly\n assembly {\n let mask := shl(6, div(not(0), 255)) // `0b010000000100000000 ...`\n let o := add(str, 0x22)\n let hashed := and(keccak256(o, 40), mul(34, mask)) // `0b10001000 ... `\n let t := shl(240, 136) // `0b10001000 << 240`\n for { let i := 0 } 1 {} {\n mstore(add(i, i), mul(t, byte(i, hashed)))\n i := add(i, 1)\n if eq(i, 20) { break }\n }\n mstore(o, xor(mload(o), shr(1, and(mload(0x00), and(mload(o), mask)))))\n o := add(o, 0x20)\n mstore(o, xor(mload(o), shr(1, and(mload(0x20), and(mload(o), mask)))))\n }\n }\n\n /// @dev Returns the hexadecimal representation of `value`.\n /// The output is prefixed with \"0x\" and encoded using 2 hexadecimal digits per byte.\n function toHexString(address value) internal pure returns (string memory str) {\n str = toHexStringNoPrefix(value);\n /// @solidity memory-safe-assembly\n assembly {\n let strLength := add(mload(str), 2) // Compute the length.\n mstore(str, 0x3078) // Write the \"0x\" prefix.\n str := sub(str, 2) // Move the pointer.\n mstore(str, strLength) // Write the length.\n }\n }\n\n /// @dev Returns the hexadecimal representation of `value`.\n /// The output is encoded using 2 hexadecimal digits per byte.\n function toHexStringNoPrefix(address value) internal pure returns (string memory str) {\n /// @solidity memory-safe-assembly\n assembly {\n str := mload(0x40)\n\n // Allocate the memory.\n // We need 0x20 bytes for the trailing zeros padding, 0x20 bytes for the length,\n // 0x02 bytes for the prefix, and 0x28 bytes for the digits.\n // The next multiple of 0x20 above (0x20 + 0x20 + 0x02 + 0x28) is 0x80.\n mstore(0x40, add(str, 0x80))\n\n // Store \"0123456789abcdef\" in scratch space.\n mstore(0x0f, 0x30313233343536373839616263646566)\n\n str := add(str, 2)\n mstore(str, 40)\n\n let o := add(str, 0x20)\n mstore(add(o, 40), 0)\n\n value := shl(96, value)\n\n // We write the string from rightmost digit to leftmost digit.\n // The following is essentially a do-while loop that also handles the zero case.\n for { let i := 0 } 1 {} {\n let p := add(o, add(i, i))\n let temp := byte(i, value)\n mstore8(add(p, 1), mload(and(temp, 15)))\n mstore8(p, mload(shr(4, temp)))\n i := add(i, 1)\n if eq(i, 20) { break }\n }\n }\n }\n\n /// @dev Returns the hex encoded string from the raw bytes.\n /// The output is encoded using 2 hexadecimal digits per byte.\n function toHexString(bytes memory raw) internal pure returns (string memory str) {\n str = toHexStringNoPrefix(raw);\n /// @solidity memory-safe-assembly\n assembly {\n let strLength := add(mload(str), 2) // Compute the length.\n mstore(str, 0x3078) // Write the \"0x\" prefix.\n str := sub(str, 2) // Move the pointer.\n mstore(str, strLength) // Write the length.\n }\n }\n\n /// @dev Returns the hex encoded string from the raw bytes.\n /// The output is encoded using 2 hexadecimal digits per byte.\n function toHexStringNoPrefix(bytes memory raw) internal pure returns (string memory str) {\n /// @solidity memory-safe-assembly\n assembly {\n let length := mload(raw)\n str := add(mload(0x40), 2) // Skip 2 bytes for the optional prefix.\n mstore(str, add(length, length)) // Store the length of the output.\n\n // Store \"0123456789abcdef\" in scratch space.\n mstore(0x0f, 0x30313233343536373839616263646566)\n\n let o := add(str, 0x20)\n let end := add(raw, length)\n\n for {} iszero(eq(raw, end)) {} {\n raw := add(raw, 1)\n mstore8(add(o, 1), mload(and(mload(raw), 15)))\n mstore8(o, mload(and(shr(4, mload(raw)), 15)))\n o := add(o, 2)\n }\n mstore(o, 0) // Zeroize the slot after the string.\n mstore(0x40, add(o, 0x20)) // Allocate the memory.\n }\n }\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* RUNE STRING OPERATIONS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev Returns the number of UTF characters in the string.\n function runeCount(string memory s) internal pure returns (uint256 result) {\n /// @solidity memory-safe-assembly\n assembly {\n if mload(s) {\n mstore(0x00, div(not(0), 255))\n mstore(0x20, 0x0202020202020202020202020202020202020202020202020303030304040506)\n let o := add(s, 0x20)\n let end := add(o, mload(s))\n for { result := 1 } 1 { result := add(result, 1) } {\n o := add(o, byte(0, mload(shr(250, mload(o)))))\n if iszero(lt(o, end)) { break }\n }\n }\n }\n }\n\n /// @dev Returns if this string is a 7-bit ASCII string.\n /// (i.e. all characters codes are in [0..127])\n function is7BitASCII(string memory s) internal pure returns (bool result) {\n /// @solidity memory-safe-assembly\n assembly {\n let mask := shl(7, div(not(0), 255))\n result := 1\n let n := mload(s)\n if n {\n let o := add(s, 0x20)\n let end := add(o, n)\n let last := mload(end)\n mstore(end, 0)\n for {} 1 {} {\n if and(mask, mload(o)) {\n result := 0\n break\n }\n o := add(o, 0x20)\n if iszero(lt(o, end)) { break }\n }\n mstore(end, last)\n }\n }\n }\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* BYTE STRING OPERATIONS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n // For performance and bytecode compactness, byte string operations are restricted\n // to 7-bit ASCII strings. All offsets are byte offsets, not UTF character offsets.\n // Usage of byte string operations on charsets with runes spanning two or more bytes\n // can lead to undefined behavior.\n\n /// @dev Returns `subject` all occurrences of `search` replaced with `replacement`.\n function replace(string memory subject, string memory search, string memory replacement)\n internal\n pure\n returns (string memory result)\n {\n /// @solidity memory-safe-assembly\n assembly {\n let subjectLength := mload(subject)\n let searchLength := mload(search)\n let replacementLength := mload(replacement)\n\n subject := add(subject, 0x20)\n search := add(search, 0x20)\n replacement := add(replacement, 0x20)\n result := add(mload(0x40), 0x20)\n\n let subjectEnd := add(subject, subjectLength)\n if iszero(gt(searchLength, subjectLength)) {\n let subjectSearchEnd := add(sub(subjectEnd, searchLength), 1)\n let h := 0\n if iszero(lt(searchLength, 0x20)) { h := keccak256(search, searchLength) }\n let m := shl(3, sub(0x20, and(searchLength, 0x1f)))\n let s := mload(search)\n for {} 1 {} {\n let t := mload(subject)\n // Whether the first `searchLength % 32` bytes of\n // `subject` and `search` matches.\n if iszero(shr(m, xor(t, s))) {\n if h {\n if iszero(eq(keccak256(subject, searchLength), h)) {\n mstore(result, t)\n result := add(result, 1)\n subject := add(subject, 1)\n if iszero(lt(subject, subjectSearchEnd)) { break }\n continue\n }\n }\n // Copy the `replacement` one word at a time.\n for { let o := 0 } 1 {} {\n mstore(add(result, o), mload(add(replacement, o)))\n o := add(o, 0x20)\n if iszero(lt(o, replacementLength)) { break }\n }\n result := add(result, replacementLength)\n subject := add(subject, searchLength)\n if searchLength {\n if iszero(lt(subject, subjectSearchEnd)) { break }\n continue\n }\n }\n mstore(result, t)\n result := add(result, 1)\n subject := add(subject, 1)\n if iszero(lt(subject, subjectSearchEnd)) { break }\n }\n }\n\n let resultRemainder := result\n result := add(mload(0x40), 0x20)\n let k := add(sub(resultRemainder, result), sub(subjectEnd, subject))\n // Copy the rest of the string one word at a time.\n for {} lt(subject, subjectEnd) {} {\n mstore(resultRemainder, mload(subject))\n resultRemainder := add(resultRemainder, 0x20)\n subject := add(subject, 0x20)\n }\n result := sub(result, 0x20)\n let last := add(add(result, 0x20), k) // Zeroize the slot after the string.\n mstore(last, 0)\n mstore(0x40, add(last, 0x20)) // Allocate the memory.\n mstore(result, k) // Store the length.\n }\n }\n\n /// @dev Returns the byte index of the first location of `search` in `subject`,\n /// searching from left to right, starting from `from`.\n /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.\n function indexOf(string memory subject, string memory search, uint256 from)\n internal\n pure\n returns (uint256 result)\n {\n /// @solidity memory-safe-assembly\n assembly {\n for { let subjectLength := mload(subject) } 1 {} {\n if iszero(mload(search)) {\n if iszero(gt(from, subjectLength)) {\n result := from\n break\n }\n result := subjectLength\n break\n }\n let searchLength := mload(search)\n let subjectStart := add(subject, 0x20)\n\n result := not(0) // Initialize to `NOT_FOUND`.\n\n subject := add(subjectStart, from)\n let end := add(sub(add(subjectStart, subjectLength), searchLength), 1)\n\n let m := shl(3, sub(0x20, and(searchLength, 0x1f)))\n let s := mload(add(search, 0x20))\n\n if iszero(and(lt(subject, end), lt(from, subjectLength))) { break }\n\n if iszero(lt(searchLength, 0x20)) {\n for { let h := keccak256(add(search, 0x20), searchLength) } 1 {} {\n if iszero(shr(m, xor(mload(subject), s))) {\n if eq(keccak256(subject, searchLength), h) {\n result := sub(subject, subjectStart)\n break\n }\n }\n subject := add(subject, 1)\n if iszero(lt(subject, end)) { break }\n }\n break\n }\n for {} 1 {} {\n if iszero(shr(m, xor(mload(subject), s))) {\n result := sub(subject, subjectStart)\n break\n }\n subject := add(subject, 1)\n if iszero(lt(subject, end)) { break }\n }\n break\n }\n }\n }\n\n /// @dev Returns the byte index of the first location of `search` in `subject`,\n /// searching from left to right.\n /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.\n function indexOf(string memory subject, string memory search)\n internal\n pure\n returns (uint256 result)\n {\n result = indexOf(subject, search, 0);\n }\n\n /// @dev Returns the byte index of the first location of `search` in `subject`,\n /// searching from right to left, starting from `from`.\n /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.\n function lastIndexOf(string memory subject, string memory search, uint256 from)\n internal\n pure\n returns (uint256 result)\n {\n /// @solidity memory-safe-assembly\n assembly {\n for {} 1 {} {\n result := not(0) // Initialize to `NOT_FOUND`.\n let searchLength := mload(search)\n if gt(searchLength, mload(subject)) { break }\n let w := result\n\n let fromMax := sub(mload(subject), searchLength)\n if iszero(gt(fromMax, from)) { from := fromMax }\n\n let end := add(add(subject, 0x20), w)\n subject := add(add(subject, 0x20), from)\n if iszero(gt(subject, end)) { break }\n // As this function is not too often used,\n // we shall simply use keccak256 for smaller bytecode size.\n for { let h := keccak256(add(search, 0x20), searchLength) } 1 {} {\n if eq(keccak256(subject, searchLength), h) {\n result := sub(subject, add(end, 1))\n break\n }\n subject := add(subject, w) // `sub(subject, 1)`.\n if iszero(gt(subject, end)) { break }\n }\n break\n }\n }\n }\n\n /// @dev Returns the byte index of the first location of `search` in `subject`,\n /// searching from right to left.\n /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.\n function lastIndexOf(string memory subject, string memory search)\n internal\n pure\n returns (uint256 result)\n {\n result = lastIndexOf(subject, search, uint256(int256(-1)));\n }\n\n /// @dev Returns true if `search` is found in `subject`, false otherwise.\n function contains(string memory subject, string memory search) internal pure returns (bool) {\n return indexOf(subject, search) != NOT_FOUND;\n }\n\n /// @dev Returns whether `subject` starts with `search`.\n function startsWith(string memory subject, string memory search)\n internal\n pure\n returns (bool result)\n {\n /// @solidity memory-safe-assembly\n assembly {\n let searchLength := mload(search)\n // Just using keccak256 directly is actually cheaper.\n // forgefmt: disable-next-item\n result := and(\n iszero(gt(searchLength, mload(subject))),\n eq(\n keccak256(add(subject, 0x20), searchLength),\n keccak256(add(search, 0x20), searchLength)\n )\n )\n }\n }\n\n /// @dev Returns whether `subject` ends with `search`.\n function endsWith(string memory subject, string memory search)\n internal\n pure\n returns (bool result)\n {\n /// @solidity memory-safe-assembly\n assembly {\n let searchLength := mload(search)\n let subjectLength := mload(subject)\n // Whether `search` is not longer than `subject`.\n let withinRange := iszero(gt(searchLength, subjectLength))\n // Just using keccak256 directly is actually cheaper.\n // forgefmt: disable-next-item\n result := and(\n withinRange,\n eq(\n keccak256(\n // `subject + 0x20 + max(subjectLength - searchLength, 0)`.\n add(add(subject, 0x20), mul(withinRange, sub(subjectLength, searchLength))),\n searchLength\n ),\n keccak256(add(search, 0x20), searchLength)\n )\n )\n }\n }\n\n /// @dev Returns `subject` repeated `times`.\n function repeat(string memory subject, uint256 times)\n internal\n pure\n returns (string memory result)\n {\n /// @solidity memory-safe-assembly\n assembly {\n let subjectLength := mload(subject)\n if iszero(or(iszero(times), iszero(subjectLength))) {\n subject := add(subject, 0x20)\n result := mload(0x40)\n let output := add(result, 0x20)\n for {} 1 {} {\n // Copy the `subject` one word at a time.\n for { let o := 0 } 1 {} {\n mstore(add(output, o), mload(add(subject, o)))\n o := add(o, 0x20)\n if iszero(lt(o, subjectLength)) { break }\n }\n output := add(output, subjectLength)\n times := sub(times, 1)\n if iszero(times) { break }\n }\n mstore(output, 0) // Zeroize the slot after the string.\n let resultLength := sub(output, add(result, 0x20))\n mstore(result, resultLength) // Store the length.\n // Allocate the memory.\n mstore(0x40, add(result, add(resultLength, 0x20)))\n }\n }\n }\n\n /// @dev Returns a copy of `subject` sliced from `start` to `end` (exclusive).\n /// `start` and `end` are byte offsets.\n function slice(string memory subject, uint256 start, uint256 end)\n internal\n pure\n returns (string memory result)\n {\n /// @solidity memory-safe-assembly\n assembly {\n let subjectLength := mload(subject)\n if iszero(gt(subjectLength, end)) { end := subjectLength }\n if iszero(gt(subjectLength, start)) { start := subjectLength }\n if lt(start, end) {\n result := mload(0x40)\n let resultLength := sub(end, start)\n mstore(result, resultLength)\n subject := add(subject, start)\n let w := not(0x1f)\n // Copy the `subject` one word at a time, backwards.\n for { let o := and(add(resultLength, 0x1f), w) } 1 {} {\n mstore(add(result, o), mload(add(subject, o)))\n o := add(o, w) // `sub(o, 0x20)`.\n if iszero(o) { break }\n }\n // Zeroize the slot after the string.\n mstore(add(add(result, 0x20), resultLength), 0)\n // Allocate memory for the length and the bytes,\n // rounded up to a multiple of 32.\n mstore(0x40, add(result, and(add(resultLength, 0x3f), w)))\n }\n }\n }\n\n /// @dev Returns a copy of `subject` sliced from `start` to the end of the string.\n /// `start` is a byte offset.\n function slice(string memory subject, uint256 start)\n internal\n pure\n returns (string memory result)\n {\n result = slice(subject, start, uint256(int256(-1)));\n }\n\n /// @dev Returns all the indices of `search` in `subject`.\n /// The indices are byte offsets.\n function indicesOf(string memory subject, string memory search)\n internal\n pure\n returns (uint256[] memory result)\n {\n /// @solidity memory-safe-assembly\n assembly {\n let subjectLength := mload(subject)\n let searchLength := mload(search)\n\n if iszero(gt(searchLength, subjectLength)) {\n subject := add(subject, 0x20)\n search := add(search, 0x20)\n result := add(mload(0x40), 0x20)\n\n let subjectStart := subject\n let subjectSearchEnd := add(sub(add(subject, subjectLength), searchLength), 1)\n let h := 0\n if iszero(lt(searchLength, 0x20)) { h := keccak256(search, searchLength) }\n let m := shl(3, sub(0x20, and(searchLength, 0x1f)))\n let s := mload(search)\n for {} 1 {} {\n let t := mload(subject)\n // Whether the first `searchLength % 32` bytes of\n // `subject` and `search` matches.\n if iszero(shr(m, xor(t, s))) {\n if h {\n if iszero(eq(keccak256(subject, searchLength), h)) {\n subject := add(subject, 1)\n if iszero(lt(subject, subjectSearchEnd)) { break }\n continue\n }\n }\n // Append to `result`.\n mstore(result, sub(subject, subjectStart))\n result := add(result, 0x20)\n // Advance `subject` by `searchLength`.\n subject := add(subject, searchLength)\n if searchLength {\n if iszero(lt(subject, subjectSearchEnd)) { break }\n continue\n }\n }\n subject := add(subject, 1)\n if iszero(lt(subject, subjectSearchEnd)) { break }\n }\n let resultEnd := result\n // Assign `result` to the free memory pointer.\n result := mload(0x40)\n // Store the length of `result`.\n mstore(result, shr(5, sub(resultEnd, add(result, 0x20))))\n // Allocate memory for result.\n // We allocate one more word, so this array can be recycled for {split}.\n mstore(0x40, add(resultEnd, 0x20))\n }\n }\n }\n\n /// @dev Returns a arrays of strings based on the `delimiter` inside of the `subject` string.\n function split(string memory subject, string memory delimiter)\n internal\n pure\n returns (string[] memory result)\n {\n uint256[] memory indices = indicesOf(subject, delimiter);\n /// @solidity memory-safe-assembly\n assembly {\n let w := not(0x1f)\n let indexPtr := add(indices, 0x20)\n let indicesEnd := add(indexPtr, shl(5, add(mload(indices), 1)))\n mstore(add(indicesEnd, w), mload(subject))\n mstore(indices, add(mload(indices), 1))\n let prevIndex := 0\n for {} 1 {} {\n let index := mload(indexPtr)\n mstore(indexPtr, 0x60)\n if iszero(eq(index, prevIndex)) {\n let element := mload(0x40)\n let elementLength := sub(index, prevIndex)\n mstore(element, elementLength)\n // Copy the `subject` one word at a time, backwards.\n for { let o := and(add(elementLength, 0x1f), w) } 1 {} {\n mstore(add(element, o), mload(add(add(subject, prevIndex), o)))\n o := add(o, w) // `sub(o, 0x20)`.\n if iszero(o) { break }\n }\n // Zeroize the slot after the string.\n mstore(add(add(element, 0x20), elementLength), 0)\n // Allocate memory for the length and the bytes,\n // rounded up to a multiple of 32.\n mstore(0x40, add(element, and(add(elementLength, 0x3f), w)))\n // Store the `element` into the array.\n mstore(indexPtr, element)\n }\n prevIndex := add(index, mload(delimiter))\n indexPtr := add(indexPtr, 0x20)\n if iszero(lt(indexPtr, indicesEnd)) { break }\n }\n result := indices\n if iszero(mload(delimiter)) {\n result := add(indices, 0x20)\n mstore(result, sub(mload(indices), 2))\n }\n }\n }\n\n /// @dev Returns a concatenated string of `a` and `b`.\n /// Cheaper than `string.concat()` and does not de-align the free memory pointer.\n function concat(string memory a, string memory b)\n internal\n pure\n returns (string memory result)\n {\n /// @solidity memory-safe-assembly\n assembly {\n let w := not(0x1f)\n result := mload(0x40)\n let aLength := mload(a)\n // Copy `a` one word at a time, backwards.\n for { let o := and(add(aLength, 0x20), w) } 1 {} {\n mstore(add(result, o), mload(add(a, o)))\n o := add(o, w) // `sub(o, 0x20)`.\n if iszero(o) { break }\n }\n let bLength := mload(b)\n let output := add(result, aLength)\n // Copy `b` one word at a time, backwards.\n for { let o := and(add(bLength, 0x20), w) } 1 {} {\n mstore(add(output, o), mload(add(b, o)))\n o := add(o, w) // `sub(o, 0x20)`.\n if iszero(o) { break }\n }\n let totalLength := add(aLength, bLength)\n let last := add(add(result, 0x20), totalLength)\n // Zeroize the slot after the string.\n mstore(last, 0)\n // Stores the length.\n mstore(result, totalLength)\n // Allocate memory for the length and the bytes,\n // rounded up to a multiple of 32.\n mstore(0x40, and(add(last, 0x1f), w))\n }\n }\n\n /// @dev Returns a copy of the string in either lowercase or UPPERCASE.\n /// WARNING! This function is only compatible with 7-bit ASCII strings.\n function toCase(string memory subject, bool toUpper)\n internal\n pure\n returns (string memory result)\n {\n /// @solidity memory-safe-assembly\n assembly {\n let length := mload(subject)\n if length {\n result := add(mload(0x40), 0x20)\n subject := add(subject, 1)\n let flags := shl(add(70, shl(5, toUpper)), 0x3ffffff)\n let w := not(0)\n for { let o := length } 1 {} {\n o := add(o, w)\n let b := and(0xff, mload(add(subject, o)))\n mstore8(add(result, o), xor(b, and(shr(b, flags), 0x20)))\n if iszero(o) { break }\n }\n result := mload(0x40)\n mstore(result, length) // Store the length.\n let last := add(add(result, 0x20), length)\n mstore(last, 0) // Zeroize the slot after the string.\n mstore(0x40, add(last, 0x20)) // Allocate the memory.\n }\n }\n }\n\n /// @dev Returns a string from a small bytes32 string.\n /// `s` must be null-terminated, or behavior will be undefined.\n function fromSmallString(bytes32 s) internal pure returns (string memory result) {\n /// @solidity memory-safe-assembly\n assembly {\n result := mload(0x40)\n let n := 0\n for {} byte(n, s) { n := add(n, 1) } {} // Scan for '\\0'.\n mstore(result, n)\n let o := add(result, 0x20)\n mstore(o, s)\n mstore(add(o, n), 0)\n mstore(0x40, add(result, 0x40))\n }\n }\n\n /// @dev Returns the small string, with all bytes after the first null byte zeroized.\n function normalizeSmallString(bytes32 s) internal pure returns (bytes32 result) {\n /// @solidity memory-safe-assembly\n assembly {\n for {} byte(result, s) { result := add(result, 1) } {} // Scan for '\\0'.\n mstore(0x00, s)\n mstore(result, 0x00)\n result := mload(0x00)\n }\n }\n\n /// @dev Returns the string as a normalized null-terminated small string.\n function toSmallString(string memory s) internal pure returns (bytes32 result) {\n /// @solidity memory-safe-assembly\n assembly {\n result := mload(s)\n if iszero(lt(result, 33)) {\n mstore(0x00, 0xec92f9a3) // `TooBigForSmallString()`.\n revert(0x1c, 0x04)\n }\n result := shl(shl(3, sub(32, result)), mload(add(s, result)))\n }\n }\n\n /// @dev Returns a lowercased copy of the string.\n /// WARNING! This function is only compatible with 7-bit ASCII strings.\n function lower(string memory subject) internal pure returns (string memory result) {\n result = toCase(subject, false);\n }\n\n /// @dev Returns an UPPERCASED copy of the string.\n /// WARNING! This function is only compatible with 7-bit ASCII strings.\n function upper(string memory subject) internal pure returns (string memory result) {\n result = toCase(subject, true);\n }\n\n /// @dev Escapes the string to be used within HTML tags.\n function escapeHTML(string memory s) internal pure returns (string memory result) {\n /// @solidity memory-safe-assembly\n assembly {\n let end := add(s, mload(s))\n result := add(mload(0x40), 0x20)\n // Store the bytes of the packed offsets and strides into the scratch space.\n // `packed = (stride << 5) | offset`. Max offset is 20. Max stride is 6.\n mstore(0x1f, 0x900094)\n mstore(0x08, 0xc0000000a6ab)\n // Store \""&'<>\" into the scratch space.\n mstore(0x00, shl(64, 0x2671756f743b26616d703b262333393b266c743b2667743b))\n for {} iszero(eq(s, end)) {} {\n s := add(s, 1)\n let c := and(mload(s), 0xff)\n // Not in `[\"\\\"\",\"'\",\"&\",\"<\",\">\"]`.\n if iszero(and(shl(c, 1), 0x500000c400000000)) {\n mstore8(result, c)\n result := add(result, 1)\n continue\n }\n let t := shr(248, mload(c))\n mstore(result, mload(and(t, 0x1f)))\n result := add(result, shr(5, t))\n }\n let last := result\n mstore(last, 0) // Zeroize the slot after the string.\n result := mload(0x40)\n mstore(result, sub(last, add(result, 0x20))) // Store the length.\n mstore(0x40, add(last, 0x20)) // Allocate the memory.\n }\n }\n\n /// @dev Escapes the string to be used within double-quotes in a JSON.\n /// If `addDoubleQuotes` is true, the result will be enclosed in double-quotes.\n function escapeJSON(string memory s, bool addDoubleQuotes)\n internal\n pure\n returns (string memory result)\n {\n /// @solidity memory-safe-assembly\n assembly {\n let end := add(s, mload(s))\n result := add(mload(0x40), 0x20)\n if addDoubleQuotes {\n mstore8(result, 34)\n result := add(1, result)\n }\n // Store \"\\\\u0000\" in scratch space.\n // Store \"0123456789abcdef\" in scratch space.\n // Also, store `{0x08:\"b\", 0x09:\"t\", 0x0a:\"n\", 0x0c:\"f\", 0x0d:\"r\"}`.\n // into the scratch space.\n mstore(0x15, 0x5c75303030303031323334353637383961626364656662746e006672)\n // Bitmask for detecting `[\"\\\"\",\"\\\\\"]`.\n let e := or(shl(0x22, 1), shl(0x5c, 1))\n for {} iszero(eq(s, end)) {} {\n s := add(s, 1)\n let c := and(mload(s), 0xff)\n if iszero(lt(c, 0x20)) {\n if iszero(and(shl(c, 1), e)) {\n // Not in `[\"\\\"\",\"\\\\\"]`.\n mstore8(result, c)\n result := add(result, 1)\n continue\n }\n mstore8(result, 0x5c) // \"\\\\\".\n mstore8(add(result, 1), c)\n result := add(result, 2)\n continue\n }\n if iszero(and(shl(c, 1), 0x3700)) {\n // Not in `[\"\\b\",\"\\t\",\"\\n\",\"\\f\",\"\\d\"]`.\n mstore8(0x1d, mload(shr(4, c))) // Hex value.\n mstore8(0x1e, mload(and(c, 15))) // Hex value.\n mstore(result, mload(0x19)) // \"\\\\u00XX\".\n result := add(result, 6)\n continue\n }\n mstore8(result, 0x5c) // \"\\\\\".\n mstore8(add(result, 1), mload(add(c, 8)))\n result := add(result, 2)\n }\n if addDoubleQuotes {\n mstore8(result, 34)\n result := add(1, result)\n }\n let last := result\n mstore(last, 0) // Zeroize the slot after the string.\n result := mload(0x40)\n mstore(result, sub(last, add(result, 0x20))) // Store the length.\n mstore(0x40, add(last, 0x20)) // Allocate the memory.\n }\n }\n\n /// @dev Escapes the string to be used within double-quotes in a JSON.\n function escapeJSON(string memory s) internal pure returns (string memory result) {\n result = escapeJSON(s, false);\n }\n\n /// @dev Returns whether `a` equals `b`.\n function eq(string memory a, string memory b) internal pure returns (bool result) {\n /// @solidity memory-safe-assembly\n assembly {\n result := eq(keccak256(add(a, 0x20), mload(a)), keccak256(add(b, 0x20), mload(b)))\n }\n }\n\n /// @dev Returns whether `a` equals `b`, where `b` is a null-terminated small string.\n function eqs(string memory a, bytes32 b) internal pure returns (bool result) {\n /// @solidity memory-safe-assembly\n assembly {\n // These should be evaluated on compile time, as far as possible.\n let m := not(shl(7, div(not(iszero(b)), 255))) // `0x7f7f ...`.\n let x := not(or(m, or(b, add(m, and(b, m)))))\n let r := shl(7, iszero(iszero(shr(128, x))))\n r := or(r, shl(6, iszero(iszero(shr(64, shr(r, x))))))\n r := or(r, shl(5, lt(0xffffffff, shr(r, x))))\n r := or(r, shl(4, lt(0xffff, shr(r, x))))\n r := or(r, shl(3, lt(0xff, shr(r, x))))\n // forgefmt: disable-next-item\n result := gt(eq(mload(a), add(iszero(x), xor(31, shr(3, r)))),\n xor(shr(add(8, r), b), shr(add(8, r), mload(add(a, 0x20)))))\n }\n }\n\n /// @dev Packs a single string with its length into a single word.\n /// Returns `bytes32(0)` if the length is zero or greater than 31.\n function packOne(string memory a) internal pure returns (bytes32 result) {\n /// @solidity memory-safe-assembly\n assembly {\n // We don't need to zero right pad the string,\n // since this is our own custom non-standard packing scheme.\n result :=\n mul(\n // Load the length and the bytes.\n mload(add(a, 0x1f)),\n // `length != 0 && length < 32`. Abuses underflow.\n // Assumes that the length is valid and within the block gas limit.\n lt(sub(mload(a), 1), 0x1f)\n )\n }\n }\n\n /// @dev Unpacks a string packed using {packOne}.\n /// Returns the empty string if `packed` is `bytes32(0)`.\n /// If `packed` is not an output of {packOne}, the output behavior is undefined.\n function unpackOne(bytes32 packed) internal pure returns (string memory result) {\n /// @solidity memory-safe-assembly\n assembly {\n // Grab the free memory pointer.\n result := mload(0x40)\n // Allocate 2 words (1 for the length, 1 for the bytes).\n mstore(0x40, add(result, 0x40))\n // Zeroize the length slot.\n mstore(result, 0)\n // Store the length and bytes.\n mstore(add(result, 0x1f), packed)\n // Right pad with zeroes.\n mstore(add(add(result, 0x20), mload(result)), 0)\n }\n }\n\n /// @dev Packs two strings with their lengths into a single word.\n /// Returns `bytes32(0)` if combined length is zero or greater than 30.\n function packTwo(string memory a, string memory b) internal pure returns (bytes32 result) {\n /// @solidity memory-safe-assembly\n assembly {\n let aLength := mload(a)\n // We don't need to zero right pad the strings,\n // since this is our own custom non-standard packing scheme.\n result :=\n mul(\n // Load the length and the bytes of `a` and `b`.\n or(\n shl(shl(3, sub(0x1f, aLength)), mload(add(a, aLength))),\n mload(sub(add(b, 0x1e), aLength))\n ),\n // `totalLength != 0 && totalLength < 31`. Abuses underflow.\n // Assumes that the lengths are valid and within the block gas limit.\n lt(sub(add(aLength, mload(b)), 1), 0x1e)\n )\n }\n }\n\n /// @dev Unpacks strings packed using {packTwo}.\n /// Returns the empty strings if `packed` is `bytes32(0)`.\n /// If `packed` is not an output of {packTwo}, the output behavior is undefined.\n function unpackTwo(bytes32 packed)\n internal\n pure\n returns (string memory resultA, string memory resultB)\n {\n /// @solidity memory-safe-assembly\n assembly {\n // Grab the free memory pointer.\n resultA := mload(0x40)\n resultB := add(resultA, 0x40)\n // Allocate 2 words for each string (1 for the length, 1 for the byte). Total 4 words.\n mstore(0x40, add(resultB, 0x40))\n // Zeroize the length slots.\n mstore(resultA, 0)\n mstore(resultB, 0)\n // Store the lengths and bytes.\n mstore(add(resultA, 0x1f), packed)\n mstore(add(resultB, 0x1f), mload(add(add(resultA, 0x20), mload(resultA))))\n // Right pad with zeroes.\n mstore(add(add(resultA, 0x20), mload(resultA)), 0)\n mstore(add(add(resultB, 0x20), mload(resultB)), 0)\n }\n }\n\n /// @dev Directly returns `a` without copying.\n function directReturn(string memory a) internal pure {\n assembly {\n // Assumes that the string does not start from the scratch space.\n let retStart := sub(a, 0x20)\n let retSize := add(mload(a), 0x40)\n // Right pad with zeroes. Just in case the string is produced\n // by a method that doesn't zero right pad.\n mstore(add(retStart, retSize), 0)\n // Store the return offset.\n mstore(retStart, 0x20)\n // End the transaction, returning the string.\n return(retStart, retSize)\n }\n }\n}\n"},"src/utils/DateTimeUtils.sol":{"content":"// SPDX-License-Identifier: GPL-3.0\npragma solidity ^0.8.0;\n\nimport {DateTimeLib} from \"solady/utils/DateTimeLib.sol\";\nimport {LibString} from \"solady/utils/LibString.sol\";\n\nlibrary DateTimeUtils {\n using LibString for string;\n\n /*\n * @dev Convert a DER-encoded time to a unix timestamp\n * @param x509Time The DER-encoded time\n * @return The unix timestamp\n */\n function fromDERToTimestamp(bytes memory x509Time) internal pure returns (uint256) {\n uint16 yrs;\n uint8 mnths;\n uint8 dys;\n uint8 hrs;\n uint8 mins;\n uint8 secs;\n uint8 offset;\n\n if (x509Time.length == 13) {\n if (uint8(x509Time[0]) - 48 < 5) yrs += 2000;\n else yrs += 1900;\n } else {\n yrs += (uint8(x509Time[0]) - 48) * 1000 + (uint8(x509Time[1]) - 48) * 100;\n offset = 2;\n }\n yrs += (uint8(x509Time[offset + 0]) - 48) * 10 + uint8(x509Time[offset + 1]) - 48;\n mnths = (uint8(x509Time[offset + 2]) - 48) * 10 + uint8(x509Time[offset + 3]) - 48;\n dys += (uint8(x509Time[offset + 4]) - 48) * 10 + uint8(x509Time[offset + 5]) - 48;\n hrs += (uint8(x509Time[offset + 6]) - 48) * 10 + uint8(x509Time[offset + 7]) - 48;\n mins += (uint8(x509Time[offset + 8]) - 48) * 10 + uint8(x509Time[offset + 9]) - 48;\n secs += (uint8(x509Time[offset + 10]) - 48) * 10 + uint8(x509Time[offset + 11]) - 48;\n\n return DateTimeLib.dateTimeToTimestamp(yrs, mnths, dys, hrs, mins, secs);\n }\n\n /// @dev iso follows pattern: \"YYYY-MM-DDTHH:mm:ssZ\"\n function fromISOToTimestamp(string memory iso) internal pure returns (uint256) {\n require(bytes(iso).length == 20, \"invalid iso string length\");\n uint256 y = stringToUint(iso.slice(0, 4));\n uint256 m = stringToUint(iso.slice(5, 7));\n uint256 d = stringToUint(iso.slice(8, 10));\n uint256 h = stringToUint(iso.slice(11, 13));\n uint256 min = stringToUint(iso.slice(14, 16));\n uint256 s = stringToUint(iso.slice(17, 19));\n\n return DateTimeLib.dateTimeToTimestamp(y, m, d, h, min, s);\n }\n\n // https://ethereum.stackexchange.com/questions/10932/how-to-convert-string-to-int\n function stringToUint(string memory s) private pure returns (uint256 result) {\n bytes memory b = bytes(s);\n result = 0;\n for (uint256 i = 0; i < b.length; i++) {\n uint256 c = uint256(uint8(b[i]));\n if (c >= 48 && c <= 57) {\n result = result * 10 + (c - 48);\n }\n }\n }\n}\n"},"lib/solady/src/utils/DateTimeLib.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\n/// @notice Library for date time operations.\n/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/DateTimeLib.sol)\n///\n/// Conventions:\n/// --------------------------------------------------------------------+\n/// Unit | Range | Notes |\n/// --------------------------------------------------------------------|\n/// timestamp | 0..0x1e18549868c76ff | Unix timestamp. |\n/// epochDay | 0..0x16d3e098039 | Days since 1970-01-01. |\n/// year | 1970..0xffffffff | Gregorian calendar year. |\n/// month | 1..12 | Gregorian calendar month. |\n/// day | 1..31 | Gregorian calendar day of month. |\n/// weekday | 1..7 | The day of the week (1-indexed). |\n/// --------------------------------------------------------------------+\n/// All timestamps of days are rounded down to 00:00:00 UTC.\nlibrary DateTimeLib {\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* CONSTANTS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n // Weekdays are 1-indexed for a traditional rustic feel.\n\n // \"And on the seventh day God finished his work that he had done,\n // and he rested on the seventh day from all his work that he had done.\"\n // -- Genesis 2:2\n\n uint256 internal constant MON = 1;\n uint256 internal constant TUE = 2;\n uint256 internal constant WED = 3;\n uint256 internal constant THU = 4;\n uint256 internal constant FRI = 5;\n uint256 internal constant SAT = 6;\n uint256 internal constant SUN = 7;\n\n // Months and days of months are 1-indexed for ease of use.\n\n uint256 internal constant JAN = 1;\n uint256 internal constant FEB = 2;\n uint256 internal constant MAR = 3;\n uint256 internal constant APR = 4;\n uint256 internal constant MAY = 5;\n uint256 internal constant JUN = 6;\n uint256 internal constant JUL = 7;\n uint256 internal constant AUG = 8;\n uint256 internal constant SEP = 9;\n uint256 internal constant OCT = 10;\n uint256 internal constant NOV = 11;\n uint256 internal constant DEC = 12;\n\n // These limits are large enough for most practical purposes.\n // Inputs that exceed these limits result in undefined behavior.\n\n uint256 internal constant MAX_SUPPORTED_YEAR = 0xffffffff;\n uint256 internal constant MAX_SUPPORTED_EPOCH_DAY = 0x16d3e098039;\n uint256 internal constant MAX_SUPPORTED_TIMESTAMP = 0x1e18549868c76ff;\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* DATE TIME OPERATIONS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev Returns the number of days since 1970-01-01 from (`year`,`month`,`day`).\n /// See: https://howardhinnant.github.io/date_algorithms.html\n /// Note: Inputs outside the supported ranges result in undefined behavior.\n /// Use {isSupportedDate} to check if the inputs are supported.\n function dateToEpochDay(uint256 year, uint256 month, uint256 day)\n internal\n pure\n returns (uint256 epochDay)\n {\n /// @solidity memory-safe-assembly\n assembly {\n year := sub(year, lt(month, 3))\n let doy := add(shr(11, add(mul(62719, mod(add(month, 9), 12)), 769)), day)\n let yoe := mod(year, 400)\n let doe := sub(add(add(mul(yoe, 365), shr(2, yoe)), doy), div(yoe, 100))\n epochDay := sub(add(mul(div(year, 400), 146097), doe), 719469)\n }\n }\n\n /// @dev Returns (`year`,`month`,`day`) from the number of days since 1970-01-01.\n /// Note: Inputs outside the supported ranges result in undefined behavior.\n /// Use {isSupportedDays} to check if the inputs is supported.\n function epochDayToDate(uint256 epochDay)\n internal\n pure\n returns (uint256 year, uint256 month, uint256 day)\n {\n /// @solidity memory-safe-assembly\n assembly {\n epochDay := add(epochDay, 719468)\n let doe := mod(epochDay, 146097)\n let yoe :=\n div(sub(sub(add(doe, div(doe, 36524)), div(doe, 1460)), eq(doe, 146096)), 365)\n let doy := sub(doe, sub(add(mul(365, yoe), shr(2, yoe)), div(yoe, 100)))\n let mp := div(add(mul(5, doy), 2), 153)\n day := add(sub(doy, shr(11, add(mul(mp, 62719), 769))), 1)\n month := byte(mp, shl(160, 0x030405060708090a0b0c0102))\n year := add(add(yoe, mul(div(epochDay, 146097), 400)), lt(month, 3))\n }\n }\n\n /// @dev Returns the unix timestamp from (`year`,`month`,`day`).\n /// Note: Inputs outside the supported ranges result in undefined behavior.\n /// Use {isSupportedDate} to check if the inputs are supported.\n function dateToTimestamp(uint256 year, uint256 month, uint256 day)\n internal\n pure\n returns (uint256 result)\n {\n unchecked {\n result = dateToEpochDay(year, month, day) * 86400;\n }\n }\n\n /// @dev Returns (`year`,`month`,`day`) from the given unix timestamp.\n /// Note: Inputs outside the supported ranges result in undefined behavior.\n /// Use {isSupportedTimestamp} to check if the inputs are supported.\n function timestampToDate(uint256 timestamp)\n internal\n pure\n returns (uint256 year, uint256 month, uint256 day)\n {\n (year, month, day) = epochDayToDate(timestamp / 86400);\n }\n\n /// @dev Returns the unix timestamp from\n /// (`year`,`month`,`day`,`hour`,`minute`,`second`).\n /// Note: Inputs outside the supported ranges result in undefined behavior.\n /// Use {isSupportedDateTime} to check if the inputs are supported.\n function dateTimeToTimestamp(\n uint256 year,\n uint256 month,\n uint256 day,\n uint256 hour,\n uint256 minute,\n uint256 second\n ) internal pure returns (uint256 result) {\n unchecked {\n result = dateToEpochDay(year, month, day) * 86400 + hour * 3600 + minute * 60 + second;\n }\n }\n\n /// @dev Returns (`year`,`month`,`day`,`hour`,`minute`,`second`)\n /// from the given unix timestamp.\n /// Note: Inputs outside the supported ranges result in undefined behavior.\n /// Use {isSupportedTimestamp} to check if the inputs are supported.\n function timestampToDateTime(uint256 timestamp)\n internal\n pure\n returns (\n uint256 year,\n uint256 month,\n uint256 day,\n uint256 hour,\n uint256 minute,\n uint256 second\n )\n {\n unchecked {\n (year, month, day) = epochDayToDate(timestamp / 86400);\n uint256 secs = timestamp % 86400;\n hour = secs / 3600;\n secs = secs % 3600;\n minute = secs / 60;\n second = secs % 60;\n }\n }\n\n /// @dev Returns if the `year` is leap.\n function isLeapYear(uint256 year) internal pure returns (bool leap) {\n /// @solidity memory-safe-assembly\n assembly {\n leap := iszero(and(add(mul(iszero(mod(year, 25)), 12), 3), year))\n }\n }\n\n /// @dev Returns number of days in given `month` of `year`.\n function daysInMonth(uint256 year, uint256 month) internal pure returns (uint256 result) {\n bool flag = isLeapYear(year);\n /// @solidity memory-safe-assembly\n assembly {\n // `daysInMonths = [31,28,31,30,31,30,31,31,30,31,30,31]`.\n // `result = daysInMonths[month - 1] + isLeapYear(year)`.\n result :=\n add(byte(month, shl(152, 0x1F1C1F1E1F1E1F1F1E1F1E1F)), and(eq(month, 2), flag))\n }\n }\n\n /// @dev Returns the weekday from the unix timestamp.\n /// Monday: 1, Tuesday: 2, ....., Sunday: 7.\n function weekday(uint256 timestamp) internal pure returns (uint256 result) {\n unchecked {\n result = ((timestamp / 86400 + 3) % 7) + 1;\n }\n }\n\n /// @dev Returns if (`year`,`month`,`day`) is a supported date.\n /// - `1970 <= year <= MAX_SUPPORTED_YEAR`.\n /// - `1 <= month <= 12`.\n /// - `1 <= day <= daysInMonth(year, month)`.\n function isSupportedDate(uint256 year, uint256 month, uint256 day)\n internal\n pure\n returns (bool result)\n {\n uint256 md = daysInMonth(year, month);\n /// @solidity memory-safe-assembly\n assembly {\n let w := not(0)\n result :=\n and(\n lt(sub(year, 1970), sub(MAX_SUPPORTED_YEAR, 1969)),\n and(lt(add(month, w), 12), lt(add(day, w), md))\n )\n }\n }\n\n /// @dev Returns if (`year`,`month`,`day`,`hour`,`minute`,`second`) is a supported date time.\n /// - `1970 <= year <= MAX_SUPPORTED_YEAR`.\n /// - `1 <= month <= 12`.\n /// - `1 <= day <= daysInMonth(year, month)`.\n /// - `hour < 24`.\n /// - `minute < 60`.\n /// - `second < 60`.\n function isSupportedDateTime(\n uint256 year,\n uint256 month,\n uint256 day,\n uint256 hour,\n uint256 minute,\n uint256 second\n ) internal pure returns (bool result) {\n if (isSupportedDate(year, month, day)) {\n /// @solidity memory-safe-assembly\n assembly {\n result := and(lt(hour, 24), and(lt(minute, 60), lt(second, 60)))\n }\n }\n }\n\n /// @dev Returns if `epochDay` is a supported unix epoch day.\n function isSupportedEpochDay(uint256 epochDay) internal pure returns (bool result) {\n unchecked {\n result = epochDay < MAX_SUPPORTED_EPOCH_DAY + 1;\n }\n }\n\n /// @dev Returns if `timestamp` is a supported unix timestamp.\n function isSupportedTimestamp(uint256 timestamp) internal pure returns (bool result) {\n unchecked {\n result = timestamp < MAX_SUPPORTED_TIMESTAMP + 1;\n }\n }\n\n /// @dev Returns the unix timestamp of the given `n`th weekday `wd`, in `month` of `year`.\n /// Example: 3rd Friday of Feb 2022 is `nthWeekdayInMonthOfYearTimestamp(2022, 2, 3, 5)`\n /// Note: `n` is 1-indexed for traditional consistency.\n /// Invalid weekdays (i.e. `wd == 0 || wd > 7`) result in undefined behavior.\n function nthWeekdayInMonthOfYearTimestamp(uint256 year, uint256 month, uint256 n, uint256 wd)\n internal\n pure\n returns (uint256 result)\n {\n uint256 d = dateToEpochDay(year, month, 1);\n uint256 md = daysInMonth(year, month);\n /// @solidity memory-safe-assembly\n assembly {\n let diff := sub(wd, add(mod(add(d, 3), 7), 1))\n let date := add(mul(sub(n, 1), 7), add(mul(gt(diff, 6), 7), diff))\n result := mul(mul(86400, add(date, d)), and(lt(date, md), iszero(iszero(n))))\n }\n }\n\n /// @dev Returns the unix timestamp of the most recent Monday.\n function mondayTimestamp(uint256 timestamp) internal pure returns (uint256 result) {\n uint256 t = timestamp;\n /// @solidity memory-safe-assembly\n assembly {\n let day := div(t, 86400)\n result := mul(mul(sub(day, mod(add(day, 3), 7)), 86400), gt(t, 345599))\n }\n }\n\n /// @dev Returns whether the unix timestamp falls on a Saturday or Sunday.\n /// To check whether it is a week day, just take the negation of the result.\n function isWeekEnd(uint256 timestamp) internal pure returns (bool result) {\n result = weekday(timestamp) > FRI;\n }\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* DATE TIME ARITHMETIC OPERATIONS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev Adds `numYears` to the unix timestamp, and returns the result.\n /// Note: The result will share the same Gregorian calendar month,\n /// but different Gregorian calendar years for non-zero `numYears`.\n /// If the Gregorian calendar month of the result has less days\n /// than the Gregorian calendar month day of the `timestamp`,\n /// the result's month day will be the maximum possible value for the month.\n /// (e.g. from 29th Feb to 28th Feb)\n function addYears(uint256 timestamp, uint256 numYears) internal pure returns (uint256 result) {\n (uint256 year, uint256 month, uint256 day) = epochDayToDate(timestamp / 86400);\n result = _offsetted(year + numYears, month, day, timestamp);\n }\n\n /// @dev Adds `numMonths` to the unix timestamp, and returns the result.\n /// Note: If the Gregorian calendar month of the result has less days\n /// than the Gregorian calendar month day of the `timestamp`,\n /// the result's month day will be the maximum possible value for the month.\n /// (e.g. from 29th Feb to 28th Feb)\n function addMonths(uint256 timestamp, uint256 numMonths)\n internal\n pure\n returns (uint256 result)\n {\n (uint256 year, uint256 month, uint256 day) = epochDayToDate(timestamp / 86400);\n month = _sub(month + numMonths, 1);\n result = _offsetted(year + month / 12, _add(month % 12, 1), day, timestamp);\n }\n\n /// @dev Adds `numDays` to the unix timestamp, and returns the result.\n function addDays(uint256 timestamp, uint256 numDays) internal pure returns (uint256 result) {\n result = timestamp + numDays * 86400;\n }\n\n /// @dev Adds `numHours` to the unix timestamp, and returns the result.\n function addHours(uint256 timestamp, uint256 numHours) internal pure returns (uint256 result) {\n result = timestamp + numHours * 3600;\n }\n\n /// @dev Adds `numMinutes` to the unix timestamp, and returns the result.\n function addMinutes(uint256 timestamp, uint256 numMinutes)\n internal\n pure\n returns (uint256 result)\n {\n result = timestamp + numMinutes * 60;\n }\n\n /// @dev Adds `numSeconds` to the unix timestamp, and returns the result.\n function addSeconds(uint256 timestamp, uint256 numSeconds)\n internal\n pure\n returns (uint256 result)\n {\n result = timestamp + numSeconds;\n }\n\n /// @dev Subtracts `numYears` from the unix timestamp, and returns the result.\n /// Note: The result will share the same Gregorian calendar month,\n /// but different Gregorian calendar years for non-zero `numYears`.\n /// If the Gregorian calendar month of the result has less days\n /// than the Gregorian calendar month day of the `timestamp`,\n /// the result's month day will be the maximum possible value for the month.\n /// (e.g. from 29th Feb to 28th Feb)\n function subYears(uint256 timestamp, uint256 numYears) internal pure returns (uint256 result) {\n (uint256 year, uint256 month, uint256 day) = epochDayToDate(timestamp / 86400);\n result = _offsetted(year - numYears, month, day, timestamp);\n }\n\n /// @dev Subtracts `numYears` from the unix timestamp, and returns the result.\n /// Note: If the Gregorian calendar month of the result has less days\n /// than the Gregorian calendar month day of the `timestamp`,\n /// the result's month day will be the maximum possible value for the month.\n /// (e.g. from 29th Feb to 28th Feb)\n function subMonths(uint256 timestamp, uint256 numMonths)\n internal\n pure\n returns (uint256 result)\n {\n (uint256 year, uint256 month, uint256 day) = epochDayToDate(timestamp / 86400);\n uint256 yearMonth = _totalMonths(year, month) - _add(numMonths, 1);\n result = _offsetted(yearMonth / 12, _add(yearMonth % 12, 1), day, timestamp);\n }\n\n /// @dev Subtracts `numDays` from the unix timestamp, and returns the result.\n function subDays(uint256 timestamp, uint256 numDays) internal pure returns (uint256 result) {\n result = timestamp - numDays * 86400;\n }\n\n /// @dev Subtracts `numHours` from the unix timestamp, and returns the result.\n function subHours(uint256 timestamp, uint256 numHours) internal pure returns (uint256 result) {\n result = timestamp - numHours * 3600;\n }\n\n /// @dev Subtracts `numMinutes` from the unix timestamp, and returns the result.\n function subMinutes(uint256 timestamp, uint256 numMinutes)\n internal\n pure\n returns (uint256 result)\n {\n result = timestamp - numMinutes * 60;\n }\n\n /// @dev Subtracts `numSeconds` from the unix timestamp, and returns the result.\n function subSeconds(uint256 timestamp, uint256 numSeconds)\n internal\n pure\n returns (uint256 result)\n {\n result = timestamp - numSeconds;\n }\n\n /// @dev Returns the difference in Gregorian calendar years\n /// between `fromTimestamp` and `toTimestamp`.\n /// Note: Even if the true time difference is less than a year,\n /// the difference can be non-zero is the timestamps are\n /// from different Gregorian calendar years\n function diffYears(uint256 fromTimestamp, uint256 toTimestamp)\n internal\n pure\n returns (uint256 result)\n {\n toTimestamp - fromTimestamp;\n (uint256 fromYear,,) = epochDayToDate(fromTimestamp / 86400);\n (uint256 toYear,,) = epochDayToDate(toTimestamp / 86400);\n result = _sub(toYear, fromYear);\n }\n\n /// @dev Returns the difference in Gregorian calendar months\n /// between `fromTimestamp` and `toTimestamp`.\n /// Note: Even if the true time difference is less than a month,\n /// the difference can be non-zero is the timestamps are\n /// from different Gregorian calendar months.\n function diffMonths(uint256 fromTimestamp, uint256 toTimestamp)\n internal\n pure\n returns (uint256 result)\n {\n toTimestamp - fromTimestamp;\n (uint256 fromYear, uint256 fromMonth,) = epochDayToDate(fromTimestamp / 86400);\n (uint256 toYear, uint256 toMonth,) = epochDayToDate(toTimestamp / 86400);\n result = _sub(_totalMonths(toYear, toMonth), _totalMonths(fromYear, fromMonth));\n }\n\n /// @dev Returns the difference in days between `fromTimestamp` and `toTimestamp`.\n function diffDays(uint256 fromTimestamp, uint256 toTimestamp)\n internal\n pure\n returns (uint256 result)\n {\n result = (toTimestamp - fromTimestamp) / 86400;\n }\n\n /// @dev Returns the difference in hours between `fromTimestamp` and `toTimestamp`.\n function diffHours(uint256 fromTimestamp, uint256 toTimestamp)\n internal\n pure\n returns (uint256 result)\n {\n result = (toTimestamp - fromTimestamp) / 3600;\n }\n\n /// @dev Returns the difference in minutes between `fromTimestamp` and `toTimestamp`.\n function diffMinutes(uint256 fromTimestamp, uint256 toTimestamp)\n internal\n pure\n returns (uint256 result)\n {\n result = (toTimestamp - fromTimestamp) / 60;\n }\n\n /// @dev Returns the difference in seconds between `fromTimestamp` and `toTimestamp`.\n function diffSeconds(uint256 fromTimestamp, uint256 toTimestamp)\n internal\n pure\n returns (uint256 result)\n {\n result = toTimestamp - fromTimestamp;\n }\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* PRIVATE HELPERS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev Unchecked arithmetic for computing the total number of months.\n function _totalMonths(uint256 numYears, uint256 numMonths)\n private\n pure\n returns (uint256 total)\n {\n unchecked {\n total = numYears * 12 + numMonths;\n }\n }\n\n /// @dev Unchecked arithmetic for adding two numbers.\n function _add(uint256 a, uint256 b) private pure returns (uint256 c) {\n unchecked {\n c = a + b;\n }\n }\n\n /// @dev Unchecked arithmetic for subtracting two numbers.\n function _sub(uint256 a, uint256 b) private pure returns (uint256 c) {\n unchecked {\n c = a - b;\n }\n }\n\n /// @dev Returns the offsetted timestamp.\n function _offsetted(uint256 year, uint256 month, uint256 day, uint256 timestamp)\n private\n pure\n returns (uint256 result)\n {\n uint256 dm = daysInMonth(year, month);\n if (day >= dm) {\n day = dm;\n }\n result = dateToEpochDay(year, month, day) * 86400 + (timestamp % 86400);\n }\n}\n"}},"settings":{"remappings":["solady/=lib/solady/src/","@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/","ds-test/=lib/forge-std/lib/ds-test/src/","erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/","forge-std/=lib/forge-std/src/","halmos-cheatcodes/=lib/openzeppelin-contracts/lib/halmos-cheatcodes/src/","openzeppelin-contracts/=lib/openzeppelin-contracts/"],"optimizer":{"enabled":true,"runs":999999},"metadata":{"useLiteralContent":false,"bytecodeHash":"ipfs","appendCBOR":true},"outputSelection":{"*":{"*":["abi","evm.bytecode","evm.deployedBytecode","evm.methodIdentifiers","metadata"]}},"evmVersion":"paris","viaIR":true,"libraries":{}}} diff --git a/standard-json-input/x509crlhelper.json b/standard-json-input/x509crlhelper.json deleted file mode 100644 index b9dc8c4..0000000 --- a/standard-json-input/x509crlhelper.json +++ /dev/null @@ -1 +0,0 @@ -{"language":"Solidity","sources":{"src/helpers/X509CRLHelper.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport {Asn1Decode, NodePtr} from \"../utils/Asn1Decode.sol\";\nimport {BytesUtils} from \"../utils/BytesUtils.sol\";\nimport {DateTimeUtils} from \"../utils/DateTimeUtils.sol\";\n\n/**\n * @title Solidity Structure representing X509 CRL\n * @notice This is a simplified structure of a DER-decoded X509 CRL\n */\nstruct X509CRLObj {\n uint256 serialNumber;\n string issuerCommonName;\n uint256 validityNotBefore;\n uint256 validityNotAfter;\n uint256[] serialNumbersRevoked;\n // for signature verification in the cert chain\n bytes signature;\n bytes tbs;\n}\n\n/**\n * @title X509 CRL Helper Contract\n * @notice This is a standalone contract that can be used by off-chain applications and smart contracts\n * to parse DER-encoded CRLs.\n */\ncontract X509CRLHelper {\n using Asn1Decode for bytes;\n using NodePtr for uint256;\n using BytesUtils for bytes;\n\n // 2.5.29.20\n bytes constant CRL_NUMBER_OID = hex\"551d14\";\n\n /// =================================================================================\n /// USE THE GETTERS BELOW IF YOU DON'T WANT TO PARSE THE ENTIRE X509 CRL\n /// =================================================================================\n\n function getTbsAndSig(bytes calldata der) external pure returns (bytes memory tbs, bytes memory sig) {\n uint256 root = der.root();\n uint256 tbsParentPtr = der.firstChildOf(root);\n uint256 sigPtr = der.nextSiblingOf(tbsParentPtr);\n sigPtr = der.nextSiblingOf(sigPtr);\n\n tbs = der.allBytesAt(tbsParentPtr);\n sig = _getSignature(der, sigPtr);\n }\n\n function getSerialNumber(bytes calldata der) external pure returns (uint256 serialNum) {\n uint256 root = der.root();\n uint256 tbsParentPtr = der.firstChildOf(root);\n uint256 tbsPtr = der.firstChildOf(tbsParentPtr);\n serialNum = _parseSerialNumber(der.bytesAt(tbsPtr));\n }\n\n function getIssuerCommonName(bytes calldata der) external pure returns (string memory issuerCommonName) {\n uint256 root = der.root();\n uint256 tbsParentPtr = der.firstChildOf(root);\n uint256 tbsPtr = der.firstChildOf(tbsParentPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n issuerCommonName = _getCommonName(der, der.firstChildOf(tbsPtr));\n }\n\n function crlIsNotExpired(bytes calldata der) external view returns (bool isValid) {\n uint256 root = der.root();\n uint256 tbsParentPtr = der.firstChildOf(root);\n uint256 tbsPtr = der.firstChildOf(tbsParentPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n (uint256 validityNotBefore, uint256 validityNotAfter) = _getValidity(der, tbsPtr);\n isValid = block.timestamp > validityNotBefore && block.timestamp < validityNotAfter;\n }\n\n function serialNumberIsRevoked(uint256 serialNumber, bytes calldata der) external pure returns (bool revoked) {\n uint256 root = der.root();\n uint256 tbsParentPtr = der.firstChildOf(root);\n uint256 tbsPtr = der.firstChildOf(tbsParentPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n uint256[] memory ret = _getRevokedSerialNumbers(der, tbsPtr, true, serialNumber);\n revoked = ret[0] == serialNumber;\n }\n\n /// x509 CRL generally contain a sequence of elements in the following order:\n /// 1. tbs\n /// - 1a. serial number\n /// - 1b. signature algorithm\n /// - 1c. issuer\n /// - - 1c(a). common name\n /// - - 1c(b). organization name\n /// - - 1c(c). locality name\n /// - - 1c(d). state or province name\n /// - - 1c(e). country name\n /// - 1d. not before\n /// - 1e. not after\n /// - 1f. revoked certificates\n /// - - A list consists of revoked serial numbers and reasons.\n /// - 1g. CRL extensions\n /// - - 1g(a) CRL number\n /// - - 1g(b) Authority Key Identifier\n /// 2. Signature Algorithm\n /// 3. Signature\n /// - 3a. X value\n /// - 3b. Y value\n function parseCRLDER(bytes calldata der) external pure returns (X509CRLObj memory crl) {\n uint256 root = der.root();\n\n uint256 tbsParentPtr = der.firstChildOf(root);\n\n uint256 tbsPtr = der.firstChildOf(tbsParentPtr);\n\n crl.serialNumber = uint256(bytes32(der.bytesAt(tbsPtr)));\n\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n\n crl.issuerCommonName = _getCommonName(der, der.firstChildOf(tbsPtr));\n\n tbsPtr = der.nextSiblingOf(tbsPtr);\n (crl.validityNotBefore, crl.validityNotAfter) = _getValidity(der, tbsPtr);\n\n tbsPtr = der.nextSiblingOf(tbsPtr);\n tbsPtr = der.nextSiblingOf(tbsPtr);\n\n crl.serialNumbersRevoked = _getRevokedSerialNumbers(der, tbsPtr, false, 0);\n\n // tbs iteration completed\n // now we just need to look for the signature\n\n uint256 sigPtr = der.nextSiblingOf(tbsParentPtr);\n sigPtr = der.nextSiblingOf(sigPtr);\n crl.signature = _getSignature(der, sigPtr);\n }\n\n function _getCommonName(bytes calldata der, uint256 commonNameParentPtr)\n private\n pure\n returns (string memory commonName)\n {\n commonNameParentPtr = der.firstChildOf(commonNameParentPtr);\n commonNameParentPtr = der.firstChildOf(commonNameParentPtr);\n commonNameParentPtr = der.nextSiblingOf(commonNameParentPtr);\n commonName = string(der.bytesAt(commonNameParentPtr));\n }\n\n function _getValidity(bytes calldata der, uint256 validityPtr)\n private\n pure\n returns (uint256 notBefore, uint256 notAfter)\n {\n uint256 notBeforePtr = validityPtr;\n uint256 notAfterPtr = der.nextSiblingOf(notBeforePtr);\n notBefore = DateTimeUtils.fromDERToTimestamp(der.bytesAt(notBeforePtr));\n notAfter = DateTimeUtils.fromDERToTimestamp(der.bytesAt(notAfterPtr));\n }\n\n function _getRevokedSerialNumbers(bytes calldata der, uint256 revokedParentPtr, bool breakIfFound, uint256 filter)\n private\n pure\n returns (uint256[] memory serialNumbers)\n {\n uint256 revokedPtr = der.firstChildOf(revokedParentPtr);\n\n if (der[revokedPtr.ixs()] == 0xA0) {\n uint256 crlExtensionPtr = der.firstChildOf(revokedPtr);\n require(BytesUtils.compareBytes(der.bytesAt(crlExtensionPtr), CRL_NUMBER_OID), \"invalid CRL\");\n } else {\n bytes memory serials;\n while (revokedPtr.ixl() <= revokedParentPtr.ixl()) {\n uint256 serialPtr = der.firstChildOf(revokedPtr);\n bytes memory serialBytes = der.bytesAt(serialPtr);\n uint256 serialNumber = _parseSerialNumber(serialBytes);\n serials = abi.encodePacked(serials, serialNumber);\n if (breakIfFound && filter == serialNumber) {\n serialNumbers = new uint256[](1);\n serialNumbers[0] = filter;\n return serialNumbers;\n }\n revokedPtr = der.nextSiblingOf(revokedPtr);\n }\n uint256 count = serials.length / 32;\n // ABI encoding format for a dynamic uint256[] value\n serials = abi.encodePacked(abi.encode(0x20), abi.encode(count), serials);\n serialNumbers = new uint256[](count);\n serialNumbers = abi.decode(serials, (uint256[]));\n }\n }\n\n function _parseSerialNumber(bytes memory serialBytes) private pure returns (uint256 serial) {\n uint256 shift = 8 * (32 - serialBytes.length);\n serial = uint256(bytes32(serialBytes) >> shift);\n }\n\n function _getSignature(bytes calldata der, uint256 sigPtr) private pure returns (bytes memory sig) {\n sigPtr = der.rootOfBitStringAt(sigPtr);\n\n sigPtr = der.firstChildOf(sigPtr);\n bytes memory sigX = _trimBytes(der.bytesAt(sigPtr), 32);\n\n sigPtr = der.nextSiblingOf(sigPtr);\n bytes memory sigY = _trimBytes(der.bytesAt(sigPtr), 32);\n\n sig = abi.encodePacked(sigX, sigY);\n }\n\n /// @dev remove unnecessary prefix from the input\n /// @dev remove unnecessary prefix from the input\n function _trimBytes(bytes memory input, uint256 expectedLength) private pure returns (bytes memory output) {\n uint256 n = input.length;\n if (n == expectedLength) {\n output = input;\n } else if (n < expectedLength) {\n output = new bytes(expectedLength);\n uint256 padLength = expectedLength - n;\n for (uint256 i = 0; i < n; i++) {\n output[padLength + i] = input[i];\n }\n } else {\n uint256 lengthDiff = n - expectedLength;\n output = input.substring(lengthDiff, expectedLength);\n }\n }\n}\n"},"src/utils/Asn1Decode.sol":{"content":"// SPDX-License-Identifier: MIT\n// Original source: https://github.com/JonahGroendal/asn1-decode\npragma solidity ^0.8.0;\n\n// Inspired by PufferFinance/rave - Apache-2.0 license\n// https://github.com/JonahGroendal/asn1-decode/blob/5c2d1469fc678513753786acb441e597969192ec/contracts/Asn1Decode.sol\n\nimport \"./BytesUtils.sol\";\n\nlibrary NodePtr {\n // Unpack first byte index\n function ixs(uint256 self) internal pure returns (uint256) {\n return uint80(self);\n }\n // Unpack first content byte index\n\n function ixf(uint256 self) internal pure returns (uint256) {\n return uint80(self >> 80);\n }\n // Unpack last content byte index\n\n function ixl(uint256 self) internal pure returns (uint256) {\n return uint80(self >> 160);\n }\n // Pack 3 uint80s into a uint256\n\n function getPtr(uint256 _ixs, uint256 _ixf, uint256 _ixl) internal pure returns (uint256) {\n _ixs |= _ixf << 80;\n _ixs |= _ixl << 160;\n return _ixs;\n }\n}\n\nlibrary Asn1Decode {\n using NodePtr for uint256;\n using BytesUtils for bytes;\n\n /*\n * @dev Get the root node. First step in traversing an ASN1 structure\n * @param der The DER-encoded ASN1 structure\n * @return A pointer to the outermost node\n */\n function root(bytes memory der) internal pure returns (uint256) {\n return readNodeLength(der, 0);\n }\n\n /*\n * @dev Get the root node of an ASN1 structure that's within a bit string value\n * @param der The DER-encoded ASN1 structure\n * @return A pointer to the outermost node\n */\n function rootOfBitStringAt(bytes memory der, uint256 ptr) internal pure returns (uint256) {\n require(der[ptr.ixs()] == 0x03, \"Not type BIT STRING\");\n return readNodeLength(der, ptr.ixf() + 1);\n }\n\n /*\n * @dev Get the root node of an ASN1 structure that's within an octet string value\n * @param der The DER-encoded ASN1 structure\n * @return A pointer to the outermost node\n */\n function rootOfOctetStringAt(bytes memory der, uint256 ptr) internal pure returns (uint256) {\n require(der[ptr.ixs()] == 0x04, \"Not type OCTET STRING\");\n return readNodeLength(der, ptr.ixf());\n }\n\n /*\n * @dev Get the next sibling node\n * @param der The DER-encoded ASN1 structure\n * @param ptr Points to the indices of the current node\n * @return A pointer to the next sibling node\n */\n function nextSiblingOf(bytes memory der, uint256 ptr) internal pure returns (uint256) {\n return readNodeLength(der, ptr.ixl() + 1);\n }\n\n /*\n * @dev Get the first child node of the current node\n * @param der The DER-encoded ASN1 structure\n * @param ptr Points to the indices of the current node\n * @return A pointer to the first child node\n */\n function firstChildOf(bytes memory der, uint256 ptr) internal pure returns (uint256) {\n require(der[ptr.ixs()] & 0x20 == 0x20, \"Not a constructed type\");\n return readNodeLength(der, ptr.ixf());\n }\n\n /*\n * @dev Use for looping through children of a node (either i or j).\n * @param i Pointer to an ASN1 node\n * @param j Pointer to another ASN1 node of the same ASN1 structure\n * @return True iff j is child of i or i is child of j.\n */\n function isChildOf(uint256 i, uint256 j) internal pure returns (bool) {\n return (((i.ixf() <= j.ixs()) && (j.ixl() <= i.ixl())) || ((j.ixf() <= i.ixs()) && (i.ixl() <= j.ixl())));\n }\n\n /*\n * @dev Extract value of node from DER-encoded structure\n * @param der The der-encoded ASN1 structure\n * @param ptr Points to the indices of the current node\n * @return Value bytes of node\n */\n function bytesAt(bytes memory der, uint256 ptr) internal pure returns (bytes memory) {\n return der.substring(ptr.ixf(), ptr.ixl() + 1 - ptr.ixf());\n }\n\n /*\n * @dev Extract entire node from DER-encoded structure\n * @param der The DER-encoded ASN1 structure\n * @param ptr Points to the indices of the current node\n * @return All bytes of node\n */\n function allBytesAt(bytes memory der, uint256 ptr) internal pure returns (bytes memory) {\n return der.substring(ptr.ixs(), ptr.ixl() + 1 - ptr.ixs());\n }\n\n /*\n * @dev Extract value of node from DER-encoded structure\n * @param der The DER-encoded ASN1 structure\n * @param ptr Points to the indices of the current node\n * @return Value bytes of node as bytes32\n */\n function bytes32At(bytes memory der, uint256 ptr) internal pure returns (bytes32) {\n return der.readBytesN(ptr.ixf(), ptr.ixl() + 1 - ptr.ixf());\n }\n\n /*\n * @dev Extract value of node from DER-encoded structure\n * @param der The der-encoded ASN1 structure\n * @param ptr Points to the indices of the current node\n * @return Uint value of node\n */\n function uintAt(bytes memory der, uint256 ptr) internal pure returns (uint256) {\n require(der[ptr.ixs()] == 0x02, \"Not type INTEGER\");\n require(der[ptr.ixf()] & 0x80 == 0, \"Not positive\");\n uint256 len = ptr.ixl() + 1 - ptr.ixf();\n return uint256(der.readBytesN(ptr.ixf(), len) >> (32 - len) * 8);\n }\n\n /*\n * @dev Extract value of a positive integer node from DER-encoded structure\n * @param der The DER-encoded ASN1 structure\n * @param ptr Points to the indices of the current node\n * @return Value bytes of a positive integer node\n */\n function uintBytesAt(bytes memory der, uint256 ptr) internal pure returns (bytes memory) {\n require(der[ptr.ixs()] == 0x02, \"Not type INTEGER\");\n require(der[ptr.ixf()] & 0x80 == 0, \"Not positive\");\n uint256 valueLength = ptr.ixl() + 1 - ptr.ixf();\n if (der[ptr.ixf()] == 0) {\n return der.substring(ptr.ixf() + 1, valueLength - 1);\n } else {\n return der.substring(ptr.ixf(), valueLength);\n }\n }\n\n function keccakOfBytesAt(bytes memory der, uint256 ptr) internal pure returns (bytes32) {\n return der.keccak(ptr.ixf(), ptr.ixl() + 1 - ptr.ixf());\n }\n\n function keccakOfAllBytesAt(bytes memory der, uint256 ptr) internal pure returns (bytes32) {\n return der.keccak(ptr.ixs(), ptr.ixl() + 1 - ptr.ixs());\n }\n\n /*\n * @dev Extract value of bitstring node from DER-encoded structure\n * @param der The DER-encoded ASN1 structure\n * @param ptr Points to the indices of the current node\n * @return Value of bitstring converted to bytes\n */\n function bitstringAt(bytes memory der, uint256 ptr) internal pure returns (bytes memory) {\n require(der[ptr.ixs()] == 0x03, \"Not type BIT STRING\");\n // Only 00 padded bitstr can be converted to bytestr!\n require(der[ptr.ixf()] == 0x00);\n uint256 valueLength = ptr.ixl() + 1 - ptr.ixf();\n return der.substring(ptr.ixf() + 1, valueLength - 1);\n }\n\n function readNodeLength(bytes memory der, uint256 ix) private pure returns (uint256) {\n uint256 length;\n uint80 ixFirstContentByte;\n uint80 ixLastContentByte;\n if ((der[ix + 1] & 0x80) == 0) {\n length = uint8(der[ix + 1]);\n ixFirstContentByte = uint80(ix + 2);\n ixLastContentByte = uint80(ixFirstContentByte + length - 1);\n } else {\n uint8 lengthbytesLength = uint8(der[ix + 1] & 0x7F);\n if (lengthbytesLength == 1) {\n length = der.readUint8(ix + 2);\n } else if (lengthbytesLength == 2) {\n length = der.readUint16(ix + 2);\n } else {\n length = uint256(der.readBytesN(ix + 2, lengthbytesLength) >> (32 - lengthbytesLength) * 8);\n }\n ixFirstContentByte = uint80(ix + 2 + lengthbytesLength);\n ixLastContentByte = uint80(ixFirstContentByte + length - 1);\n }\n return NodePtr.getPtr(ix, ixFirstContentByte, ixLastContentByte);\n }\n}\n"},"src/utils/BytesUtils.sol":{"content":"// SPDX-License-Identifier: BSD 2-Clause License\npragma solidity ^0.8.0;\n\n// Inspired by ensdomains/dnssec-oracle - BSD-2-Clause license\n// https://github.com/ensdomains/dnssec-oracle/blob/master/contracts/BytesUtils.sol\n\nlibrary BytesUtils {\n /*\n * @dev Returns the keccak-256 hash of a byte range.\n * @param self The byte string to hash.\n * @param offset The position to start hashing at.\n * @param len The number of bytes to hash.\n * @return The hash of the byte range.\n */\n function keccak(bytes memory self, uint256 offset, uint256 len) internal pure returns (bytes32 ret) {\n require(offset + len <= self.length);\n assembly {\n ret := keccak256(add(add(self, 32), offset), len)\n }\n }\n\n /*\n * @dev Returns a positive number if `other` comes lexicographically after\n * `self`, a negative number if it comes before, or zero if the\n * contents of the two bytes are equal.\n * @param self The first bytes to compare.\n * @param other The second bytes to compare.\n * @return The result of the comparison.\n */\n function compare(bytes memory self, bytes memory other) internal pure returns (int256) {\n return compare(self, 0, self.length, other, 0, other.length);\n }\n\n /*\n * @dev Returns a positive number if `other` comes lexicographically after\n * `self`, a negative number if it comes before, or zero if the\n * contents of the two bytes are equal. Comparison is done per-rune,\n * on unicode codepoints.\n * @param self The first bytes to compare.\n * @param offset The offset of self.\n * @param len The length of self.\n * @param other The second bytes to compare.\n * @param otheroffset The offset of the other string.\n * @param otherlen The length of the other string.\n * @return The result of the comparison.\n */\n function compare(\n bytes memory self,\n uint256 offset,\n uint256 len,\n bytes memory other,\n uint256 otheroffset,\n uint256 otherlen\n ) internal pure returns (int256) {\n uint256 shortest = len;\n if (otherlen < len) {\n shortest = otherlen;\n }\n\n uint256 selfptr;\n uint256 otherptr;\n\n assembly {\n selfptr := add(self, add(offset, 32))\n otherptr := add(other, add(otheroffset, 32))\n }\n for (uint256 idx = 0; idx < shortest; idx += 32) {\n uint256 a;\n uint256 b;\n assembly {\n a := mload(selfptr)\n b := mload(otherptr)\n }\n if (a != b) {\n // Mask out irrelevant bytes and check again\n uint256 mask;\n if (shortest > 32) {\n mask = type(uint256).max; // aka 0xffffff....\n } else {\n mask = ~(2 ** (8 * (32 - shortest + idx)) - 1);\n }\n uint256 diff = (a & mask) - (b & mask);\n if (diff != 0) {\n return int256(diff);\n }\n }\n selfptr += 32;\n otherptr += 32;\n }\n\n return int256(len) - int256(otherlen);\n }\n\n /*\n * @dev Returns true if the two byte ranges are equal.\n * @param self The first byte range to compare.\n * @param offset The offset into the first byte range.\n * @param other The second byte range to compare.\n * @param otherOffset The offset into the second byte range.\n * @param len The number of bytes to compare\n * @return True if the byte ranges are equal, false otherwise.\n */\n function equals(bytes memory self, uint256 offset, bytes memory other, uint256 otherOffset, uint256 len)\n internal\n pure\n returns (bool)\n {\n return keccak(self, offset, len) == keccak(other, otherOffset, len);\n }\n\n /*\n * @dev Returns true if the two byte ranges are equal with offsets.\n * @param self The first byte range to compare.\n * @param offset The offset into the first byte range.\n * @param other The second byte range to compare.\n * @param otherOffset The offset into the second byte range.\n * @return True if the byte ranges are equal, false otherwise.\n */\n function equals(bytes memory self, uint256 offset, bytes memory other, uint256 otherOffset)\n internal\n pure\n returns (bool)\n {\n return keccak(self, offset, self.length - offset) == keccak(other, otherOffset, other.length - otherOffset);\n }\n\n /*\n * @dev Compares a range of 'self' to all of 'other' and returns True iff\n * they are equal.\n * @param self The first byte range to compare.\n * @param offset The offset into the first byte range.\n * @param other The second byte range to compare.\n * @return True if the byte ranges are equal, false otherwise.\n */\n function equals(bytes memory self, uint256 offset, bytes memory other) internal pure returns (bool) {\n return self.length >= offset + other.length && equals(self, offset, other, 0, other.length);\n }\n\n /*\n * @dev Returns true if the two byte ranges are equal.\n * @param self The first byte range to compare.\n * @param other The second byte range to compare.\n * @return True if the byte ranges are equal, false otherwise.\n */\n function equals(bytes memory self, bytes memory other) internal pure returns (bool) {\n return self.length == other.length && equals(self, 0, other, 0, self.length);\n }\n\n /*\n * @dev Returns the 8-bit number at the specified index of self.\n * @param self The byte string.\n * @param idx The index into the bytes\n * @return The specified 8 bits of the string, interpreted as an integer.\n */\n function readUint8(bytes memory self, uint256 idx) internal pure returns (uint8 ret) {\n return uint8(self[idx]);\n }\n\n /*\n * @dev Returns the 16-bit number at the specified index of self.\n * @param self The byte string.\n * @param idx The index into the bytes\n * @return The specified 16 bits of the string, interpreted as an integer.\n */\n function readUint16(bytes memory self, uint256 idx) internal pure returns (uint16 ret) {\n require(idx + 2 <= self.length);\n assembly {\n ret := and(mload(add(add(self, 2), idx)), 0xFFFF)\n }\n }\n\n /*\n * @dev Returns the 32-bit number at the specified index of self.\n * @param self The byte string.\n * @param idx The index into the bytes\n * @return The specified 32 bits of the string, interpreted as an integer.\n */\n function readUint32(bytes memory self, uint256 idx) internal pure returns (uint32 ret) {\n require(idx + 4 <= self.length);\n assembly {\n ret := and(mload(add(add(self, 4), idx)), 0xFFFFFFFF)\n }\n }\n\n /*\n * @dev Returns the 32 byte value at the specified index of self.\n * @param self The byte string.\n * @param idx The index into the bytes\n * @return The specified 32 bytes of the string.\n */\n function readBytes32(bytes memory self, uint256 idx) internal pure returns (bytes32 ret) {\n require(idx + 32 <= self.length);\n assembly {\n ret := mload(add(add(self, 32), idx))\n }\n }\n\n /*\n * @dev Returns the 32 byte value at the specified index of self.\n * @param self The byte string.\n * @param idx The index into the bytes\n * @return The specified 32 bytes of the string.\n */\n function readBytes20(bytes memory self, uint256 idx) internal pure returns (bytes20 ret) {\n require(idx + 20 <= self.length);\n assembly {\n ret :=\n and(mload(add(add(self, 32), idx)), 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000000)\n }\n }\n\n /*\n * @dev Returns the n byte value at the specified index of self.\n * @param self The byte string.\n * @param idx The index into the bytes.\n * @param len The number of bytes.\n * @return The specified 32 bytes of the string.\n */\n function readBytesN(bytes memory self, uint256 idx, uint256 len) internal pure returns (bytes32 ret) {\n require(len <= 32);\n require(idx + len <= self.length);\n assembly {\n let mask := not(sub(exp(256, sub(32, len)), 1))\n ret := and(mload(add(add(self, 32), idx)), mask)\n }\n }\n\n function memcpy(uint256 dest, uint256 src, uint256 len) private pure {\n // Copy word-length chunks while possible\n for (; len >= 32; len -= 32) {\n assembly {\n mstore(dest, mload(src))\n }\n dest += 32;\n src += 32;\n }\n\n // Copy remaining bytes\n uint256 mask;\n if (len == 0) {\n mask = type(uint256).max; // Set to maximum value of uint256\n } else {\n mask = 256 ** (32 - len) - 1;\n }\n\n assembly {\n let srcpart := and(mload(src), not(mask))\n let destpart := and(mload(dest), mask)\n mstore(dest, or(destpart, srcpart))\n }\n }\n\n /*\n * @dev Copies a substring into a new byte string.\n * @param self The byte string to copy from.\n * @param offset The offset to start copying at.\n * @param len The number of bytes to copy.\n */\n function substring(bytes memory self, uint256 offset, uint256 len) internal pure returns (bytes memory) {\n require(offset + len <= self.length);\n\n bytes memory ret = new bytes(len);\n uint256 dest;\n uint256 src;\n\n assembly {\n dest := add(ret, 32)\n src := add(add(self, 32), offset)\n }\n memcpy(dest, src, len);\n\n return ret;\n }\n\n // Maps characters from 0x30 to 0x7A to their base32 values.\n // 0xFF represents invalid characters in that range.\n bytes constant base32HexTable =\n hex\"00010203040506070809FFFFFFFFFFFFFF0A0B0C0D0E0F101112131415161718191A1B1C1D1E1FFFFFFFFFFFFFFFFFFFFF0A0B0C0D0E0F101112131415161718191A1B1C1D1E1F\";\n\n /**\n * @dev Decodes unpadded base32 data of up to one word in length.\n * @param self The data to decode.\n * @param off Offset into the string to start at.\n * @param len Number of characters to decode.\n * @return The decoded data, left aligned.\n */\n function base32HexDecodeWord(bytes memory self, uint256 off, uint256 len) internal pure returns (bytes32) {\n require(len <= 52);\n\n uint256 ret = 0;\n uint8 decoded;\n for (uint256 i = 0; i < len; i++) {\n bytes1 char = self[off + i];\n require(char >= 0x30 && char <= 0x7A);\n decoded = uint8(base32HexTable[uint256(uint8(char)) - 0x30]);\n require(decoded <= 0x20);\n if (i == len - 1) {\n break;\n }\n ret = (ret << 5) | decoded;\n }\n\n uint256 bitlen = len * 5;\n if (len % 8 == 0) {\n // Multiple of 8 characters, no padding\n ret = (ret << 5) | decoded;\n } else if (len % 8 == 2) {\n // Two extra characters - 1 byte\n ret = (ret << 3) | (decoded >> 2);\n bitlen -= 2;\n } else if (len % 8 == 4) {\n // Four extra characters - 2 bytes\n ret = (ret << 1) | (decoded >> 4);\n bitlen -= 4;\n } else if (len % 8 == 5) {\n // Five extra characters - 3 bytes\n ret = (ret << 4) | (decoded >> 1);\n bitlen -= 1;\n } else if (len % 8 == 7) {\n // Seven extra characters - 4 bytes\n ret = (ret << 2) | (decoded >> 3);\n bitlen -= 3;\n } else {\n revert();\n }\n\n return bytes32(ret << (256 - bitlen));\n }\n\n function compareBytes(bytes memory a, bytes memory b) internal pure returns (bool) {\n if (a.length != b.length) {\n return false;\n }\n for (uint256 i = 0; i < a.length; i++) {\n if (a[i] != b[i]) {\n return false;\n }\n }\n return true;\n }\n}\n"},"src/utils/DateTimeUtils.sol":{"content":"// SPDX-License-Identifier: GPL-3.0\npragma solidity ^0.8.0;\n\nimport {DateTimeLib} from \"solady/utils/DateTimeLib.sol\";\nimport {LibString} from \"solady/utils/LibString.sol\";\n\nlibrary DateTimeUtils {\n using LibString for string;\n\n /*\n * @dev Convert a DER-encoded time to a unix timestamp\n * @param x509Time The DER-encoded time\n * @return The unix timestamp\n */\n function fromDERToTimestamp(bytes memory x509Time) internal pure returns (uint256) {\n uint16 yrs;\n uint8 mnths;\n uint8 dys;\n uint8 hrs;\n uint8 mins;\n uint8 secs;\n uint8 offset;\n\n if (x509Time.length == 13) {\n if (uint8(x509Time[0]) - 48 < 5) yrs += 2000;\n else yrs += 1900;\n } else {\n yrs += (uint8(x509Time[0]) - 48) * 1000 + (uint8(x509Time[1]) - 48) * 100;\n offset = 2;\n }\n yrs += (uint8(x509Time[offset + 0]) - 48) * 10 + uint8(x509Time[offset + 1]) - 48;\n mnths = (uint8(x509Time[offset + 2]) - 48) * 10 + uint8(x509Time[offset + 3]) - 48;\n dys += (uint8(x509Time[offset + 4]) - 48) * 10 + uint8(x509Time[offset + 5]) - 48;\n hrs += (uint8(x509Time[offset + 6]) - 48) * 10 + uint8(x509Time[offset + 7]) - 48;\n mins += (uint8(x509Time[offset + 8]) - 48) * 10 + uint8(x509Time[offset + 9]) - 48;\n secs += (uint8(x509Time[offset + 10]) - 48) * 10 + uint8(x509Time[offset + 11]) - 48;\n\n return DateTimeLib.dateTimeToTimestamp(yrs, mnths, dys, hrs, mins, secs);\n }\n\n /// @dev iso follows pattern: \"YYYY-MM-DDTHH:mm:ssZ\"\n function fromISOToTimestamp(string memory iso) internal pure returns (uint256) {\n require(bytes(iso).length == 20, \"invalid iso string length\");\n uint256 y = stringToUint(iso.slice(0, 4));\n uint256 m = stringToUint(iso.slice(5, 7));\n uint256 d = stringToUint(iso.slice(8, 10));\n uint256 h = stringToUint(iso.slice(11, 13));\n uint256 min = stringToUint(iso.slice(14, 16));\n uint256 s = stringToUint(iso.slice(17, 19));\n\n return DateTimeLib.dateTimeToTimestamp(y, m, d, h, min, s);\n }\n\n // https://ethereum.stackexchange.com/questions/10932/how-to-convert-string-to-int\n function stringToUint(string memory s) private pure returns (uint256 result) {\n bytes memory b = bytes(s);\n result = 0;\n for (uint256 i = 0; i < b.length; i++) {\n uint256 c = uint256(uint8(b[i]));\n if (c >= 48 && c <= 57) {\n result = result * 10 + (c - 48);\n }\n }\n }\n}\n"},"lib/solady/src/utils/DateTimeLib.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\n/// @notice Library for date time operations.\n/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/DateTimeLib.sol)\n///\n/// Conventions:\n/// --------------------------------------------------------------------+\n/// Unit | Range | Notes |\n/// --------------------------------------------------------------------|\n/// timestamp | 0..0x1e18549868c76ff | Unix timestamp. |\n/// epochDay | 0..0x16d3e098039 | Days since 1970-01-01. |\n/// year | 1970..0xffffffff | Gregorian calendar year. |\n/// month | 1..12 | Gregorian calendar month. |\n/// day | 1..31 | Gregorian calendar day of month. |\n/// weekday | 1..7 | The day of the week (1-indexed). |\n/// --------------------------------------------------------------------+\n/// All timestamps of days are rounded down to 00:00:00 UTC.\nlibrary DateTimeLib {\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* CONSTANTS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n // Weekdays are 1-indexed for a traditional rustic feel.\n\n // \"And on the seventh day God finished his work that he had done,\n // and he rested on the seventh day from all his work that he had done.\"\n // -- Genesis 2:2\n\n uint256 internal constant MON = 1;\n uint256 internal constant TUE = 2;\n uint256 internal constant WED = 3;\n uint256 internal constant THU = 4;\n uint256 internal constant FRI = 5;\n uint256 internal constant SAT = 6;\n uint256 internal constant SUN = 7;\n\n // Months and days of months are 1-indexed for ease of use.\n\n uint256 internal constant JAN = 1;\n uint256 internal constant FEB = 2;\n uint256 internal constant MAR = 3;\n uint256 internal constant APR = 4;\n uint256 internal constant MAY = 5;\n uint256 internal constant JUN = 6;\n uint256 internal constant JUL = 7;\n uint256 internal constant AUG = 8;\n uint256 internal constant SEP = 9;\n uint256 internal constant OCT = 10;\n uint256 internal constant NOV = 11;\n uint256 internal constant DEC = 12;\n\n // These limits are large enough for most practical purposes.\n // Inputs that exceed these limits result in undefined behavior.\n\n uint256 internal constant MAX_SUPPORTED_YEAR = 0xffffffff;\n uint256 internal constant MAX_SUPPORTED_EPOCH_DAY = 0x16d3e098039;\n uint256 internal constant MAX_SUPPORTED_TIMESTAMP = 0x1e18549868c76ff;\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* DATE TIME OPERATIONS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev Returns the number of days since 1970-01-01 from (`year`,`month`,`day`).\n /// See: https://howardhinnant.github.io/date_algorithms.html\n /// Note: Inputs outside the supported ranges result in undefined behavior.\n /// Use {isSupportedDate} to check if the inputs are supported.\n function dateToEpochDay(uint256 year, uint256 month, uint256 day)\n internal\n pure\n returns (uint256 epochDay)\n {\n /// @solidity memory-safe-assembly\n assembly {\n year := sub(year, lt(month, 3))\n let doy := add(shr(11, add(mul(62719, mod(add(month, 9), 12)), 769)), day)\n let yoe := mod(year, 400)\n let doe := sub(add(add(mul(yoe, 365), shr(2, yoe)), doy), div(yoe, 100))\n epochDay := sub(add(mul(div(year, 400), 146097), doe), 719469)\n }\n }\n\n /// @dev Returns (`year`,`month`,`day`) from the number of days since 1970-01-01.\n /// Note: Inputs outside the supported ranges result in undefined behavior.\n /// Use {isSupportedDays} to check if the inputs is supported.\n function epochDayToDate(uint256 epochDay)\n internal\n pure\n returns (uint256 year, uint256 month, uint256 day)\n {\n /// @solidity memory-safe-assembly\n assembly {\n epochDay := add(epochDay, 719468)\n let doe := mod(epochDay, 146097)\n let yoe :=\n div(sub(sub(add(doe, div(doe, 36524)), div(doe, 1460)), eq(doe, 146096)), 365)\n let doy := sub(doe, sub(add(mul(365, yoe), shr(2, yoe)), div(yoe, 100)))\n let mp := div(add(mul(5, doy), 2), 153)\n day := add(sub(doy, shr(11, add(mul(mp, 62719), 769))), 1)\n month := byte(mp, shl(160, 0x030405060708090a0b0c0102))\n year := add(add(yoe, mul(div(epochDay, 146097), 400)), lt(month, 3))\n }\n }\n\n /// @dev Returns the unix timestamp from (`year`,`month`,`day`).\n /// Note: Inputs outside the supported ranges result in undefined behavior.\n /// Use {isSupportedDate} to check if the inputs are supported.\n function dateToTimestamp(uint256 year, uint256 month, uint256 day)\n internal\n pure\n returns (uint256 result)\n {\n unchecked {\n result = dateToEpochDay(year, month, day) * 86400;\n }\n }\n\n /// @dev Returns (`year`,`month`,`day`) from the given unix timestamp.\n /// Note: Inputs outside the supported ranges result in undefined behavior.\n /// Use {isSupportedTimestamp} to check if the inputs are supported.\n function timestampToDate(uint256 timestamp)\n internal\n pure\n returns (uint256 year, uint256 month, uint256 day)\n {\n (year, month, day) = epochDayToDate(timestamp / 86400);\n }\n\n /// @dev Returns the unix timestamp from\n /// (`year`,`month`,`day`,`hour`,`minute`,`second`).\n /// Note: Inputs outside the supported ranges result in undefined behavior.\n /// Use {isSupportedDateTime} to check if the inputs are supported.\n function dateTimeToTimestamp(\n uint256 year,\n uint256 month,\n uint256 day,\n uint256 hour,\n uint256 minute,\n uint256 second\n ) internal pure returns (uint256 result) {\n unchecked {\n result = dateToEpochDay(year, month, day) * 86400 + hour * 3600 + minute * 60 + second;\n }\n }\n\n /// @dev Returns (`year`,`month`,`day`,`hour`,`minute`,`second`)\n /// from the given unix timestamp.\n /// Note: Inputs outside the supported ranges result in undefined behavior.\n /// Use {isSupportedTimestamp} to check if the inputs are supported.\n function timestampToDateTime(uint256 timestamp)\n internal\n pure\n returns (\n uint256 year,\n uint256 month,\n uint256 day,\n uint256 hour,\n uint256 minute,\n uint256 second\n )\n {\n unchecked {\n (year, month, day) = epochDayToDate(timestamp / 86400);\n uint256 secs = timestamp % 86400;\n hour = secs / 3600;\n secs = secs % 3600;\n minute = secs / 60;\n second = secs % 60;\n }\n }\n\n /// @dev Returns if the `year` is leap.\n function isLeapYear(uint256 year) internal pure returns (bool leap) {\n /// @solidity memory-safe-assembly\n assembly {\n leap := iszero(and(add(mul(iszero(mod(year, 25)), 12), 3), year))\n }\n }\n\n /// @dev Returns number of days in given `month` of `year`.\n function daysInMonth(uint256 year, uint256 month) internal pure returns (uint256 result) {\n bool flag = isLeapYear(year);\n /// @solidity memory-safe-assembly\n assembly {\n // `daysInMonths = [31,28,31,30,31,30,31,31,30,31,30,31]`.\n // `result = daysInMonths[month - 1] + isLeapYear(year)`.\n result :=\n add(byte(month, shl(152, 0x1F1C1F1E1F1E1F1F1E1F1E1F)), and(eq(month, 2), flag))\n }\n }\n\n /// @dev Returns the weekday from the unix timestamp.\n /// Monday: 1, Tuesday: 2, ....., Sunday: 7.\n function weekday(uint256 timestamp) internal pure returns (uint256 result) {\n unchecked {\n result = ((timestamp / 86400 + 3) % 7) + 1;\n }\n }\n\n /// @dev Returns if (`year`,`month`,`day`) is a supported date.\n /// - `1970 <= year <= MAX_SUPPORTED_YEAR`.\n /// - `1 <= month <= 12`.\n /// - `1 <= day <= daysInMonth(year, month)`.\n function isSupportedDate(uint256 year, uint256 month, uint256 day)\n internal\n pure\n returns (bool result)\n {\n uint256 md = daysInMonth(year, month);\n /// @solidity memory-safe-assembly\n assembly {\n let w := not(0)\n result :=\n and(\n lt(sub(year, 1970), sub(MAX_SUPPORTED_YEAR, 1969)),\n and(lt(add(month, w), 12), lt(add(day, w), md))\n )\n }\n }\n\n /// @dev Returns if (`year`,`month`,`day`,`hour`,`minute`,`second`) is a supported date time.\n /// - `1970 <= year <= MAX_SUPPORTED_YEAR`.\n /// - `1 <= month <= 12`.\n /// - `1 <= day <= daysInMonth(year, month)`.\n /// - `hour < 24`.\n /// - `minute < 60`.\n /// - `second < 60`.\n function isSupportedDateTime(\n uint256 year,\n uint256 month,\n uint256 day,\n uint256 hour,\n uint256 minute,\n uint256 second\n ) internal pure returns (bool result) {\n if (isSupportedDate(year, month, day)) {\n /// @solidity memory-safe-assembly\n assembly {\n result := and(lt(hour, 24), and(lt(minute, 60), lt(second, 60)))\n }\n }\n }\n\n /// @dev Returns if `epochDay` is a supported unix epoch day.\n function isSupportedEpochDay(uint256 epochDay) internal pure returns (bool result) {\n unchecked {\n result = epochDay < MAX_SUPPORTED_EPOCH_DAY + 1;\n }\n }\n\n /// @dev Returns if `timestamp` is a supported unix timestamp.\n function isSupportedTimestamp(uint256 timestamp) internal pure returns (bool result) {\n unchecked {\n result = timestamp < MAX_SUPPORTED_TIMESTAMP + 1;\n }\n }\n\n /// @dev Returns the unix timestamp of the given `n`th weekday `wd`, in `month` of `year`.\n /// Example: 3rd Friday of Feb 2022 is `nthWeekdayInMonthOfYearTimestamp(2022, 2, 3, 5)`\n /// Note: `n` is 1-indexed for traditional consistency.\n /// Invalid weekdays (i.e. `wd == 0 || wd > 7`) result in undefined behavior.\n function nthWeekdayInMonthOfYearTimestamp(uint256 year, uint256 month, uint256 n, uint256 wd)\n internal\n pure\n returns (uint256 result)\n {\n uint256 d = dateToEpochDay(year, month, 1);\n uint256 md = daysInMonth(year, month);\n /// @solidity memory-safe-assembly\n assembly {\n let diff := sub(wd, add(mod(add(d, 3), 7), 1))\n let date := add(mul(sub(n, 1), 7), add(mul(gt(diff, 6), 7), diff))\n result := mul(mul(86400, add(date, d)), and(lt(date, md), iszero(iszero(n))))\n }\n }\n\n /// @dev Returns the unix timestamp of the most recent Monday.\n function mondayTimestamp(uint256 timestamp) internal pure returns (uint256 result) {\n uint256 t = timestamp;\n /// @solidity memory-safe-assembly\n assembly {\n let day := div(t, 86400)\n result := mul(mul(sub(day, mod(add(day, 3), 7)), 86400), gt(t, 345599))\n }\n }\n\n /// @dev Returns whether the unix timestamp falls on a Saturday or Sunday.\n /// To check whether it is a week day, just take the negation of the result.\n function isWeekEnd(uint256 timestamp) internal pure returns (bool result) {\n result = weekday(timestamp) > FRI;\n }\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* DATE TIME ARITHMETIC OPERATIONS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev Adds `numYears` to the unix timestamp, and returns the result.\n /// Note: The result will share the same Gregorian calendar month,\n /// but different Gregorian calendar years for non-zero `numYears`.\n /// If the Gregorian calendar month of the result has less days\n /// than the Gregorian calendar month day of the `timestamp`,\n /// the result's month day will be the maximum possible value for the month.\n /// (e.g. from 29th Feb to 28th Feb)\n function addYears(uint256 timestamp, uint256 numYears) internal pure returns (uint256 result) {\n (uint256 year, uint256 month, uint256 day) = epochDayToDate(timestamp / 86400);\n result = _offsetted(year + numYears, month, day, timestamp);\n }\n\n /// @dev Adds `numMonths` to the unix timestamp, and returns the result.\n /// Note: If the Gregorian calendar month of the result has less days\n /// than the Gregorian calendar month day of the `timestamp`,\n /// the result's month day will be the maximum possible value for the month.\n /// (e.g. from 29th Feb to 28th Feb)\n function addMonths(uint256 timestamp, uint256 numMonths)\n internal\n pure\n returns (uint256 result)\n {\n (uint256 year, uint256 month, uint256 day) = epochDayToDate(timestamp / 86400);\n month = _sub(month + numMonths, 1);\n result = _offsetted(year + month / 12, _add(month % 12, 1), day, timestamp);\n }\n\n /// @dev Adds `numDays` to the unix timestamp, and returns the result.\n function addDays(uint256 timestamp, uint256 numDays) internal pure returns (uint256 result) {\n result = timestamp + numDays * 86400;\n }\n\n /// @dev Adds `numHours` to the unix timestamp, and returns the result.\n function addHours(uint256 timestamp, uint256 numHours) internal pure returns (uint256 result) {\n result = timestamp + numHours * 3600;\n }\n\n /// @dev Adds `numMinutes` to the unix timestamp, and returns the result.\n function addMinutes(uint256 timestamp, uint256 numMinutes)\n internal\n pure\n returns (uint256 result)\n {\n result = timestamp + numMinutes * 60;\n }\n\n /// @dev Adds `numSeconds` to the unix timestamp, and returns the result.\n function addSeconds(uint256 timestamp, uint256 numSeconds)\n internal\n pure\n returns (uint256 result)\n {\n result = timestamp + numSeconds;\n }\n\n /// @dev Subtracts `numYears` from the unix timestamp, and returns the result.\n /// Note: The result will share the same Gregorian calendar month,\n /// but different Gregorian calendar years for non-zero `numYears`.\n /// If the Gregorian calendar month of the result has less days\n /// than the Gregorian calendar month day of the `timestamp`,\n /// the result's month day will be the maximum possible value for the month.\n /// (e.g. from 29th Feb to 28th Feb)\n function subYears(uint256 timestamp, uint256 numYears) internal pure returns (uint256 result) {\n (uint256 year, uint256 month, uint256 day) = epochDayToDate(timestamp / 86400);\n result = _offsetted(year - numYears, month, day, timestamp);\n }\n\n /// @dev Subtracts `numYears` from the unix timestamp, and returns the result.\n /// Note: If the Gregorian calendar month of the result has less days\n /// than the Gregorian calendar month day of the `timestamp`,\n /// the result's month day will be the maximum possible value for the month.\n /// (e.g. from 29th Feb to 28th Feb)\n function subMonths(uint256 timestamp, uint256 numMonths)\n internal\n pure\n returns (uint256 result)\n {\n (uint256 year, uint256 month, uint256 day) = epochDayToDate(timestamp / 86400);\n uint256 yearMonth = _totalMonths(year, month) - _add(numMonths, 1);\n result = _offsetted(yearMonth / 12, _add(yearMonth % 12, 1), day, timestamp);\n }\n\n /// @dev Subtracts `numDays` from the unix timestamp, and returns the result.\n function subDays(uint256 timestamp, uint256 numDays) internal pure returns (uint256 result) {\n result = timestamp - numDays * 86400;\n }\n\n /// @dev Subtracts `numHours` from the unix timestamp, and returns the result.\n function subHours(uint256 timestamp, uint256 numHours) internal pure returns (uint256 result) {\n result = timestamp - numHours * 3600;\n }\n\n /// @dev Subtracts `numMinutes` from the unix timestamp, and returns the result.\n function subMinutes(uint256 timestamp, uint256 numMinutes)\n internal\n pure\n returns (uint256 result)\n {\n result = timestamp - numMinutes * 60;\n }\n\n /// @dev Subtracts `numSeconds` from the unix timestamp, and returns the result.\n function subSeconds(uint256 timestamp, uint256 numSeconds)\n internal\n pure\n returns (uint256 result)\n {\n result = timestamp - numSeconds;\n }\n\n /// @dev Returns the difference in Gregorian calendar years\n /// between `fromTimestamp` and `toTimestamp`.\n /// Note: Even if the true time difference is less than a year,\n /// the difference can be non-zero is the timestamps are\n /// from different Gregorian calendar years\n function diffYears(uint256 fromTimestamp, uint256 toTimestamp)\n internal\n pure\n returns (uint256 result)\n {\n toTimestamp - fromTimestamp;\n (uint256 fromYear,,) = epochDayToDate(fromTimestamp / 86400);\n (uint256 toYear,,) = epochDayToDate(toTimestamp / 86400);\n result = _sub(toYear, fromYear);\n }\n\n /// @dev Returns the difference in Gregorian calendar months\n /// between `fromTimestamp` and `toTimestamp`.\n /// Note: Even if the true time difference is less than a month,\n /// the difference can be non-zero is the timestamps are\n /// from different Gregorian calendar months.\n function diffMonths(uint256 fromTimestamp, uint256 toTimestamp)\n internal\n pure\n returns (uint256 result)\n {\n toTimestamp - fromTimestamp;\n (uint256 fromYear, uint256 fromMonth,) = epochDayToDate(fromTimestamp / 86400);\n (uint256 toYear, uint256 toMonth,) = epochDayToDate(toTimestamp / 86400);\n result = _sub(_totalMonths(toYear, toMonth), _totalMonths(fromYear, fromMonth));\n }\n\n /// @dev Returns the difference in days between `fromTimestamp` and `toTimestamp`.\n function diffDays(uint256 fromTimestamp, uint256 toTimestamp)\n internal\n pure\n returns (uint256 result)\n {\n result = (toTimestamp - fromTimestamp) / 86400;\n }\n\n /// @dev Returns the difference in hours between `fromTimestamp` and `toTimestamp`.\n function diffHours(uint256 fromTimestamp, uint256 toTimestamp)\n internal\n pure\n returns (uint256 result)\n {\n result = (toTimestamp - fromTimestamp) / 3600;\n }\n\n /// @dev Returns the difference in minutes between `fromTimestamp` and `toTimestamp`.\n function diffMinutes(uint256 fromTimestamp, uint256 toTimestamp)\n internal\n pure\n returns (uint256 result)\n {\n result = (toTimestamp - fromTimestamp) / 60;\n }\n\n /// @dev Returns the difference in seconds between `fromTimestamp` and `toTimestamp`.\n function diffSeconds(uint256 fromTimestamp, uint256 toTimestamp)\n internal\n pure\n returns (uint256 result)\n {\n result = toTimestamp - fromTimestamp;\n }\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* PRIVATE HELPERS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev Unchecked arithmetic for computing the total number of months.\n function _totalMonths(uint256 numYears, uint256 numMonths)\n private\n pure\n returns (uint256 total)\n {\n unchecked {\n total = numYears * 12 + numMonths;\n }\n }\n\n /// @dev Unchecked arithmetic for adding two numbers.\n function _add(uint256 a, uint256 b) private pure returns (uint256 c) {\n unchecked {\n c = a + b;\n }\n }\n\n /// @dev Unchecked arithmetic for subtracting two numbers.\n function _sub(uint256 a, uint256 b) private pure returns (uint256 c) {\n unchecked {\n c = a - b;\n }\n }\n\n /// @dev Returns the offsetted timestamp.\n function _offsetted(uint256 year, uint256 month, uint256 day, uint256 timestamp)\n private\n pure\n returns (uint256 result)\n {\n uint256 dm = daysInMonth(year, month);\n if (day >= dm) {\n day = dm;\n }\n result = dateToEpochDay(year, month, day) * 86400 + (timestamp % 86400);\n }\n}\n"},"lib/solady/src/utils/LibString.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\n/// @notice Library for converting numbers into strings and other string operations.\n/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibString.sol)\n/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/LibString.sol)\n///\n/// Note:\n/// For performance and bytecode compactness, most of the string operations are restricted to\n/// byte strings (7-bit ASCII), except where otherwise specified.\n/// Usage of byte string operations on charsets with runes spanning two or more bytes\n/// can lead to undefined behavior.\nlibrary LibString {\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* CUSTOM ERRORS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev The length of the output is too small to contain all the hex digits.\n error HexLengthInsufficient();\n\n /// @dev The length of the string is more than 32 bytes.\n error TooBigForSmallString();\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* CONSTANTS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev The constant returned when the `search` is not found in the string.\n uint256 internal constant NOT_FOUND = type(uint256).max;\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* DECIMAL OPERATIONS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev Returns the base 10 decimal representation of `value`.\n function toString(uint256 value) internal pure returns (string memory str) {\n /// @solidity memory-safe-assembly\n assembly {\n // The maximum value of a uint256 contains 78 digits (1 byte per digit), but\n // we allocate 0xa0 bytes to keep the free memory pointer 32-byte word aligned.\n // We will need 1 word for the trailing zeros padding, 1 word for the length,\n // and 3 words for a maximum of 78 digits.\n str := add(mload(0x40), 0x80)\n // Update the free memory pointer to allocate.\n mstore(0x40, add(str, 0x20))\n // Zeroize the slot after the string.\n mstore(str, 0)\n\n // Cache the end of the memory to calculate the length later.\n let end := str\n\n let w := not(0) // Tsk.\n // We write the string from rightmost digit to leftmost digit.\n // The following is essentially a do-while loop that also handles the zero case.\n for { let temp := value } 1 {} {\n str := add(str, w) // `sub(str, 1)`.\n // Write the character to the pointer.\n // The ASCII index of the '0' character is 48.\n mstore8(str, add(48, mod(temp, 10)))\n // Keep dividing `temp` until zero.\n temp := div(temp, 10)\n if iszero(temp) { break }\n }\n\n let length := sub(end, str)\n // Move the pointer 32 bytes leftwards to make room for the length.\n str := sub(str, 0x20)\n // Store the length.\n mstore(str, length)\n }\n }\n\n /// @dev Returns the base 10 decimal representation of `value`.\n function toString(int256 value) internal pure returns (string memory str) {\n if (value >= 0) {\n return toString(uint256(value));\n }\n unchecked {\n str = toString(uint256(-value));\n }\n /// @solidity memory-safe-assembly\n assembly {\n // We still have some spare memory space on the left,\n // as we have allocated 3 words (96 bytes) for up to 78 digits.\n let length := mload(str) // Load the string length.\n mstore(str, 0x2d) // Store the '-' character.\n str := sub(str, 1) // Move back the string pointer by a byte.\n mstore(str, add(length, 1)) // Update the string length.\n }\n }\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* HEXADECIMAL OPERATIONS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev Returns the hexadecimal representation of `value`,\n /// left-padded to an input length of `length` bytes.\n /// The output is prefixed with \"0x\" encoded using 2 hexadecimal digits per byte,\n /// giving a total length of `length * 2 + 2` bytes.\n /// Reverts if `length` is too small for the output to contain all the digits.\n function toHexString(uint256 value, uint256 length) internal pure returns (string memory str) {\n str = toHexStringNoPrefix(value, length);\n /// @solidity memory-safe-assembly\n assembly {\n let strLength := add(mload(str), 2) // Compute the length.\n mstore(str, 0x3078) // Write the \"0x\" prefix.\n str := sub(str, 2) // Move the pointer.\n mstore(str, strLength) // Write the length.\n }\n }\n\n /// @dev Returns the hexadecimal representation of `value`,\n /// left-padded to an input length of `length` bytes.\n /// The output is prefixed with \"0x\" encoded using 2 hexadecimal digits per byte,\n /// giving a total length of `length * 2` bytes.\n /// Reverts if `length` is too small for the output to contain all the digits.\n function toHexStringNoPrefix(uint256 value, uint256 length)\n internal\n pure\n returns (string memory str)\n {\n /// @solidity memory-safe-assembly\n assembly {\n // We need 0x20 bytes for the trailing zeros padding, `length * 2` bytes\n // for the digits, 0x02 bytes for the prefix, and 0x20 bytes for the length.\n // We add 0x20 to the total and round down to a multiple of 0x20.\n // (0x20 + 0x20 + 0x02 + 0x20) = 0x62.\n str := add(mload(0x40), and(add(shl(1, length), 0x42), not(0x1f)))\n // Allocate the memory.\n mstore(0x40, add(str, 0x20))\n // Zeroize the slot after the string.\n mstore(str, 0)\n\n // Cache the end to calculate the length later.\n let end := str\n // Store \"0123456789abcdef\" in scratch space.\n mstore(0x0f, 0x30313233343536373839616263646566)\n\n let start := sub(str, add(length, length))\n let w := not(1) // Tsk.\n let temp := value\n // We write the string from rightmost digit to leftmost digit.\n // The following is essentially a do-while loop that also handles the zero case.\n for {} 1 {} {\n str := add(str, w) // `sub(str, 2)`.\n mstore8(add(str, 1), mload(and(temp, 15)))\n mstore8(str, mload(and(shr(4, temp), 15)))\n temp := shr(8, temp)\n if iszero(xor(str, start)) { break }\n }\n\n if temp {\n mstore(0x00, 0x2194895a) // `HexLengthInsufficient()`.\n revert(0x1c, 0x04)\n }\n\n // Compute the string's length.\n let strLength := sub(end, str)\n // Move the pointer and write the length.\n str := sub(str, 0x20)\n mstore(str, strLength)\n }\n }\n\n /// @dev Returns the hexadecimal representation of `value`.\n /// The output is prefixed with \"0x\" and encoded using 2 hexadecimal digits per byte.\n /// As address are 20 bytes long, the output will left-padded to have\n /// a length of `20 * 2 + 2` bytes.\n function toHexString(uint256 value) internal pure returns (string memory str) {\n str = toHexStringNoPrefix(value);\n /// @solidity memory-safe-assembly\n assembly {\n let strLength := add(mload(str), 2) // Compute the length.\n mstore(str, 0x3078) // Write the \"0x\" prefix.\n str := sub(str, 2) // Move the pointer.\n mstore(str, strLength) // Write the length.\n }\n }\n\n /// @dev Returns the hexadecimal representation of `value`.\n /// The output is prefixed with \"0x\".\n /// The output excludes leading \"0\" from the `toHexString` output.\n /// `0x00: \"0x0\", 0x01: \"0x1\", 0x12: \"0x12\", 0x123: \"0x123\"`.\n function toMinimalHexString(uint256 value) internal pure returns (string memory str) {\n str = toHexStringNoPrefix(value);\n /// @solidity memory-safe-assembly\n assembly {\n let o := eq(byte(0, mload(add(str, 0x20))), 0x30) // Whether leading zero is present.\n let strLength := add(mload(str), 2) // Compute the length.\n mstore(add(str, o), 0x3078) // Write the \"0x\" prefix, accounting for leading zero.\n str := sub(add(str, o), 2) // Move the pointer, accounting for leading zero.\n mstore(str, sub(strLength, o)) // Write the length, accounting for leading zero.\n }\n }\n\n /// @dev Returns the hexadecimal representation of `value`.\n /// The output excludes leading \"0\" from the `toHexStringNoPrefix` output.\n /// `0x00: \"0\", 0x01: \"1\", 0x12: \"12\", 0x123: \"123\"`.\n function toMinimalHexStringNoPrefix(uint256 value) internal pure returns (string memory str) {\n str = toHexStringNoPrefix(value);\n /// @solidity memory-safe-assembly\n assembly {\n let o := eq(byte(0, mload(add(str, 0x20))), 0x30) // Whether leading zero is present.\n let strLength := mload(str) // Get the length.\n str := add(str, o) // Move the pointer, accounting for leading zero.\n mstore(str, sub(strLength, o)) // Write the length, accounting for leading zero.\n }\n }\n\n /// @dev Returns the hexadecimal representation of `value`.\n /// The output is encoded using 2 hexadecimal digits per byte.\n /// As address are 20 bytes long, the output will left-padded to have\n /// a length of `20 * 2` bytes.\n function toHexStringNoPrefix(uint256 value) internal pure returns (string memory str) {\n /// @solidity memory-safe-assembly\n assembly {\n // We need 0x20 bytes for the trailing zeros padding, 0x20 bytes for the length,\n // 0x02 bytes for the prefix, and 0x40 bytes for the digits.\n // The next multiple of 0x20 above (0x20 + 0x20 + 0x02 + 0x40) is 0xa0.\n str := add(mload(0x40), 0x80)\n // Allocate the memory.\n mstore(0x40, add(str, 0x20))\n // Zeroize the slot after the string.\n mstore(str, 0)\n\n // Cache the end to calculate the length later.\n let end := str\n // Store \"0123456789abcdef\" in scratch space.\n mstore(0x0f, 0x30313233343536373839616263646566)\n\n let w := not(1) // Tsk.\n // We write the string from rightmost digit to leftmost digit.\n // The following is essentially a do-while loop that also handles the zero case.\n for { let temp := value } 1 {} {\n str := add(str, w) // `sub(str, 2)`.\n mstore8(add(str, 1), mload(and(temp, 15)))\n mstore8(str, mload(and(shr(4, temp), 15)))\n temp := shr(8, temp)\n if iszero(temp) { break }\n }\n\n // Compute the string's length.\n let strLength := sub(end, str)\n // Move the pointer and write the length.\n str := sub(str, 0x20)\n mstore(str, strLength)\n }\n }\n\n /// @dev Returns the hexadecimal representation of `value`.\n /// The output is prefixed with \"0x\", encoded using 2 hexadecimal digits per byte,\n /// and the alphabets are capitalized conditionally according to\n /// https://eips.ethereum.org/EIPS/eip-55\n function toHexStringChecksummed(address value) internal pure returns (string memory str) {\n str = toHexString(value);\n /// @solidity memory-safe-assembly\n assembly {\n let mask := shl(6, div(not(0), 255)) // `0b010000000100000000 ...`\n let o := add(str, 0x22)\n let hashed := and(keccak256(o, 40), mul(34, mask)) // `0b10001000 ... `\n let t := shl(240, 136) // `0b10001000 << 240`\n for { let i := 0 } 1 {} {\n mstore(add(i, i), mul(t, byte(i, hashed)))\n i := add(i, 1)\n if eq(i, 20) { break }\n }\n mstore(o, xor(mload(o), shr(1, and(mload(0x00), and(mload(o), mask)))))\n o := add(o, 0x20)\n mstore(o, xor(mload(o), shr(1, and(mload(0x20), and(mload(o), mask)))))\n }\n }\n\n /// @dev Returns the hexadecimal representation of `value`.\n /// The output is prefixed with \"0x\" and encoded using 2 hexadecimal digits per byte.\n function toHexString(address value) internal pure returns (string memory str) {\n str = toHexStringNoPrefix(value);\n /// @solidity memory-safe-assembly\n assembly {\n let strLength := add(mload(str), 2) // Compute the length.\n mstore(str, 0x3078) // Write the \"0x\" prefix.\n str := sub(str, 2) // Move the pointer.\n mstore(str, strLength) // Write the length.\n }\n }\n\n /// @dev Returns the hexadecimal representation of `value`.\n /// The output is encoded using 2 hexadecimal digits per byte.\n function toHexStringNoPrefix(address value) internal pure returns (string memory str) {\n /// @solidity memory-safe-assembly\n assembly {\n str := mload(0x40)\n\n // Allocate the memory.\n // We need 0x20 bytes for the trailing zeros padding, 0x20 bytes for the length,\n // 0x02 bytes for the prefix, and 0x28 bytes for the digits.\n // The next multiple of 0x20 above (0x20 + 0x20 + 0x02 + 0x28) is 0x80.\n mstore(0x40, add(str, 0x80))\n\n // Store \"0123456789abcdef\" in scratch space.\n mstore(0x0f, 0x30313233343536373839616263646566)\n\n str := add(str, 2)\n mstore(str, 40)\n\n let o := add(str, 0x20)\n mstore(add(o, 40), 0)\n\n value := shl(96, value)\n\n // We write the string from rightmost digit to leftmost digit.\n // The following is essentially a do-while loop that also handles the zero case.\n for { let i := 0 } 1 {} {\n let p := add(o, add(i, i))\n let temp := byte(i, value)\n mstore8(add(p, 1), mload(and(temp, 15)))\n mstore8(p, mload(shr(4, temp)))\n i := add(i, 1)\n if eq(i, 20) { break }\n }\n }\n }\n\n /// @dev Returns the hex encoded string from the raw bytes.\n /// The output is encoded using 2 hexadecimal digits per byte.\n function toHexString(bytes memory raw) internal pure returns (string memory str) {\n str = toHexStringNoPrefix(raw);\n /// @solidity memory-safe-assembly\n assembly {\n let strLength := add(mload(str), 2) // Compute the length.\n mstore(str, 0x3078) // Write the \"0x\" prefix.\n str := sub(str, 2) // Move the pointer.\n mstore(str, strLength) // Write the length.\n }\n }\n\n /// @dev Returns the hex encoded string from the raw bytes.\n /// The output is encoded using 2 hexadecimal digits per byte.\n function toHexStringNoPrefix(bytes memory raw) internal pure returns (string memory str) {\n /// @solidity memory-safe-assembly\n assembly {\n let length := mload(raw)\n str := add(mload(0x40), 2) // Skip 2 bytes for the optional prefix.\n mstore(str, add(length, length)) // Store the length of the output.\n\n // Store \"0123456789abcdef\" in scratch space.\n mstore(0x0f, 0x30313233343536373839616263646566)\n\n let o := add(str, 0x20)\n let end := add(raw, length)\n\n for {} iszero(eq(raw, end)) {} {\n raw := add(raw, 1)\n mstore8(add(o, 1), mload(and(mload(raw), 15)))\n mstore8(o, mload(and(shr(4, mload(raw)), 15)))\n o := add(o, 2)\n }\n mstore(o, 0) // Zeroize the slot after the string.\n mstore(0x40, add(o, 0x20)) // Allocate the memory.\n }\n }\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* RUNE STRING OPERATIONS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n /// @dev Returns the number of UTF characters in the string.\n function runeCount(string memory s) internal pure returns (uint256 result) {\n /// @solidity memory-safe-assembly\n assembly {\n if mload(s) {\n mstore(0x00, div(not(0), 255))\n mstore(0x20, 0x0202020202020202020202020202020202020202020202020303030304040506)\n let o := add(s, 0x20)\n let end := add(o, mload(s))\n for { result := 1 } 1 { result := add(result, 1) } {\n o := add(o, byte(0, mload(shr(250, mload(o)))))\n if iszero(lt(o, end)) { break }\n }\n }\n }\n }\n\n /// @dev Returns if this string is a 7-bit ASCII string.\n /// (i.e. all characters codes are in [0..127])\n function is7BitASCII(string memory s) internal pure returns (bool result) {\n /// @solidity memory-safe-assembly\n assembly {\n let mask := shl(7, div(not(0), 255))\n result := 1\n let n := mload(s)\n if n {\n let o := add(s, 0x20)\n let end := add(o, n)\n let last := mload(end)\n mstore(end, 0)\n for {} 1 {} {\n if and(mask, mload(o)) {\n result := 0\n break\n }\n o := add(o, 0x20)\n if iszero(lt(o, end)) { break }\n }\n mstore(end, last)\n }\n }\n }\n\n /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/\n /* BYTE STRING OPERATIONS */\n /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/\n\n // For performance and bytecode compactness, byte string operations are restricted\n // to 7-bit ASCII strings. All offsets are byte offsets, not UTF character offsets.\n // Usage of byte string operations on charsets with runes spanning two or more bytes\n // can lead to undefined behavior.\n\n /// @dev Returns `subject` all occurrences of `search` replaced with `replacement`.\n function replace(string memory subject, string memory search, string memory replacement)\n internal\n pure\n returns (string memory result)\n {\n /// @solidity memory-safe-assembly\n assembly {\n let subjectLength := mload(subject)\n let searchLength := mload(search)\n let replacementLength := mload(replacement)\n\n subject := add(subject, 0x20)\n search := add(search, 0x20)\n replacement := add(replacement, 0x20)\n result := add(mload(0x40), 0x20)\n\n let subjectEnd := add(subject, subjectLength)\n if iszero(gt(searchLength, subjectLength)) {\n let subjectSearchEnd := add(sub(subjectEnd, searchLength), 1)\n let h := 0\n if iszero(lt(searchLength, 0x20)) { h := keccak256(search, searchLength) }\n let m := shl(3, sub(0x20, and(searchLength, 0x1f)))\n let s := mload(search)\n for {} 1 {} {\n let t := mload(subject)\n // Whether the first `searchLength % 32` bytes of\n // `subject` and `search` matches.\n if iszero(shr(m, xor(t, s))) {\n if h {\n if iszero(eq(keccak256(subject, searchLength), h)) {\n mstore(result, t)\n result := add(result, 1)\n subject := add(subject, 1)\n if iszero(lt(subject, subjectSearchEnd)) { break }\n continue\n }\n }\n // Copy the `replacement` one word at a time.\n for { let o := 0 } 1 {} {\n mstore(add(result, o), mload(add(replacement, o)))\n o := add(o, 0x20)\n if iszero(lt(o, replacementLength)) { break }\n }\n result := add(result, replacementLength)\n subject := add(subject, searchLength)\n if searchLength {\n if iszero(lt(subject, subjectSearchEnd)) { break }\n continue\n }\n }\n mstore(result, t)\n result := add(result, 1)\n subject := add(subject, 1)\n if iszero(lt(subject, subjectSearchEnd)) { break }\n }\n }\n\n let resultRemainder := result\n result := add(mload(0x40), 0x20)\n let k := add(sub(resultRemainder, result), sub(subjectEnd, subject))\n // Copy the rest of the string one word at a time.\n for {} lt(subject, subjectEnd) {} {\n mstore(resultRemainder, mload(subject))\n resultRemainder := add(resultRemainder, 0x20)\n subject := add(subject, 0x20)\n }\n result := sub(result, 0x20)\n let last := add(add(result, 0x20), k) // Zeroize the slot after the string.\n mstore(last, 0)\n mstore(0x40, add(last, 0x20)) // Allocate the memory.\n mstore(result, k) // Store the length.\n }\n }\n\n /// @dev Returns the byte index of the first location of `search` in `subject`,\n /// searching from left to right, starting from `from`.\n /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.\n function indexOf(string memory subject, string memory search, uint256 from)\n internal\n pure\n returns (uint256 result)\n {\n /// @solidity memory-safe-assembly\n assembly {\n for { let subjectLength := mload(subject) } 1 {} {\n if iszero(mload(search)) {\n if iszero(gt(from, subjectLength)) {\n result := from\n break\n }\n result := subjectLength\n break\n }\n let searchLength := mload(search)\n let subjectStart := add(subject, 0x20)\n\n result := not(0) // Initialize to `NOT_FOUND`.\n\n subject := add(subjectStart, from)\n let end := add(sub(add(subjectStart, subjectLength), searchLength), 1)\n\n let m := shl(3, sub(0x20, and(searchLength, 0x1f)))\n let s := mload(add(search, 0x20))\n\n if iszero(and(lt(subject, end), lt(from, subjectLength))) { break }\n\n if iszero(lt(searchLength, 0x20)) {\n for { let h := keccak256(add(search, 0x20), searchLength) } 1 {} {\n if iszero(shr(m, xor(mload(subject), s))) {\n if eq(keccak256(subject, searchLength), h) {\n result := sub(subject, subjectStart)\n break\n }\n }\n subject := add(subject, 1)\n if iszero(lt(subject, end)) { break }\n }\n break\n }\n for {} 1 {} {\n if iszero(shr(m, xor(mload(subject), s))) {\n result := sub(subject, subjectStart)\n break\n }\n subject := add(subject, 1)\n if iszero(lt(subject, end)) { break }\n }\n break\n }\n }\n }\n\n /// @dev Returns the byte index of the first location of `search` in `subject`,\n /// searching from left to right.\n /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.\n function indexOf(string memory subject, string memory search)\n internal\n pure\n returns (uint256 result)\n {\n result = indexOf(subject, search, 0);\n }\n\n /// @dev Returns the byte index of the first location of `search` in `subject`,\n /// searching from right to left, starting from `from`.\n /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.\n function lastIndexOf(string memory subject, string memory search, uint256 from)\n internal\n pure\n returns (uint256 result)\n {\n /// @solidity memory-safe-assembly\n assembly {\n for {} 1 {} {\n result := not(0) // Initialize to `NOT_FOUND`.\n let searchLength := mload(search)\n if gt(searchLength, mload(subject)) { break }\n let w := result\n\n let fromMax := sub(mload(subject), searchLength)\n if iszero(gt(fromMax, from)) { from := fromMax }\n\n let end := add(add(subject, 0x20), w)\n subject := add(add(subject, 0x20), from)\n if iszero(gt(subject, end)) { break }\n // As this function is not too often used,\n // we shall simply use keccak256 for smaller bytecode size.\n for { let h := keccak256(add(search, 0x20), searchLength) } 1 {} {\n if eq(keccak256(subject, searchLength), h) {\n result := sub(subject, add(end, 1))\n break\n }\n subject := add(subject, w) // `sub(subject, 1)`.\n if iszero(gt(subject, end)) { break }\n }\n break\n }\n }\n }\n\n /// @dev Returns the byte index of the first location of `search` in `subject`,\n /// searching from right to left.\n /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.\n function lastIndexOf(string memory subject, string memory search)\n internal\n pure\n returns (uint256 result)\n {\n result = lastIndexOf(subject, search, uint256(int256(-1)));\n }\n\n /// @dev Returns true if `search` is found in `subject`, false otherwise.\n function contains(string memory subject, string memory search) internal pure returns (bool) {\n return indexOf(subject, search) != NOT_FOUND;\n }\n\n /// @dev Returns whether `subject` starts with `search`.\n function startsWith(string memory subject, string memory search)\n internal\n pure\n returns (bool result)\n {\n /// @solidity memory-safe-assembly\n assembly {\n let searchLength := mload(search)\n // Just using keccak256 directly is actually cheaper.\n // forgefmt: disable-next-item\n result := and(\n iszero(gt(searchLength, mload(subject))),\n eq(\n keccak256(add(subject, 0x20), searchLength),\n keccak256(add(search, 0x20), searchLength)\n )\n )\n }\n }\n\n /// @dev Returns whether `subject` ends with `search`.\n function endsWith(string memory subject, string memory search)\n internal\n pure\n returns (bool result)\n {\n /// @solidity memory-safe-assembly\n assembly {\n let searchLength := mload(search)\n let subjectLength := mload(subject)\n // Whether `search` is not longer than `subject`.\n let withinRange := iszero(gt(searchLength, subjectLength))\n // Just using keccak256 directly is actually cheaper.\n // forgefmt: disable-next-item\n result := and(\n withinRange,\n eq(\n keccak256(\n // `subject + 0x20 + max(subjectLength - searchLength, 0)`.\n add(add(subject, 0x20), mul(withinRange, sub(subjectLength, searchLength))),\n searchLength\n ),\n keccak256(add(search, 0x20), searchLength)\n )\n )\n }\n }\n\n /// @dev Returns `subject` repeated `times`.\n function repeat(string memory subject, uint256 times)\n internal\n pure\n returns (string memory result)\n {\n /// @solidity memory-safe-assembly\n assembly {\n let subjectLength := mload(subject)\n if iszero(or(iszero(times), iszero(subjectLength))) {\n subject := add(subject, 0x20)\n result := mload(0x40)\n let output := add(result, 0x20)\n for {} 1 {} {\n // Copy the `subject` one word at a time.\n for { let o := 0 } 1 {} {\n mstore(add(output, o), mload(add(subject, o)))\n o := add(o, 0x20)\n if iszero(lt(o, subjectLength)) { break }\n }\n output := add(output, subjectLength)\n times := sub(times, 1)\n if iszero(times) { break }\n }\n mstore(output, 0) // Zeroize the slot after the string.\n let resultLength := sub(output, add(result, 0x20))\n mstore(result, resultLength) // Store the length.\n // Allocate the memory.\n mstore(0x40, add(result, add(resultLength, 0x20)))\n }\n }\n }\n\n /// @dev Returns a copy of `subject` sliced from `start` to `end` (exclusive).\n /// `start` and `end` are byte offsets.\n function slice(string memory subject, uint256 start, uint256 end)\n internal\n pure\n returns (string memory result)\n {\n /// @solidity memory-safe-assembly\n assembly {\n let subjectLength := mload(subject)\n if iszero(gt(subjectLength, end)) { end := subjectLength }\n if iszero(gt(subjectLength, start)) { start := subjectLength }\n if lt(start, end) {\n result := mload(0x40)\n let resultLength := sub(end, start)\n mstore(result, resultLength)\n subject := add(subject, start)\n let w := not(0x1f)\n // Copy the `subject` one word at a time, backwards.\n for { let o := and(add(resultLength, 0x1f), w) } 1 {} {\n mstore(add(result, o), mload(add(subject, o)))\n o := add(o, w) // `sub(o, 0x20)`.\n if iszero(o) { break }\n }\n // Zeroize the slot after the string.\n mstore(add(add(result, 0x20), resultLength), 0)\n // Allocate memory for the length and the bytes,\n // rounded up to a multiple of 32.\n mstore(0x40, add(result, and(add(resultLength, 0x3f), w)))\n }\n }\n }\n\n /// @dev Returns a copy of `subject` sliced from `start` to the end of the string.\n /// `start` is a byte offset.\n function slice(string memory subject, uint256 start)\n internal\n pure\n returns (string memory result)\n {\n result = slice(subject, start, uint256(int256(-1)));\n }\n\n /// @dev Returns all the indices of `search` in `subject`.\n /// The indices are byte offsets.\n function indicesOf(string memory subject, string memory search)\n internal\n pure\n returns (uint256[] memory result)\n {\n /// @solidity memory-safe-assembly\n assembly {\n let subjectLength := mload(subject)\n let searchLength := mload(search)\n\n if iszero(gt(searchLength, subjectLength)) {\n subject := add(subject, 0x20)\n search := add(search, 0x20)\n result := add(mload(0x40), 0x20)\n\n let subjectStart := subject\n let subjectSearchEnd := add(sub(add(subject, subjectLength), searchLength), 1)\n let h := 0\n if iszero(lt(searchLength, 0x20)) { h := keccak256(search, searchLength) }\n let m := shl(3, sub(0x20, and(searchLength, 0x1f)))\n let s := mload(search)\n for {} 1 {} {\n let t := mload(subject)\n // Whether the first `searchLength % 32` bytes of\n // `subject` and `search` matches.\n if iszero(shr(m, xor(t, s))) {\n if h {\n if iszero(eq(keccak256(subject, searchLength), h)) {\n subject := add(subject, 1)\n if iszero(lt(subject, subjectSearchEnd)) { break }\n continue\n }\n }\n // Append to `result`.\n mstore(result, sub(subject, subjectStart))\n result := add(result, 0x20)\n // Advance `subject` by `searchLength`.\n subject := add(subject, searchLength)\n if searchLength {\n if iszero(lt(subject, subjectSearchEnd)) { break }\n continue\n }\n }\n subject := add(subject, 1)\n if iszero(lt(subject, subjectSearchEnd)) { break }\n }\n let resultEnd := result\n // Assign `result` to the free memory pointer.\n result := mload(0x40)\n // Store the length of `result`.\n mstore(result, shr(5, sub(resultEnd, add(result, 0x20))))\n // Allocate memory for result.\n // We allocate one more word, so this array can be recycled for {split}.\n mstore(0x40, add(resultEnd, 0x20))\n }\n }\n }\n\n /// @dev Returns a arrays of strings based on the `delimiter` inside of the `subject` string.\n function split(string memory subject, string memory delimiter)\n internal\n pure\n returns (string[] memory result)\n {\n uint256[] memory indices = indicesOf(subject, delimiter);\n /// @solidity memory-safe-assembly\n assembly {\n let w := not(0x1f)\n let indexPtr := add(indices, 0x20)\n let indicesEnd := add(indexPtr, shl(5, add(mload(indices), 1)))\n mstore(add(indicesEnd, w), mload(subject))\n mstore(indices, add(mload(indices), 1))\n let prevIndex := 0\n for {} 1 {} {\n let index := mload(indexPtr)\n mstore(indexPtr, 0x60)\n if iszero(eq(index, prevIndex)) {\n let element := mload(0x40)\n let elementLength := sub(index, prevIndex)\n mstore(element, elementLength)\n // Copy the `subject` one word at a time, backwards.\n for { let o := and(add(elementLength, 0x1f), w) } 1 {} {\n mstore(add(element, o), mload(add(add(subject, prevIndex), o)))\n o := add(o, w) // `sub(o, 0x20)`.\n if iszero(o) { break }\n }\n // Zeroize the slot after the string.\n mstore(add(add(element, 0x20), elementLength), 0)\n // Allocate memory for the length and the bytes,\n // rounded up to a multiple of 32.\n mstore(0x40, add(element, and(add(elementLength, 0x3f), w)))\n // Store the `element` into the array.\n mstore(indexPtr, element)\n }\n prevIndex := add(index, mload(delimiter))\n indexPtr := add(indexPtr, 0x20)\n if iszero(lt(indexPtr, indicesEnd)) { break }\n }\n result := indices\n if iszero(mload(delimiter)) {\n result := add(indices, 0x20)\n mstore(result, sub(mload(indices), 2))\n }\n }\n }\n\n /// @dev Returns a concatenated string of `a` and `b`.\n /// Cheaper than `string.concat()` and does not de-align the free memory pointer.\n function concat(string memory a, string memory b)\n internal\n pure\n returns (string memory result)\n {\n /// @solidity memory-safe-assembly\n assembly {\n let w := not(0x1f)\n result := mload(0x40)\n let aLength := mload(a)\n // Copy `a` one word at a time, backwards.\n for { let o := and(add(aLength, 0x20), w) } 1 {} {\n mstore(add(result, o), mload(add(a, o)))\n o := add(o, w) // `sub(o, 0x20)`.\n if iszero(o) { break }\n }\n let bLength := mload(b)\n let output := add(result, aLength)\n // Copy `b` one word at a time, backwards.\n for { let o := and(add(bLength, 0x20), w) } 1 {} {\n mstore(add(output, o), mload(add(b, o)))\n o := add(o, w) // `sub(o, 0x20)`.\n if iszero(o) { break }\n }\n let totalLength := add(aLength, bLength)\n let last := add(add(result, 0x20), totalLength)\n // Zeroize the slot after the string.\n mstore(last, 0)\n // Stores the length.\n mstore(result, totalLength)\n // Allocate memory for the length and the bytes,\n // rounded up to a multiple of 32.\n mstore(0x40, and(add(last, 0x1f), w))\n }\n }\n\n /// @dev Returns a copy of the string in either lowercase or UPPERCASE.\n /// WARNING! This function is only compatible with 7-bit ASCII strings.\n function toCase(string memory subject, bool toUpper)\n internal\n pure\n returns (string memory result)\n {\n /// @solidity memory-safe-assembly\n assembly {\n let length := mload(subject)\n if length {\n result := add(mload(0x40), 0x20)\n subject := add(subject, 1)\n let flags := shl(add(70, shl(5, toUpper)), 0x3ffffff)\n let w := not(0)\n for { let o := length } 1 {} {\n o := add(o, w)\n let b := and(0xff, mload(add(subject, o)))\n mstore8(add(result, o), xor(b, and(shr(b, flags), 0x20)))\n if iszero(o) { break }\n }\n result := mload(0x40)\n mstore(result, length) // Store the length.\n let last := add(add(result, 0x20), length)\n mstore(last, 0) // Zeroize the slot after the string.\n mstore(0x40, add(last, 0x20)) // Allocate the memory.\n }\n }\n }\n\n /// @dev Returns a string from a small bytes32 string.\n /// `s` must be null-terminated, or behavior will be undefined.\n function fromSmallString(bytes32 s) internal pure returns (string memory result) {\n /// @solidity memory-safe-assembly\n assembly {\n result := mload(0x40)\n let n := 0\n for {} byte(n, s) { n := add(n, 1) } {} // Scan for '\\0'.\n mstore(result, n)\n let o := add(result, 0x20)\n mstore(o, s)\n mstore(add(o, n), 0)\n mstore(0x40, add(result, 0x40))\n }\n }\n\n /// @dev Returns the small string, with all bytes after the first null byte zeroized.\n function normalizeSmallString(bytes32 s) internal pure returns (bytes32 result) {\n /// @solidity memory-safe-assembly\n assembly {\n for {} byte(result, s) { result := add(result, 1) } {} // Scan for '\\0'.\n mstore(0x00, s)\n mstore(result, 0x00)\n result := mload(0x00)\n }\n }\n\n /// @dev Returns the string as a normalized null-terminated small string.\n function toSmallString(string memory s) internal pure returns (bytes32 result) {\n /// @solidity memory-safe-assembly\n assembly {\n result := mload(s)\n if iszero(lt(result, 33)) {\n mstore(0x00, 0xec92f9a3) // `TooBigForSmallString()`.\n revert(0x1c, 0x04)\n }\n result := shl(shl(3, sub(32, result)), mload(add(s, result)))\n }\n }\n\n /// @dev Returns a lowercased copy of the string.\n /// WARNING! This function is only compatible with 7-bit ASCII strings.\n function lower(string memory subject) internal pure returns (string memory result) {\n result = toCase(subject, false);\n }\n\n /// @dev Returns an UPPERCASED copy of the string.\n /// WARNING! This function is only compatible with 7-bit ASCII strings.\n function upper(string memory subject) internal pure returns (string memory result) {\n result = toCase(subject, true);\n }\n\n /// @dev Escapes the string to be used within HTML tags.\n function escapeHTML(string memory s) internal pure returns (string memory result) {\n /// @solidity memory-safe-assembly\n assembly {\n let end := add(s, mload(s))\n result := add(mload(0x40), 0x20)\n // Store the bytes of the packed offsets and strides into the scratch space.\n // `packed = (stride << 5) | offset`. Max offset is 20. Max stride is 6.\n mstore(0x1f, 0x900094)\n mstore(0x08, 0xc0000000a6ab)\n // Store \""&'<>\" into the scratch space.\n mstore(0x00, shl(64, 0x2671756f743b26616d703b262333393b266c743b2667743b))\n for {} iszero(eq(s, end)) {} {\n s := add(s, 1)\n let c := and(mload(s), 0xff)\n // Not in `[\"\\\"\",\"'\",\"&\",\"<\",\">\"]`.\n if iszero(and(shl(c, 1), 0x500000c400000000)) {\n mstore8(result, c)\n result := add(result, 1)\n continue\n }\n let t := shr(248, mload(c))\n mstore(result, mload(and(t, 0x1f)))\n result := add(result, shr(5, t))\n }\n let last := result\n mstore(last, 0) // Zeroize the slot after the string.\n result := mload(0x40)\n mstore(result, sub(last, add(result, 0x20))) // Store the length.\n mstore(0x40, add(last, 0x20)) // Allocate the memory.\n }\n }\n\n /// @dev Escapes the string to be used within double-quotes in a JSON.\n /// If `addDoubleQuotes` is true, the result will be enclosed in double-quotes.\n function escapeJSON(string memory s, bool addDoubleQuotes)\n internal\n pure\n returns (string memory result)\n {\n /// @solidity memory-safe-assembly\n assembly {\n let end := add(s, mload(s))\n result := add(mload(0x40), 0x20)\n if addDoubleQuotes {\n mstore8(result, 34)\n result := add(1, result)\n }\n // Store \"\\\\u0000\" in scratch space.\n // Store \"0123456789abcdef\" in scratch space.\n // Also, store `{0x08:\"b\", 0x09:\"t\", 0x0a:\"n\", 0x0c:\"f\", 0x0d:\"r\"}`.\n // into the scratch space.\n mstore(0x15, 0x5c75303030303031323334353637383961626364656662746e006672)\n // Bitmask for detecting `[\"\\\"\",\"\\\\\"]`.\n let e := or(shl(0x22, 1), shl(0x5c, 1))\n for {} iszero(eq(s, end)) {} {\n s := add(s, 1)\n let c := and(mload(s), 0xff)\n if iszero(lt(c, 0x20)) {\n if iszero(and(shl(c, 1), e)) {\n // Not in `[\"\\\"\",\"\\\\\"]`.\n mstore8(result, c)\n result := add(result, 1)\n continue\n }\n mstore8(result, 0x5c) // \"\\\\\".\n mstore8(add(result, 1), c)\n result := add(result, 2)\n continue\n }\n if iszero(and(shl(c, 1), 0x3700)) {\n // Not in `[\"\\b\",\"\\t\",\"\\n\",\"\\f\",\"\\d\"]`.\n mstore8(0x1d, mload(shr(4, c))) // Hex value.\n mstore8(0x1e, mload(and(c, 15))) // Hex value.\n mstore(result, mload(0x19)) // \"\\\\u00XX\".\n result := add(result, 6)\n continue\n }\n mstore8(result, 0x5c) // \"\\\\\".\n mstore8(add(result, 1), mload(add(c, 8)))\n result := add(result, 2)\n }\n if addDoubleQuotes {\n mstore8(result, 34)\n result := add(1, result)\n }\n let last := result\n mstore(last, 0) // Zeroize the slot after the string.\n result := mload(0x40)\n mstore(result, sub(last, add(result, 0x20))) // Store the length.\n mstore(0x40, add(last, 0x20)) // Allocate the memory.\n }\n }\n\n /// @dev Escapes the string to be used within double-quotes in a JSON.\n function escapeJSON(string memory s) internal pure returns (string memory result) {\n result = escapeJSON(s, false);\n }\n\n /// @dev Returns whether `a` equals `b`.\n function eq(string memory a, string memory b) internal pure returns (bool result) {\n /// @solidity memory-safe-assembly\n assembly {\n result := eq(keccak256(add(a, 0x20), mload(a)), keccak256(add(b, 0x20), mload(b)))\n }\n }\n\n /// @dev Returns whether `a` equals `b`, where `b` is a null-terminated small string.\n function eqs(string memory a, bytes32 b) internal pure returns (bool result) {\n /// @solidity memory-safe-assembly\n assembly {\n // These should be evaluated on compile time, as far as possible.\n let m := not(shl(7, div(not(iszero(b)), 255))) // `0x7f7f ...`.\n let x := not(or(m, or(b, add(m, and(b, m)))))\n let r := shl(7, iszero(iszero(shr(128, x))))\n r := or(r, shl(6, iszero(iszero(shr(64, shr(r, x))))))\n r := or(r, shl(5, lt(0xffffffff, shr(r, x))))\n r := or(r, shl(4, lt(0xffff, shr(r, x))))\n r := or(r, shl(3, lt(0xff, shr(r, x))))\n // forgefmt: disable-next-item\n result := gt(eq(mload(a), add(iszero(x), xor(31, shr(3, r)))),\n xor(shr(add(8, r), b), shr(add(8, r), mload(add(a, 0x20)))))\n }\n }\n\n /// @dev Packs a single string with its length into a single word.\n /// Returns `bytes32(0)` if the length is zero or greater than 31.\n function packOne(string memory a) internal pure returns (bytes32 result) {\n /// @solidity memory-safe-assembly\n assembly {\n // We don't need to zero right pad the string,\n // since this is our own custom non-standard packing scheme.\n result :=\n mul(\n // Load the length and the bytes.\n mload(add(a, 0x1f)),\n // `length != 0 && length < 32`. Abuses underflow.\n // Assumes that the length is valid and within the block gas limit.\n lt(sub(mload(a), 1), 0x1f)\n )\n }\n }\n\n /// @dev Unpacks a string packed using {packOne}.\n /// Returns the empty string if `packed` is `bytes32(0)`.\n /// If `packed` is not an output of {packOne}, the output behavior is undefined.\n function unpackOne(bytes32 packed) internal pure returns (string memory result) {\n /// @solidity memory-safe-assembly\n assembly {\n // Grab the free memory pointer.\n result := mload(0x40)\n // Allocate 2 words (1 for the length, 1 for the bytes).\n mstore(0x40, add(result, 0x40))\n // Zeroize the length slot.\n mstore(result, 0)\n // Store the length and bytes.\n mstore(add(result, 0x1f), packed)\n // Right pad with zeroes.\n mstore(add(add(result, 0x20), mload(result)), 0)\n }\n }\n\n /// @dev Packs two strings with their lengths into a single word.\n /// Returns `bytes32(0)` if combined length is zero or greater than 30.\n function packTwo(string memory a, string memory b) internal pure returns (bytes32 result) {\n /// @solidity memory-safe-assembly\n assembly {\n let aLength := mload(a)\n // We don't need to zero right pad the strings,\n // since this is our own custom non-standard packing scheme.\n result :=\n mul(\n // Load the length and the bytes of `a` and `b`.\n or(\n shl(shl(3, sub(0x1f, aLength)), mload(add(a, aLength))),\n mload(sub(add(b, 0x1e), aLength))\n ),\n // `totalLength != 0 && totalLength < 31`. Abuses underflow.\n // Assumes that the lengths are valid and within the block gas limit.\n lt(sub(add(aLength, mload(b)), 1), 0x1e)\n )\n }\n }\n\n /// @dev Unpacks strings packed using {packTwo}.\n /// Returns the empty strings if `packed` is `bytes32(0)`.\n /// If `packed` is not an output of {packTwo}, the output behavior is undefined.\n function unpackTwo(bytes32 packed)\n internal\n pure\n returns (string memory resultA, string memory resultB)\n {\n /// @solidity memory-safe-assembly\n assembly {\n // Grab the free memory pointer.\n resultA := mload(0x40)\n resultB := add(resultA, 0x40)\n // Allocate 2 words for each string (1 for the length, 1 for the byte). Total 4 words.\n mstore(0x40, add(resultB, 0x40))\n // Zeroize the length slots.\n mstore(resultA, 0)\n mstore(resultB, 0)\n // Store the lengths and bytes.\n mstore(add(resultA, 0x1f), packed)\n mstore(add(resultB, 0x1f), mload(add(add(resultA, 0x20), mload(resultA))))\n // Right pad with zeroes.\n mstore(add(add(resultA, 0x20), mload(resultA)), 0)\n mstore(add(add(resultB, 0x20), mload(resultB)), 0)\n }\n }\n\n /// @dev Directly returns `a` without copying.\n function directReturn(string memory a) internal pure {\n assembly {\n // Assumes that the string does not start from the scratch space.\n let retStart := sub(a, 0x20)\n let retSize := add(mload(a), 0x40)\n // Right pad with zeroes. Just in case the string is produced\n // by a method that doesn't zero right pad.\n mstore(add(retStart, retSize), 0)\n // Store the return offset.\n mstore(retStart, 0x20)\n // End the transaction, returning the string.\n return(retStart, retSize)\n }\n }\n}\n"}},"settings":{"remappings":["solady/=lib/solady/src/","@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/","ds-test/=lib/forge-std/lib/ds-test/src/","erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/","forge-std/=lib/forge-std/src/","halmos-cheatcodes/=lib/openzeppelin-contracts/lib/halmos-cheatcodes/src/","openzeppelin-contracts/=lib/openzeppelin-contracts/"],"optimizer":{"enabled":true,"runs":999999},"metadata":{"useLiteralContent":false,"bytecodeHash":"ipfs","appendCBOR":true},"outputSelection":{"*":{"*":["abi","evm.bytecode","evm.deployedBytecode","evm.methodIdentifiers","metadata"]}},"evmVersion":"paris","viaIR":true,"libraries":{}}}