diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index dac6d5e8d..f0d991876 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -14,6 +14,7 @@ on: types: [created] permissions: contents: write + id-token: write env: NODE_VERSION: 22.18 EVM_ARTIFACTS_PATHS: | @@ -29,21 +30,23 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 # Note we set an `id` called `release`. We'll use that later... - name: Validate and extract release information id: release uses: manovotny/github-releases-for-automated-package-publishing-action@v2.0.1 - # Setup .npmrc file to publish to npm - - uses: actions/setup-node@v3 + # Enable npm trusted publishing (OIDC) for releases + - uses: actions/setup-node@v4 with: node-version: "${{ env.NODE_VERSION }}" - always-auth: true registry-url: "https://registry.npmjs.org" cache: "yarn" + - name: Update npm + run: npm install -g npm@latest + - name: Install packages run: yarn install --frozen-lockfile @@ -88,25 +91,23 @@ jobs: # If there *is not* a tag (ie. `beta`, `canary`, etc.), we publish a # version of a package (ie. 1.2.3). # - # This example is using npm to publish, but you could just as easily - # use yarn, if you prefer. It's also publishing to the NPM registry, - # thus, it's using `NPM_TOKEN`, but you could just as easily use - # `GITHUB_TOKEN` if you were publishing to the GitHub Package registry. + # This workflow publishes via npm using GitHub's OIDC integration for + # trusted publishing, so no long-lived tokens are required. # This will publish a "pre-release" or "tagged" version of a package. # This will publish a version of a package. - name: Publish version if: steps.release.outputs.tag == '' - run: yarn publish + run: npm publish env: - NPM_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + NPM_CONFIG_PROVENANCE: "false" - name: Publish tagged version if: steps.release.outputs.tag != '' - run: yarn publish --tag ${{ steps.release.outputs.tag }} + run: npm publish --tag ${{ steps.release.outputs.tag }} env: - NPM_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + NPM_CONFIG_PROVENANCE: "false" # Due to GitHub cache scoping, we cannot easily share the cache between releases even if the cache key is the same. # In order to not slow down the NPM package publishing, we move the building of SVM binaries for GitHub release to a @@ -115,7 +116,7 @@ jobs: name: Release SVM production binaries on GitHub runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Setup Anchor & Solana uses: ./.github/actions/setup-solana-anchor @@ -138,7 +139,7 @@ jobs: name: Release SVM test binaries on GitHub runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Setup Anchor & Solana uses: ./.github/actions/setup-solana-anchor diff --git a/.solhint.json b/.solhint.json index ad76487ec..5f61aa9a7 100644 --- a/.solhint.json +++ b/.solhint.json @@ -3,6 +3,7 @@ "rules": { "compiler-version": ["error", "^0.8.0"], "func-visibility": ["warn", { "ignoreConstructors": true }], - "const-name-snakecase": "off" + "const-name-snakecase": "off", + "no-console": "off" } } diff --git a/broadcast/113DeploySponsoredCCTPSrcPeriphery.sol/42161/run-latest.json b/broadcast/113DeploySponsoredCCTPSrcPeriphery.sol/42161/run-latest.json new file mode 100644 index 000000000..77b94a44c --- /dev/null +++ b/broadcast/113DeploySponsoredCCTPSrcPeriphery.sol/42161/run-latest.json @@ -0,0 +1,66 @@ +{ + "transactions": [ + { + "hash": "0x6c4f6d7537530911757ecc317e0b5a39b5caf7089ab5549cb4299c830a9d854c", + "transactionType": "CREATE", + "contractName": "SponsoredCCTPSrcPeriphery", + "contractAddress": "0xce1ffe01ebb4f8521c12e74363a396ee3d337e1b", + "function": null, + "arguments": ["0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d", "3", "0x9A8f92a830A5cB89a3816e3D267CB7791c16b04D"], + "transaction": { + "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "gas": "0x1271f2", + "value": "0x0", + "input": "0x60c03461012157601f61100738819003918201601f19168301916001600160401b03831184841017610125578084926060946040528339810103126101215761004781610139565b60208201519163ffffffff831683036101215760406100669101610139565b905f549260018060a01b03908160018060a01b03199333858816175f5560405196823391167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a31660805260a0527ff0a1b42b86a218bb35dbc2254545839ce4b1bf1d3780b5099e3e0abfc7a5b200921690825416179055610eb9908161014e8239608051818181610170015281816107b701528181610826015281816108b001526109b3015260a0518181816101b1015261061c0152f35b5f80fd5b634e487b7160e01b5f52604160045260245ffd5b51906001600160a01b03821682036101215756fe6080806040526004361015610012575f80fd5b5f905f3560e01c908163238ac93314610b645750806363cd663b146102d45780636c19e78314610261578063715018a6146101fb5780638da5cb5b146101d55780638ddb4a89146101945780639748cf7c14610150578063f2fde38b146100d15763feb6172414610081575f80fd5b346100ce5760203660031901126100ce5760ff604060209260043581527ff0a1b42b86a218bb35dbc2254545839ce4b1bf1d3780b5099e3e0abfc7a5b20184522054166040519015158152f35b80fd5b50346100ce5760203660031901126100ce576004356001600160a01b0380821680920361014c57610100610cb2565b811561014c575f548273ffffffffffffffffffffffffffffffffffffffff198216175f55167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a380f35b8280fd5b50346100ce57806003193601126100ce5760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b50346100ce57806003193601126100ce57602060405163ffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b50346100ce57806003193601126100ce576001600160a01b036020915416604051908152f35b50346100ce57806003193601126100ce57610214610cb2565b5f6001600160a01b03815473ffffffffffffffffffffffffffffffffffffffff1981168355167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a380f35b50346100ce5760203660031901126100ce576004356001600160a01b0381168091036102d05761028f610cb2565b7ff0a1b42b86a218bb35dbc2254545839ce4b1bf1d3780b5099e3e0abfc7a5b2009073ffffffffffffffffffffffffffffffffffffffff1982541617905580f35b5080fd5b5034610995576003196040368201126109955767ffffffffffffffff60043511610995576102008091600435360301126109955760405190810181811067ffffffffffffffff82111761097657604052610332600435600401610c01565b8152610342602460043501610c01565b602082015260043560448101356040830152606481013560608301526084810135608083015260a481013560a083015260c481013560c08301526103889060e401610c01565b60e08201526004356101048101356101008301526101248101356101208301526101448101356101408301526101648101356101608301526101848101356101808301526101a48101356101a08301526101c4013560ff81169003610995576004356101c48101356101c08301526101e4013567ffffffffffffffff81116109955761041b906004369181350101610c2e565b6101e082015267ffffffffffffffff602435116109955761044136602435600401610c2e565b6001600160a01b037ff0a1b42b86a218bb35dbc2254545839ce4b1bf1d3780b5099e3e0abfc7a5b200541663ffffffff83511663ffffffff60208501511660408501516060860151608087015160a08801519060c08901519263ffffffff60e08b01511694604051966020880198895260408801526060870152608086015260a085015260c084015260e083015261010082015261010081526104e381610ba6565b5190206101008401516101208501516101408601516101608701516101808801516101a08901519060ff6101c08b015116926101e08b01516020815191012094604051966020880198895260408801526060870152608086015260a085015260c084015260e0830152610100820152610100815261056081610ba6565b51902060405191602083015260408201526040815280606081011067ffffffffffffffff6060830111176109765760608101604052602081519101206105a68382610daa565b6005819492941015610b5057159283610b3d575b508215610aac575b505015610a9a576101008201515f527ff0a1b42b86a218bb35dbc2254545839ce4b1bf1d3780b5099e3e0abfc7a5b20160205260ff60405f205416610a88576101208201514211610a765763ffffffff82511663ffffffff7f00000000000000000000000000000000000000000000000000000000000000001603610a6457606082015163ffffffff602084015116906040840151916080850151918260a01c610a525760a08601519260c087015163ffffffff60e089015116610100890151916107036101208b0151936106f56101408d01518d6101608101516101808201516101a0830151916101e060ff6101c086015116940151946040519b8c9860208a015260408901526060880152608087015260a086015260c085015260e084015261010080840152610120830190610c74565b03601f198101855284610bdf565b6101008a01515f527ff0a1b42b86a218bb35dbc2254545839ce4b1bf1d3780b5099e3e0abfc7a5b20160205260405f20600160ff198254161790556040516323b872dd60e01b6020820152336024820152306044820152856064820152606481528060a081011067ffffffffffffffff60a083011117610976578060a061079792016040526001600160a01b038616610cdd565b60405160208101905f8063095ea7b360e01b938481526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000166024850152896044850152604484526107ef84610bc3565b835190826001600160a01b038b165af1610807610d7b565b81610a23575b5080610a10575b15610999575b50506001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000163b15610995575f966001600160a01b036108a3956040519a8b998a9963779b432d60e01b8b5260048b015260248a01526044890152166064870152608486015260a485015260c484015261010060e4840152610104830190610c74565b0381836001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000165af1801561098a57610959575b506101008201517f42d1b5f3692944aee65b659fda3e120f817f17d8f2ac9a256f6fc5d642a591fe6101808401519361012081015190610140810151906101a06101608201519101519160405193845260208401526040830152606082015260a0608082015280610953339560a0830190610c74565b0390a480f35b90925067ffffffffffffffff8111610976576040525f915f6108dd565b634e487b7160e01b5f52604160045260245ffd5b6040513d5f823e3d90fd5b5f80fd5b6109fa610a09926040519060208201526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001660248201525f6044820152604481526109eb81610bc3565b6001600160a01b038816610cdd565b6001600160a01b038616610cdd565b5f8061081a565b506001600160a01b0386163b1515610814565b8051801592508215610a38575b50505f61080d565b610a4b9250602080918301019101610cc5565b5f80610a30565b6040516379ec0ed760e11b8152600490fd5b604051631b91204960e01b8152600490fd5b604051631da7447960e21b8152600490fd5b604051633ab3447f60e11b8152600490fd5b604051638baa579f60e01b8152600490fd5b5f91925081906040516020810190630b135d3f60e11b95868352602482015260406044820152610af181610ae3606482018a610c74565b03601f198101835282610bdf565b51915afa90610afe610d7b565b82610b2f575b82610b13575b50505f806105c2565b9091506020818051810103126109955760200151145f80610b0a565b915060208251101591610b04565b6001600160a01b0316811492505f6105ba565b634e487b7160e01b5f52602160045260245ffd5b34610995575f366003190112610995576020906001600160a01b037ff0a1b42b86a218bb35dbc2254545839ce4b1bf1d3780b5099e3e0abfc7a5b20054168152f35b610120810190811067ffffffffffffffff82111761097657604052565b6080810190811067ffffffffffffffff82111761097657604052565b90601f8019910116810190811067ffffffffffffffff82111761097657604052565b359063ffffffff8216820361099557565b67ffffffffffffffff811161097657601f01601f191660200190565b81601f8201121561099557803590610c4582610c12565b92610c536040519485610bdf565b8284526020838301011161099557815f926020809301838601378301015290565b91908251928382525f5b848110610c9e575050825f602080949584010152601f8019910116010190565b602081830181015184830182015201610c7e565b6001600160a01b035f5416330361099557565b90816020910312610995575180151581036109955790565b6001600160a01b03166040516040810181811067ffffffffffffffff82111761097657610d4b937f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c656460205f948594604052818152015260208151910182855af1610d45610d7b565b91610e5a565b8051908115918215610d61575b50501561099557565b610d749250602080918301019101610cc5565b5f80610d58565b3d15610da5573d90610d8c82610c12565b91610d9a6040519384610bdf565b82523d5f602084013e565b606090565b9060418151145f14610dd657610dd291602082015190606060408401519301515f1a90610ddf565b9091565b50505f90600290565b7f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a08411610e4f576020935f9360ff60809460405194855216868401526040830152606082015282805260015afa1561098a575f516001600160a01b03811615610e4757905f90565b505f90600190565b505050505f90600390565b9015610e7457815115610e6b575090565b3b156109955790565b50805190811561099557602001fdfea26469706673582212208c663c4d9608c58c567c60bd0a3b59cd34b106eb0822cc185cd73394904c00ef64736f6c6343000818003300000000000000000000000028b5a0e9c621a5badaa536219b3a228c8168cf5d00000000000000000000000000000000000000000000000000000000000000030000000000000000000000009a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "nonce": "0x1491", + "chainId": "0xa4b1" + }, + "additionalContracts": [], + "isFixedGasLimit": false + } + ], + "receipts": [ + { + "status": "0x1", + "cumulativeGasUsed": "0x1566bc", + "logs": [ + { + "address": "0xce1ffe01ebb4f8521c12e74363a396ee3d337e1b", + "topics": [ + "0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000009a8f92a830a5cb89a3816e3d267cb7791c16b04d" + ], + "data": "0x", + "blockHash": "0xb96390ff4cb98b461e0035779e43c62874dd281b874612d90ceb474c7af460e7", + "blockNumber": "0x183aa1d0", + "blockTimestamp": "0x692f41c1", + "transactionHash": "0x6c4f6d7537530911757ecc317e0b5a39b5caf7089ab5549cb4299c830a9d854c", + "transactionIndex": "0x5", + "logIndex": "0xc", + "removed": false + } + ], + "logsBloom": "0x00000000000000020000000000000000000000000000000000800000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000020000000000000000000800000000000000000000000000040000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000010000000000000000000000000000020000001000000000000000000000000000000000000000000000000000000000000", + "type": "0x2", + "transactionHash": "0x6c4f6d7537530911757ecc317e0b5a39b5caf7089ab5549cb4299c830a9d854c", + "transactionIndex": "0x5", + "blockHash": "0xb96390ff4cb98b461e0035779e43c62874dd281b874612d90ceb474c7af460e7", + "blockNumber": "0x183aa1d0", + "gasUsed": "0xe0c5d", + "effectiveGasPrice": "0xa67930", + "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "to": null, + "contractAddress": "0xce1ffe01ebb4f8521c12e74363a396ee3d337e1b", + "gasUsedForL1": "0xc55", + "l1BlockNumber": "0x16d1c86", + "timeboosted": false + } + ], + "libraries": [], + "pending": [], + "returns": {}, + "timestamp": 1764704723344, + "chain": 42161, + "commit": "4b103fe" +} diff --git a/broadcast/113DeploySponsoredCCTPSrcPeriphery.sol/8453/run-latest.json b/broadcast/113DeploySponsoredCCTPSrcPeriphery.sol/8453/run-latest.json new file mode 100644 index 000000000..2543d0af5 --- /dev/null +++ b/broadcast/113DeploySponsoredCCTPSrcPeriphery.sol/8453/run-latest.json @@ -0,0 +1,71 @@ +{ + "transactions": [ + { + "hash": "0xa7a480cad9b735b44890b80945b33b22d171820c6343349343813bb25e5be017", + "transactionType": "CREATE", + "contractName": "SponsoredCCTPSrcPeriphery", + "contractAddress": "0xa7a8d1efc1ee3e69999d370380949092251a5c20", + "function": null, + "arguments": ["0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d", "6", "0x9A8f92a830A5cB89a3816e3D267CB7791c16b04D"], + "transaction": { + "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "gas": "0x12333d", + "value": "0x0", + "input": "0x60c03461012157601f61100738819003918201601f19168301916001600160401b03831184841017610125578084926060946040528339810103126101215761004781610139565b60208201519163ffffffff831683036101215760406100669101610139565b905f549260018060a01b03908160018060a01b03199333858816175f5560405196823391167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a31660805260a0527ff0a1b42b86a218bb35dbc2254545839ce4b1bf1d3780b5099e3e0abfc7a5b200921690825416179055610eb9908161014e8239608051818181610170015281816107b701528181610826015281816108b001526109b3015260a0518181816101b1015261061c0152f35b5f80fd5b634e487b7160e01b5f52604160045260245ffd5b51906001600160a01b03821682036101215756fe6080806040526004361015610012575f80fd5b5f905f3560e01c908163238ac93314610b645750806363cd663b146102d45780636c19e78314610261578063715018a6146101fb5780638da5cb5b146101d55780638ddb4a89146101945780639748cf7c14610150578063f2fde38b146100d15763feb6172414610081575f80fd5b346100ce5760203660031901126100ce5760ff604060209260043581527ff0a1b42b86a218bb35dbc2254545839ce4b1bf1d3780b5099e3e0abfc7a5b20184522054166040519015158152f35b80fd5b50346100ce5760203660031901126100ce576004356001600160a01b0380821680920361014c57610100610cb2565b811561014c575f548273ffffffffffffffffffffffffffffffffffffffff198216175f55167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a380f35b8280fd5b50346100ce57806003193601126100ce5760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b50346100ce57806003193601126100ce57602060405163ffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b50346100ce57806003193601126100ce576001600160a01b036020915416604051908152f35b50346100ce57806003193601126100ce57610214610cb2565b5f6001600160a01b03815473ffffffffffffffffffffffffffffffffffffffff1981168355167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a380f35b50346100ce5760203660031901126100ce576004356001600160a01b0381168091036102d05761028f610cb2565b7ff0a1b42b86a218bb35dbc2254545839ce4b1bf1d3780b5099e3e0abfc7a5b2009073ffffffffffffffffffffffffffffffffffffffff1982541617905580f35b5080fd5b5034610995576003196040368201126109955767ffffffffffffffff60043511610995576102008091600435360301126109955760405190810181811067ffffffffffffffff82111761097657604052610332600435600401610c01565b8152610342602460043501610c01565b602082015260043560448101356040830152606481013560608301526084810135608083015260a481013560a083015260c481013560c08301526103889060e401610c01565b60e08201526004356101048101356101008301526101248101356101208301526101448101356101408301526101648101356101608301526101848101356101808301526101a48101356101a08301526101c4013560ff81169003610995576004356101c48101356101c08301526101e4013567ffffffffffffffff81116109955761041b906004369181350101610c2e565b6101e082015267ffffffffffffffff602435116109955761044136602435600401610c2e565b6001600160a01b037ff0a1b42b86a218bb35dbc2254545839ce4b1bf1d3780b5099e3e0abfc7a5b200541663ffffffff83511663ffffffff60208501511660408501516060860151608087015160a08801519060c08901519263ffffffff60e08b01511694604051966020880198895260408801526060870152608086015260a085015260c084015260e083015261010082015261010081526104e381610ba6565b5190206101008401516101208501516101408601516101608701516101808801516101a08901519060ff6101c08b015116926101e08b01516020815191012094604051966020880198895260408801526060870152608086015260a085015260c084015260e0830152610100820152610100815261056081610ba6565b51902060405191602083015260408201526040815280606081011067ffffffffffffffff6060830111176109765760608101604052602081519101206105a68382610daa565b6005819492941015610b5057159283610b3d575b508215610aac575b505015610a9a576101008201515f527ff0a1b42b86a218bb35dbc2254545839ce4b1bf1d3780b5099e3e0abfc7a5b20160205260ff60405f205416610a88576101208201514211610a765763ffffffff82511663ffffffff7f00000000000000000000000000000000000000000000000000000000000000001603610a6457606082015163ffffffff602084015116906040840151916080850151918260a01c610a525760a08601519260c087015163ffffffff60e089015116610100890151916107036101208b0151936106f56101408d01518d6101608101516101808201516101a0830151916101e060ff6101c086015116940151946040519b8c9860208a015260408901526060880152608087015260a086015260c085015260e084015261010080840152610120830190610c74565b03601f198101855284610bdf565b6101008a01515f527ff0a1b42b86a218bb35dbc2254545839ce4b1bf1d3780b5099e3e0abfc7a5b20160205260405f20600160ff198254161790556040516323b872dd60e01b6020820152336024820152306044820152856064820152606481528060a081011067ffffffffffffffff60a083011117610976578060a061079792016040526001600160a01b038616610cdd565b60405160208101905f8063095ea7b360e01b938481526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000166024850152896044850152604484526107ef84610bc3565b835190826001600160a01b038b165af1610807610d7b565b81610a23575b5080610a10575b15610999575b50506001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000163b15610995575f966001600160a01b036108a3956040519a8b998a9963779b432d60e01b8b5260048b015260248a01526044890152166064870152608486015260a485015260c484015261010060e4840152610104830190610c74565b0381836001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000165af1801561098a57610959575b506101008201517f42d1b5f3692944aee65b659fda3e120f817f17d8f2ac9a256f6fc5d642a591fe6101808401519361012081015190610140810151906101a06101608201519101519160405193845260208401526040830152606082015260a0608082015280610953339560a0830190610c74565b0390a480f35b90925067ffffffffffffffff8111610976576040525f915f6108dd565b634e487b7160e01b5f52604160045260245ffd5b6040513d5f823e3d90fd5b5f80fd5b6109fa610a09926040519060208201526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001660248201525f6044820152604481526109eb81610bc3565b6001600160a01b038816610cdd565b6001600160a01b038616610cdd565b5f8061081a565b506001600160a01b0386163b1515610814565b8051801592508215610a38575b50505f61080d565b610a4b9250602080918301019101610cc5565b5f80610a30565b6040516379ec0ed760e11b8152600490fd5b604051631b91204960e01b8152600490fd5b604051631da7447960e21b8152600490fd5b604051633ab3447f60e11b8152600490fd5b604051638baa579f60e01b8152600490fd5b5f91925081906040516020810190630b135d3f60e11b95868352602482015260406044820152610af181610ae3606482018a610c74565b03601f198101835282610bdf565b51915afa90610afe610d7b565b82610b2f575b82610b13575b50505f806105c2565b9091506020818051810103126109955760200151145f80610b0a565b915060208251101591610b04565b6001600160a01b0316811492505f6105ba565b634e487b7160e01b5f52602160045260245ffd5b34610995575f366003190112610995576020906001600160a01b037ff0a1b42b86a218bb35dbc2254545839ce4b1bf1d3780b5099e3e0abfc7a5b20054168152f35b610120810190811067ffffffffffffffff82111761097657604052565b6080810190811067ffffffffffffffff82111761097657604052565b90601f8019910116810190811067ffffffffffffffff82111761097657604052565b359063ffffffff8216820361099557565b67ffffffffffffffff811161097657601f01601f191660200190565b81601f8201121561099557803590610c4582610c12565b92610c536040519485610bdf565b8284526020838301011161099557815f926020809301838601378301015290565b91908251928382525f5b848110610c9e575050825f602080949584010152601f8019910116010190565b602081830181015184830182015201610c7e565b6001600160a01b035f5416330361099557565b90816020910312610995575180151581036109955790565b6001600160a01b03166040516040810181811067ffffffffffffffff82111761097657610d4b937f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c656460205f948594604052818152015260208151910182855af1610d45610d7b565b91610e5a565b8051908115918215610d61575b50501561099557565b610d749250602080918301019101610cc5565b5f80610d58565b3d15610da5573d90610d8c82610c12565b91610d9a6040519384610bdf565b82523d5f602084013e565b606090565b9060418151145f14610dd657610dd291602082015190606060408401519301515f1a90610ddf565b9091565b50505f90600290565b7f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a08411610e4f576020935f9360ff60809460405194855216868401526040830152606082015282805260015afa1561098a575f516001600160a01b03811615610e4757905f90565b505f90600190565b505050505f90600390565b9015610e7457815115610e6b575090565b3b156109955790565b50805190811561099557602001fdfea26469706673582212208c663c4d9608c58c567c60bd0a3b59cd34b106eb0822cc185cd73394904c00ef64736f6c6343000818003300000000000000000000000028b5a0e9c621a5badaa536219b3a228c8168cf5d00000000000000000000000000000000000000000000000000000000000000060000000000000000000000009a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "nonce": "0x9d0", + "chainId": "0x2105" + }, + "additionalContracts": [], + "isFixedGasLimit": false + } + ], + "receipts": [ + { + "status": "0x1", + "cumulativeGasUsed": "0x14c0b42", + "logs": [ + { + "address": "0xa7a8d1efc1ee3e69999d370380949092251a5c20", + "topics": [ + "0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000009a8f92a830a5cb89a3816e3d267cb7791c16b04d" + ], + "data": "0x", + "blockHash": "0x939880c390ff6989e37ee7b43b26af09c2dbbbfbb80dc841225af782e87fa21b", + "blockNumber": "0x2527309", + "blockTimestamp": "0x692f42f5", + "transactionHash": "0xa7a480cad9b735b44890b80945b33b22d171820c6343349343813bb25e5be017", + "transactionIndex": "0x66", + "logIndex": "0x103", + "removed": false + } + ], + "logsBloom": "0x00000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000020000000000000000000800000000000000000000000000040000400000000000000000000000000000000008000000000000000000200000000000000000000000000000000000000800000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000020000001000000000000000000000000000000000000000000000000000000000000", + "type": "0x2", + "transactionHash": "0xa7a480cad9b735b44890b80945b33b22d171820c6343349343813bb25e5be017", + "transactionIndex": "0x66", + "blockHash": "0x939880c390ff6989e37ee7b43b26af09c2dbbbfbb80dc841225af782e87fa21b", + "blockNumber": "0x2527309", + "gasUsed": "0xe0008", + "effectiveGasPrice": "0x73af", + "blobGasUsed": "0xe6aa0", + "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "to": null, + "contractAddress": "0xa7a8d1efc1ee3e69999d370380949092251a5c20", + "daFootprintGasScalar": "0x190", + "l1BaseFeeScalar": "0x8dd", + "l1BlobBaseFee": "0x147055", + "l1BlobBaseFeeScalar": "0x101c12", + "l1Fee": "0x1cce5a375", + "l1GasPrice": "0x30d620f", + "l1GasUsed": "0x93a5" + } + ], + "libraries": [], + "pending": [], + "returns": {}, + "timestamp": 1764705036690, + "chain": 8453, + "commit": "4b103fe" +} diff --git a/broadcast/114DeploySponsoredCCTPDstPeriphery.sol/999/run-latest.json b/broadcast/114DeploySponsoredCCTPDstPeriphery.sol/999/run-latest.json new file mode 100644 index 000000000..24f6cc66c --- /dev/null +++ b/broadcast/114DeploySponsoredCCTPDstPeriphery.sol/999/run-latest.json @@ -0,0 +1,214 @@ +{ + "transactions": [ + { + "hash": "0x16b942e66df79bdf146ba03b8232604d966ae571282c4229eb3d0e9f8fcebf8d", + "transactionType": "CREATE", + "contractName": "DonationBox", + "contractAddress": "0x002e76dc036a1eff1488ee5435ee66c6abf32674", + "function": null, + "arguments": null, + "transaction": { + "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "gas": "0x522e7", + "value": "0x0", + "input": "0x6080806040523461005a575f8054336001600160a01b0319821681178355916001600160a01b03909116907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09080a3610344908161005f8239f35b5f80fdfe608060409080825260049081361015610016575f80fd5b5f3560e01c908163715018a61461026e5781638da5cb5b1461024c57508063f2fde38b146101d45763f3fef3a31461004c575f80fd5b346101545781600319360112610154578035916001600160a01b0383168093036101545760249261007b6102d2565b8151916020830163a9059cbb60e01b815233868501528535604485015260448452608084019367ffffffffffffffff94818110868211176101c25760c08201818110878211176101b0578452602090527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c656460a0820152515f9182919082865af1923d1561019f573d9181831161018d57805195601f8401601f19908116603f011687019283118784101761017b575052835261013e93503d5f602085013e6102e5565b8051908115918215610158575b50501561015457005b5f80fd5b819250906020918101031261015457602001518015158103610154575f8061014b565b60418891634e487b7160e01b5f52525ffd5b86604187634e487b7160e01b5f52525ffd5b5050915061013e92506060916102e5565b88604189634e487b7160e01b5f52525ffd5b87604188634e487b7160e01b5f52525ffd5b503461015457602036600319011261015457356001600160a01b03808216809203610154576102016102d2565b8115610154575f548273ffffffffffffffffffffffffffffffffffffffff198216175f55167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a3005b34610154575f366003190112610154576020906001600160a01b035f54168152f35b34610154575f366003190112610154576102866102d2565b5f6001600160a01b03815473ffffffffffffffffffffffffffffffffffffffff1981168355167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3005b6001600160a01b035f5416330361015457565b90156102ff578151156102f6575090565b3b156101545790565b50805190811561015457602001fdfea2646970667358221220c55691de465342d56d68c31160f5d7d661422ef39ebccaa5223872c823ad6d6964736f6c63430008180033", + "nonce": "0x243", + "chainId": "0x3e7" + }, + "additionalContracts": [], + "isFixedGasLimit": false + }, + { + "hash": "0xb08e0d23618a1447781b9071792c660bfd76db006b105822ea41d65e0ddc2802", + "transactionType": "CREATE", + "contractName": "SponsoredCCTPDstPeriphery", + "contractAddress": "0x83e245941befbde29682df068bcda006a804eb0c", + "function": null, + "arguments": [ + "0x81D40F21F12A8F0E3252Bccb954D722d4c464B64", + "0x9A8f92a830A5cB89a3816e3D267CB7791c16b04D", + "0x002E76DC036A1efF1488ee5435eE66C6aBF32674", + "0xb88339CB7199b77E23DB6E890353E22632Ba630f", + "0x5E7840E06fAcCb6d1c3b5F5E0d1d3d07F2829bba" + ], + "transaction": { + "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "gas": "0x8ec9d8", + "value": "0x0", + "input": "0x610100346200027257601f6200833338819003918201601f19168301926001600160401b03929091838511838610176200025e578160a092849260409788528339810103126200027257620000548162000276565b91620000636020830162000276565b926200007185840162000276565b926200008e6080620000866060840162000276565b920162000276565b60015f5586519093615d3e808301918211838310176200025e5788918391620025f583396001600160a01b03978816815284881660208201520301905ff09283156200025457848094166080525f7f5300fde95a5e446527bf6aa7c91bd6661bef5398afc77061d9bc87efb80b7ef68082527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268006020528160018a82200181815491557fbd79b86ffe0ab8e8776151514217cd7cacd52c909f66475c3af44e129f0b00ff92838380a47f880a9ba888678c7fe4e8c4f028c224f26ce12a3bed6e96025c61ef8a5db6312f80835260018a8420019183835493558380a460a05260e0521660c0527fb788edf5b6d001c4df53cb371352fd225afa05a1712075d5f89a08d6b6f79f0080546001600160a01b031916919092161790556107087fb788edf5b6d001c4df53cb371352fd225afa05a1712075d5f89a08d6b6f79f0155620001f7336200028b565b50516122b890816200033d82396080518181816101fe01526112fa015260a051818181610711015281816114650152612030015260c0518181816107db0152610aef015260e0518181816108aa01528181610c3f0152610cf50152f35b86513d5f823e3d90fd5b634e487b7160e01b5f52604160045260245ffd5b5f80fd5b51906001600160a01b03821682036200027257565b6001600160a01b03165f8181527fb7db2dd08fcb62d0c9e08c51941cae53c267786a0b75803fb7960902fc8ef97d60205260409020547f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268009060ff1662000336575f805260205260405f20815f5260205260405f20600160ff1982541617905533905f7f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d8180a4600190565b50505f9056fe60806040526004361015610030575b361561002e573461002a5761002236610dbb565b602081519101f35b5f80fd5b005b5f3560e01c806301ffc9a71461017f57806309cfd6751461017a5780631b1062b81461017557806321081d3c14610170578063238ac9331461016b578063248a9ca3146101665780632561efb214610161578063277e661d1461015c5780632f2ff15d1461015757806336568abe14610152578063490e662f1461014d5780634b3b029b146101485780634f7d9d2e14610143578063657cad8a1461013e5780636c19e783146101395780638c73eb041461013457806391d148541461012f578063a217fddf1461012a578063c55dae6314610125578063d547741f146101205763feb617240361000e5761092d565b6108ce565b61088b565b610871565b6107ff565b6107bc565b610735565b6106f2565b6106b6565b61067c565b610642565b6105f7565b610598565b610542565b6104f6565b6104aa565b610465565b61042b565b610388565b6101df565b3461002a57602036600319011261002a5760043563ffffffff60e01b811680910361002a57602090637965db0b60e01b81149081156101c4575b506040519015158152f35b6301ffc9a760e01b1490505f6101b9565b5f91031261002a57565b3461002a575f36600319011261002a5760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b634e487b7160e01b5f52604160045260245ffd5b67ffffffffffffffff811161024a57604052565b610222565b6040810190811067ffffffffffffffff82111761024a57604052565b6080810190811067ffffffffffffffff82111761024a57604052565b90601f8019910116810190811067ffffffffffffffff82111761024a57604052565b6040519060c0820182811067ffffffffffffffff82111761024a57604052565b604051906102d68261026b565b565b60405190610200820182811067ffffffffffffffff82111761024a57604052565b604051906060820182811067ffffffffffffffff82111761024a57604052565b604051906102d68261024f565b67ffffffffffffffff811161024a57601f01601f191660200190565b81601f8201121561002a5780359061035982610326565b926103676040519485610287565b8284526020838301011161002a57815f926020809301838601378301015290565b3461002a57606036600319011261002a5767ffffffffffffffff60043581811161002a576103ba903690600401610342565b60243582811161002a576103d2903690600401610342565b9160443590811161002a575f926103f0610424923690600401610342565b906103f9610e00565b7fc56a3250645180a53cd9e196b2ee0a634a4f54e2edf59ea457f2083917e4d1009360018555610abc565b5560015f55005b3461002a575f36600319011261002a5760206040517f880a9ba888678c7fe4e8c4f028c224f26ce12a3bed6e96025c61ef8a5db6312f8152f35b3461002a575f36600319011261002a5760206001600160a01b037fb788edf5b6d001c4df53cb371352fd225afa05a1712075d5f89a08d6b6f79f005416604051908152f35b3461002a57602036600319011261002a576004355f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268006020526020600160405f200154604051908152f35b3461002a57602036600319011261002a5761050f610e00565b61051761175e565b6004357fb788edf5b6d001c4df53cb371352fd225afa05a1712075d5f89a08d6b6f79f015560015f55005b3461002a57602036600319011261002a5760043567ffffffffffffffff811161002a5761057d6105786020923690600401610342565b611823565b6040519015158152f35b6001600160a01b0381160361002a57565b3461002a57604036600319011261002a5761002e6024356004356105bb82610587565b805f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268006020526105f2600160405f2001546117b4565b6118cf565b3461002a57604036600319011261002a5760243561061481610587565b336001600160a01b038216036106305761002e90600435611988565b60405163334bd91960e11b8152600490fd5b3461002a575f36600319011261002a5760206040517fc56a3250645180a53cd9e196b2ee0a634a4f54e2edf59ea457f2083917e4d1008152f35b3461002a575f36600319011261002a5760206040517f5300fde95a5e446527bf6aa7c91bd6661bef5398afc77061d9bc87efb80b7ef68152f35b3461002a575f36600319011261002a5760207fb788edf5b6d001c4df53cb371352fd225afa05a1712075d5f89a08d6b6f79f0154604051908152f35b3461002a575f36600319011261002a5760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b3461002a57602036600319011261002a5760043561075281610587565b61075a610e00565b61076261175e565b6001600160a01b037fb788edf5b6d001c4df53cb371352fd225afa05a1712075d5f89a08d6b6f79f0091167fffffffffffffffffffffffff000000000000000000000000000000000000000082541617905560015f555f80f35b3461002a575f36600319011261002a5760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b3461002a57604036600319011261002a57602060ff61086560243561082381610587565b6004355f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800845260405f20906001600160a01b03165f5260205260405f2090565b54166040519015158152f35b3461002a575f36600319011261002a5760206040515f8152f35b3461002a575f36600319011261002a5760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b3461002a57604036600319011261002a5761002e6024356004356108f182610587565b805f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800602052610928600160405f2001546117b4565b611988565b3461002a57602036600319011261002a576004355f527fb788edf5b6d001c4df53cb371352fd225afa05a1712075d5f89a08d6b6f79f02602052602060ff60405f2054166040519015158152f35b9081602091031261002a5751801515810361002a5790565b5f5b8381106109a45750505f910152565b8181015183820152602001610995565b906020916109cd81518092818552858086019101610993565b601f01601f1916010190565b90916109f06109fe936040845260408401906109b4565b9160208184039101526109b4565b90565b6040513d5f823e3d90fd5b9060206109fe9281815201906109b4565b634e487b7160e01b5f52601160045260245ffd5b5f19810191908211610a3f57565b610a1d565b91908203918211610a3f57565b634e487b7160e01b5f52602160045260245ffd5b60c09093929193610ab88160e081019660a08091805184526020810151602085015260408101516001600160a01b0380911660408601526060820151166060850152608081015160808501520151910152565b0152565b604051630afd9fa560e31b81526020939192849082908190610ae29087600484016109d9565b03815f6001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000165af18015610db657610d99575b5060405163277e661d60e01b8152838180610b3a8660048301610a0c565b0381305afa5f9181610d6a575b50610b525750505050565b15610d6557610b63610b6c92610f1f565b92909182611049565b80610d1a575b610b80836060840151610a44565b92610100830151610bee610b9861018086015161129a565b8415610cf057610bde610baf6101a088015161129a565b915b8615610ce957610140880151945b610bc76102a9565b998a528a8a01526001600160a01b03166040890152565b6001600160a01b03166060870152565b608085015260a084015280610cb9575b15610c78576102d69281610c65600160ff610c276101c06101e0610c7398015195015160ff1690565b161492610c326102c9565b9586526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690860152565b604084015215156060830152565b611383565b610cb692610ca3610160610cb193015160405194859363073ffe1360e31b9085015260248401610a65565b03601f198101835282610287565b6112ef565b50565b5060ff610ccb6101c083015160ff1690565b1660018114908115610cde575b50610bfe565b60029150145f610cd8565b5f94610bbf565b610bde7f000000000000000000000000000000000000000000000000000000000000000091610bb1565b610d60610d536101008401515f527fb788edf5b6d001c4df53cb371352fd225afa05a1712075d5f89a08d6b6f79f0260205260405f2090565b805460ff19166001179055565b610b72565b505050565b610d8b919250853d8711610d92575b610d838183610287565b81019061097b565b905f610b47565b503d610d79565b610daf90843d8611610d9257610d838183610287565b505f610b1c565b610a01565b610dc3610e00565b610dcc81610326565b610dd96040519182610287565b81815236821161002a575f602083610df9948383860137830101526112ef565b9060015f55565b60025f541461002a5760025f55565b610e176102d8565b905f82525f60208301525f60408301525f60608301525f60808301525f60a08301525f60c08301525f60e08301525f6101008301525f6101208301525f6101408301525f6101608301525f6101808301525f6101a08301525f6101c083015260606101e0830152565b81601f8201121561002a578051610e9681610326565b92610ea46040519485610287565b8184526020828401011161002a576109fe9160208085019101610993565b9190916101008184031261002a5780519260208201519260408301519260608101519260808201519260a08301519260c081015160ff8116810361002a579260e082015167ffffffffffffffff811161002a576109fe9201610e80565b90610f8a610f2b610e0f565b92610f42610f3882611a38565b63ffffffff168552565b610f5b610f4e82611a5a565b63ffffffff166020860152565b610f6481611a7a565b60a0850152610f82610f7582611a6a565b63ffffffff1660e0860152565b805190611c56565b90610f9482611a8a565b6040840152610fa282611a9a565b6060840152610fb082611aaa565b6080840152610fbe82611aba565b60c0840152611008610fed610fde610fd585611aca565b94805190611cb0565b60208082518301019101610ec2565b6101e08c999394959697989901526101c08b019060ff169052565b6101a0890152610180880152610160870152610140860152610120850152610100840152565b9060018201809211610a3f57565b91908201809211610a3f57565b6112086001600160a01b037fb788edf5b6d001c4df53cb371352fd225afa05a1712075d5f89a08d6b6f79f00541692611086835163ffffffff1690565b93611098602085015163ffffffff1690565b946040850151956111186060870151608088015160a089015160c08a0151916110c860e08c015163ffffffff1690565b936040519c8d9760208901998a96929360e09692959199989461010089019a63ffffffff97888092168b521660208a015260408901526060880152608087015260a086015260c085015216910152565b039561112c601f1997888101835282610287565b5190206111f36111ff61010087019788516101208901986111d48a516111c861014084015193610160810151906101808101516101a0820151906101e06111786101c085015160ff1690565b9301516020815191012093604051988997602089019b8c9490989796929360ff9460e0979361010088019b8852602088015260408701526060860152608085015260a08401521660c08201520152565b03848101835282610287565b5190206040805160208101968752908101919091529283906060820190565b03908101835282610287565b51902090611cdd565b918261124d575b5081611219575090565b6112479150517fb788edf5b6d001c4df53cb371352fd225afa05a1712075d5f89a08d6b6f79f01549061103c565b42111590565b61129391925061128861128f91515f527fb788edf5b6d001c4df53cb371352fd225afa05a1712075d5f89a08d6b6f79f0260205260405f2090565b5460ff1690565b1590565b905f61120f565b8060a01c6112ae576001600160a01b031690565b6040516379ec0ed760e11b8152600490fd5b3d156112ea573d906112d182610326565b916112df6040519384610287565b82523d5f602084013e565b606090565b5f80916020815191017f00000000000000000000000000000000000000000000000000000000000000005af46113236112c0565b901561132c5790565b602081519101fd5b6102d69092919260c081019360a08091805184526020810151602085015260408101516001600160a01b0380911660408601526060820151166060850152608081015160808501520151910152565b61138b611aea565b5060409081810151916113a983519360208080968301019101611b43565b92808301936113d16113c56113c587516001600160a01b031690565b6001600160a01b031690565b83516370a0823160e01b80825230600483015296909290918490849060249082905afa928315610db6575f9361173f575b506060926114216113c56113c5868a51016001600160a01b0390511690565b865189815230600482015291908690839060249082905afa918215610db6575f92611720575b506114a661145f6113c586516001600160a01b031690565b9361148f7f000000000000000000000000000000000000000000000000000000000000000095868c515191611da9565b89518701513091906001600160a01b031690611fab565b6001600160a01b038094166114c286516001600160a01b031690565b918a515191803b1561002a576114f3935f80948d5196879586948593633a5be8cb60e01b8552309160048601611c28565b03925af18015610db657611707575b5061151a6113c56113c586516001600160a01b031690565b87518a815230600482015291908790839060249082905afa918215610db6575f926116e8575b50036116575750610cb6965061157261156083516001600160a01b031690565b84885101906001600160a01b03169052565b855151915b61158a83885160a0815191015190612126565b60a088510152828751527fb88fc27be67e678ffb77faf8f8bb00d39b66b4845e4f7ec1e623b0f15abd52138751926115cd8785015193516001600160a01b031690565b946116046115e68887519701516001600160a01b031690565b91838b51948594169816968360209093929193604081019481520152565b0390a4835190840151156116365750610ca3610cb19293519351938492632132ff4360e11b9084015260248301611334565b9250610ca3610cb19251938492627f9f5760e91b9084015260248301611334565b846116736113c56113c5878b51016001600160a01b0390511690565b8751998a523060048b0152899060249082905afa908115610db657610cb6985f926116b9575b508082106116b0576116aa91610a44565b91611577565b50505f91611577565b6116da919250863d88116116e1575b6116d28183610287565b810190611c19565b905f611699565b503d6116c8565b611700919250873d89116116e1576116d28183610287565b905f611540565b8061171461171a92610236565b806101d5565b5f611502565b611738919250863d88116116e1576116d28183610287565b905f611447565b611757919350843d86116116e1576116d28183610287565b915f611402565b335f9081527fb7db2dd08fcb62d0c9e08c51941cae53c267786a0b75803fb7960902fc8ef97d602052604090205460ff161561179657565b60405163e2517d3f60e01b81523360048201525f6024820152604490fd5b805f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b62680060205260ff6117fb3360405f20906001600160a01b03165f5260205260405f2090565b5416156118055750565b6044906040519063e2517d3f60e01b82523360048301526024820152fd5b6102988151106118ca5761183e61183982611ada565b61129a565b6001600160a01b03309116036118ca578051908160941082609418028218808303928311610a3f5782610fde9260206118796118ab96610326565b936118876040519586610287565b83855261189384610326565b8583019390601f19013685370101905e805190611cb0565b505094509250505060a01c1590816118c1575090565b905060a01c1590565b505f90565b805f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268008060205260ff6119178460405f20906001600160a01b03165f5260205260405f2090565b541661198157815f526020526119418260405f20906001600160a01b03165f5260205260405f2090565b805460ff1916600117905533916001600160a01b0316907f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d5f80a4600190565b5050505f90565b805f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268008060205260ff6119d08460405f20906001600160a01b03165f5260205260405f2090565b54161561198157815f526020526119fb8260405f20906001600160a01b03165f5260205260405f2090565b805460ff1916905533916001600160a01b0316907ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b5f80a4600190565b6008815110611a48576008015190565b604051632d0483c560e21b8152600490fd5b600c815110611a4857600c015190565b6090815110611a48576090015190565b608c815110611a4857608c015190565b6044815110611a48576044015190565b6064815110611a48576064015190565b6024815110611a48576024015190565b60a4815110611a485760a4015190565b60c4815110611a485760c4015190565b60d8815110611a485760d8015190565b6040519060c0820182811067ffffffffffffffff82111761024a576040525f60a0838281528260208201528260408201528260608201528260808201520152565b67ffffffffffffffff811161024a5760051b60200190565b90602090818382031261002a57825167ffffffffffffffff9384821161002a57019080601f8301121561002a578151611b7b81611b2b565b94604090611b8c6040519788610287565b828752858088019360051b8601019484861161002a57868101935b868510611bb957505050505050505090565b845183811161002a5782019084601f19838903011261002a57845190611bde8261024f565b89830151611beb81610587565b8252858301519185831161002a57611c0a898c80969581960101610e80565b83820152815201940193611ba7565b9081602091031261002a575190565b906109fe94936080936001600160a01b038093168452602084015216604082015281606082015201906109b4565b908151908180821091180218806094108160941802811891828203918211610a3f576020611c8383610326565b93611c916040519586610287565b838552611c9d84610326565b8583019390601f19013685370101905e90565b9081519081808210911802188060e4108160e41802811891828203918211610a3f576020611c8383610326565b611ce783836121a9565b6005819592951015611da457159384611d8e575b508315611d09575b50505090565b5f929350908291604051611d4181610ca36020820194630b135d3f60e11b998a875260248401526040604484015260648301906109b4565b51915afa90611d4e6112c0565b82611d80575b82611d64575b50505f8080611d03565b90915060208180518101031261002a5760200151145f80611d5a565b915060208251101591611d54565b6001600160a01b0383811691161493505f611cfb565b610a51565b905f611e3093819260405194602086019263a9059cbb60e01b84526001600160a01b038093166024880152604487015260448652611de68661026b565b16937f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65646020604051611e178161024f565b8181520152519082855af1611e2a6112c0565b91612259565b8051908115918215611e46575b50501561002a57565b611e59925060208091830101910161097b565b5f80611e3d565b90611e6a82611b2b565b6040611e796040519283610287565b8382528193611e8a601f1991611b2b565b01905f5b828110611e9b5750505050565b8151906060918281019281841067ffffffffffffffff85111761024a5760209385525f82528390818301525f85830152828701015201611e8e565b8051821015611eea5760209160051b010190565b634e487b7160e01b5f52603260045260245ffd5b9190916020908181526060916060820192855193604080848601528551809252608085018460808460051b8801019701935f925b848410611f5a575050505050506040906109fe93949501519101906001600160a01b03169052565b9091929394978680600192607f198b82030187528b51906001600160a01b0382511681528580611f95858501518a878601528a8501906109b4565b9301519101529a01940194019294939190611f32565b92918351611fc0611fbb8261102e565b611e60565b925f5b82811061209a575060405163ef8738d360e01b60208201526001600160a01b03918216602482015290831660448083019190915281529394506111f3936109fe93601f1993909261208b9261206f919061201e606482610287565b6120266102f9565b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001681529060208201525f60408201526120688287611ed6565b5284611ed6565b50612078610319565b9283526001600160a01b03166020830152565b60405193849160208301611efe565b806120b86120aa6001938a611ed6565b51516001600160a01b031690565b6020806120c5848c611ed6565b510151906120e36120d46102f9565b6001600160a01b039094168452565b8201525f60408201526120f68288611ed6565b526121018187611ed6565b5001611fc3565b8115612112570490565b634e487b7160e01b5f52601260045260245ffd5b9190808301809311610a3f57670de0b6b3a76400009283820291808304851490151715610a3f57808201809211610a3f575f198201918211610a3f5761216b91612108565b820391808311610a3f57808202908282041482151715610a3f57828101809111610a3f576109fe9261219f6121a492610a31565b612108565b610a44565b9060418151145f146121d5576121d191602082015190606060408401519301515f1a906121de565b9091565b50505f90600290565b7f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0841161224e576020935f9360ff60809460405194855216868401526040830152606082015282805260015afa15610db6575f516001600160a01b0381161561224657905f90565b505f90600190565b505050505f90600390565b90156122735781511561226a575090565b3b1561002a5790565b50805190811561002a57602001fdfea26469706673582212205aa57543ac0ca275e5f6cf9aea12226da06ca1a789701807a553fd671792689c64736f6c6343000818003360c0346200010457601f62005d3e38819003918201601f19168301916001600160401b038311848410176200010857808492604094855283398101031262000104576200005a602062000052836200011c565b92016200011c565b6001600160a01b0390911660805260a052604051615c0c90816200013282396080518181816108b7015281816109ff0152818161158b0152818161182f01528181612a1701528181614a0501528181614b7c01528181614f68015261504e015260a051818181610670015281816107de01528181611a7b01528181613a6e01528181614188015281816141e401528181614288015281816142b40152818161435301526146990152f35b5f80fd5b634e487b7160e01b5f52604160045260245ffd5b51906001600160a01b0382168203620001045756fe6080604052600436101562000012575f80fd5b5f803560e01c806246912e146200257057806301ffc9a71462002515578063037a06a414620022c957806304c73f6014620022a9578063057f037014620021f45780631f74a0b5146200217257806321081d3c1462002135578063248a9ca314620020e75780632e748b211462001f7f5780632f2ff15d1462001f1a578063319adf9f1462001b6657806336568abe1462001b1757806337710e201462001abd57806339fff0981462001a225780633b1c6a0114620017275780633cf3a02514620016eb5780634265fe861462001699578063490e662f146200165c5780634b3b029b146200161f578063502a82e21462001554578063521c98ba1462000d9157806369b97ac71462000d7157806379c7b60b1462000d1757806379c7f2891462000c1557806390a0827b1462000bd457806391d148541462000b6657806396cc2cfb14620008f9578063a217fddf14620008db578063a4b672b61462000895578063af5de6f91462000802578063c55dae6314620007bc578063ccbedaec1462000542578063d06e28ed1462000443578063d547741f14620003da578063e38b73a914620003bc578063ea0aaf241462000359578063eb84e7f2146200024b5763ff3eae0014620001e2575f80fd5b34620002485760c0366003190112620002485762000200366200279e565b7fc56a3250645180a53cd9e196b2ee0a634a4f54e2edf59ea457f2083917e4d10054156200023657620002339062004e31565b80f35b60405163cd6d8f7d60e01b8152600490fd5b80fd5b503462000248576020366003190112620002485760408160c09260a0835162000274816200270b565b8281528260208201528285820152826060820152826080820152015260043581527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0360205220604051620002c8816200270b565b6001600160a01b039060028284541693848352600181015490602084019185811683526001600160401b0393849283604088019360a01c1683520154956060860193838816855260ff60a06080890198828b60401c1615158a52019860481c1615158852604051988952511660208801525116604086015251166060840152511515608083015251151560a0820152f35b50346200024857602036600319011262000248576020620003b36200037d620025de565b6001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0560205260405f2090565b54604051908152f35b50346200024857806003193601126200024857602060405160068152f35b50346200024857604036600319011262000248576200043f600435620003ff620025f5565b908084527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b62680060205262000439600160408620015462002e64565b620039b1565b5080f35b503462000248576040366003190112620002485762000461620025de565b602435906fffffffffffffffffffffffffffffffff82168092036200053e576200048a62002e08565b6200049581620032e8565b90836001600160a01b039263ffffffff84608083015116915116813b156200053a57829160448392604051948593849263435354d360e01b845260048401528a60248401525af180156200052f5762000513575b5050167f02366c0d102495be1ee805b749be7baebab4fc0710c6d3f38751f1a22bd711648380a380f35b6200051e90620026f7565b6200052b57835f620004e9565b8380fd5b6040513d84823e3d90fd5b8280fd5b5f80fd5b50346200024857606036600319011262000248578062000561620025de565b6200056b6200260c565b906200057662002623565b906200058162002d37565b620005bc816001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0260205260405f2090565b544311156200071a574362000601826001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0260205260405f2090565b556001600160a01b0380600162000648846001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0160205260405f2090565b01541693816001600160401b03938483166200071f575b5050508183166200066e578480f35b7f00000000000000000000000000000000000000000000000000000000000000001684525f8051602062005bb783398151915260205260046040852001541690823b156200071a5760405163a703334f60e01b81526001600160401b039283166004820152336024820152911660448201529082908290606490829084905af180156200052f5762000702575b8080808480f35b6200070d90620026f7565b6200024857805f620006fb565b505050fd5b1686525f8051602062005bb783398151915260205282600460408820015416853b15620007b85760405163a703334f60e01b81526001600160401b03918216600482015233602482015291166044820152858160648183895af1908115620007ad57869162000791575b82906200065f565b6200079c90620026f7565b620007a957845f62000789565b8480fd5b6040513d88823e3d90fd5b8680fd5b5034620002485780600319360112620002485760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b503462000248576020366003190112620002485760206001600160a01b03620008346200082e620025de565b620031ff565b6109ab60405162000848858301826200277c565b818152848101916200520c8339519020604051908482019260ff60f81b84523060601b60218401526035830152605582015260558152620008898162002744565b51902016604051908152f35b5034620002485780600319360112620002485760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b50346200024857806003193601126200024857602090604051908152f35b503462000248576040366003190112620002485762000917620025de565b906200092262002e08565b6200092d8262002f58565b6200093883620032e8565b926200094e60e083510151840b60243562004da2565b9460208401906001600160401b036200097381845116898360a08a01511691620038d2565b1562000b3657869750620009b7856001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0460205260405f2090565b620009c48582546200287c565b90556001600160a01b03808616807f410b9a8c926b6c439cdceb39c0bb8f829838a25bc5a26af9d4c263d1313cc46b6020604051898152a2817f000000000000000000000000000000000000000000000000000000000000000016803b1562000b325760405163f3fef3a360e01b81526001600160a01b038316600482015260248101889052908a908290604490829084905af1801562000b275787918b9162000b08575b505062000a8290608060e0960192848451169062003353565b511692511694510151850b93813b1562000b0457604051639c45c34b60e01b81526001600160a01b039490941660048501526001600160401b0316602484015260448301919091525f9290920b60648201529082908290608490829084905af180156200052f5762000af15750f35b62000afc90620026f7565b620002485780f35b8580fd5b62000b15919250620026f7565b62000b235785895f62000a69565b8880fd5b6040513d8c823e3d90fd5b8980fd5b6040516377e88bc960e11b81526001600160a01b03861660048201526001600160401b0389166024820152604490fd5b50346200024857604036600319011262000248576001600160a01b03604062000b8e620025f5565b9260043581527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268006020522091165f52602052602060ff60405f2054166040519015158152f35b50346200024857604036600319011262000248576200023362000bf6620025de565b62000c0062002d37565b602435906001600160a01b0333911662003353565b503462000248576020366003190112620002485760a062000c7a62000c39620025de565b62000c4362002d0b565b506001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0160205260405f2090565b6001600160a01b0360016040519262000c9384620026db565b805463ffffffff90818116865260ff8160201c1615156020870152818160281c16604087015260481c166060850152015416608082015262000d1560405180926001600160a01b036080809263ffffffff8082511686526020820151151560208701528060408301511660408701526060820151166060860152015116910152565bf35b50346200024857602036600319011262000248576020620003b362000d3b620025de565b6001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0460205260405f2090565b503462000248578060031936011262000248576020604051620f42408152f35b5034620002485760a0366003190112620002485762000daf620025de565b62000db96200263a565b9062000dc46200264e565b6001600160401b0360643516606435036200053e57608435906001600160401b03821682036200053e5762000df862002db1565b62000e0262002c05565b508480604051602081019063ffffffff881682526020815262000e258162002760565b519061080c5afa62000e3662002cd7565b901562001542578051810190602081830312620007b85760208101516001600160401b038111620015055761010081830184031262001505576040519262000e7e8462002727565b602082840101516001600160401b03811162000b325760208201603f828587010101121562000b32576020818486010101519062000ebc8262002cbb565b9162000ecc60405193846200277c565b8083526020840160408284888a01010101116200152a5762000efa9160406020850191878901010162002691565b8452604082840101516001600160401b03811162000b325760208201603f828587010101121562000b3257602081848601010151916001600160401b0383116200152e578260051b6040519362000f5560208301866200277c565b84526020808501920160408285888a01010101116200152a576040838688010101915b60408285888a01010101831062001509575050505060208401526101009062000fa683820160600162003428565b604085015262000fbb6080828501016200512c565b606085015262000fd060a0828501016200512c565b9283608086015262000fe760c08383010162005141565b60a086015262000ffc60e08383010162005141565b60c08601520101519081880b820362001505578160e08401526001600160a01b03808716911603620007b8576200103890870b60643562003829565b506040519162001048836200270b565b825263ffffffff86166020830152821515604083015260608201526001600160401b036064351660808201526001600160401b03831660a08201526001600160a01b03841686525f8051602062005bb783398151915260205260408620815180518051906001600160401b03821162001438578190620010c9855462002c80565b601f8111620014c5575b50602090601f831160011462001458578b926200144c575b50508160011b915f199060031b1c19161782555b60208101518051906001600160401b038211620014385768010000000000000000821162001438576020906001850154836001870155808410620013e5575b500190600184018a5260208a20908a5b8160021c81106200139d5750600319811680820362001334575b50505050936001600160a01b03937f12cf3d04179e82c834f3ee7169a5df80651aa65530127f9ddb04c8cd82244353969360068460809860026001600160401b039701876040830151168154907bffffffffffffffffffffffffffffffffffffffff0000000000000000606085015160401b169163ffffffff60e01b16171790556003820190898b820151169082549174ff000000000000000000000000000000000000000060a083015160a01b16907fffffffffffffffffff000000000000000000000000000000000000000000000060e075ff00000000000000000000000000000000000000000060c086015160a81b1694015160b01b76ff00000000000000000000000000000000000000000000169416171717179055620012c5600482018760208601511688198254161781556040850151151568ff0000000000000000825491151560401b169068ff00000000000000001916179055565b6060830151600582015501908488820151166fffffffffffffffff000000000000000060a0845493015160401b16916fffffffffffffffffffffffffffffffff19161717905563ffffffff6040519816885215156020880152816064351660408801521660608601521692a280f35b928b938c5b81840381106200135d5750505060021c0155838360066001600160401b0362001168565b9091946020620013926001926001600160401b03895116908560031b60031b916001600160401b03809116831b921b19161790565b960192910162001339565b8b8c5b60048110620013b75750838201556001016200114e565b85519095916001916020916001600160401b0360068a901b81811b199092169216901b1792019501620013a0565b6200141890600187018d52838d20600380870160021c820192601888831b16806200141f575b500160021c019062004d8a565b5f6200113e565b5f1990818601918254918a03851b1c1690555f6200140b565b634e487b7160e01b8a52604160045260248afd5b015190505f80620010eb565b9250848b5260208b20908b935b601f1984168510620014a9576001945083601f1981161062001490575b505050811b018255620010ff565b01515f1960f88460031b161c191690555f808062001482565b8181015183556020948501946001909301929091019062001465565b620014f390868d5260208d20601f850160051c81019160208610620014fa575b601f0160051c019062004d8a565b5f620010d3565b9091508190620014e5565b8780fd5b906020806040936200151b8662003428565b81520193019290915062000f78565b8b80fd5b634e487b7160e01b8b52604160045260248bfd5b604051639b0c335d60e01b8152600490fd5b503462000248576040366003190112620002485762001572620025de565b6024356200157f62002d37565b826001600160a01b03807f000000000000000000000000000000000000000000000000000000000000000016931692803b156200161b5760405163f3fef3a360e01b81526001600160a01b0385166004820152602481018490529082908290604490829084905af180156200052f5762001603575b50506200023391339062003353565b6200160e90620026f7565b6200053a57825f620015f4565b5080fd5b5034620002485780600319360112620002485760206040517f5300fde95a5e446527bf6aa7c91bd6661bef5398afc77061d9bc87efb80b7ef68152f35b5034620002485780600319360112620002485760206040517fc56a3250645180a53cd9e196b2ee0a634a4f54e2edf59ea457f2083917e4d1008152f35b5034620002485760c03660031901126200024857620016b8366200279e565b7fc56a3250645180a53cd9e196b2ee0a634a4f54e2edf59ea457f2083917e4d100541562000236576200023390620046c4565b50346200024857602036600319011262000248576200170962002d37565b80808080600435335af16200171d62002cd7565b5015620002485780f35b503462000248576060366003190112620002485762001745620025f5565b6001600160a01b0360443581811692918382036200053e576200176762002e08565b620017728262002f58565b906200177e8462003257565b62000b045760408201511562000b04576001600160401b0391600183608083015116019083821162001a0e57620017bd60e082510151890b8362003829565b5094620017db602083019386855116908760a08601511691620038d2565b1562000b23576200181c906001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0560205260405f2090565b620018298682546200287c565b905587837f000000000000000000000000000000000000000000000000000000000000000016803b156200161b5760405163f3fef3a360e01b81526001600160a01b038a166004820152602481018890529082908290604490829084905af180156200052f57620019f0575b50509183826200192494620018bc60e062001913986024975116925101518c0b8962004da2565b508481620019d0575b505050511695604051916020830191169687825260408301526001606083015260608252620018f48262002744565b6040519485926280000360e11b60208501525180928585019062002691565b81010360048101845201826200277c565b733333333333333333333333333333333333333333803b156200053e576040516317938e1360e01b815260206004820152915f91839182908490829062001970906024830190620026b4565b03925af18015620019c557620019b3575b506040519081527f2b348084e891b20d449a69f90114c5ab7bf7c84d64c25445c8ab440d469a6b4d602060043592a480f35b620019be90620026f7565b5f62001981565b6040513d5f823e3d90fd5b620019df620019e79362002847565b168b62003353565b5f8084620018c5565b620019ff9094929394620026f7565b62001505579190875f62001895565b634e487b7160e01b5f52601160045260245ffd5b5034620002485760e0366003190112620002485762001a41366200279e565b7fc56a3250645180a53cd9e196b2ee0a634a4f54e2edf59ea457f2083917e4d100541562000236576001600160a01b0380606083015116907f000000000000000000000000000000000000000000000000000000000000000016145f1462001aae576200023390620046c4565b620002339060c4359062003a45565b50346200024857602036600319011262000248576020620003b362001ae1620025de565b6001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0260205260405f2090565b503462000248576040366003190112620002485762001b35620025f5565b336001600160a01b0382160362001b54576200043f90600435620039b1565b60405163334bd91960e11b8152600490fd5b50346200024857602036600319011262000248576001600160a01b0362001b8c620025de565b62001b9662002c45565b501681525f8051602062005bb783398151915260205260408120906040519162001bc0836200270b565b60405162001bce8162002727565b6040518254818562001be08362002c80565b808352926001811690811562001ef9575060011462001eb4575b62001c08925003826200277c565b815260405160018301805480835290855260208086209083019186915b81600384011062001e72579360069593819362001c73936001600160401b039997549181811062001e5e575b81811062001e47575b81811062001e30575b1062001e21575b5003826200277c565b60208201526001600160a01b036002830154858116604084015260401c16606082015260038201546001600160a01b038116608083015260ff8160a01c1660a083015260ff8160a81c1660c083015260b01c850b60e0820152855260ff6004820154848116602088015260401c1615156040860152600581015460608601520154818116608085015260401c1660a0830152604051906020825282519060c0602084015262001d31825161010060e08601526101e0850190620026b4565b60208084015185830360df190161010087015280518084529282019492910190835b81811062001e015750505083946001600160401b039260e08385604060a0960151166101208901526001600160a01b036060820151166101408901526001600160a01b0360808201511661016089015260ff858201511661018089015260ff60c0820151166101a08901520151900b6101c086015282602082015116604086015260408101511515606086015260608101516080860152826080820151168286015201511660c08301520390f35b82516001600160401b031686526020958601959092019160010162001d53565b60c01c81526020015f62001c6a565b9260206001918c8560801c16815201930162001c63565b9260206001918c8560401c16815201930162001c5a565b9260206001918c8516815201930162001c51565b9260016004918554906001600160401b03918281168252828160401c16602083015260809281841c16604083015260c01c606082015201940192019162001c25565b5090848652602086209086915b81831062001edc57505090602062001c089282010162001bfa565b602091935080600191548385880101520191019091839262001ec1565b6020925062001c0894915060ff191682840152151560051b82010162001bfa565b50346200024857604036600319011262000248576200043f60043562001f3f620025f5565b908084527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b62680060205262001f79600160408620015462002e64565b62003912565b503462000248576080366003190112620002485762001f9d620025de565b62001fa76200260c565b9062001fb262002623565b90606435926fffffffffffffffffffffffffffffffff84168094036200053e5762001fdc62002e08565b62001fe782620032e8565b856001600160a01b03928360808401511692833b156200053a57620020646101048492836040519586948593636f0d192560e11b855260048501906001600160a01b036080809263ffffffff8082511686526020820151151560208701528060408301511660408701526060820151166060860152015116910152565b6001600160401b038091169b8c60a485015216978860c48401528c60e48401525af180156200052f57620020cb575b5050907f500d805a349357fe5d4759fe052d79bd744b82c8452837f52a7456ec7d3d751c92604092835195865260208601521692a380f35b620020d990939293620026f7565b62000b045790855f62002093565b50346200024857602036600319011262000248576001604060209260043581527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268008452200154604051908152f35b5034620002485780600319360112620002485760206040517f880a9ba888678c7fe4e8c4f028c224f26ce12a3bed6e96025c61ef8a5db6312f8152f35b503462000248576060366003190112620002485762002190620025de565b6001600160401b036024358181116200052b57620021b39036906004016200265e565b91909360443591821162000248576020620021ec868686620021d936600489016200265e565b939092620021e662002e08565b6200288a565b604051908152f35b50346200053e5760403660031901126200053e5762002212620025de565b906024356200222062002d37565b6001600160a01b03928360806200223783620032e8565b015116803b156200053e576040516390a0827b60e01b81526001600160a01b038316600482015260248101849052905f908290604490829084905af18015620019c55762002290575b5062000233929333911662003353565b620002339350620022a190620026f7565b5f9262002280565b346200053e575f3660031901126200053e5760206040516305f5e1008152f35b346200053e5760a03660031901126200053e57620022e6620025de565b620022f06200263a565b620022fa6200264e565b6064359063ffffffff938483168093036200053e57608435908582168092036200053e57620023298162002f58565b506200233462002db1565b6001600160a01b03918260016200237b846001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0160205260405f2090565b0154168015620024bc575b8360018960405199620023998b620026db565b16988981526020810197151597888152604082018a81526060830190878252856080850197169d8e8852620023fe8a6001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0160205260405f2090565b9451166cffffffff00000000000000000068ffffffff000000000064ff0000000087549651151560201b16935160281b16935160481b16936cffffffffffffffffffffffffff19161717171781550191511673ffffffffffffffffffffffffffffffffffffffff19825416179055620024778762003257565b156200053e577ff0dcc8957a27613dd82c92382ad37254b9744169d0caa5f3873cfec7ba794eb9946080946040519788526020880152604087015260608601521692a3005b50620024c882620031ff565b6040516109ab8082018281106001600160401b03821117620025015782916200520c833903905ff58062002386576040513d5f823e3d90fd5b634e487b7160e01b5f52604160045260245ffd5b346200053e5760203660031901126200053e5760043563ffffffff60e01b81168091036200053e57602090637965db0b60e01b81149081156200255e575b506040519015158152f35b6301ffc9a760e01b1490508262002553565b346200053e5760403660031901126200053e57620025dc62002591620025de565b6001600160a01b03620025a36200260c565b91620025ae62002d37565b165f525f8051602062005bb78339815191526020526001600160401b03600460405f20015416339062002ea6565b005b600435906001600160a01b03821682036200053e57565b602435906001600160a01b03821682036200053e57565b602435906001600160401b03821682036200053e57565b604435906001600160401b03821682036200053e57565b6024359063ffffffff821682036200053e57565b6044359081151582036200053e57565b9181601f840112156200053e578235916001600160401b0383116200053e576020808501948460051b0101116200053e57565b5f5b838110620026a35750505f910152565b818101518382015260200162002693565b90602091620026cf8151809281855285808601910162002691565b601f01601f1916010190565b60a081019081106001600160401b038211176200250157604052565b6001600160401b0381116200250157604052565b60c081019081106001600160401b038211176200250157604052565b61010081019081106001600160401b038211176200250157604052565b608081019081106001600160401b038211176200250157604052565b604081019081106001600160401b038211176200250157604052565b90601f801991011681019081106001600160401b038211176200250157604052565b60c09060031901126200053e5760405190620027ba826200270b565b60043582526024356020830152816001600160a01b0360443581811681036200053e57604083015260643590811681036200053e576060820152608435608082015260a060a435910152565b9190811015620028175760051b0190565b634e487b7160e01b5f52603260045260245ffd5b9190916001600160401b038080941691160191821162001a0e57565b9073200000000000000000000000000000000000000091820180921162001a0e57565b906305f5e0ff820180921162001a0e57565b9190820180921162001a0e57565b919392935f945f948083036200053e57620028d5856001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0260205260405f2090565b544311156200053e57869796939291620028ef8662002f58565b926080620028fd88620032e8565b01906001600160a01b0392838351169460208701976200292a6001600160401b0397888b5116906200343d565b845f9b5b1062002b94575b50508c15925062002b8791505057436200297f886001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0260205260405f2090565b5582861662002993575b5050505050505050565b620029c195620029ab60e0865101515f0b8262003829565b85889992995116908660a08901511691620038d2565b1562002b4d575062002a03876001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0460205260405f2090565b62002a108782546200287c565b90558682167f00000000000000000000000000000000000000000000000000000000000000008316803b156200053e5760405163f3fef3a360e01b81526001600160a01b038316600482015260248101899052905f908290604490829084905af18015620019c55762002b29575b509062002a9587849360e096958451169062003353565b511693511691510151850b93823b1562000b0457604051639c45c34b60e01b81526001600160a01b0390911660048201526001600160401b03909116602482015260448101929092525f9290920b606482015290829082908183816084810103925af180156200052f5762002b11575b80808080808062002989565b62002b1d8291620026f7565b62000248578062002b05565b60e094939291995062002b3c90620026f7565b5f98929350909162002a9562002a7e565b8451608001516040516377e88bc960e11b81529084166001600160a01b031660048201526001600160401b03919091166024820152604490fd5b505f985050505050505050565b62002ba6859e9f809c95968462002806565b359062002bb58c858862002806565b359189831683036200053e578a62002bd2938a8a51169262003520565b9490911562002bfb5762002beb8f93926001926200282b565b9b019e9d8f95949192956200292e565b9a9e9d5062002935565b6040519062002c148262002727565b5f60e08360608152606060208201528260408201528260608201528260808201528260a08201528260c08201520152565b6040519062002c54826200270b565b5f60a08362002c6262002c05565b81528260208201528260408201528260608201528260808201520152565b90600182811c9216801562002cb0575b602083101462002c9c57565b634e487b7160e01b5f52602260045260245ffd5b91607f169162002c90565b6001600160401b0381116200250157601f01601f191660200190565b3d1562002d06573d9062002ceb8262002cbb565b9162002cfb60405193846200277c565b82523d5f602084013e565b606090565b6040519062002d1a82620026db565b5f6080838281528260208201528260408201528260608201520152565b335f9081527f176c3a4e1b4d1d61213187c0dde7f83e2e2c933a339cb5403b4090bc2f0aa49a60205260409020547f880a9ba888678c7fe4e8c4f028c224f26ce12a3bed6e96025c61ef8a5db6312f9060ff161562002d935750565b6044906040519063e2517d3f60e01b82523360048301526024820152fd5b335f9081527fb7db2dd08fcb62d0c9e08c51941cae53c267786a0b75803fb7960902fc8ef97d602052604090205460ff161562002dea57565b60405163e2517d3f60e01b81523360048201525f6024820152604490fd5b335f9081527fcf7720576cdb70176b3017d893bcb89ba0f36ed0261d4350fd0180e938b3bacd60205260409020547f5300fde95a5e446527bf6aa7c91bd6661bef5398afc77061d9bc87efb80b7ef69060ff161562002d935750565b805f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b62680060205260405f20335f5260205260ff60405f2054161562002d935750565b620019139262002eec92602492604051926001600160a01b0360208501931683526001600160401b03809216604085015216606083015260608252620018f48262002744565b733333333333333333333333333333333333333333803b156200053e576040516317938e1360e01b815260206004820152915f91839182908490829062002f38906024830190620026b4565b03925af18015620019c55762002f4b5750565b62002f5690620026f7565b565b9062002f6362002c45565b506001600160a01b038092165f5260205f8051602062005bb78339815191528152604092835f2084519062002f98826200270b565b85519562002fa68762002727565b80518254905f8162002fb88462002c80565b918282526001948a86821691825f14620031e05750506001146200319f575b62002fe5925003826200277c565b88528083019082519087829384928282549586815201915f52825f20945f5b8160038201106200315a57620030469654928583831062003140575b83831062003124575b83831062003107575b505010620030f8575b50905003826200277c565b858801526006600283015492856001600160401b0394858116858c0152841c1660608a015260ff60038201549760808b019a888a168c52828a60a01c1660a082015260c0810199838160a81c168b5260b01c5f0b60e0820152875260048301549086821690880152841c161515838601526005810154606086015201549082821660808501521c1660a082015293511615159081620030e9575b50156200053e57565b60ff9150511615155f620030e0565b60c01c81520188905f6200303b565b9091946001600160401b038560801c1681520193015f8562003032565b8192956001600160401b03868d1c168152019401908562003029565b8192956001600160401b0386168152019401908562003020565b86546001600160401b038082168652818b1c811696860196909652608081811c9096168a86015260c01c6060850152958201958795508c949093019260040162003004565b5050845f528188805f20855f915b858310620031c657505062002fe5935082010162002fd7565b8091929450548385880101520191018990858593620031ad565b60ff19168482015262002fe594151560051b840101915062002fd79050565b60405160208101913060601b83526bffffffffffffffffffffffff199060601b16603482015260288152606081018181106001600160401b03821117620025015760405251902090565b519081151582036200053e57565b5f80916040516001600160a01b036020820192168252602081526200327c8162002760565b51906108105afa6200328d62002cd7565b9015620032d6576020818051810103126200053e5760405190602082018281106001600160401b038211176200250157604052620032ce9060200162003249565b809152151590565b6040516313dd7ccd60e31b8152600490fd5b620032f79062000c4362002d0b565b906001600160a01b036001604051936200331185620026db565b805463ffffffff90818116875260ff8160201c1615156020880152818160281c16604088015260481c1660608601520154169182608082015291156200053e57565b60405163a9059cbb60e01b602082019081526001600160a01b039384166024830152604480830195909552938152620033ea9390925f92839291906200339b6064876200277c565b16937f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65646020604051620033ce8162002760565b8181520152519082855af1620033e362002cd7565b91620051de565b805190811591821562003402575b5050156200053e57565b81925090602091810103126200053e57602062003420910162003249565b5f80620033f8565b51906001600160401b03821682036200053e57565b604080516001600160a01b0392909216602083019081526001600160401b039390931682820152808252915f918291906200347a6060826200277c565b51906108015afa6200348b62002cd7565b9015620034f5576060818051810103126200053e5781516001600160401b0392606082018481118382101762002501578152620034ee6060620034d16020860162003428565b94858552620034e284820162003428565b60208601520162003428565b9101521690565b8151639d2c8fcb60e01b8152600490fd5b6001600160401b03918216908216039190821162001a0e57565b949192909394805f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0360205260405f20906001600160a01b039485835416958615620037ab57600284019788549860ff8a60481c166200379957600186015483608086510151168482160362003780576001600160401b039060a01c166001600160401b038b1660ff8c60401c165f146200371f575080926001600160401b03811682115f146200371657620035d79162003506565b995b620035e58b8d6200282b565b9b6001600160401b038d166001600160401b0385161162003704575069010000000000000000009069ff00000000000000000019161790556200362b8160019b62003506565b976001600160401b036020846200364b8d60e0895101515f0b9062003829565b50991695015116843b156200053e5760405163a703334f60e01b81526001600160401b0391821660048201526001600160a01b0392909216602483015282166044820152925f908490606490829084905af1918215620019c5577f53b9d5645f8b7ccd861ebd6036860fd21716451d1f238cb3720f12f3c49b0c4393604093620036f2575b5060018187541696015416956001600160401b038351921682526020820152a4565b620036fd90620026f7565b5f620036d0565b5f9c508c9b5099505050505050505050565b50505f620035d7565b9092809b93806001600160401b038316105f1462003773576200374f91620037479162003506565b809c6200282b565b816001600160401b038216115f146200376b57505b91620035d9565b905062003764565b50506200374f5f62003747565b60405163358d72d160e01b815260048101879052602490fd5b60405163f7348a7960e01b8152600490fd5b604051631a40316d60e01b8152600490fd5b60ff16604d811162001a0e57600a0a90565b9190820391821162001a0e57565b8115620037e8570490565b634e487b7160e01b5f52601260045260245ffd5b90620f42409182810292818404149015171562001a0e57565b8181029291811591840414171562001a0e57565b9190805f0b80155f14620038475750506001600160401b0382169190565b5f81131562003877575062003863620038739160ff16620037bd565b6001600160401b03841662003815565b9190565b9050607f19811462001a0e5762003893905f0360ff16620037bd565b91620038aa836001600160401b038093166200287c565b5f19810190811162001a0e57620038c684620038ce92620037dd565b938462003815565b1690565b6200390a6200390392936001600160401b0393836001600160a01b03620038fc8780971662002847565b166200343d565b946200282b565b169116101590565b90815f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800806020526001600160a01b0360405f20921691825f5260205260ff60405f205416155f14620039aa57825f5260205260405f20815f5260205260405f20600160ff1982541617905533917f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d5f80a4600190565b5050505f90565b90815f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800806020526001600160a01b0360405f20921691825f5260205260ff60405f2054165f14620039aa57825f5260205260405f20815f5260205260405f2060ff19815416905533917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b5f80a4600190565b9062003a5e6001600160a01b0360408401511662003257565b1562004618576001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000165f525f8051602062005bb783398151915260205260405f20906040519162003ab6836200270b565b60405162003ac48162002727565b6040518254815f62003ad68362002c80565b8083529260018116908115620045f75750600114620045b2575b62003afe925003826200277c565b8152600182016040519081602082549182815201915f5260205f20905f915b81600384011062004570579360069593819362003b67936001600160401b039997549181811062001e5e5781811062001e475781811062001e30571062001e21575003826200277c565b60208201526001600160a01b036002830154858116604084015260401c16606082015260038201546001600160a01b038116608083015260ff8160a01c1660a083015260ff8160a81c1660c083015260b01c5f0b60e0820152855260ff6004820154848116602088015260401c1615156040860152600581015460608601520154818116608085015260401c1660a08301526001600160a01b036060840151165f525f8051602062005bb783398151915260205260405f206040519062003c2e826200270b565b60405162003c3c8162002727565b6040518254815f62003c4e8362002c80565b80835292600181169081156200454f57506001146200450a575b62003c76925003826200277c565b8152600182016040519081602082549182815201915f5260205f20905f915b816003840110620044bd579360069593819362003cdf936001600160401b039997549181811062001e5e5781811062001e475781811062001e30571062001e21575003826200277c565b60208201526001600160a01b036002830154858116604084015260401c16606082015260038201546001600160a01b038116608083015260ff8160a01c1660a083015260ff8160a81c1660c083015260b01c5f0b60e0820152845260ff6004820154848116602087015260401c1615156040850152600581015460608501520154818116608084015260401c1660a082015262003d896001600160a01b03606086015116620032e8565b9184519062003dd662003dbb62003dac60a089015160808a01511515956200287c565b60e0885101515f0b9062004da2565b905060ff60c0885101511660ff60c087510151169162005165565b91156200448c5781925b5f8063ffffffff875116604051602081019182526020815262003e038162002760565b51906108085afa9062003e1562002cd7565b91156200447a576020828051810103126200053e576001600160401b0362003e44602062003e6d940162003428565b60208801519116919015620044665760ff60a062003e669251015116620037bd565b9062003815565b602085015115620044175763ffffffff62003e8e8160608801511662005026565b16620f4240818101811162001a0e576001600160401b039262003e6662003ec792620f42409463ffffffff60408c01511691016200287c565b0416906080870151908115155f146200440e5750905b602085015115620043c5576305f5e1008110156200437e57505f5b62003f2462003f0b60a0890151620037fc565b62003f1d895160a08b0151906200287c565b906200287c565b5f1981019190821162001a0e5762003f1d62003f539262003f4c8a5160a08c0151906200287c565b90620037dd565b606482028281046064148315171562001a0e578111620042df57505062003f84855160e0865101515f0b9062004da2565b92909562003faf6001600160401b03602088015116856001600160401b0360a08a01511691620038d2565b156200423b5791608093917f550576b2f9e0ac12dfd5dd2d5743b5b7f11f34302b5f6bec6ad60db81bd6a91860a0856001600160401b036001600160a01b036040819b990151168a606084015116908a8401511515916040519162004014836200270b565b82526020820152828816604082015282871660608201528a81019182528481015f815262004118602086019384515f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b03602052856060600260405f206001600160a01b0388511673ffffffffffffffffffffffffffffffffffffffff19825416178155600181016001600160a01b0360208a0151168154907bffffffffffffffff000000000000000000000000000000000000000060408c015160a01b169163ffffffff60e01b161717905501950151168619855416178455511515839068ff0000000000000000825491151560401b169068ff00000000000000001916179055565b51151569ff00000000000000000082549160481b169069ff00000000000000000019161790555194818b60408501511697818d6060870151169a87875197015160405197885260208801521660408601521660608401521688820152a4015116620041ae83826001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001662003353565b60e06001600160401b03602084015116925101515f0b90803b156200053e57604051639c45c34b60e01b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001660048201526001600160401b03909316602484015260448301939093525f90810b6064830152909182908183816084810162002f38565b94505050905062002f56925060208201516001600160401b03604051921682527f8d61555dcf69ba745e97fdea78e2652d9cdd464ca521faad482fe796f6d5841c60206001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001693a36001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016606082015262004e31565b9350949350505060208201516001600160a01b0360608401511691606481019081811162001a0e5760630190811162001a0e5760407fc8f90125c6a36c77a571201afc10310420481ab4895fadabb596d0ba71c22e3e9162002f569660648351920482526020820152a36001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000166060820152620046c4565b6001600160401b036305f5e0ff1982011162001a0e57620043be620043b86001600160401b036305f5e100936305f5e0ff190116620037fc565b6200286a565b0462003ef8565b6305f5e100811115620043da57505f62003ef8565b6001600160401b03816305f5e100031162001a0e57620043be620043b86001600160401b036305f5e10093840316620037fc565b90509062003edd565b63ffffffff6200442d8160608801511662005026565b16620f4240808281031162001a0e576001600160401b039262003e6662003ec792620f42409463ffffffff60408c0151169103620037cf565b5062003e6660ff60a08951015116620037bd565b604051635cffc5fb60e11b8152600490fd5b6127108181031162001a0e576001600160401b03612710620044b383820383861662003815565b0416919262003de0565b926001608060049286546001600160401b03811682526001600160401b038160401c1660208301526001600160401b0381841c16604083015260c01c606082015201940192019162003c95565b5090845f5260205f20905f915b8183106200453257505090602062003c769282010162003c68565b602091935080600191548385880101520191019091839262004517565b6020925062003c7694915060ff191682840152151560051b82010162003c68565b9260016004918554906001600160401b03918281168252828160401c16602083015260809281841c16604083015260c01c606082015201940192019162003b1d565b5090845f5260205f20905f915b818310620045da57505090602062003afe9282010162003af0565b6020919350806001915483858801015201910190918392620045bf565b6020925062003afe94915060ff191682840152151560051b82010162003af0565b506080810151909190156200464c5760246001600160a01b03604084015116604051906320a2097d60e11b82526004820152fd5b9080602062002f569201517f31604f5790fbed7a37471cea010a8f08483441a6e1235ac48bb59337439a630d60206001600160a01b03604085015116604051908152a26001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016606082015262004e31565b6060906060810151905f6001600160a01b0380931692835f526020945f8051602062005bb78339815191528652604091825f2096835162004705816200270b565b845193620047138562002727565b85518a5491905f81620047268562002c80565b918282526001958887821691825f1462004d6b57505060011462004d2a575b62004756925094939403826200277c565b8652808b019184885193898586948484549283815201935f52845f20965f915b83600384011062004cde575085620047b998549484841062004cc4575b84841062004ca7575b5083831062004c8a575b50501062004c7b5750905003826200277c565b82850152600289015495836001600160401b039788811688880152871c16606086015260038a0154946080958581168783015260ff8160a01c1660a083015260ff8160a81c1660c083015260b01c5f0b60e08201528252600660048b01549a60ff8585019c8d8b82169052891c1615158885015260058101546060850152015490878216868401528760a0840192881c168252868901976200485e868a511662003257565b1562004c0d57899a9b5f9a98999a508c612710620048958b519b8b6200488b60a083019e8f51906200287c565b9101519062003815565b04988a5199808b1162004c04575b508d8a8062004b2e575b620048c2620048d191620048e493516200287c565b60e08a5101515f0b9062004da2565b91909782878088511692511691620038d2565b1562004ae65750908c9695949392918a620049f9575b509260e0896200498f94620049c59b9997947fb021c853215aadb12b6fa8afa7b3158201517d9abf7f756cdbb67bd66abc5a1c9f9e9d9b99976200496e906001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0460205260405f2090565b6200497b8d82546200287c565b9055511696511694510151900b9062004da2565b9190928d84620049ca575b5050505050880151965116965192519351938493846040919493926060820195825260208201520152565b0390a4565b620049ee94620049e89188620049e08562002847565b169062003353565b62002ea6565b5f8080808d6200499a565b919293949596505087807f00000000000000000000000000000000000000000000000000000000000000001691875101511690803b156200053e578b5163f3fef3a360e01b81526001600160a01b03929092166004830152602482018a90525f908290604490829084905af1801562004adc578c948f91998098956200498f957fb021c853215aadb12b6fa8afa7b3158201517d9abf7f756cdbb67bd66abc5a1c9f9e9d9b9995620049c59d62004ac0575b509497995094509950939597999a9b620048fa565b60e0945062004acf90620026f7565b6200496e5f945062004aab565b8b513d5f823e3d90fd5b9a50505050955050505093957f8d61555dcf69ba745e97fdea78e2652d9cdd464ca521faad482fe796f6d5841c955084915062004b238162004e31565b0151945191168152a3565b62004b45908a8301518c858c51015116906200503f565b1562004b53575b8a620048ad565b509192939495968881999a50602492508389510151168c51928380926370a0823160e01b82528d7f00000000000000000000000000000000000000000000000000000000000000001660048301525afa90811562004adc57918e918e9a999897969594935f9162004bc7575b509962004b4c565b92505098508681813d831162004bfc575b62004be481836200277c565b810103126200053e57518c988e91620048d162004bbf565b503d62004bd8565b99505f620048a3565b505050509296509450839293015115155f1462004c405750505191516320a2097d60e11b81529116600482015260249150fd5b62002f56948294827f31604f5790fbed7a37471cea010a8f08483441a6e1235ac48bb59337439a630d9401519451169051908152a262004e31565b60c01c81520185905f6200303b565b9091946001600160401b038560801c1681520193015f85620047a6565b956001600160401b03868495981c16815201940190858e6200479c565b6001600160401b0386168752958101959282019262004793565b95509360049296509284919388546001600160401b039283821683528d8483831c169084015260809382851c169083015260c01c8782015201960191019386948c9296938a9562004776565b50508c5f528186805f20865f915b85831062004d5157505062004756935082010162004745565b809192945054838588010152019101879086859362004d38565b60ff1916848201526200475694151560051b8401019150620047459050565b81811062004d96575050565b5f815560010162004d8a565b9190805f0b9081155f1462004dc05750506001600160401b03821690565b5f82131562004e035762004dd8915060ff16620037bd565b918215620037e857620038ce62004dfb82856001600160401b03940690620037cf565b9384620037dd565b505f0380805f0b0362001a0e57620038ce62004e2a6001600160401b039260ff16620037bd565b8462003815565b805161271062004e5862004e4c60a08501938451906200287c565b60808501519062003815565b0481518181115f146200501d5750905b8190602084019182519162004e8f60608701956001600160a01b039485885116906200503f565b1562005014575b8162004f61575b620049c5837f4755f239bb1b047245415cb917deced72a3ca8baebcef109c396ff332ea6f50f9462004ed1858a516200287c565b9662004eeb828a51169860408c0199848b51169062003353565b62004f29828a51166001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0460205260405f2090565b62004f368782546200287c565b9055519651169651169651915192604051938493846040919493926060820195825260208201520152565b84518316927f00000000000000000000000000000000000000000000000000000000000000008116803b156200053e5760405163f3fef3a360e01b81526001600160a01b03959095166004860152602485018490525f908590604490829084905af1908115620019c5577f4755f239bb1b047245415cb917deced72a3ca8baebcef109c396ff332ea6f50f94620049c59262005002575b5093505062004e9d565b6200500d90620026f7565b5f62004ff8565b5f915062004e96565b90509062004e68565b90606463ffffffff8093160291821691820362001a0e57565b6040516370a0823160e01b81527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0390811660048301529092169392909190602082602481885afa918215620019c5575f92620050f2575b5080821094851595620050b2575b50505050565b7f5180f0ad9e9bd2296de2ee38c85d11c56613fa73f8ee66792f26ac318f1274749260609260405192835260208301526040820152a25f808080620050ac565b9091506020813d60201162005123575b8162005111602093836200277c565b810103126200053e5751905f6200509e565b3d915062005102565b51906001600160a01b03821682036200053e57565b519060ff821682036200053e57565b9060ff8091169116039060ff821162001a0e57565b919060ff821660ff82168181145f1462005180575050505090565b929391921015620051b857620038ce9192620051b0620051aa6001600160401b0395869362005150565b620037bd565b911662003815565b620038ce91620051d6620051aa6001600160401b0395869362005150565b9116620037dd565b9015620051fb57815115620051f1575090565b3b156200053e5790565b5080519081156200053e57602001fdfe60a080604052346100455733608052610961908161004a8239608051818181608b015281816101ac0152818161038e015281816104a20152818161051b015261069c0152f35b5f80fdfe60406080815260049081361015610014575f80fd5b5f915f3560e01c908163435354d3146104d757816390a0827b1461047b5781639c45c34b14610469578163a703334f1461033c578163de1a324a146100d457508063e94b77c1146100b35763eba61c0e1461006d575f80fd5b346100af57816003193601126100af57602090516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b5080fd5b82346100d1576100ce6100c5366105b5565b9291909161068d565b80f35b80fd5b91905034610338576101009136600319018381126103345760a01361033057815167ffffffffffffffff939060a081018581118282101761031d5784526101196105a2565b91828252602435918215158303610319576020810192835263ffffffff9360443585811681036102ce578783015260643585811681036102ce5760608301526084356001600160a01b03928382168203610315576080015260a4358881168091036102ce5760c435918983168093036102ce5760e435936fffffffffffffffffffffffffffffffff85168095036102ce577f000000000000000000000000000000000000000000000000000000000000000016330361031557866101dd9116610663565b9451151581156103055782156102f5578a6102e557885196602088019616865288870152606086015260808501525f60a0850152600260c085015260e084015260e08352820194828610908611176102d25761026891602491868652630100000160e01b6101208301526102598251809261012485019061079c565b81010383810186520184610641565b73333333333333333333333333333333333333333392833b156102ce576102a5935f928385518097819582946317938e1360e01b845283016107bd565b03925af19081156102c557506102b9575080f35b6102c391506105fd565b005b513d5f823e3d90fd5b5f80fd5b604183634e487b7160e01b5f525260245ffd5b88516376d4929560e11b81528890fd5b88516313c0a8df60e01b81528890fd5b885163017461b760e71b81528890fd5b8a80fd5b8780fd5b604184634e487b7160e01b5f525260245ffd5b8380fd5b8480fd5b8280fd5b919050346103385760603660031901126103385781359067ffffffffffffffff928383168093036102ce576024356001600160a01b03948582168092036102ce576044359081168091036102ce5786957f000000000000000000000000000000000000000000000000000000000000000016330361046557610259946103fc9260249286519260208401928352878401526060830152606082526103df82610625565b85519687926280000360e11b60208501525180928585019061079c565b73333333333333333333333333333333333333333392833b15610334576104399385928385518097819582946317938e1360e01b845283016107bd565b03925af190811561045c575061044c5750f35b610455906105fd565b6100d15780f35b513d84823e3d90fd5b8580fd5b83346100d1576100ce6100c5366105b5565b91905034610338573660031901126100af57356001600160a01b038082168092036102ce577f00000000000000000000000000000000000000000000000000000000000000001633036100af576100ce906024359033906107e9565b919050346102ce57806003193601126102ce576104f26105a2565b91602435926fffffffffffffffffffffffffffffffff84168094036102ce576001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001633036102ce5761054a90610663565b9282519063ffffffff602083019516855283820152828152606081019381851067ffffffffffffffff8611176102d25761026891602491868652630100000b60e01b608083015261025982518092608485019061079c565b6004359063ffffffff821682036102ce57565b60809060031901126102ce576004356001600160a01b03811681036102ce579060243567ffffffffffffffff811681036102ce579060443590606435805f0b81036102ce5790565b67ffffffffffffffff811161061157604052565b634e487b7160e01b5f52604160045260245ffd5b6080810190811067ffffffffffffffff82111761061157604052565b90601f8019910116810190811067ffffffffffffffff82111761061157604052565b9061271063ffffffff8093160191821161067957565b634e487b7160e01b5f52601160045260245ffd5b9092916001600160a01b0392837f00000000000000000000000000000000000000000000000000000000000000001633036102ce57805f0b80155f1461071c575050915b826106dd575b50505050565b67ffffffffffffffff732000000000000000000000000000000000000000941684018094116106795780610713941691166107e9565b5f8080806106d7565b5f81131561075c57506107319060ff166108f1565b8015610748578106810390811161067957916106d1565b634e487b7160e01b5f52601260045260245ffd5b90505f9391930380805f0b03610679576107789060ff166108f1565b9280938181810204149015176106d157634e487b7160e01b5f52601160045260245ffd5b5f5b8381106107ad5750505f910152565b818101518382015260200161079e565b604091602082526107dd815180928160208601526020868601910161079c565b601f01601f1916010190565b9190604051906020938483019363a9059cbb60e01b85526001600160a01b03809316602485015260448401526044835261082283610625565b169160405190604082019067ffffffffffffffff9383831085841117610611577f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564875f9586956040528181520152519082865af13d156108e4573d918211610611576108ac926040519261089f86601f19601f8401160185610641565b83523d5f8685013e610902565b80519182159182156108c4575b50509050156102ce57565b8092508193810103126102ce57015180151581036102ce57805f806108b9565b6108ac9260609250610902565b60ff16604d811161067957600a0a90565b901561091c57815115610913575090565b3b156102ce5790565b5080519081156102ce57602001fdfea2646970667358221220c909e7dbe656d7c973709e34eb673837a89b1ae88012e9262063134290e6f21f64736f6c634300081800336c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b00a2646970667358221220d7009235facd2afacf7dbd784aa03c97dd6444be46f4a33aa486147317b81a7764736f6c6343000818003300000000000000000000000081d40f21f12a8f0e3252bccb954d722d4c464b640000000000000000000000009a8f92a830a5cb89a3816e3d267cb7791c16b04d000000000000000000000000002e76dc036a1eff1488ee5435ee66c6abf32674000000000000000000000000b88339cb7199b77e23db6e890353e22632ba630f0000000000000000000000005e7840e06faccb6d1c3b5f5e0d1d3d07f2829bba", + "nonce": "0x244", + "chainId": "0x3e7" + }, + "additionalContracts": [ + { + "transactionType": "CREATE", + "contractName": "HyperCoreFlowExecutor", + "address": "0xc5b7162e6a0fde52cc180dc8ab273feb8b32f57d", + "initCode": "0x60c0346200010457601f62005d3e38819003918201601f19168301916001600160401b038311848410176200010857808492604094855283398101031262000104576200005a602062000052836200011c565b92016200011c565b6001600160a01b0390911660805260a052604051615c0c90816200013282396080518181816108b7015281816109ff0152818161158b0152818161182f01528181612a1701528181614a0501528181614b7c01528181614f68015261504e015260a051818181610670015281816107de01528181611a7b01528181613a6e01528181614188015281816141e401528181614288015281816142b40152818161435301526146990152f35b5f80fd5b634e487b7160e01b5f52604160045260245ffd5b51906001600160a01b0382168203620001045756fe6080604052600436101562000012575f80fd5b5f803560e01c806246912e146200257057806301ffc9a71462002515578063037a06a414620022c957806304c73f6014620022a9578063057f037014620021f45780631f74a0b5146200217257806321081d3c1462002135578063248a9ca314620020e75780632e748b211462001f7f5780632f2ff15d1462001f1a578063319adf9f1462001b6657806336568abe1462001b1757806337710e201462001abd57806339fff0981462001a225780633b1c6a0114620017275780633cf3a02514620016eb5780634265fe861462001699578063490e662f146200165c5780634b3b029b146200161f578063502a82e21462001554578063521c98ba1462000d9157806369b97ac71462000d7157806379c7b60b1462000d1757806379c7f2891462000c1557806390a0827b1462000bd457806391d148541462000b6657806396cc2cfb14620008f9578063a217fddf14620008db578063a4b672b61462000895578063af5de6f91462000802578063c55dae6314620007bc578063ccbedaec1462000542578063d06e28ed1462000443578063d547741f14620003da578063e38b73a914620003bc578063ea0aaf241462000359578063eb84e7f2146200024b5763ff3eae0014620001e2575f80fd5b34620002485760c0366003190112620002485762000200366200279e565b7fc56a3250645180a53cd9e196b2ee0a634a4f54e2edf59ea457f2083917e4d10054156200023657620002339062004e31565b80f35b60405163cd6d8f7d60e01b8152600490fd5b80fd5b503462000248576020366003190112620002485760408160c09260a0835162000274816200270b565b8281528260208201528285820152826060820152826080820152015260043581527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0360205220604051620002c8816200270b565b6001600160a01b039060028284541693848352600181015490602084019185811683526001600160401b0393849283604088019360a01c1683520154956060860193838816855260ff60a06080890198828b60401c1615158a52019860481c1615158852604051988952511660208801525116604086015251166060840152511515608083015251151560a0820152f35b50346200024857602036600319011262000248576020620003b36200037d620025de565b6001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0560205260405f2090565b54604051908152f35b50346200024857806003193601126200024857602060405160068152f35b50346200024857604036600319011262000248576200043f600435620003ff620025f5565b908084527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b62680060205262000439600160408620015462002e64565b620039b1565b5080f35b503462000248576040366003190112620002485762000461620025de565b602435906fffffffffffffffffffffffffffffffff82168092036200053e576200048a62002e08565b6200049581620032e8565b90836001600160a01b039263ffffffff84608083015116915116813b156200053a57829160448392604051948593849263435354d360e01b845260048401528a60248401525af180156200052f5762000513575b5050167f02366c0d102495be1ee805b749be7baebab4fc0710c6d3f38751f1a22bd711648380a380f35b6200051e90620026f7565b6200052b57835f620004e9565b8380fd5b6040513d84823e3d90fd5b8280fd5b5f80fd5b50346200024857606036600319011262000248578062000561620025de565b6200056b6200260c565b906200057662002623565b906200058162002d37565b620005bc816001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0260205260405f2090565b544311156200071a574362000601826001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0260205260405f2090565b556001600160a01b0380600162000648846001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0160205260405f2090565b01541693816001600160401b03938483166200071f575b5050508183166200066e578480f35b7f00000000000000000000000000000000000000000000000000000000000000001684525f8051602062005bb783398151915260205260046040852001541690823b156200071a5760405163a703334f60e01b81526001600160401b039283166004820152336024820152911660448201529082908290606490829084905af180156200052f5762000702575b8080808480f35b6200070d90620026f7565b6200024857805f620006fb565b505050fd5b1686525f8051602062005bb783398151915260205282600460408820015416853b15620007b85760405163a703334f60e01b81526001600160401b03918216600482015233602482015291166044820152858160648183895af1908115620007ad57869162000791575b82906200065f565b6200079c90620026f7565b620007a957845f62000789565b8480fd5b6040513d88823e3d90fd5b8680fd5b5034620002485780600319360112620002485760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b503462000248576020366003190112620002485760206001600160a01b03620008346200082e620025de565b620031ff565b6109ab60405162000848858301826200277c565b818152848101916200520c8339519020604051908482019260ff60f81b84523060601b60218401526035830152605582015260558152620008898162002744565b51902016604051908152f35b5034620002485780600319360112620002485760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b50346200024857806003193601126200024857602090604051908152f35b503462000248576040366003190112620002485762000917620025de565b906200092262002e08565b6200092d8262002f58565b6200093883620032e8565b926200094e60e083510151840b60243562004da2565b9460208401906001600160401b036200097381845116898360a08a01511691620038d2565b1562000b3657869750620009b7856001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0460205260405f2090565b620009c48582546200287c565b90556001600160a01b03808616807f410b9a8c926b6c439cdceb39c0bb8f829838a25bc5a26af9d4c263d1313cc46b6020604051898152a2817f000000000000000000000000000000000000000000000000000000000000000016803b1562000b325760405163f3fef3a360e01b81526001600160a01b038316600482015260248101889052908a908290604490829084905af1801562000b275787918b9162000b08575b505062000a8290608060e0960192848451169062003353565b511692511694510151850b93813b1562000b0457604051639c45c34b60e01b81526001600160a01b039490941660048501526001600160401b0316602484015260448301919091525f9290920b60648201529082908290608490829084905af180156200052f5762000af15750f35b62000afc90620026f7565b620002485780f35b8580fd5b62000b15919250620026f7565b62000b235785895f62000a69565b8880fd5b6040513d8c823e3d90fd5b8980fd5b6040516377e88bc960e11b81526001600160a01b03861660048201526001600160401b0389166024820152604490fd5b50346200024857604036600319011262000248576001600160a01b03604062000b8e620025f5565b9260043581527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268006020522091165f52602052602060ff60405f2054166040519015158152f35b50346200024857604036600319011262000248576200023362000bf6620025de565b62000c0062002d37565b602435906001600160a01b0333911662003353565b503462000248576020366003190112620002485760a062000c7a62000c39620025de565b62000c4362002d0b565b506001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0160205260405f2090565b6001600160a01b0360016040519262000c9384620026db565b805463ffffffff90818116865260ff8160201c1615156020870152818160281c16604087015260481c166060850152015416608082015262000d1560405180926001600160a01b036080809263ffffffff8082511686526020820151151560208701528060408301511660408701526060820151166060860152015116910152565bf35b50346200024857602036600319011262000248576020620003b362000d3b620025de565b6001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0460205260405f2090565b503462000248578060031936011262000248576020604051620f42408152f35b5034620002485760a0366003190112620002485762000daf620025de565b62000db96200263a565b9062000dc46200264e565b6001600160401b0360643516606435036200053e57608435906001600160401b03821682036200053e5762000df862002db1565b62000e0262002c05565b508480604051602081019063ffffffff881682526020815262000e258162002760565b519061080c5afa62000e3662002cd7565b901562001542578051810190602081830312620007b85760208101516001600160401b038111620015055761010081830184031262001505576040519262000e7e8462002727565b602082840101516001600160401b03811162000b325760208201603f828587010101121562000b32576020818486010101519062000ebc8262002cbb565b9162000ecc60405193846200277c565b8083526020840160408284888a01010101116200152a5762000efa9160406020850191878901010162002691565b8452604082840101516001600160401b03811162000b325760208201603f828587010101121562000b3257602081848601010151916001600160401b0383116200152e578260051b6040519362000f5560208301866200277c565b84526020808501920160408285888a01010101116200152a576040838688010101915b60408285888a01010101831062001509575050505060208401526101009062000fa683820160600162003428565b604085015262000fbb6080828501016200512c565b606085015262000fd060a0828501016200512c565b9283608086015262000fe760c08383010162005141565b60a086015262000ffc60e08383010162005141565b60c08601520101519081880b820362001505578160e08401526001600160a01b03808716911603620007b8576200103890870b60643562003829565b506040519162001048836200270b565b825263ffffffff86166020830152821515604083015260608201526001600160401b036064351660808201526001600160401b03831660a08201526001600160a01b03841686525f8051602062005bb783398151915260205260408620815180518051906001600160401b03821162001438578190620010c9855462002c80565b601f8111620014c5575b50602090601f831160011462001458578b926200144c575b50508160011b915f199060031b1c19161782555b60208101518051906001600160401b038211620014385768010000000000000000821162001438576020906001850154836001870155808410620013e5575b500190600184018a5260208a20908a5b8160021c81106200139d5750600319811680820362001334575b50505050936001600160a01b03937f12cf3d04179e82c834f3ee7169a5df80651aa65530127f9ddb04c8cd82244353969360068460809860026001600160401b039701876040830151168154907bffffffffffffffffffffffffffffffffffffffff0000000000000000606085015160401b169163ffffffff60e01b16171790556003820190898b820151169082549174ff000000000000000000000000000000000000000060a083015160a01b16907fffffffffffffffffff000000000000000000000000000000000000000000000060e075ff00000000000000000000000000000000000000000060c086015160a81b1694015160b01b76ff00000000000000000000000000000000000000000000169416171717179055620012c5600482018760208601511688198254161781556040850151151568ff0000000000000000825491151560401b169068ff00000000000000001916179055565b6060830151600582015501908488820151166fffffffffffffffff000000000000000060a0845493015160401b16916fffffffffffffffffffffffffffffffff19161717905563ffffffff6040519816885215156020880152816064351660408801521660608601521692a280f35b928b938c5b81840381106200135d5750505060021c0155838360066001600160401b0362001168565b9091946020620013926001926001600160401b03895116908560031b60031b916001600160401b03809116831b921b19161790565b960192910162001339565b8b8c5b60048110620013b75750838201556001016200114e565b85519095916001916020916001600160401b0360068a901b81811b199092169216901b1792019501620013a0565b6200141890600187018d52838d20600380870160021c820192601888831b16806200141f575b500160021c019062004d8a565b5f6200113e565b5f1990818601918254918a03851b1c1690555f6200140b565b634e487b7160e01b8a52604160045260248afd5b015190505f80620010eb565b9250848b5260208b20908b935b601f1984168510620014a9576001945083601f1981161062001490575b505050811b018255620010ff565b01515f1960f88460031b161c191690555f808062001482565b8181015183556020948501946001909301929091019062001465565b620014f390868d5260208d20601f850160051c81019160208610620014fa575b601f0160051c019062004d8a565b5f620010d3565b9091508190620014e5565b8780fd5b906020806040936200151b8662003428565b81520193019290915062000f78565b8b80fd5b634e487b7160e01b8b52604160045260248bfd5b604051639b0c335d60e01b8152600490fd5b503462000248576040366003190112620002485762001572620025de565b6024356200157f62002d37565b826001600160a01b03807f000000000000000000000000000000000000000000000000000000000000000016931692803b156200161b5760405163f3fef3a360e01b81526001600160a01b0385166004820152602481018490529082908290604490829084905af180156200052f5762001603575b50506200023391339062003353565b6200160e90620026f7565b6200053a57825f620015f4565b5080fd5b5034620002485780600319360112620002485760206040517f5300fde95a5e446527bf6aa7c91bd6661bef5398afc77061d9bc87efb80b7ef68152f35b5034620002485780600319360112620002485760206040517fc56a3250645180a53cd9e196b2ee0a634a4f54e2edf59ea457f2083917e4d1008152f35b5034620002485760c03660031901126200024857620016b8366200279e565b7fc56a3250645180a53cd9e196b2ee0a634a4f54e2edf59ea457f2083917e4d100541562000236576200023390620046c4565b50346200024857602036600319011262000248576200170962002d37565b80808080600435335af16200171d62002cd7565b5015620002485780f35b503462000248576060366003190112620002485762001745620025f5565b6001600160a01b0360443581811692918382036200053e576200176762002e08565b620017728262002f58565b906200177e8462003257565b62000b045760408201511562000b04576001600160401b0391600183608083015116019083821162001a0e57620017bd60e082510151890b8362003829565b5094620017db602083019386855116908760a08601511691620038d2565b1562000b23576200181c906001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0560205260405f2090565b620018298682546200287c565b905587837f000000000000000000000000000000000000000000000000000000000000000016803b156200161b5760405163f3fef3a360e01b81526001600160a01b038a166004820152602481018890529082908290604490829084905af180156200052f57620019f0575b50509183826200192494620018bc60e062001913986024975116925101518c0b8962004da2565b508481620019d0575b505050511695604051916020830191169687825260408301526001606083015260608252620018f48262002744565b6040519485926280000360e11b60208501525180928585019062002691565b81010360048101845201826200277c565b733333333333333333333333333333333333333333803b156200053e576040516317938e1360e01b815260206004820152915f91839182908490829062001970906024830190620026b4565b03925af18015620019c557620019b3575b506040519081527f2b348084e891b20d449a69f90114c5ab7bf7c84d64c25445c8ab440d469a6b4d602060043592a480f35b620019be90620026f7565b5f62001981565b6040513d5f823e3d90fd5b620019df620019e79362002847565b168b62003353565b5f8084620018c5565b620019ff9094929394620026f7565b62001505579190875f62001895565b634e487b7160e01b5f52601160045260245ffd5b5034620002485760e0366003190112620002485762001a41366200279e565b7fc56a3250645180a53cd9e196b2ee0a634a4f54e2edf59ea457f2083917e4d100541562000236576001600160a01b0380606083015116907f000000000000000000000000000000000000000000000000000000000000000016145f1462001aae576200023390620046c4565b620002339060c4359062003a45565b50346200024857602036600319011262000248576020620003b362001ae1620025de565b6001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0260205260405f2090565b503462000248576040366003190112620002485762001b35620025f5565b336001600160a01b0382160362001b54576200043f90600435620039b1565b60405163334bd91960e11b8152600490fd5b50346200024857602036600319011262000248576001600160a01b0362001b8c620025de565b62001b9662002c45565b501681525f8051602062005bb783398151915260205260408120906040519162001bc0836200270b565b60405162001bce8162002727565b6040518254818562001be08362002c80565b808352926001811690811562001ef9575060011462001eb4575b62001c08925003826200277c565b815260405160018301805480835290855260208086209083019186915b81600384011062001e72579360069593819362001c73936001600160401b039997549181811062001e5e575b81811062001e47575b81811062001e30575b1062001e21575b5003826200277c565b60208201526001600160a01b036002830154858116604084015260401c16606082015260038201546001600160a01b038116608083015260ff8160a01c1660a083015260ff8160a81c1660c083015260b01c850b60e0820152855260ff6004820154848116602088015260401c1615156040860152600581015460608601520154818116608085015260401c1660a0830152604051906020825282519060c0602084015262001d31825161010060e08601526101e0850190620026b4565b60208084015185830360df190161010087015280518084529282019492910190835b81811062001e015750505083946001600160401b039260e08385604060a0960151166101208901526001600160a01b036060820151166101408901526001600160a01b0360808201511661016089015260ff858201511661018089015260ff60c0820151166101a08901520151900b6101c086015282602082015116604086015260408101511515606086015260608101516080860152826080820151168286015201511660c08301520390f35b82516001600160401b031686526020958601959092019160010162001d53565b60c01c81526020015f62001c6a565b9260206001918c8560801c16815201930162001c63565b9260206001918c8560401c16815201930162001c5a565b9260206001918c8516815201930162001c51565b9260016004918554906001600160401b03918281168252828160401c16602083015260809281841c16604083015260c01c606082015201940192019162001c25565b5090848652602086209086915b81831062001edc57505090602062001c089282010162001bfa565b602091935080600191548385880101520191019091839262001ec1565b6020925062001c0894915060ff191682840152151560051b82010162001bfa565b50346200024857604036600319011262000248576200043f60043562001f3f620025f5565b908084527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b62680060205262001f79600160408620015462002e64565b62003912565b503462000248576080366003190112620002485762001f9d620025de565b62001fa76200260c565b9062001fb262002623565b90606435926fffffffffffffffffffffffffffffffff84168094036200053e5762001fdc62002e08565b62001fe782620032e8565b856001600160a01b03928360808401511692833b156200053a57620020646101048492836040519586948593636f0d192560e11b855260048501906001600160a01b036080809263ffffffff8082511686526020820151151560208701528060408301511660408701526060820151166060860152015116910152565b6001600160401b038091169b8c60a485015216978860c48401528c60e48401525af180156200052f57620020cb575b5050907f500d805a349357fe5d4759fe052d79bd744b82c8452837f52a7456ec7d3d751c92604092835195865260208601521692a380f35b620020d990939293620026f7565b62000b045790855f62002093565b50346200024857602036600319011262000248576001604060209260043581527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268008452200154604051908152f35b5034620002485780600319360112620002485760206040517f880a9ba888678c7fe4e8c4f028c224f26ce12a3bed6e96025c61ef8a5db6312f8152f35b503462000248576060366003190112620002485762002190620025de565b6001600160401b036024358181116200052b57620021b39036906004016200265e565b91909360443591821162000248576020620021ec868686620021d936600489016200265e565b939092620021e662002e08565b6200288a565b604051908152f35b50346200053e5760403660031901126200053e5762002212620025de565b906024356200222062002d37565b6001600160a01b03928360806200223783620032e8565b015116803b156200053e576040516390a0827b60e01b81526001600160a01b038316600482015260248101849052905f908290604490829084905af18015620019c55762002290575b5062000233929333911662003353565b620002339350620022a190620026f7565b5f9262002280565b346200053e575f3660031901126200053e5760206040516305f5e1008152f35b346200053e5760a03660031901126200053e57620022e6620025de565b620022f06200263a565b620022fa6200264e565b6064359063ffffffff938483168093036200053e57608435908582168092036200053e57620023298162002f58565b506200233462002db1565b6001600160a01b03918260016200237b846001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0160205260405f2090565b0154168015620024bc575b8360018960405199620023998b620026db565b16988981526020810197151597888152604082018a81526060830190878252856080850197169d8e8852620023fe8a6001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0160205260405f2090565b9451166cffffffff00000000000000000068ffffffff000000000064ff0000000087549651151560201b16935160281b16935160481b16936cffffffffffffffffffffffffff19161717171781550191511673ffffffffffffffffffffffffffffffffffffffff19825416179055620024778762003257565b156200053e577ff0dcc8957a27613dd82c92382ad37254b9744169d0caa5f3873cfec7ba794eb9946080946040519788526020880152604087015260608601521692a3005b50620024c882620031ff565b6040516109ab8082018281106001600160401b03821117620025015782916200520c833903905ff58062002386576040513d5f823e3d90fd5b634e487b7160e01b5f52604160045260245ffd5b346200053e5760203660031901126200053e5760043563ffffffff60e01b81168091036200053e57602090637965db0b60e01b81149081156200255e575b506040519015158152f35b6301ffc9a760e01b1490508262002553565b346200053e5760403660031901126200053e57620025dc62002591620025de565b6001600160a01b03620025a36200260c565b91620025ae62002d37565b165f525f8051602062005bb78339815191526020526001600160401b03600460405f20015416339062002ea6565b005b600435906001600160a01b03821682036200053e57565b602435906001600160a01b03821682036200053e57565b602435906001600160401b03821682036200053e57565b604435906001600160401b03821682036200053e57565b6024359063ffffffff821682036200053e57565b6044359081151582036200053e57565b9181601f840112156200053e578235916001600160401b0383116200053e576020808501948460051b0101116200053e57565b5f5b838110620026a35750505f910152565b818101518382015260200162002693565b90602091620026cf8151809281855285808601910162002691565b601f01601f1916010190565b60a081019081106001600160401b038211176200250157604052565b6001600160401b0381116200250157604052565b60c081019081106001600160401b038211176200250157604052565b61010081019081106001600160401b038211176200250157604052565b608081019081106001600160401b038211176200250157604052565b604081019081106001600160401b038211176200250157604052565b90601f801991011681019081106001600160401b038211176200250157604052565b60c09060031901126200053e5760405190620027ba826200270b565b60043582526024356020830152816001600160a01b0360443581811681036200053e57604083015260643590811681036200053e576060820152608435608082015260a060a435910152565b9190811015620028175760051b0190565b634e487b7160e01b5f52603260045260245ffd5b9190916001600160401b038080941691160191821162001a0e57565b9073200000000000000000000000000000000000000091820180921162001a0e57565b906305f5e0ff820180921162001a0e57565b9190820180921162001a0e57565b919392935f945f948083036200053e57620028d5856001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0260205260405f2090565b544311156200053e57869796939291620028ef8662002f58565b926080620028fd88620032e8565b01906001600160a01b0392838351169460208701976200292a6001600160401b0397888b5116906200343d565b845f9b5b1062002b94575b50508c15925062002b8791505057436200297f886001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0260205260405f2090565b5582861662002993575b5050505050505050565b620029c195620029ab60e0865101515f0b8262003829565b85889992995116908660a08901511691620038d2565b1562002b4d575062002a03876001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0460205260405f2090565b62002a108782546200287c565b90558682167f00000000000000000000000000000000000000000000000000000000000000008316803b156200053e5760405163f3fef3a360e01b81526001600160a01b038316600482015260248101899052905f908290604490829084905af18015620019c55762002b29575b509062002a9587849360e096958451169062003353565b511693511691510151850b93823b1562000b0457604051639c45c34b60e01b81526001600160a01b0390911660048201526001600160401b03909116602482015260448101929092525f9290920b606482015290829082908183816084810103925af180156200052f5762002b11575b80808080808062002989565b62002b1d8291620026f7565b62000248578062002b05565b60e094939291995062002b3c90620026f7565b5f98929350909162002a9562002a7e565b8451608001516040516377e88bc960e11b81529084166001600160a01b031660048201526001600160401b03919091166024820152604490fd5b505f985050505050505050565b62002ba6859e9f809c95968462002806565b359062002bb58c858862002806565b359189831683036200053e578a62002bd2938a8a51169262003520565b9490911562002bfb5762002beb8f93926001926200282b565b9b019e9d8f95949192956200292e565b9a9e9d5062002935565b6040519062002c148262002727565b5f60e08360608152606060208201528260408201528260608201528260808201528260a08201528260c08201520152565b6040519062002c54826200270b565b5f60a08362002c6262002c05565b81528260208201528260408201528260608201528260808201520152565b90600182811c9216801562002cb0575b602083101462002c9c57565b634e487b7160e01b5f52602260045260245ffd5b91607f169162002c90565b6001600160401b0381116200250157601f01601f191660200190565b3d1562002d06573d9062002ceb8262002cbb565b9162002cfb60405193846200277c565b82523d5f602084013e565b606090565b6040519062002d1a82620026db565b5f6080838281528260208201528260408201528260608201520152565b335f9081527f176c3a4e1b4d1d61213187c0dde7f83e2e2c933a339cb5403b4090bc2f0aa49a60205260409020547f880a9ba888678c7fe4e8c4f028c224f26ce12a3bed6e96025c61ef8a5db6312f9060ff161562002d935750565b6044906040519063e2517d3f60e01b82523360048301526024820152fd5b335f9081527fb7db2dd08fcb62d0c9e08c51941cae53c267786a0b75803fb7960902fc8ef97d602052604090205460ff161562002dea57565b60405163e2517d3f60e01b81523360048201525f6024820152604490fd5b335f9081527fcf7720576cdb70176b3017d893bcb89ba0f36ed0261d4350fd0180e938b3bacd60205260409020547f5300fde95a5e446527bf6aa7c91bd6661bef5398afc77061d9bc87efb80b7ef69060ff161562002d935750565b805f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b62680060205260405f20335f5260205260ff60405f2054161562002d935750565b620019139262002eec92602492604051926001600160a01b0360208501931683526001600160401b03809216604085015216606083015260608252620018f48262002744565b733333333333333333333333333333333333333333803b156200053e576040516317938e1360e01b815260206004820152915f91839182908490829062002f38906024830190620026b4565b03925af18015620019c55762002f4b5750565b62002f5690620026f7565b565b9062002f6362002c45565b506001600160a01b038092165f5260205f8051602062005bb78339815191528152604092835f2084519062002f98826200270b565b85519562002fa68762002727565b80518254905f8162002fb88462002c80565b918282526001948a86821691825f14620031e05750506001146200319f575b62002fe5925003826200277c565b88528083019082519087829384928282549586815201915f52825f20945f5b8160038201106200315a57620030469654928583831062003140575b83831062003124575b83831062003107575b505010620030f8575b50905003826200277c565b858801526006600283015492856001600160401b0394858116858c0152841c1660608a015260ff60038201549760808b019a888a168c52828a60a01c1660a082015260c0810199838160a81c168b5260b01c5f0b60e0820152875260048301549086821690880152841c161515838601526005810154606086015201549082821660808501521c1660a082015293511615159081620030e9575b50156200053e57565b60ff9150511615155f620030e0565b60c01c81520188905f6200303b565b9091946001600160401b038560801c1681520193015f8562003032565b8192956001600160401b03868d1c168152019401908562003029565b8192956001600160401b0386168152019401908562003020565b86546001600160401b038082168652818b1c811696860196909652608081811c9096168a86015260c01c6060850152958201958795508c949093019260040162003004565b5050845f528188805f20855f915b858310620031c657505062002fe5935082010162002fd7565b8091929450548385880101520191018990858593620031ad565b60ff19168482015262002fe594151560051b840101915062002fd79050565b60405160208101913060601b83526bffffffffffffffffffffffff199060601b16603482015260288152606081018181106001600160401b03821117620025015760405251902090565b519081151582036200053e57565b5f80916040516001600160a01b036020820192168252602081526200327c8162002760565b51906108105afa6200328d62002cd7565b9015620032d6576020818051810103126200053e5760405190602082018281106001600160401b038211176200250157604052620032ce9060200162003249565b809152151590565b6040516313dd7ccd60e31b8152600490fd5b620032f79062000c4362002d0b565b906001600160a01b036001604051936200331185620026db565b805463ffffffff90818116875260ff8160201c1615156020880152818160281c16604088015260481c1660608601520154169182608082015291156200053e57565b60405163a9059cbb60e01b602082019081526001600160a01b039384166024830152604480830195909552938152620033ea9390925f92839291906200339b6064876200277c565b16937f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65646020604051620033ce8162002760565b8181520152519082855af1620033e362002cd7565b91620051de565b805190811591821562003402575b5050156200053e57565b81925090602091810103126200053e57602062003420910162003249565b5f80620033f8565b51906001600160401b03821682036200053e57565b604080516001600160a01b0392909216602083019081526001600160401b039390931682820152808252915f918291906200347a6060826200277c565b51906108015afa6200348b62002cd7565b9015620034f5576060818051810103126200053e5781516001600160401b0392606082018481118382101762002501578152620034ee6060620034d16020860162003428565b94858552620034e284820162003428565b60208601520162003428565b9101521690565b8151639d2c8fcb60e01b8152600490fd5b6001600160401b03918216908216039190821162001a0e57565b949192909394805f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0360205260405f20906001600160a01b039485835416958615620037ab57600284019788549860ff8a60481c166200379957600186015483608086510151168482160362003780576001600160401b039060a01c166001600160401b038b1660ff8c60401c165f146200371f575080926001600160401b03811682115f146200371657620035d79162003506565b995b620035e58b8d6200282b565b9b6001600160401b038d166001600160401b0385161162003704575069010000000000000000009069ff00000000000000000019161790556200362b8160019b62003506565b976001600160401b036020846200364b8d60e0895101515f0b9062003829565b50991695015116843b156200053e5760405163a703334f60e01b81526001600160401b0391821660048201526001600160a01b0392909216602483015282166044820152925f908490606490829084905af1918215620019c5577f53b9d5645f8b7ccd861ebd6036860fd21716451d1f238cb3720f12f3c49b0c4393604093620036f2575b5060018187541696015416956001600160401b038351921682526020820152a4565b620036fd90620026f7565b5f620036d0565b5f9c508c9b5099505050505050505050565b50505f620035d7565b9092809b93806001600160401b038316105f1462003773576200374f91620037479162003506565b809c6200282b565b816001600160401b038216115f146200376b57505b91620035d9565b905062003764565b50506200374f5f62003747565b60405163358d72d160e01b815260048101879052602490fd5b60405163f7348a7960e01b8152600490fd5b604051631a40316d60e01b8152600490fd5b60ff16604d811162001a0e57600a0a90565b9190820391821162001a0e57565b8115620037e8570490565b634e487b7160e01b5f52601260045260245ffd5b90620f42409182810292818404149015171562001a0e57565b8181029291811591840414171562001a0e57565b9190805f0b80155f14620038475750506001600160401b0382169190565b5f81131562003877575062003863620038739160ff16620037bd565b6001600160401b03841662003815565b9190565b9050607f19811462001a0e5762003893905f0360ff16620037bd565b91620038aa836001600160401b038093166200287c565b5f19810190811162001a0e57620038c684620038ce92620037dd565b938462003815565b1690565b6200390a6200390392936001600160401b0393836001600160a01b03620038fc8780971662002847565b166200343d565b946200282b565b169116101590565b90815f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800806020526001600160a01b0360405f20921691825f5260205260ff60405f205416155f14620039aa57825f5260205260405f20815f5260205260405f20600160ff1982541617905533917f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d5f80a4600190565b5050505f90565b90815f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800806020526001600160a01b0360405f20921691825f5260205260ff60405f2054165f14620039aa57825f5260205260405f20815f5260205260405f2060ff19815416905533917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b5f80a4600190565b9062003a5e6001600160a01b0360408401511662003257565b1562004618576001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000165f525f8051602062005bb783398151915260205260405f20906040519162003ab6836200270b565b60405162003ac48162002727565b6040518254815f62003ad68362002c80565b8083529260018116908115620045f75750600114620045b2575b62003afe925003826200277c565b8152600182016040519081602082549182815201915f5260205f20905f915b81600384011062004570579360069593819362003b67936001600160401b039997549181811062001e5e5781811062001e475781811062001e30571062001e21575003826200277c565b60208201526001600160a01b036002830154858116604084015260401c16606082015260038201546001600160a01b038116608083015260ff8160a01c1660a083015260ff8160a81c1660c083015260b01c5f0b60e0820152855260ff6004820154848116602088015260401c1615156040860152600581015460608601520154818116608085015260401c1660a08301526001600160a01b036060840151165f525f8051602062005bb783398151915260205260405f206040519062003c2e826200270b565b60405162003c3c8162002727565b6040518254815f62003c4e8362002c80565b80835292600181169081156200454f57506001146200450a575b62003c76925003826200277c565b8152600182016040519081602082549182815201915f5260205f20905f915b816003840110620044bd579360069593819362003cdf936001600160401b039997549181811062001e5e5781811062001e475781811062001e30571062001e21575003826200277c565b60208201526001600160a01b036002830154858116604084015260401c16606082015260038201546001600160a01b038116608083015260ff8160a01c1660a083015260ff8160a81c1660c083015260b01c5f0b60e0820152845260ff6004820154848116602087015260401c1615156040850152600581015460608501520154818116608084015260401c1660a082015262003d896001600160a01b03606086015116620032e8565b9184519062003dd662003dbb62003dac60a089015160808a01511515956200287c565b60e0885101515f0b9062004da2565b905060ff60c0885101511660ff60c087510151169162005165565b91156200448c5781925b5f8063ffffffff875116604051602081019182526020815262003e038162002760565b51906108085afa9062003e1562002cd7565b91156200447a576020828051810103126200053e576001600160401b0362003e44602062003e6d940162003428565b60208801519116919015620044665760ff60a062003e669251015116620037bd565b9062003815565b602085015115620044175763ffffffff62003e8e8160608801511662005026565b16620f4240818101811162001a0e576001600160401b039262003e6662003ec792620f42409463ffffffff60408c01511691016200287c565b0416906080870151908115155f146200440e5750905b602085015115620043c5576305f5e1008110156200437e57505f5b62003f2462003f0b60a0890151620037fc565b62003f1d895160a08b0151906200287c565b906200287c565b5f1981019190821162001a0e5762003f1d62003f539262003f4c8a5160a08c0151906200287c565b90620037dd565b606482028281046064148315171562001a0e578111620042df57505062003f84855160e0865101515f0b9062004da2565b92909562003faf6001600160401b03602088015116856001600160401b0360a08a01511691620038d2565b156200423b5791608093917f550576b2f9e0ac12dfd5dd2d5743b5b7f11f34302b5f6bec6ad60db81bd6a91860a0856001600160401b036001600160a01b036040819b990151168a606084015116908a8401511515916040519162004014836200270b565b82526020820152828816604082015282871660608201528a81019182528481015f815262004118602086019384515f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b03602052856060600260405f206001600160a01b0388511673ffffffffffffffffffffffffffffffffffffffff19825416178155600181016001600160a01b0360208a0151168154907bffffffffffffffff000000000000000000000000000000000000000060408c015160a01b169163ffffffff60e01b161717905501950151168619855416178455511515839068ff0000000000000000825491151560401b169068ff00000000000000001916179055565b51151569ff00000000000000000082549160481b169069ff00000000000000000019161790555194818b60408501511697818d6060870151169a87875197015160405197885260208801521660408601521660608401521688820152a4015116620041ae83826001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001662003353565b60e06001600160401b03602084015116925101515f0b90803b156200053e57604051639c45c34b60e01b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001660048201526001600160401b03909316602484015260448301939093525f90810b6064830152909182908183816084810162002f38565b94505050905062002f56925060208201516001600160401b03604051921682527f8d61555dcf69ba745e97fdea78e2652d9cdd464ca521faad482fe796f6d5841c60206001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001693a36001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016606082015262004e31565b9350949350505060208201516001600160a01b0360608401511691606481019081811162001a0e5760630190811162001a0e5760407fc8f90125c6a36c77a571201afc10310420481ab4895fadabb596d0ba71c22e3e9162002f569660648351920482526020820152a36001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000166060820152620046c4565b6001600160401b036305f5e0ff1982011162001a0e57620043be620043b86001600160401b036305f5e100936305f5e0ff190116620037fc565b6200286a565b0462003ef8565b6305f5e100811115620043da57505f62003ef8565b6001600160401b03816305f5e100031162001a0e57620043be620043b86001600160401b036305f5e10093840316620037fc565b90509062003edd565b63ffffffff6200442d8160608801511662005026565b16620f4240808281031162001a0e576001600160401b039262003e6662003ec792620f42409463ffffffff60408c0151169103620037cf565b5062003e6660ff60a08951015116620037bd565b604051635cffc5fb60e11b8152600490fd5b6127108181031162001a0e576001600160401b03612710620044b383820383861662003815565b0416919262003de0565b926001608060049286546001600160401b03811682526001600160401b038160401c1660208301526001600160401b0381841c16604083015260c01c606082015201940192019162003c95565b5090845f5260205f20905f915b8183106200453257505090602062003c769282010162003c68565b602091935080600191548385880101520191019091839262004517565b6020925062003c7694915060ff191682840152151560051b82010162003c68565b9260016004918554906001600160401b03918281168252828160401c16602083015260809281841c16604083015260c01c606082015201940192019162003b1d565b5090845f5260205f20905f915b818310620045da57505090602062003afe9282010162003af0565b6020919350806001915483858801015201910190918392620045bf565b6020925062003afe94915060ff191682840152151560051b82010162003af0565b506080810151909190156200464c5760246001600160a01b03604084015116604051906320a2097d60e11b82526004820152fd5b9080602062002f569201517f31604f5790fbed7a37471cea010a8f08483441a6e1235ac48bb59337439a630d60206001600160a01b03604085015116604051908152a26001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016606082015262004e31565b6060906060810151905f6001600160a01b0380931692835f526020945f8051602062005bb78339815191528652604091825f2096835162004705816200270b565b845193620047138562002727565b85518a5491905f81620047268562002c80565b918282526001958887821691825f1462004d6b57505060011462004d2a575b62004756925094939403826200277c565b8652808b019184885193898586948484549283815201935f52845f20965f915b83600384011062004cde575085620047b998549484841062004cc4575b84841062004ca7575b5083831062004c8a575b50501062004c7b5750905003826200277c565b82850152600289015495836001600160401b039788811688880152871c16606086015260038a0154946080958581168783015260ff8160a01c1660a083015260ff8160a81c1660c083015260b01c5f0b60e08201528252600660048b01549a60ff8585019c8d8b82169052891c1615158885015260058101546060850152015490878216868401528760a0840192881c168252868901976200485e868a511662003257565b1562004c0d57899a9b5f9a98999a508c612710620048958b519b8b6200488b60a083019e8f51906200287c565b9101519062003815565b04988a5199808b1162004c04575b508d8a8062004b2e575b620048c2620048d191620048e493516200287c565b60e08a5101515f0b9062004da2565b91909782878088511692511691620038d2565b1562004ae65750908c9695949392918a620049f9575b509260e0896200498f94620049c59b9997947fb021c853215aadb12b6fa8afa7b3158201517d9abf7f756cdbb67bd66abc5a1c9f9e9d9b99976200496e906001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0460205260405f2090565b6200497b8d82546200287c565b9055511696511694510151900b9062004da2565b9190928d84620049ca575b5050505050880151965116965192519351938493846040919493926060820195825260208201520152565b0390a4565b620049ee94620049e89188620049e08562002847565b169062003353565b62002ea6565b5f8080808d6200499a565b919293949596505087807f00000000000000000000000000000000000000000000000000000000000000001691875101511690803b156200053e578b5163f3fef3a360e01b81526001600160a01b03929092166004830152602482018a90525f908290604490829084905af1801562004adc578c948f91998098956200498f957fb021c853215aadb12b6fa8afa7b3158201517d9abf7f756cdbb67bd66abc5a1c9f9e9d9b9995620049c59d62004ac0575b509497995094509950939597999a9b620048fa565b60e0945062004acf90620026f7565b6200496e5f945062004aab565b8b513d5f823e3d90fd5b9a50505050955050505093957f8d61555dcf69ba745e97fdea78e2652d9cdd464ca521faad482fe796f6d5841c955084915062004b238162004e31565b0151945191168152a3565b62004b45908a8301518c858c51015116906200503f565b1562004b53575b8a620048ad565b509192939495968881999a50602492508389510151168c51928380926370a0823160e01b82528d7f00000000000000000000000000000000000000000000000000000000000000001660048301525afa90811562004adc57918e918e9a999897969594935f9162004bc7575b509962004b4c565b92505098508681813d831162004bfc575b62004be481836200277c565b810103126200053e57518c988e91620048d162004bbf565b503d62004bd8565b99505f620048a3565b505050509296509450839293015115155f1462004c405750505191516320a2097d60e11b81529116600482015260249150fd5b62002f56948294827f31604f5790fbed7a37471cea010a8f08483441a6e1235ac48bb59337439a630d9401519451169051908152a262004e31565b60c01c81520185905f6200303b565b9091946001600160401b038560801c1681520193015f85620047a6565b956001600160401b03868495981c16815201940190858e6200479c565b6001600160401b0386168752958101959282019262004793565b95509360049296509284919388546001600160401b039283821683528d8483831c169084015260809382851c169083015260c01c8782015201960191019386948c9296938a9562004776565b50508c5f528186805f20865f915b85831062004d5157505062004756935082010162004745565b809192945054838588010152019101879086859362004d38565b60ff1916848201526200475694151560051b8401019150620047459050565b81811062004d96575050565b5f815560010162004d8a565b9190805f0b9081155f1462004dc05750506001600160401b03821690565b5f82131562004e035762004dd8915060ff16620037bd565b918215620037e857620038ce62004dfb82856001600160401b03940690620037cf565b9384620037dd565b505f0380805f0b0362001a0e57620038ce62004e2a6001600160401b039260ff16620037bd565b8462003815565b805161271062004e5862004e4c60a08501938451906200287c565b60808501519062003815565b0481518181115f146200501d5750905b8190602084019182519162004e8f60608701956001600160a01b039485885116906200503f565b1562005014575b8162004f61575b620049c5837f4755f239bb1b047245415cb917deced72a3ca8baebcef109c396ff332ea6f50f9462004ed1858a516200287c565b9662004eeb828a51169860408c0199848b51169062003353565b62004f29828a51166001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0460205260405f2090565b62004f368782546200287c565b9055519651169651169651915192604051938493846040919493926060820195825260208201520152565b84518316927f00000000000000000000000000000000000000000000000000000000000000008116803b156200053e5760405163f3fef3a360e01b81526001600160a01b03959095166004860152602485018490525f908590604490829084905af1908115620019c5577f4755f239bb1b047245415cb917deced72a3ca8baebcef109c396ff332ea6f50f94620049c59262005002575b5093505062004e9d565b6200500d90620026f7565b5f62004ff8565b5f915062004e96565b90509062004e68565b90606463ffffffff8093160291821691820362001a0e57565b6040516370a0823160e01b81527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0390811660048301529092169392909190602082602481885afa918215620019c5575f92620050f2575b5080821094851595620050b2575b50505050565b7f5180f0ad9e9bd2296de2ee38c85d11c56613fa73f8ee66792f26ac318f1274749260609260405192835260208301526040820152a25f808080620050ac565b9091506020813d60201162005123575b8162005111602093836200277c565b810103126200053e5751905f6200509e565b3d915062005102565b51906001600160a01b03821682036200053e57565b519060ff821682036200053e57565b9060ff8091169116039060ff821162001a0e57565b919060ff821660ff82168181145f1462005180575050505090565b929391921015620051b857620038ce9192620051b0620051aa6001600160401b0395869362005150565b620037bd565b911662003815565b620038ce91620051d6620051aa6001600160401b0395869362005150565b9116620037dd565b9015620051fb57815115620051f1575090565b3b156200053e5790565b5080519081156200053e57602001fdfe60a080604052346100455733608052610961908161004a8239608051818181608b015281816101ac0152818161038e015281816104a20152818161051b015261069c0152f35b5f80fdfe60406080815260049081361015610014575f80fd5b5f915f3560e01c908163435354d3146104d757816390a0827b1461047b5781639c45c34b14610469578163a703334f1461033c578163de1a324a146100d457508063e94b77c1146100b35763eba61c0e1461006d575f80fd5b346100af57816003193601126100af57602090516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b5080fd5b82346100d1576100ce6100c5366105b5565b9291909161068d565b80f35b80fd5b91905034610338576101009136600319018381126103345760a01361033057815167ffffffffffffffff939060a081018581118282101761031d5784526101196105a2565b91828252602435918215158303610319576020810192835263ffffffff9360443585811681036102ce578783015260643585811681036102ce5760608301526084356001600160a01b03928382168203610315576080015260a4358881168091036102ce5760c435918983168093036102ce5760e435936fffffffffffffffffffffffffffffffff85168095036102ce577f000000000000000000000000000000000000000000000000000000000000000016330361031557866101dd9116610663565b9451151581156103055782156102f5578a6102e557885196602088019616865288870152606086015260808501525f60a0850152600260c085015260e084015260e08352820194828610908611176102d25761026891602491868652630100000160e01b6101208301526102598251809261012485019061079c565b81010383810186520184610641565b73333333333333333333333333333333333333333392833b156102ce576102a5935f928385518097819582946317938e1360e01b845283016107bd565b03925af19081156102c557506102b9575080f35b6102c391506105fd565b005b513d5f823e3d90fd5b5f80fd5b604183634e487b7160e01b5f525260245ffd5b88516376d4929560e11b81528890fd5b88516313c0a8df60e01b81528890fd5b885163017461b760e71b81528890fd5b8a80fd5b8780fd5b604184634e487b7160e01b5f525260245ffd5b8380fd5b8480fd5b8280fd5b919050346103385760603660031901126103385781359067ffffffffffffffff928383168093036102ce576024356001600160a01b03948582168092036102ce576044359081168091036102ce5786957f000000000000000000000000000000000000000000000000000000000000000016330361046557610259946103fc9260249286519260208401928352878401526060830152606082526103df82610625565b85519687926280000360e11b60208501525180928585019061079c565b73333333333333333333333333333333333333333392833b15610334576104399385928385518097819582946317938e1360e01b845283016107bd565b03925af190811561045c575061044c5750f35b610455906105fd565b6100d15780f35b513d84823e3d90fd5b8580fd5b83346100d1576100ce6100c5366105b5565b91905034610338573660031901126100af57356001600160a01b038082168092036102ce577f00000000000000000000000000000000000000000000000000000000000000001633036100af576100ce906024359033906107e9565b919050346102ce57806003193601126102ce576104f26105a2565b91602435926fffffffffffffffffffffffffffffffff84168094036102ce576001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001633036102ce5761054a90610663565b9282519063ffffffff602083019516855283820152828152606081019381851067ffffffffffffffff8611176102d25761026891602491868652630100000b60e01b608083015261025982518092608485019061079c565b6004359063ffffffff821682036102ce57565b60809060031901126102ce576004356001600160a01b03811681036102ce579060243567ffffffffffffffff811681036102ce579060443590606435805f0b81036102ce5790565b67ffffffffffffffff811161061157604052565b634e487b7160e01b5f52604160045260245ffd5b6080810190811067ffffffffffffffff82111761061157604052565b90601f8019910116810190811067ffffffffffffffff82111761061157604052565b9061271063ffffffff8093160191821161067957565b634e487b7160e01b5f52601160045260245ffd5b9092916001600160a01b0392837f00000000000000000000000000000000000000000000000000000000000000001633036102ce57805f0b80155f1461071c575050915b826106dd575b50505050565b67ffffffffffffffff732000000000000000000000000000000000000000941684018094116106795780610713941691166107e9565b5f8080806106d7565b5f81131561075c57506107319060ff166108f1565b8015610748578106810390811161067957916106d1565b634e487b7160e01b5f52601260045260245ffd5b90505f9391930380805f0b03610679576107789060ff166108f1565b9280938181810204149015176106d157634e487b7160e01b5f52601160045260245ffd5b5f5b8381106107ad5750505f910152565b818101518382015260200161079e565b604091602082526107dd815180928160208601526020868601910161079c565b601f01601f1916010190565b9190604051906020938483019363a9059cbb60e01b85526001600160a01b03809316602485015260448401526044835261082283610625565b169160405190604082019067ffffffffffffffff9383831085841117610611577f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564875f9586956040528181520152519082865af13d156108e4573d918211610611576108ac926040519261089f86601f19601f8401160185610641565b83523d5f8685013e610902565b80519182159182156108c4575b50509050156102ce57565b8092508193810103126102ce57015180151581036102ce57805f806108b9565b6108ac9260609250610902565b60ff16604d811161067957600a0a90565b901561091c57815115610913575090565b3b156102ce5790565b5080519081156102ce57602001fdfea2646970667358221220c909e7dbe656d7c973709e34eb673837a89b1ae88012e9262063134290e6f21f64736f6c634300081800336c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b00a2646970667358221220d7009235facd2afacf7dbd784aa03c97dd6444be46f4a33aa486147317b81a7764736f6c63430008180033000000000000000000000000002e76dc036a1eff1488ee5435ee66c6abf32674000000000000000000000000b88339cb7199b77e23db6e890353e22632ba630f" + } + ], + "isFixedGasLimit": false + }, + { + "hash": "0x2d31d35b8f8ed4e1f7f84b15c97c9c0e95798883948b4bcf349229e1ab62d82d", + "transactionType": "CALL", + "contractName": "DonationBox", + "contractAddress": "0x002e76dc036a1eff1488ee5435ee66c6abf32674", + "function": "transferOwnership(address)", + "arguments": ["0x83e245941BefbDe29682dF068Bcda006A804eb0C"], + "transaction": { + "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "to": "0x002e76dc036a1eff1488ee5435ee66c6abf32674", + "gas": "0x9922", + "value": "0x0", + "input": "0xf2fde38b00000000000000000000000083e245941befbde29682df068bcda006a804eb0c", + "nonce": "0x245", + "chainId": "0x3e7" + }, + "additionalContracts": [], + "isFixedGasLimit": false + } + ], + "receipts": [ + { + "status": "0x1", + "cumulativeGasUsed": "0xc11a2", + "logs": [ + { + "address": "0x002e76dc036a1eff1488ee5435ee66c6abf32674", + "topics": [ + "0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000009a8f92a830a5cb89a3816e3d267cb7791c16b04d" + ], + "data": "0x", + "blockHash": "0xb057afed72386c4900b363bee6966fe39ca786f0b43f85943d107bc040a365d7", + "blockNumber": "0x13d47a9", + "blockTimestamp": "0x692f466c", + "transactionHash": "0x16b942e66df79bdf146ba03b8232604d966ae571282c4229eb3d0e9f8fcebf8d", + "transactionIndex": "0x3", + "logIndex": "0x22", + "removed": false + } + ], + "logsBloom": "0x00000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000020000000000000000000800000000000000000000000000040000400000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000880000000000000000000000000000000000000000008020000001000000000000000000000000000000000000000000000000000000000000", + "type": "0x2", + "transactionHash": "0x16b942e66df79bdf146ba03b8232604d966ae571282c4229eb3d0e9f8fcebf8d", + "transactionIndex": "0x3", + "blockHash": "0xb057afed72386c4900b363bee6966fe39ca786f0b43f85943d107bc040a365d7", + "blockNumber": "0x13d47a9", + "gasUsed": "0x3f377", + "effectiveGasPrice": "0x6052340", + "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "to": null, + "contractAddress": "0x002e76dc036a1eff1488ee5435ee66c6abf32674" + }, + { + "status": "0x1", + "cumulativeGasUsed": "0x79e7fa", + "logs": [ + { + "address": "0x83e245941befbde29682df068bcda006a804eb0c", + "topics": [ + "0xbd79b86ffe0ab8e8776151514217cd7cacd52c909f66475c3af44e129f0b00ff", + "0x5300fde95a5e446527bf6aa7c91bd6661bef5398afc77061d9bc87efb80b7ef6", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000" + ], + "data": "0x", + "blockHash": "0xb057afed72386c4900b363bee6966fe39ca786f0b43f85943d107bc040a365d7", + "blockNumber": "0x13d47a9", + "blockTimestamp": "0x692f466c", + "transactionHash": "0xb08e0d23618a1447781b9071792c660bfd76db006b105822ea41d65e0ddc2802", + "transactionIndex": "0x4", + "logIndex": "0x23", + "removed": false + }, + { + "address": "0x83e245941befbde29682df068bcda006a804eb0c", + "topics": [ + "0xbd79b86ffe0ab8e8776151514217cd7cacd52c909f66475c3af44e129f0b00ff", + "0x880a9ba888678c7fe4e8c4f028c224f26ce12a3bed6e96025c61ef8a5db6312f", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000" + ], + "data": "0x", + "blockHash": "0xb057afed72386c4900b363bee6966fe39ca786f0b43f85943d107bc040a365d7", + "blockNumber": "0x13d47a9", + "blockTimestamp": "0x692f466c", + "transactionHash": "0xb08e0d23618a1447781b9071792c660bfd76db006b105822ea41d65e0ddc2802", + "transactionIndex": "0x4", + "logIndex": "0x24", + "removed": false + }, + { + "address": "0x83e245941befbde29682df068bcda006a804eb0c", + "topics": [ + "0x2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000009a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "0x0000000000000000000000009a8f92a830a5cb89a3816e3d267cb7791c16b04d" + ], + "data": "0x", + "blockHash": "0xb057afed72386c4900b363bee6966fe39ca786f0b43f85943d107bc040a365d7", + "blockNumber": "0x13d47a9", + "blockTimestamp": "0x692f466c", + "transactionHash": "0xb08e0d23618a1447781b9071792c660bfd76db006b105822ea41d65e0ddc2802", + "transactionIndex": "0x4", + "logIndex": "0x25", + "removed": false + } + ], + "logsBloom": "0x00000004000000000800000000000000080000000000000000000080000000000100000000000000000000000000000000000000001000000000000000000000000000000000001000000000000000000000000000000000000020000000000000000000020000400000000000000800000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000800000000000000000000000000000000100000000000020100001000000000000000000000000000100000004000000000000000001000000", + "type": "0x2", + "transactionHash": "0xb08e0d23618a1447781b9071792c660bfd76db006b105822ea41d65e0ddc2802", + "transactionIndex": "0x4", + "blockHash": "0xb057afed72386c4900b363bee6966fe39ca786f0b43f85943d107bc040a365d7", + "blockNumber": "0x13d47a9", + "gasUsed": "0x6dd658", + "effectiveGasPrice": "0x6052340", + "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "to": null, + "contractAddress": "0x83e245941befbde29682df068bcda006a804eb0c" + }, + { + "status": "0x1", + "cumulativeGasUsed": "0x7a56d9", + "logs": [ + { + "address": "0x002e76dc036a1eff1488ee5435ee66c6abf32674", + "topics": [ + "0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0", + "0x0000000000000000000000009a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "0x00000000000000000000000083e245941befbde29682df068bcda006a804eb0c" + ], + "data": "0x", + "blockHash": "0xb057afed72386c4900b363bee6966fe39ca786f0b43f85943d107bc040a365d7", + "blockNumber": "0x13d47a9", + "blockTimestamp": "0x692f466c", + "transactionHash": "0x2d31d35b8f8ed4e1f7f84b15c97c9c0e95798883948b4bcf349229e1ab62d82d", + "transactionIndex": "0x5", + "logIndex": "0x26", + "removed": false + } + ], + "logsBloom": "0x00000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000001000000000000100000000000000000000000000000000000000000000000000000000000000000000000040000400000000000000000000000200000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000880000000000000000000000000000000000000000008000000001000000000000000000000000000000000000000000000000000000000000", + "type": "0x2", + "transactionHash": "0x2d31d35b8f8ed4e1f7f84b15c97c9c0e95798883948b4bcf349229e1ab62d82d", + "transactionIndex": "0x5", + "blockHash": "0xb057afed72386c4900b363bee6966fe39ca786f0b43f85943d107bc040a365d7", + "blockNumber": "0x13d47a9", + "gasUsed": "0x6edf", + "effectiveGasPrice": "0x6052340", + "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "to": "0x002e76dc036a1eff1488ee5435ee66c6abf32674", + "contractAddress": null + } + ], + "libraries": [], + "pending": [], + "returns": {}, + "timestamp": 1764705991008, + "chain": 999, + "commit": "4b103fe" +} diff --git a/broadcast/CreateSponsoredDeposit.s.sol/1/run-latest.json b/broadcast/CreateSponsoredDeposit.s.sol/1/run-latest.json new file mode 100644 index 000000000..6ad9a3995 --- /dev/null +++ b/broadcast/CreateSponsoredDeposit.s.sol/1/run-latest.json @@ -0,0 +1,220 @@ +{ + "transactions": [ + { + "hash": "0xad1f22fd9e3cec893ebf186688666aeeda5e7eaf0196a589ca95d22de61e8680", + "transactionType": "CALL", + "contractName": null, + "contractAddress": "0xdac17f958d2ee523a2206206994597c13d831ec7", + "function": "approve(address,uint256)", + "arguments": ["0xE35d1205a523B699785967FFfe99b72059B46707", "1000000"], + "transaction": { + "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "to": "0xdac17f958d2ee523a2206206994597c13d831ec7", + "gas": "0x105f1", + "value": "0x0", + "input": "0x095ea7b3000000000000000000000000e35d1205a523b699785967fffe99b72059b4670700000000000000000000000000000000000000000000000000000000000f4240", + "nonce": "0x2633", + "chainId": "0x1" + }, + "additionalContracts": [], + "isFixedGasLimit": false + }, + { + "hash": "0x50fb703efb0080e0cceaeda0e57c859005f8842eb89d8e4a52e98c9ecfba80b0", + "transactionType": "CALL", + "contractName": null, + "contractAddress": "0xe35d1205a523b699785967fffe99b72059b46707", + "function": "deposit(((uint32,uint32,bytes32,uint256,bytes32,uint256,uint256,bytes32,bytes32,uint256,uint256,uint8,bytes),(address,uint256)),bytes)", + "arguments": [ + "((30101, 30367, 0x000000000000000000000000d9f40794367a2ecb0b409ca8dbc55345c0db6e9f, 1000000, 0x00000000000000000000000000000000000000000000000000000000690d4543, 1762480979, 100, 0x0000000000000000000000009a8f92a830a5cb89a3816e3d267cb7791c16b04d, 0x000000000000000000000000b8ce59fc3717ada4c02eadf9682a9e934f625ebb, 200000, 300000, 0, 0x), (0x9A8f92a830A5cB89a3816e3D267CB7791c16b04D, 200))", + "0x012c2ad356c9063c93c00f6b9d2a68be9b88dbd21ff07f4354026a5e9eb576da07ec271d526830b21ab33d20c070958e0acfb192e02ea816e0e9e1e13a74ac771c" + ], + "transaction": { + "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "to": "0xe35d1205a523b699785967fffe99b72059b46707", + "gas": "0x96208", + "value": "0xc35dde03454", + "input": "0xfcc5b1e30000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000026000000000000000000000000000000000000000000000000000000000000000600000000000000000000000009a8f92a830a5cb89a3816e3d267cb7791c16b04d00000000000000000000000000000000000000000000000000000000000000c80000000000000000000000000000000000000000000000000000000000007595000000000000000000000000000000000000000000000000000000000000769f000000000000000000000000d9f40794367a2ecb0b409ca8dbc55345c0db6e9f00000000000000000000000000000000000000000000000000000000000f424000000000000000000000000000000000000000000000000000000000690d454300000000000000000000000000000000000000000000000000000000690d535300000000000000000000000000000000000000000000000000000000000000640000000000000000000000009a8f92a830a5cb89a3816e3d267cb7791c16b04d000000000000000000000000b8ce59fc3717ada4c02eadf9682a9e934f625ebb0000000000000000000000000000000000000000000000000000000000030d4000000000000000000000000000000000000000000000000000000000000493e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041012c2ad356c9063c93c00f6b9d2a68be9b88dbd21ff07f4354026a5e9eb576da07ec271d526830b21ab33d20c070958e0acfb192e02ea816e0e9e1e13a74ac771c00000000000000000000000000000000000000000000000000000000000000", + "nonce": "0x2634", + "chainId": "0x1" + }, + "additionalContracts": [], + "isFixedGasLimit": false + } + ], + "receipts": [ + { + "status": "0x1", + "cumulativeGasUsed": "0x121af85", + "logs": [ + { + "address": "0xdac17f958d2ee523a2206206994597c13d831ec7", + "topics": [ + "0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925", + "0x0000000000000000000000009a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "0x000000000000000000000000e35d1205a523b699785967fffe99b72059b46707" + ], + "data": "0x00000000000000000000000000000000000000000000000000000000000f4240", + "blockHash": "0x360d2478a6a3441da84a94ce952c72a9dce4679c14acafe3e57e3a1eb727dd29", + "blockNumber": "0x16a4dfa", + "blockTimestamp": "0x690d455b", + "transactionHash": "0xad1f22fd9e3cec893ebf186688666aeeda5e7eaf0196a589ca95d22de61e8680", + "transactionIndex": "0x8e", + "logIndex": "0x247", + "removed": false + } + ], + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000200000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000008000000000000000000000040000000000000000000000000000000000000000000000000000000000000000100000020000000000000000000080000000000000000000100000000000000000000000000000000000800000000000000000000000000000000000000000000000000011000000000000000000000000000000000000000000000000000000000000", + "type": "0x2", + "transactionHash": "0xad1f22fd9e3cec893ebf186688666aeeda5e7eaf0196a589ca95d22de61e8680", + "transactionIndex": "0x8e", + "blockHash": "0x360d2478a6a3441da84a94ce952c72a9dce4679c14acafe3e57e3a1eb727dd29", + "blockNumber": "0x16a4dfa", + "gasUsed": "0xbda5", + "effectiveGasPrice": "0xc9a69b7", + "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "to": "0xdac17f958d2ee523a2206206994597c13d831ec7", + "contractAddress": null + }, + { + "status": "0x1", + "cumulativeGasUsed": "0x1287a8d", + "logs": [ + { + "address": "0xdac17f958d2ee523a2206206994597c13d831ec7", + "topics": [ + "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", + "0x0000000000000000000000009a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "0x000000000000000000000000e35d1205a523b699785967fffe99b72059b46707" + ], + "data": "0x00000000000000000000000000000000000000000000000000000000000f4240", + "blockHash": "0x360d2478a6a3441da84a94ce952c72a9dce4679c14acafe3e57e3a1eb727dd29", + "blockNumber": "0x16a4dfa", + "blockTimestamp": "0x690d455b", + "transactionHash": "0x50fb703efb0080e0cceaeda0e57c859005f8842eb89d8e4a52e98c9ecfba80b0", + "transactionIndex": "0x8f", + "logIndex": "0x248", + "removed": false + }, + { + "address": "0xdac17f958d2ee523a2206206994597c13d831ec7", + "topics": [ + "0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925", + "0x000000000000000000000000e35d1205a523b699785967fffe99b72059b46707", + "0x0000000000000000000000006c96de32cea08842dcc4058c14d3aaad7fa41dee" + ], + "data": "0x00000000000000000000000000000000000000000000000000000000000f4240", + "blockHash": "0x360d2478a6a3441da84a94ce952c72a9dce4679c14acafe3e57e3a1eb727dd29", + "blockNumber": "0x16a4dfa", + "blockTimestamp": "0x690d455b", + "transactionHash": "0x50fb703efb0080e0cceaeda0e57c859005f8842eb89d8e4a52e98c9ecfba80b0", + "transactionIndex": "0x8f", + "logIndex": "0x249", + "removed": false + }, + { + "address": "0xdac17f958d2ee523a2206206994597c13d831ec7", + "topics": [ + "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", + "0x000000000000000000000000e35d1205a523b699785967fffe99b72059b46707", + "0x0000000000000000000000006c96de32cea08842dcc4058c14d3aaad7fa41dee" + ], + "data": "0x00000000000000000000000000000000000000000000000000000000000f4240", + "blockHash": "0x360d2478a6a3441da84a94ce952c72a9dce4679c14acafe3e57e3a1eb727dd29", + "blockNumber": "0x16a4dfa", + "blockTimestamp": "0x690d455b", + "transactionHash": "0x50fb703efb0080e0cceaeda0e57c859005f8842eb89d8e4a52e98c9ecfba80b0", + "transactionIndex": "0x8f", + "logIndex": "0x24a", + "removed": false + }, + { + "address": "0xbb2ea70c9e858123480642cf96acbcce1372dce1", + "topics": ["0x61ed099e74a97a1d7f8bb0952a88ca8b7b8ebd00c126ea04671f92a81213318a"], + "data": "0x000000000000000000000000173272739bd7aa6e4e214714048a9fe69945305900000000000000000000000000000000000000000000000000000b05faae930f", + "blockHash": "0x360d2478a6a3441da84a94ce952c72a9dce4679c14acafe3e57e3a1eb727dd29", + "blockNumber": "0x16a4dfa", + "blockTimestamp": "0x690d455b", + "transactionHash": "0x50fb703efb0080e0cceaeda0e57c859005f8842eb89d8e4a52e98c9ecfba80b0", + "transactionIndex": "0x8f", + "logIndex": "0x24b", + "removed": false + }, + { + "address": "0xbb2ea70c9e858123480642cf96acbcce1372dce1", + "topics": ["0x07ea52d82345d6e838192107d8fd7123d9c2ec8e916cd0aad13fd2b60db24644"], + "data": "0x000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000020000000000000000000000003b0531eb02ab4ad72e7a531180beef9493a00dd2000000000000000000000000589dedbd617e0cbcb916a9223f4d1300c294236b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000009a30f3b4ca00000000000000000000000000000000000000000000000000000095b23dec7b", + "blockHash": "0x360d2478a6a3441da84a94ce952c72a9dce4679c14acafe3e57e3a1eb727dd29", + "blockNumber": "0x16a4dfa", + "blockTimestamp": "0x690d455b", + "transactionHash": "0x50fb703efb0080e0cceaeda0e57c859005f8842eb89d8e4a52e98c9ecfba80b0", + "transactionIndex": "0x8f", + "logIndex": "0x24c", + "removed": false + }, + { + "address": "0x1a44076050125825900e736c501f859c50fe728c", + "topics": ["0x1ab700d4ced0c005b164c0f789fd09fcbb0156d4c2041b8a3bfbcd961cd1567f"], + "data": "0x00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000260000000000000000000000000bb2ea70c9e858123480642cf96acbcce1372dce100000000000000000000000000000000000000000000000000000000000001d9010000000000001931000075950000000000000000000000006c96de32cea08842dcc4058c14d3aaad7fa41dee0000769f000000000000000000000000904861a24f30ec96ea7cfc3be9ea4b476d237e988776497f38712e1cf03b1a456ad0e75f00ddea17b867fc23745e1e212ab5395c000000000000000000000000d9f40794367a2ecb0b409ca8dbc55345c0db6e9f00000000000f4240000000000000000000000000e35d1205a523b699785967fffe99b72059b4670700000000000000000000000000000000000000000000000000000000690d454300000000000000000000000000000000000000000000000000000000690d5353000000000000000000000000000000000000000000000000000000000000006400000000000000000000000000000000000000000000000000000000000000c80000000000000000000000009a8f92a830a5cb89a3816e3d267cb7791c16b04d000000000000000000000000b8ce59fc3717ada4c02eadf9682a9e934f625ebb000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000056000301001101000000000000000000000000000186a0010013030000000000000000000000000000000186a00100110100000000000000000000000000030d40010013030000000000000000000000000000000493e000000000000000000000", + "blockHash": "0x360d2478a6a3441da84a94ce952c72a9dce4679c14acafe3e57e3a1eb727dd29", + "blockNumber": "0x16a4dfa", + "blockTimestamp": "0x690d455b", + "transactionHash": "0x50fb703efb0080e0cceaeda0e57c859005f8842eb89d8e4a52e98c9ecfba80b0", + "transactionIndex": "0x8f", + "logIndex": "0x24d", + "removed": false + }, + { + "address": "0x6c96de32cea08842dcc4058c14d3aaad7fa41dee", + "topics": [ + "0x85496b760a4b7f8d66384b9df21b381f5d1b1e79f229a47aaf4c232edc2fe59a", + "0x8776497f38712e1cf03b1a456ad0e75f00ddea17b867fc23745e1e212ab5395c", + "0x000000000000000000000000e35d1205a523b699785967fffe99b72059b46707" + ], + "data": "0x000000000000000000000000000000000000000000000000000000000000769f00000000000000000000000000000000000000000000000000000000000f424000000000000000000000000000000000000000000000000000000000000f4240", + "blockHash": "0x360d2478a6a3441da84a94ce952c72a9dce4679c14acafe3e57e3a1eb727dd29", + "blockNumber": "0x16a4dfa", + "blockTimestamp": "0x690d455b", + "transactionHash": "0x50fb703efb0080e0cceaeda0e57c859005f8842eb89d8e4a52e98c9ecfba80b0", + "transactionIndex": "0x8f", + "logIndex": "0x24e", + "removed": false + }, + { + "address": "0xe35d1205a523b699785967fffe99b72059b46707", + "topics": [ + "0x8fb515a2e89f5acfca1124e69e331c2cded0ca216b578ba531720f6841139dbf", + "0x00000000000000000000000000000000000000000000000000000000690d4543", + "0x0000000000000000000000009a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "0x0000000000000000000000009a8f92a830a5cb89a3816e3d267cb7791c16b04d" + ], + "data": "0x000000000000000000000000d9f40794367a2ecb0b409ca8dbc55345c0db6e9f00000000000000000000000000000000000000000000000000000000690d5353000000000000000000000000000000000000000000000000000000000000006400000000000000000000000000000000000000000000000000000000000000c8000000000000000000000000b8ce59fc3717ada4c02eadf9682a9e934f625ebb00000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000041012c2ad356c9063c93c00f6b9d2a68be9b88dbd21ff07f4354026a5e9eb576da07ec271d526830b21ab33d20c070958e0acfb192e02ea816e0e9e1e13a74ac771c00000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0x360d2478a6a3441da84a94ce952c72a9dce4679c14acafe3e57e3a1eb727dd29", + "blockNumber": "0x16a4dfa", + "blockTimestamp": "0x690d455b", + "transactionHash": "0x50fb703efb0080e0cceaeda0e57c859005f8842eb89d8e4a52e98c9ecfba80b0", + "transactionIndex": "0x8f", + "logIndex": "0x24f", + "removed": false + } + ], + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000001000000408000000000000000000000200010000000000000000000100000000200000000202000080100000000018000000004000200000004000000000000000000000000004000000008000000000000000008000000010000000000010040000000000000000008000001000080000400000000000000000000000000000100000020000008000000000100084000400001000000000500000000440000000800000000002000000800000000080000000000000000000001000000000000000000011040000000000000000000000002000000000018000000000020004000000", + "type": "0x2", + "transactionHash": "0x50fb703efb0080e0cceaeda0e57c859005f8842eb89d8e4a52e98c9ecfba80b0", + "transactionIndex": "0x8f", + "blockHash": "0x360d2478a6a3441da84a94ce952c72a9dce4679c14acafe3e57e3a1eb727dd29", + "blockNumber": "0x16a4dfa", + "gasUsed": "0x6cb08", + "effectiveGasPrice": "0xc9a69b7", + "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "to": "0xe35d1205a523b699785967fffe99b72059b46707", + "contractAddress": null + } + ], + "libraries": [], + "pending": [], + "returns": {}, + "timestamp": 1762477406440, + "chain": 1, + "commit": "d93c7b1f" +} diff --git a/broadcast/CreateSponsoredDeposit.s.sol/42161/run-latest.json b/broadcast/CreateSponsoredDeposit.s.sol/42161/run-latest.json new file mode 100644 index 000000000..fb382c58a --- /dev/null +++ b/broadcast/CreateSponsoredDeposit.s.sol/42161/run-latest.json @@ -0,0 +1,257 @@ +{ + "transactions": [ + { + "hash": "0x727ba90684db5bf8034bf6d8fd2aefae7856cb177e049905e3394606d8641cfe", + "transactionType": "CALL", + "contractName": null, + "contractAddress": "0xfd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9", + "function": "approve(address,uint256)", + "arguments": ["0x2ac5Ee3796E027dA274fbDe84c82173a65868940", "1000000"], + "transaction": { + "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "to": "0xfd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9", + "gas": "0x1141e", + "value": "0x0", + "input": "0x095ea7b30000000000000000000000002ac5ee3796e027da274fbde84c82173a6586894000000000000000000000000000000000000000000000000000000000000f4240", + "nonce": "0x1570", + "chainId": "0xa4b1" + }, + "additionalContracts": [], + "isFixedGasLimit": false + }, + { + "hash": "0x3821ab22e999bc5c916ad9e335d03f9844cd02247fe3eeb1a08c686552f54f19", + "transactionType": "CALL", + "contractName": "SponsoredOFTSrcPeriphery", + "contractAddress": "0x2ac5ee3796e027da274fbde84c82173a65868940", + "function": "deposit(((uint32,uint32,bytes32,uint256,bytes32,uint256,uint256,bytes32,bytes32,uint256,uint256,uint8,bytes),(address,uint256)),bytes)", + "arguments": [ + "((30110, 30367, 0x00000000000000000000000040153ddfad90c49dbe3f5c9f96f2a5b25ec67461, 1000000, 0x00000000000000000000000000000000000000000000000000000000692f9f74, 1764732292, 100, 0x0000000000000000000000009a8f92a830a5cb89a3816e3d267cb7791c16b04d, 0x000000000000000000000000b8ce59fc3717ada4c02eadf9682a9e934f625ebb, 200000, 300000, 0, 0x), (0x9A8f92a830A5cB89a3816e3D267CB7791c16b04D, 200))", + "0x564eeb372a7e7e0ddba42927a0c34f2138658d5fb71f8d4752dedf14ab6a406e2230bfbf5054030e3b4af512f29f70d7e92300eb24b17f3f75b8835d0706a9171b" + ], + "transaction": { + "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "to": "0x2ac5ee3796e027da274fbde84c82173a65868940", + "gas": "0xa777f", + "value": "0x914a7d9d2ca", + "input": "0xfcc5b1e30000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000026000000000000000000000000000000000000000000000000000000000000000600000000000000000000000009a8f92a830a5cb89a3816e3d267cb7791c16b04d00000000000000000000000000000000000000000000000000000000000000c8000000000000000000000000000000000000000000000000000000000000759e000000000000000000000000000000000000000000000000000000000000769f00000000000000000000000040153ddfad90c49dbe3f5c9f96f2a5b25ec6746100000000000000000000000000000000000000000000000000000000000f424000000000000000000000000000000000000000000000000000000000692f9f7400000000000000000000000000000000000000000000000000000000692fad8400000000000000000000000000000000000000000000000000000000000000640000000000000000000000009a8f92a830a5cb89a3816e3d267cb7791c16b04d000000000000000000000000b8ce59fc3717ada4c02eadf9682a9e934f625ebb0000000000000000000000000000000000000000000000000000000000030d4000000000000000000000000000000000000000000000000000000000000493e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041564eeb372a7e7e0ddba42927a0c34f2138658d5fb71f8d4752dedf14ab6a406e2230bfbf5054030e3b4af512f29f70d7e92300eb24b17f3f75b8835d0706a9171b00000000000000000000000000000000000000000000000000000000000000", + "nonce": "0x1571", + "chainId": "0xa4b1" + }, + "additionalContracts": [], + "isFixedGasLimit": false + } + ], + "receipts": [ + { + "status": "0x1", + "cumulativeGasUsed": "0x9a1069", + "logs": [ + { + "address": "0xfd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9", + "topics": [ + "0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925", + "0x0000000000000000000000009a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "0x0000000000000000000000002ac5ee3796e027da274fbde84c82173a65868940" + ], + "data": "0x00000000000000000000000000000000000000000000000000000000000f4240", + "blockHash": "0x16950265e3ff885b1bd95a316681c3149258e795c5f25ce7775dfadd3156ccaa", + "blockNumber": "0x183c19cb", + "blockTimestamp": "0x692f9f7f", + "transactionHash": "0x727ba90684db5bf8034bf6d8fd2aefae7856cb177e049905e3394606d8641cfe", + "transactionIndex": "0xa", + "logIndex": "0x9a", + "removed": false + } + ], + "logsBloom": "0x00000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000400000000000000200000000010000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000008000000000800000000000000000000000000000000000000000000000000011000000000000000000000000000010000000000000000000000000000000", + "type": "0x2", + "transactionHash": "0x727ba90684db5bf8034bf6d8fd2aefae7856cb177e049905e3394606d8641cfe", + "transactionIndex": "0xa", + "blockHash": "0x16950265e3ff885b1bd95a316681c3149258e795c5f25ce7775dfadd3156ccaa", + "blockNumber": "0x183c19cb", + "gasUsed": "0xd148", + "effectiveGasPrice": "0x10eef60", + "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "to": "0xfd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9", + "contractAddress": null, + "gasUsedForL1": "0x6", + "l1BlockNumber": "0x16d2449", + "timeboosted": false + }, + { + "status": "0x1", + "cumulativeGasUsed": "0x7a4bb", + "logs": [ + { + "address": "0xfd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9", + "topics": [ + "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", + "0x0000000000000000000000009a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "0x0000000000000000000000002ac5ee3796e027da274fbde84c82173a65868940" + ], + "data": "0x00000000000000000000000000000000000000000000000000000000000f4240", + "blockHash": "0x1f757ef809f329acc38148659c99068e7e740de2efde03ee66740667edd394c4", + "blockNumber": "0x183c19d5", + "blockTimestamp": "0x692f9f82", + "transactionHash": "0x3821ab22e999bc5c916ad9e335d03f9844cd02247fe3eeb1a08c686552f54f19", + "transactionIndex": "0x2", + "logIndex": "0x1", + "removed": false + }, + { + "address": "0xfd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9", + "topics": [ + "0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925", + "0x0000000000000000000000009a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "0x0000000000000000000000002ac5ee3796e027da274fbde84c82173a65868940" + ], + "data": "0x0000000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0x1f757ef809f329acc38148659c99068e7e740de2efde03ee66740667edd394c4", + "blockNumber": "0x183c19d5", + "blockTimestamp": "0x692f9f82", + "transactionHash": "0x3821ab22e999bc5c916ad9e335d03f9844cd02247fe3eeb1a08c686552f54f19", + "transactionIndex": "0x2", + "logIndex": "0x2", + "removed": false + }, + { + "address": "0xfd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9", + "topics": [ + "0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925", + "0x0000000000000000000000002ac5ee3796e027da274fbde84c82173a65868940", + "0x00000000000000000000000014e4a1b13bf7f943c8ff7c51fb60fa964a298d92" + ], + "data": "0x00000000000000000000000000000000000000000000000000000000000f4240", + "blockHash": "0x1f757ef809f329acc38148659c99068e7e740de2efde03ee66740667edd394c4", + "blockNumber": "0x183c19d5", + "blockTimestamp": "0x692f9f82", + "transactionHash": "0x3821ab22e999bc5c916ad9e335d03f9844cd02247fe3eeb1a08c686552f54f19", + "transactionIndex": "0x2", + "logIndex": "0x3", + "removed": false + }, + { + "address": "0xfd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9", + "topics": [ + "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", + "0x0000000000000000000000002ac5ee3796e027da274fbde84c82173a65868940", + "0x0000000000000000000000000000000000000000000000000000000000000000" + ], + "data": "0x00000000000000000000000000000000000000000000000000000000000f4240", + "blockHash": "0x1f757ef809f329acc38148659c99068e7e740de2efde03ee66740667edd394c4", + "blockNumber": "0x183c19d5", + "blockTimestamp": "0x692f9f82", + "transactionHash": "0x3821ab22e999bc5c916ad9e335d03f9844cd02247fe3eeb1a08c686552f54f19", + "transactionIndex": "0x2", + "logIndex": "0x4", + "removed": false + }, + { + "address": "0xfd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9", + "topics": [ + "0xcc16f5dbb4873280815c1ee09dbd06736cffcc184412cf7a71a0fdb75d397ca5", + "0x0000000000000000000000002ac5ee3796e027da274fbde84c82173a65868940" + ], + "data": "0x00000000000000000000000000000000000000000000000000000000000f4240", + "blockHash": "0x1f757ef809f329acc38148659c99068e7e740de2efde03ee66740667edd394c4", + "blockNumber": "0x183c19d5", + "blockTimestamp": "0x692f9f82", + "transactionHash": "0x3821ab22e999bc5c916ad9e335d03f9844cd02247fe3eeb1a08c686552f54f19", + "transactionIndex": "0x2", + "logIndex": "0x5", + "removed": false + }, + { + "address": "0x975bcd720be66659e3eb3c0e4f1866a3020e493a", + "topics": ["0x61ed099e74a97a1d7f8bb0952a88ca8b7b8ebd00c126ea04671f92a81213318a"], + "data": "0x00000000000000000000000031cae3b7fb82d847621859fb1585353c5720660d00000000000000000000000000000000000000000000000000000899318ca136", + "blockHash": "0x1f757ef809f329acc38148659c99068e7e740de2efde03ee66740667edd394c4", + "blockNumber": "0x183c19d5", + "blockTimestamp": "0x692f9f82", + "transactionHash": "0x3821ab22e999bc5c916ad9e335d03f9844cd02247fe3eeb1a08c686552f54f19", + "transactionIndex": "0x2", + "logIndex": "0x6", + "removed": false + }, + { + "address": "0x975bcd720be66659e3eb3c0e4f1866a3020e493a", + "topics": ["0x07ea52d82345d6e838192107d8fd7123d9c2ec8e916cd0aad13fd2b60db24644"], + "data": "0x000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000020000000000000000000000002f55c492897526677c5b68fb199ea31e2c126416000000000000000000000000a8b8575fa41c305953f519c7d288cd7741727c7b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000003cd165e0c60000000000000000000000000000000000000000000000000000003ea4e750ce", + "blockHash": "0x1f757ef809f329acc38148659c99068e7e740de2efde03ee66740667edd394c4", + "blockNumber": "0x183c19d5", + "blockTimestamp": "0x692f9f82", + "transactionHash": "0x3821ab22e999bc5c916ad9e335d03f9844cd02247fe3eeb1a08c686552f54f19", + "transactionIndex": "0x2", + "logIndex": "0x7", + "removed": false + }, + { + "address": "0x1a44076050125825900e736c501f859c50fe728c", + "topics": ["0x1ab700d4ced0c005b164c0f789fd09fcbb0156d4c2041b8a3bfbcd961cd1567f"], + "data": "0x00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000260000000000000000000000000975bcd720be66659e3eb3c0e4f1866a3020e493a00000000000000000000000000000000000000000000000000000000000001d90100000000000062b40000759e00000000000000000000000014e4a1b13bf7f943c8ff7c51fb60fa964a298d920000769f000000000000000000000000904861a24f30ec96ea7cfc3be9ea4b476d237e989333579858e48aaa72df628652842a36f63b52733211b4d79dcad038b676454600000000000000000000000040153ddfad90c49dbe3f5c9f96f2a5b25ec6746100000000000f42400000000000000000000000002ac5ee3796e027da274fbde84c82173a6586894000000000000000000000000000000000000000000000000000000000692f9f7400000000000000000000000000000000000000000000000000000000692fad84000000000000000000000000000000000000000000000000000000000000006400000000000000000000000000000000000000000000000000000000000000c80000000000000000000000009a8f92a830a5cb89a3816e3d267cb7791c16b04d000000000000000000000000b8ce59fc3717ada4c02eadf9682a9e934f625ebb000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000056000301001101000000000000000000000000000186a0010013030000000000000000000000000000000186a00100110100000000000000000000000000030d40010013030000000000000000000000000000000493e000000000000000000000", + "blockHash": "0x1f757ef809f329acc38148659c99068e7e740de2efde03ee66740667edd394c4", + "blockNumber": "0x183c19d5", + "blockTimestamp": "0x692f9f82", + "transactionHash": "0x3821ab22e999bc5c916ad9e335d03f9844cd02247fe3eeb1a08c686552f54f19", + "transactionIndex": "0x2", + "logIndex": "0x8", + "removed": false + }, + { + "address": "0x14e4a1b13bf7f943c8ff7c51fb60fa964a298d92", + "topics": [ + "0x85496b760a4b7f8d66384b9df21b381f5d1b1e79f229a47aaf4c232edc2fe59a", + "0x9333579858e48aaa72df628652842a36f63b52733211b4d79dcad038b6764546", + "0x0000000000000000000000002ac5ee3796e027da274fbde84c82173a65868940" + ], + "data": "0x000000000000000000000000000000000000000000000000000000000000769f00000000000000000000000000000000000000000000000000000000000f424000000000000000000000000000000000000000000000000000000000000f4240", + "blockHash": "0x1f757ef809f329acc38148659c99068e7e740de2efde03ee66740667edd394c4", + "blockNumber": "0x183c19d5", + "blockTimestamp": "0x692f9f82", + "transactionHash": "0x3821ab22e999bc5c916ad9e335d03f9844cd02247fe3eeb1a08c686552f54f19", + "transactionIndex": "0x2", + "logIndex": "0x9", + "removed": false + }, + { + "address": "0x2ac5ee3796e027da274fbde84c82173a65868940", + "topics": [ + "0x8fb515a2e89f5acfca1124e69e331c2cded0ca216b578ba531720f6841139dbf", + "0x00000000000000000000000000000000000000000000000000000000692f9f74", + "0x0000000000000000000000009a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "0x0000000000000000000000009a8f92a830a5cb89a3816e3d267cb7791c16b04d" + ], + "data": "0x00000000000000000000000040153ddfad90c49dbe3f5c9f96f2a5b25ec6746100000000000000000000000000000000000000000000000000000000692fad84000000000000000000000000000000000000000000000000000000000000006400000000000000000000000000000000000000000000000000000000000000c8000000000000000000000000b8ce59fc3717ada4c02eadf9682a9e934f625ebb00000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000041564eeb372a7e7e0ddba42927a0c34f2138658d5fb71f8d4752dedf14ab6a406e2230bfbf5054030e3b4af512f29f70d7e92300eb24b17f3f75b8835d0706a9171b00000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0x1f757ef809f329acc38148659c99068e7e740de2efde03ee66740667edd394c4", + "blockNumber": "0x183c19d5", + "blockTimestamp": "0x692f9f82", + "transactionHash": "0x3821ab22e999bc5c916ad9e335d03f9844cd02247fe3eeb1a08c686552f54f19", + "transactionIndex": "0x2", + "logIndex": "0xa", + "removed": false + } + ], + "logsBloom": "0x000000000000000000000000000000000080000000008010000000000010000000000000000000000000000002000000000000000004400000000000002004000000120000001000000000180048000000000000000000000000400000000000000000000208000080000000000008000820000000100000000000100400000000000000000000000000000a00000000000000000000000000000000002000000a0000008000000000100000100000020000000000400000400440000000008000008002000000800000000080000000000000000000000000001000000020000011040000010000000000000000002010000000018000000080000080000000", + "type": "0x2", + "transactionHash": "0x3821ab22e999bc5c916ad9e335d03f9844cd02247fe3eeb1a08c686552f54f19", + "transactionIndex": "0x2", + "blockHash": "0x1f757ef809f329acc38148659c99068e7e740de2efde03ee66740667edd394c4", + "blockNumber": "0x183c19d5", + "gasUsed": "0x72833", + "effectiveGasPrice": "0x113ecb8", + "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "to": "0x2ac5ee3796e027da274fbde84c82173a65868940", + "contractAddress": null, + "gasUsedForL1": "0x10", + "l1BlockNumber": "0x16d2449", + "timeboosted": false + } + ], + "libraries": [], + "pending": [], + "returns": {}, + "timestamp": 1764728706358, + "chain": 42161, + "commit": "5d9f89e2" +} diff --git a/broadcast/DeployDstHandler.s.sol/999/run-latest.json b/broadcast/DeployDstHandler.s.sol/999/run-latest.json new file mode 100644 index 000000000..ea47bc78e --- /dev/null +++ b/broadcast/DeployDstHandler.s.sol/999/run-latest.json @@ -0,0 +1,418 @@ +{ + "transactions": [ + { + "hash": "0xf72c3e798991af0e7123197bfd7da40585f936b4353b91159b749008ceac541a", + "transactionType": "CREATE", + "contractName": "DonationBox", + "contractAddress": "0x90e2487764e5316a2e4109c2ed40a3b3ad423659", + "function": null, + "arguments": null, + "transaction": { + "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "gas": "0x522e7", + "value": "0x0", + "input": "0x6080806040523461005a575f8054336001600160a01b0319821681178355916001600160a01b03909116907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09080a3610344908161005f8239f35b5f80fdfe608060409080825260049081361015610016575f80fd5b5f3560e01c908163715018a61461026e5781638da5cb5b1461024c57508063f2fde38b146101d45763f3fef3a31461004c575f80fd5b346101545781600319360112610154578035916001600160a01b0383168093036101545760249261007b6102d2565b8151916020830163a9059cbb60e01b815233868501528535604485015260448452608084019367ffffffffffffffff94818110868211176101c25760c08201818110878211176101b0578452602090527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c656460a0820152515f9182919082865af1923d1561019f573d9181831161018d57805195601f8401601f19908116603f011687019283118784101761017b575052835261013e93503d5f602085013e6102e5565b8051908115918215610158575b50501561015457005b5f80fd5b819250906020918101031261015457602001518015158103610154575f8061014b565b60418891634e487b7160e01b5f52525ffd5b86604187634e487b7160e01b5f52525ffd5b5050915061013e92506060916102e5565b88604189634e487b7160e01b5f52525ffd5b87604188634e487b7160e01b5f52525ffd5b503461015457602036600319011261015457356001600160a01b03808216809203610154576102016102d2565b8115610154575f548273ffffffffffffffffffffffffffffffffffffffff198216175f55167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a3005b34610154575f366003190112610154576020906001600160a01b035f54168152f35b34610154575f366003190112610154576102866102d2565b5f6001600160a01b03815473ffffffffffffffffffffffffffffffffffffffff1981168355167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3005b6001600160a01b035f5416330361015457565b90156102ff578151156102f6575090565b3b156101545790565b50805190811561015457602001fdfea2646970667358221220c55691de465342d56d68c31160f5d7d661422ef39ebccaa5223872c823ad6d6964736f6c63430008180033", + "nonce": "0x24a", + "chainId": "0x3e7" + }, + "additionalContracts": [], + "isFixedGasLimit": false + }, + { + "hash": "0xee446c5d024fc2d807a387b7be9dc0e17f7f806efac526cc1db7bebd7fa5f7c7", + "transactionType": "CREATE", + "contractName": "PermissionedMulticallHandler", + "contractAddress": "0x0980d0f6799ca06c71ffafdc0e423cf2b0f20502", + "function": null, + "arguments": ["0x9A8f92a830A5cB89a3816e3D267CB7791c16b04D"], + "transaction": { + "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "gas": "0x15b834", + "value": "0x0", + "input": "0x6080346100d157601f6112d838819003918201601f191683019291906001600160401b038411838510176100d55781602092849260409687528339810103126100d157516001600160a01b038116908190036100d15760015f555f80526001602052815f20815f5260205260ff825f20541615610085575b50516111ee90816100ea8239f35b5f80526001602052815f20815f52602052815f20600160ff1982541617905533905f7f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d8180a45f610077565b5f80fd5b634e487b7160e01b5f52604160045260245ffdfe6080604090808252600480361015610021575b505050361561001f575f80fd5b005b5f3560e01c91826301ffc9a71461086d57508163248a9ca3146108435781632f2ff15d1461079f57816336568abe1461076d5781633a5be8cb146105635781635f6ee30c1461052957816391d14854146104e4578163a217fddf146104ca578163a58d50d3146102fd578163c41e82951461012b578163d547741f146100f2575063ef8738d3146100b3578080610012565b346100ee573660031901126100ee576100ca6108d5565b6024356001600160a01b03811681036100ee5761001f916100e9610f24565b610f3f565b5f80fd5b82346100ee57806003193601126100ee5761001f913561012660016101156108bf565b93835f52816020525f200154610b02565b610c1a565b9050346100ee5760803660031901126100ee576101466108d5565b6024929067ffffffffffffffff84358181116100ee576101699036908601610975565b916044606435928084116100ee57366023850112156100ee57838701359081116100ee57878401938836918360061b0101116100ee576101a7610f24565b604435935f5b8281106101fa57505050505f91829184519160208601915af16101ce610a9b565b50156101d657005b6020936101f6925194859463b3beda7360e01b86528501528301906109f4565b0390fd5b6001600160a01b0380610216610211848787610aca565b610aee565b16156102f35761022a610211838686610aca565b16885180916370a0823160e01b8252308c830152818d60209485935afa9182156102e9575f926102bb575b50505b602080610266848787610aca565b01358181018082116102a9578a5180821161028f575050890101805190911790526001016101ad565b88918f8f928f5193634d3ae48d60e01b8552840152820152fd5b8d60118e634e487b7160e01b5f52525ffd5b90809250813d83116102e2575b6102d28183610937565b810103126100ee57515f80610255565b503d6102c8565b8a513d5f823e3d90fd5b5047955085610258565b9050346100ee57602091826003193601126100ee5767ffffffffffffffff9082358281116100ee57366023820112156100ee578084013594602491610341876109bb565b9461034e85519687610937565b878652828601908460059960051b840101923684116100ee57858101925b848410610458575050505050610380610f24565b8351955f5b87811061038e57005b8551811015610446578281831b8701015183810190815151151580610433575b61040d575f9181886001600160a01b03859451169101519151918783519301915af16103d8610a9b565b50156103e657600101610385565b84516303918b1160e61b815280880191909152808401859052806101f66044820188610a19565b865163388ddcc360e21b8152808a01849052808701889052806101f6604482018b610a19565b506001600160a01b038151163b156103ae565b83603288634e487b7160e01b5f52525ffd5b83358381116100ee578201606060231982360301126100ee5788519161047d836108eb565b888201356001600160a01b03811681036100ee5783526044820135928584116100ee5760648994936104b586958d3691840101610975565b8584015201358b82015281520193019261036c565b82346100ee575f3660031901126100ee57602090515f8152f35b82346100ee57806003193601126100ee576020916105006108bf565b90355f52600183526001600160a01b03825f2091165f52825260ff815f20541690519015158152f35b82346100ee575f3660031901126100ee57602090517f69048ea73402a715065a3029b4059a4e97d1461c95fa4fabca1084b5f34f4abe8152f35b82346100ee5760803660031901126100ee5761057d6108d5565b916044356001600160a01b038116036100ee5760643567ffffffffffffffff81116100ee576105af9036908301610975565b917f69048ea73402a715065a3029b4059a4e97d1461c95fa4fabca1084b5f34f4abe90815f526001916001602052815f20335f5260205260ff825f2054161561061057858560025f54146100ee5761060a9160025f55610ca2565b60015f55005b610619336110ff565b908251936106268561091b565b604285526020850195606036883785511561075a576030875385516001101561075a5790607860218701536041915b8183116106f0575050506100ee576100ee936106e193604893519485937f416363657373436f6e74726f6c3a206163636f756e742000000000000000000060208601526106ac8151809260206037890191016109d3565b8401917f206973206d697373696e6720726f6c65200000000000000000000000000000006037840152518093868401906109d3565b01036028810183520190610937565b909192600f81166010811015610747576f181899199a1a9b1b9c1cb0b131b232b360811b901a61072085896110ee565b53811c928015610734575f19019190610655565b601182634e487b7160e01b5f525260245ffd5b603283634e487b7160e01b5f525260245ffd5b603290634e487b7160e01b5f525260245ffd5b82346100ee573660031901126100ee576107856108bf565b336001600160a01b038216036100ee5761001f9135610c1a565b9050346100ee57816003193601126100ee5735906107bb6108bf565b90825f5260016020526107d36001825f200154610b02565b825f5260016020526001600160a01b03815f20921691825f5260205260ff815f205416156107fd57005b825f526001602052805f20825f526020525f20600160ff1982541617905533917f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d5f80a4005b82346100ee5760203660031901126100ee57602091355f52600182526001815f2001549051908152f35b90346100ee5760203660031901126100ee57359063ffffffff60e01b82168092036100ee57602091637965db0b60e01b81149081156108ae575b5015158152f35b6301ffc9a760e01b149050836108a7565b602435906001600160a01b03821682036100ee57565b600435906001600160a01b03821682036100ee57565b6060810190811067ffffffffffffffff82111761090757604052565b634e487b7160e01b5f52604160045260245ffd5b6080810190811067ffffffffffffffff82111761090757604052565b90601f8019910116810190811067ffffffffffffffff82111761090757604052565b67ffffffffffffffff811161090757601f01601f191660200190565b81601f820112156100ee5780359061098c82610959565b9261099a6040519485610937565b828452602083830101116100ee57815f926020809301838601378301015290565b67ffffffffffffffff81116109075760051b60200190565b5f5b8381106109e45750505f910152565b81810151838201526020016109d5565b90602091610a0d815180928185528580860191016109d3565b601f01601f1916010190565b908082519081815260208091019281808460051b8301019501935f915b848310610a465750505050505090565b9091929394958480600192601f19858203018652895190610a8160606001600160a01b038451168352848401519080868501528301906109f4565b916040809101519101529801930193019194939290610a36565b3d15610ac5573d90610aac82610959565b91610aba6040519384610937565b82523d5f602084013e565b606090565b9190811015610ada5760061b0190565b634e487b7160e01b5f52603260045260245ffd5b356001600160a01b03811681036100ee5790565b805f5260018060205260405f20335f5260205260ff60405f20541615610b26575050565b610b2f336110ff565b9160405190610b3d8261091b565b6042825260208201926060368537825115610ada5760308453825160011015610ada57607860218401536041905b808211610bc35750506100ee576100ee926106e1926048926040519485937f416363657373436f6e74726f6c3a206163636f756e742000000000000000000060208601526106ac8151809260206037890191016109d3565b9091600f81166010811015610ada576f181899199a1a9b1b9c1cb0b131b232b360811b901a610bf284866110ee565b5360041c918015610c06575f190190610b6b565b634e487b7160e01b5f52601160045260245ffd5b90815f5260016020526001600160a01b0360405f20911690815f5260205260ff60405f205416610c48575050565b815f52600160205260405f20815f5260205260405f2060ff19815416905533917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b5f80a4565b51906001600160a01b03821682036100ee57565b81516020908301928184019382828203126100ee578282015167ffffffffffffffff928382116100ee57019060409182818303126100ee5782519183830183811086821117610907578452858201518581116100ee5782019088603f830112156100ee5786820151610d13816109bb565b99610d2087519b8c610937565b818b5286898c019260051b850101938185116100ee57908188809695949301925b858410610e7657505050505050610d5b9188845201610c8e565b93808201938585526001600160a01b0380961615610e1a5750610dd296505f8083518551610db281610da48782019463a58d50d360e01b86528860248401526044830190610a19565b03601f198101835282610937565b519082305af1610dc0610a9b565b5015610dd4575b505050511690610f3f565b565b7f5296f22c5d0413b66d0bf45c479c4e2ca5b278634bdbd028b48e49502105f966915190610e0f868651169451928284938452830190610a19565b0390a25f8080610dc7565b94509250509250303b156100ee57610e4e935f918451958692839263a58d50d360e01b845260048401526024830190610a19565b038183305af18015610e6c57610e6357505050565b82116109075752565b82513d5f823e3d90fd5b9091928094959650518a81116100ee578201906060828703601f1901126100ee57895190610ea3826108eb565b610eae8b8401610c8e565b825260608301518c81116100ee5783019185605f840112156100ee57828c01518c94610ee5610edc83610959565b96519687610937565b81865287606083870101116100ee578f95610f0a6080938897606089850191016109d3565b8584015201518c8201528152019301919088959493610d41565b303303610f2d57565b6040516314e1dbf760e11b8152600490fd5b6001600160a01b03919082169081156110be576040516370a0823160e01b8152306004820152926020918285602481875afa9485156110b3575f95611084575b5084610f8d575b5050505050565b169060405181810163a9059cbb60e01b8152836024830152856044830152604482526080820167ffffffffffffffff91838210838311176109075760c08401928311828410176109075761102093855f94938594604052527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c656460a0820152519082885af1611019610a9b565b908561118f565b8051918215918215611064575b50509050156100ee577f74d3741ef03417659087d2ec6af11dade8713f9b7f592569d60cf1ea0c9a44555f80a45f80808080610f86565b8092508193810103126100ee57015180151581036100ee57805f8061102d565b9094508281813d83116110ac575b61109c8183610937565b810103126100ee5751935f610f7f565b503d611092565b6040513d5f823e3d90fd5b91479150816110cc57505050565b8147106100ee575f92839283928392165af16110e6610a9b565b50156100ee57565b908151811015610ada570160200190565b6040519061110c826108eb565b602a8252602082016040368237825115610ada5760309053815160019060011015610ada57607860218401536029905b80821161114c5750506100ee5790565b9091600f81166010811015610ada576f181899199a1a9b1b9c1cb0b131b232b360811b901a61117b84866110ee565b5360041c918015610c06575f19019061113c565b90156111a9578151156111a0575090565b3b156100ee5790565b5080519081156100ee57602001fdfea2646970667358221220109638e2baa3809d56cadab8aaaa168259a1dd097f352abd8b5fc910c56a6d2b64736f6c634300081800330000000000000000000000009a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "nonce": "0x24b", + "chainId": "0x3e7" + }, + "additionalContracts": [], + "isFixedGasLimit": false + }, + { + "hash": "0xb29d240d7893c5e112a3407a86d1ae520674cb3799cc0fb88420b386dbf018c7", + "transactionType": "CREATE", + "contractName": "DstOFTHandler", + "contractAddress": "0x40153ddfad90c49dbe3f5c9f96f2a5b25ec67461", + "function": null, + "arguments": [ + "0x3A73033C0b1407574C76BdBAc67f126f6b4a9AA9", + "0x904861a24F30EC96ea7CFC3bE9EA4B476d237e98", + "0x90E2487764E5316a2e4109c2Ed40A3B3ad423659", + "0xB8CE59FC3717ada4C02eaDF9682A9e934F625ebb", + "0x0980D0F6799CA06C71fFAFdc0E423cF2B0f20502" + ], + "transaction": { + "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "gas": "0x86ec10", + "value": "0x0", + "input": "0x610120604081815234620002a85760a08262007d74803803809162000025828562000337565b833981010312620002a8576200003b826200035b565b6020906200004b8285016200035b565b90620000598486016200035b565b916200007660806200006e606089016200035b565b97016200035b565b60015f558551615d3e808201949291906001600160401b0386118287101762000323576200203682396001600160a01b039586168552888616878601819052948190038801905ff08015620003195785166080525f7f5300fde95a5e446527bf6aa7c91bd6661bef5398afc77061d9bc87efb80b7ef68082527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b62680088528160018a82200181815491557fbd79b86ffe0ab8e8776151514217cd7cacd52c909f66475c3af44e129f0b00ff92838380a47f880a9ba888678c7fe4e8c4f028c224f26ce12a3bed6e96025c61ef8a5db6312f80835260018a8420019183835493558380a460a0526101009687528551637e062a3560e11b815282851693908681600481885afa9081156200030f579086915f91620002cf575b501603620002be5760c05260e0528351635e280f1160e01b8152908390829060049082905afa928315620002b4575f9362000270575b50508060c051169116036200026057620001fc3362000370565b5051611c14918262000422833960805182818161022c01526110e2015260a0518281816105bb0152818161124d0152611a30015260c05182818161026f0152610df8015260e0518281816101e90152610dcf01525181818161068a01526108db0152f35b516312354ac760e21b8152600490fd5b9080929350813d8311620002ac575b6200028b818362000337565b81010312620002a857518181168103620002a857905f80620001e2565b5f80fd5b503d6200027f565b84513d5f823e3d90fd5b8551633722464560e11b8152600490fd5b809250888092503d831162000307575b620002eb818362000337565b81010312620002a8576200030086916200035b565b5f620001ac565b503d620002df565b88513d5f823e3d90fd5b87513d5f823e3d90fd5b634e487b7160e01b5f52604160045260245ffd5b601f909101601f19168101906001600160401b038211908210176200032357604052565b51906001600160a01b0382168203620002a857565b6001600160a01b03165f8181527fb7db2dd08fcb62d0c9e08c51941cae53c267786a0b75803fb7960902fc8ef97d60205260409020547f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268009060ff166200041b575f805260205260405f20815f5260205260405f20600160ff1982541617905533905f7f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d8180a4600190565b50505f9056fe60806040526004361015610030575b361561002e573461002a5761002236610b73565b602081519101f35b5f80fd5b005b5f3560e01c806301ffc9a71461016a57806305b513bf1461016557806309cfd675146101605780630b2969841461015b57806318f794991461015657806321081d3c14610151578063248a9ca31461014c5780632f2ff15d1461014757806336568abe14610142578063490e662f1461013d5780634b3b029b1461013857806352e12a1414610133578063657cad8a1461012e5780638e35e4451461012457806391d1485414610129578063a217fddf14610124578063c55dae631461011f578063d0a102601461011a578063d547741f146101155763feb617240361000e576109eb565b61098c565b6106dc565b61066b565b6105df565b6105f9565b61059c565b6104a3565b610469565b61042f565b6103e4565b610385565b610328565b6102ee565b610293565b610250565b61020d565b6101ca565b3461002a57602036600319011261002a5760043563ffffffff60e01b811680910361002a57602090637965db0b60e01b81149081156101af575b506040519015158152f35b6301ffc9a760e01b1490505f6101a4565b5f91031261002a57565b3461002a575f36600319011261002a5760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b3461002a575f36600319011261002a5760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b3461002a575f36600319011261002a5760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b3461002a57602036600319011261002a5760043567ffffffffffffffff811680910361002a575f527fe61a4c968926ec08fb0c5bf5be95077bf8b3ddd75ead66c94187ce8d5509de00602052602060405f2054604051908152f35b3461002a575f36600319011261002a5760206040517f880a9ba888678c7fe4e8c4f028c224f26ce12a3bed6e96025c61ef8a5db6312f8152f35b3461002a57602036600319011261002a576004355f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268006020526020600160405f200154604051908152f35b6001600160a01b0381160361002a57565b3461002a57604036600319011261002a5761002e6024356004356103a882610374565b805f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268006020526103df600160405f200154610bd4565b610c43565b3461002a57604036600319011261002a5760243561040181610374565b336001600160a01b0382160361041d5761002e90600435610cfc565b60405163334bd91960e11b8152600490fd5b3461002a575f36600319011261002a5760206040517fc56a3250645180a53cd9e196b2ee0a634a4f54e2edf59ea457f2083917e4d1008152f35b3461002a575f36600319011261002a5760206040517f5300fde95a5e446527bf6aa7c91bd6661bef5398afc77061d9bc87efb80b7ef68152f35b3461002a5760408060031936011261002a576004359063ffffffff821680920361002a57602435916104d3610db3565b5f80527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b62680060205260ff61051933845f20906001600160a01b03165f5260205260405f2090565b54161561057f577ff3adc8db618887d7b76838e244efb05fc99475bb5a904a914d939fbdc41b7e8d92815f527fe61a4c968926ec08fb0c5bf5be95077bf8b3ddd75ead66c94187ce8d5509de0060205280835f205582519182526020820152a160015f55005b815163e2517d3f60e01b81523360048201525f6024820152604490fd5b3461002a575f36600319011261002a5760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b3461002a575f36600319011261002a5760206040515f8152f35b3461002a57604036600319011261002a57602060ff61065f60243561061d81610374565b6004355f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800845260405f20906001600160a01b03165f5260205260405f2090565b54166040519015158152f35b3461002a575f36600319011261002a5760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b9181601f8401121561002a5782359167ffffffffffffffff831161002a576020838186019501011161002a57565b60a036600319011261002a576004356106f481610374565b67ffffffffffffffff9060443582811161002a576107169036906004016106ae565b92610722606435610374565b60843590811161002a5761073a9036906004016106ae565b5050610744610db3565b61077483827fc56a3250645180a53cd9e196b2ee0a634a4f54e2edf59ea457f2083917e4d1009460018655610dc2565b61077e8382610f2f565b80516101201161097b5761079181611575565b916107cd6107c6845f527fe61a4c968926ec08fb0c5bf5be95077bf8b3ddd75ead66c94187ce8d5509de0160205260405f2090565b5460ff1690565b61096a575f9461081d9161081861080b865f527fe61a4c968926ec08fb0c5bf5be95077bf8b3ddd75ead66c94187ce8d5509de0160205260405f2090565b805460ff19166001179055565b610f74565b9160ff61082983611597565b92610833816115a7565b9361089b610848610843846115b7565b610f83565b61088b610857610843866115c7565b9161086a6108648761106c565b9661108a565b97610873610ac0565b9a8b5260208b01526001600160a01b031660408a0152565b6001600160a01b03166060880152565b60808601528660a08601521691600183148093811561095f575b50861461091d5750610913929161090e916108ce610ae0565b9384526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000166020850152604084015215156060830152565b61116b565b5561002e60015f55565b9150506109546109599261094660405193849263073ffe1360e31b602085015260248401610b1c565b03601f198101835282610a9e565b6110d7565b50610913565b60029150145f6108b5565b604051623f613760e71b8152600490fd5b60405162dae46b60e61b8152600490fd5b3461002a57604036600319011261002a5761002e6024356004356109af82610374565b805f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268006020526109e6600160405f200154610bd4565b610cfc565b3461002a57602036600319011261002a576004355f527fe61a4c968926ec08fb0c5bf5be95077bf8b3ddd75ead66c94187ce8d5509de01602052602060ff60405f2054166040519015158152f35b634e487b7160e01b5f52604160045260245ffd5b67ffffffffffffffff8111610a6157604052565b610a39565b6040810190811067ffffffffffffffff821117610a6157604052565b6080810190811067ffffffffffffffff821117610a6157604052565b90601f8019910116810190811067ffffffffffffffff821117610a6157604052565b6040519060c0820182811067ffffffffffffffff821117610a6157604052565b60405190610aed82610a82565b565b604051906060820182811067ffffffffffffffff821117610a6157604052565b60405190610aed82610a66565b60c09093929193610b6f8160e081019660a08091805184526020810151602085015260408101516001600160a01b0380911660408601526060820151166060850152608081015160808501520151910152565b0152565b610b7b610db3565b610b8481610bb8565b610b916040519182610a9e565b81815236821161002a575f602083610bb1948383860137830101526110d7565b9060015f55565b67ffffffffffffffff8111610a6157601f01601f191660200190565b805f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b62680060205260ff610c1b3360405f20906001600160a01b03165f5260205260405f2090565b541615610c255750565b6044906040519063e2517d3f60e01b82523360048301526024820152fd5b805f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268008060205260ff610c8b8460405f20906001600160a01b03165f5260205260405f2090565b5416610cf557815f52602052610cb58260405f20906001600160a01b03165f5260205260405f2090565b805460ff1916600117905533916001600160a01b0316907f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d5f80a4600190565b5050505f90565b805f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268008060205260ff610d448460405f20906001600160a01b03165f5260205260405f2090565b541615610cf557815f52602052610d6f8260405f20906001600160a01b03165f5260205260405f2090565b805460ff1916905533916001600160a01b0316907ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b5f80a4600190565b1561002a57565b60025f541461002a5760025f55565b6001600160a01b039081807f000000000000000000000000000000000000000000000000000000000000000016911603610f0c577f0000000000000000000000000000000000000000000000000000000000000000163303610efa57610e288282610f1e565b7fffffffff0000000000000000000000000000000000000000000000000000000091358281169160048110610ee5575b505060e01c5f8181527fe61a4c968926ec08fb0c5bf5be95077bf8b3ddd75ead66c94187ce8d5509de00602052604090209093915054918215610ec75790610e9f9161178e565b03610ea75750565b604051635f45fa3b60e11b815263ffffffff919091166004820152602490fd5b604051634f1c97f160e11b815263ffffffff85166004820152602490fd5b8391925060040360031b1b1616805f80610e58565b60405163088b800b60e41b8152600490fd5b604051630919067560e41b8152600490fd5b90600c1161002a5760080190600490565b81604c1161002a57604b19820191610f4683610bb8565b92610f546040519485610a9e565b808452368284011161002a57604c5f930160208501378201602b19015290565b90602c1161002a57600c013590565b8060a01c610f97576001600160a01b031690565b6040516379ec0ed760e11b8152600490fd5b5f5b838110610fba5750505f910152565b8181015183820152602001610fab565b81601f8201121561002a578051610fe081610bb8565b92610fee6040519485610a9e565b8184526020828401011161002a5761100c9160208085019101610fa9565b90565b9190916101008184031261002a5780519260208201519260408301519260608101519260808201519260a08301519260c081015160ff8116810361002a579260e082015167ffffffffffffffff811161002a5761100c9201610fca565b61107f906020808251830101910161100f565b509550505050505090565b61109d906020808251830101910161100f565b965050505050505090565b3d156110d2573d906110b982610bb8565b916110c76040519384610a9e565b82523d5f602084013e565b606090565b5f80916020815191017f00000000000000000000000000000000000000000000000000000000000000005af461110b6110a8565b90156111145790565b602081519101fd5b610aed9092919260c081019360a08091805184526020810151602085015260408101516001600160a01b0380911660408601526060820151166060850152608081015160808501520151910152565b6111736115d7565b50604090818101519161119183519360208080968301019101611630565b92808301936111b96111ad6111ad87516001600160a01b031690565b6001600160a01b031690565b83516370a0823160e01b80825230600483015296909290918490849060249082905afa9283156114d3575f9361152f575b506060926112096111ad6111ad868a51016001600160a01b0390511690565b865189815230600482015291908690839060249082905afa9182156114d3575f92611510575b5061128e6112476111ad86516001600160a01b031690565b936112777f000000000000000000000000000000000000000000000000000000000000000095868c51519161179d565b89518701513091906001600160a01b0316906119ab565b6001600160a01b038094166112aa86516001600160a01b031690565b918a515191803b1561002a576112db935f80948d5196879586948593633a5be8cb60e01b8552309160048601611745565b03925af180156114d3576114f7575b506113026111ad6111ad86516001600160a01b031690565b87518a815230600482015291908790839060249082905afa9182156114d3575f926114d8575b5003611442575061141e965061135a61134883516001600160a01b031690565b84885101906001600160a01b03169052565b855151915b61137283885160a0815191015190611b32565b60a088510152828751527fb88fc27be67e678ffb77faf8f8bb00d39b66b4845e4f7ec1e623b0f15abd52138751926113b58785015193516001600160a01b031690565b946113ec6113ce8887519701516001600160a01b031690565b91838b51948594169816968360209093929193604081019481520152565b0390a48351908401511561142157506109466109549293519351938492632132ff4360e11b908401526024830161111c565b50565b92506109466109549251938492627f9f5760e91b908401526024830161111c565b8461145e6111ad6111ad878b51016001600160a01b0390511690565b8751998a523060048b0152899060249082905afa9081156114d35761141e985f926114a4575b5080821061149b5761149591611781565b9161135f565b50505f9161135f565b6114c5919250863d88116114cc575b6114bd8183610a9e565b810190611706565b905f611484565b503d6114b3565b611715565b6114f0919250873d89116114cc576114bd8183610a9e565b905f611328565b8061150461150a92610a4d565b806101c0565b5f6112ea565b611528919250863d88116114cc576114bd8183610a9e565b905f61122f565b611547919350843d86116114cc576114bd8183610a9e565b915f6111ea565b634e487b7160e01b5f52601160045260245ffd5b906001820180921161157057565b61154e565b6020815110611585576020015190565b604051632d0483c560e21b8152600490fd5b6060815110611585576060015190565b6080815110611585576080015190565b60a08151106115855760a0015190565b60c08151106115855760c0015190565b6040519060c0820182811067ffffffffffffffff821117610a61576040525f60a0838281528260208201528260408201528260608201528260808201520152565b67ffffffffffffffff8111610a615760051b60200190565b90602090818382031261002a57825167ffffffffffffffff9384821161002a57019080601f8301121561002a57815161166881611618565b946040906116796040519788610a9e565b828752858088019360051b8601019484861161002a57868101935b8685106116a657505050505050505090565b845183811161002a5782019084601f19838903011261002a578451906116cb82610a66565b898301516116d881610374565b8252858301519185831161002a576116f7898c80969581960101610fca565b83820152815201940193611694565b9081602091031261002a575190565b6040513d5f823e3d90fd5b9060209161173981518092818552858086019101610fa9565b601f01601f1916010190565b9061100c94936080936001600160a01b03809316845260208401521660408201528160608201520190611720565b5f1981019190821161157057565b9190820391821161157057565b90604c1161002a57602c013590565b905f61182493819260405194602086019263a9059cbb60e01b84526001600160a01b0380931660248801526044870152604486526117da86610a82565b16937f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564602060405161180b81610a66565b8181520152519082855af161181e6110a8565b91611bb5565b8051801590811561183b575b50610aed9150610dac565b6020915082908101031261002a5760200151801515810361002a57610aed905f611830565b9061186a82611618565b60406118796040519283610a9e565b838252819361188a601f1991611618565b01905f5b82811061189b5750505050565b8151906060918281019281841067ffffffffffffffff851117610a615760209385525f82528390818301525f8583015282870101520161188e565b80518210156118ea5760209160051b010190565b634e487b7160e01b5f52603260045260245ffd5b9190916020908181526060916060820192855193604080848601528551809252608085018460808460051b8801019701935f925b84841061195a5750505050505060409061100c93949501519101906001600160a01b03169052565b9091929394978680600192607f198b82030187528b51906001600160a01b0382511681528580611995858501518a878601528a850190611720565b9301519101529a01940194019294939190611932565b929183516119c06119bb82611562565b611860565b925f5b828110611aa6575060405163ef8738d360e01b60208201526001600160a01b0391821660248201529083166044808301919091528152939450611a9a9361100c93601f19939092611a8b92611a6f9190611a1e606482610a9e565b611a26610aef565b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001681529060208201525f6040820152611a6882876118d6565b52846118d6565b50611a78610b0f565b9283526001600160a01b03166020830152565b604051938491602083016118fe565b03908101835282610a9e565b80611ac4611ab66001938a6118d6565b51516001600160a01b031690565b602080611ad1848c6118d6565b51015190611aef611ae0610aef565b6001600160a01b039094168452565b8201525f6040820152611b0282886118d6565b52611b0d81876118d6565b50016119c3565b8115611b1e570490565b634e487b7160e01b5f52601260045260245ffd5b919080830180931161157057670de0b6b3a7640000928382029180830485149015171561157057808201809211611570575f19820191821161157057611b7791611b14565b82039180831161157057808202908282041482151715611570578281018091116115705761100c92611bab611bb092611773565b611b14565b611781565b9015611bcf57815115611bc6575090565b3b1561002a5790565b50805190811561002a57602001fdfea26469706673582212204fa917b55b4b9c6370fa7cdd144e4324ce2c961a2790a5ae4b12d18b22af67d064736f6c6343000818003360c0346200010457601f62005d3e38819003918201601f19168301916001600160401b038311848410176200010857808492604094855283398101031262000104576200005a602062000052836200011c565b92016200011c565b6001600160a01b0390911660805260a052604051615c0c90816200013282396080518181816108b7015281816109ff0152818161158b0152818161182f01528181612a1701528181614a0501528181614b7c01528181614f68015261504e015260a051818181610670015281816107de01528181611a7b01528181613a6e01528181614188015281816141e401528181614288015281816142b40152818161435301526146990152f35b5f80fd5b634e487b7160e01b5f52604160045260245ffd5b51906001600160a01b0382168203620001045756fe6080604052600436101562000012575f80fd5b5f803560e01c806246912e146200257057806301ffc9a71462002515578063037a06a414620022c957806304c73f6014620022a9578063057f037014620021f45780631f74a0b5146200217257806321081d3c1462002135578063248a9ca314620020e75780632e748b211462001f7f5780632f2ff15d1462001f1a578063319adf9f1462001b6657806336568abe1462001b1757806337710e201462001abd57806339fff0981462001a225780633b1c6a0114620017275780633cf3a02514620016eb5780634265fe861462001699578063490e662f146200165c5780634b3b029b146200161f578063502a82e21462001554578063521c98ba1462000d9157806369b97ac71462000d7157806379c7b60b1462000d1757806379c7f2891462000c1557806390a0827b1462000bd457806391d148541462000b6657806396cc2cfb14620008f9578063a217fddf14620008db578063a4b672b61462000895578063af5de6f91462000802578063c55dae6314620007bc578063ccbedaec1462000542578063d06e28ed1462000443578063d547741f14620003da578063e38b73a914620003bc578063ea0aaf241462000359578063eb84e7f2146200024b5763ff3eae0014620001e2575f80fd5b34620002485760c0366003190112620002485762000200366200279e565b7fc56a3250645180a53cd9e196b2ee0a634a4f54e2edf59ea457f2083917e4d10054156200023657620002339062004e31565b80f35b60405163cd6d8f7d60e01b8152600490fd5b80fd5b503462000248576020366003190112620002485760408160c09260a0835162000274816200270b565b8281528260208201528285820152826060820152826080820152015260043581527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0360205220604051620002c8816200270b565b6001600160a01b039060028284541693848352600181015490602084019185811683526001600160401b0393849283604088019360a01c1683520154956060860193838816855260ff60a06080890198828b60401c1615158a52019860481c1615158852604051988952511660208801525116604086015251166060840152511515608083015251151560a0820152f35b50346200024857602036600319011262000248576020620003b36200037d620025de565b6001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0560205260405f2090565b54604051908152f35b50346200024857806003193601126200024857602060405160068152f35b50346200024857604036600319011262000248576200043f600435620003ff620025f5565b908084527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b62680060205262000439600160408620015462002e64565b620039b1565b5080f35b503462000248576040366003190112620002485762000461620025de565b602435906fffffffffffffffffffffffffffffffff82168092036200053e576200048a62002e08565b6200049581620032e8565b90836001600160a01b039263ffffffff84608083015116915116813b156200053a57829160448392604051948593849263435354d360e01b845260048401528a60248401525af180156200052f5762000513575b5050167f02366c0d102495be1ee805b749be7baebab4fc0710c6d3f38751f1a22bd711648380a380f35b6200051e90620026f7565b6200052b57835f620004e9565b8380fd5b6040513d84823e3d90fd5b8280fd5b5f80fd5b50346200024857606036600319011262000248578062000561620025de565b6200056b6200260c565b906200057662002623565b906200058162002d37565b620005bc816001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0260205260405f2090565b544311156200071a574362000601826001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0260205260405f2090565b556001600160a01b0380600162000648846001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0160205260405f2090565b01541693816001600160401b03938483166200071f575b5050508183166200066e578480f35b7f00000000000000000000000000000000000000000000000000000000000000001684525f8051602062005bb783398151915260205260046040852001541690823b156200071a5760405163a703334f60e01b81526001600160401b039283166004820152336024820152911660448201529082908290606490829084905af180156200052f5762000702575b8080808480f35b6200070d90620026f7565b6200024857805f620006fb565b505050fd5b1686525f8051602062005bb783398151915260205282600460408820015416853b15620007b85760405163a703334f60e01b81526001600160401b03918216600482015233602482015291166044820152858160648183895af1908115620007ad57869162000791575b82906200065f565b6200079c90620026f7565b620007a957845f62000789565b8480fd5b6040513d88823e3d90fd5b8680fd5b5034620002485780600319360112620002485760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b503462000248576020366003190112620002485760206001600160a01b03620008346200082e620025de565b620031ff565b6109ab60405162000848858301826200277c565b818152848101916200520c8339519020604051908482019260ff60f81b84523060601b60218401526035830152605582015260558152620008898162002744565b51902016604051908152f35b5034620002485780600319360112620002485760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b50346200024857806003193601126200024857602090604051908152f35b503462000248576040366003190112620002485762000917620025de565b906200092262002e08565b6200092d8262002f58565b6200093883620032e8565b926200094e60e083510151840b60243562004da2565b9460208401906001600160401b036200097381845116898360a08a01511691620038d2565b1562000b3657869750620009b7856001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0460205260405f2090565b620009c48582546200287c565b90556001600160a01b03808616807f410b9a8c926b6c439cdceb39c0bb8f829838a25bc5a26af9d4c263d1313cc46b6020604051898152a2817f000000000000000000000000000000000000000000000000000000000000000016803b1562000b325760405163f3fef3a360e01b81526001600160a01b038316600482015260248101889052908a908290604490829084905af1801562000b275787918b9162000b08575b505062000a8290608060e0960192848451169062003353565b511692511694510151850b93813b1562000b0457604051639c45c34b60e01b81526001600160a01b039490941660048501526001600160401b0316602484015260448301919091525f9290920b60648201529082908290608490829084905af180156200052f5762000af15750f35b62000afc90620026f7565b620002485780f35b8580fd5b62000b15919250620026f7565b62000b235785895f62000a69565b8880fd5b6040513d8c823e3d90fd5b8980fd5b6040516377e88bc960e11b81526001600160a01b03861660048201526001600160401b0389166024820152604490fd5b50346200024857604036600319011262000248576001600160a01b03604062000b8e620025f5565b9260043581527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268006020522091165f52602052602060ff60405f2054166040519015158152f35b50346200024857604036600319011262000248576200023362000bf6620025de565b62000c0062002d37565b602435906001600160a01b0333911662003353565b503462000248576020366003190112620002485760a062000c7a62000c39620025de565b62000c4362002d0b565b506001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0160205260405f2090565b6001600160a01b0360016040519262000c9384620026db565b805463ffffffff90818116865260ff8160201c1615156020870152818160281c16604087015260481c166060850152015416608082015262000d1560405180926001600160a01b036080809263ffffffff8082511686526020820151151560208701528060408301511660408701526060820151166060860152015116910152565bf35b50346200024857602036600319011262000248576020620003b362000d3b620025de565b6001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0460205260405f2090565b503462000248578060031936011262000248576020604051620f42408152f35b5034620002485760a0366003190112620002485762000daf620025de565b62000db96200263a565b9062000dc46200264e565b6001600160401b0360643516606435036200053e57608435906001600160401b03821682036200053e5762000df862002db1565b62000e0262002c05565b508480604051602081019063ffffffff881682526020815262000e258162002760565b519061080c5afa62000e3662002cd7565b901562001542578051810190602081830312620007b85760208101516001600160401b038111620015055761010081830184031262001505576040519262000e7e8462002727565b602082840101516001600160401b03811162000b325760208201603f828587010101121562000b32576020818486010101519062000ebc8262002cbb565b9162000ecc60405193846200277c565b8083526020840160408284888a01010101116200152a5762000efa9160406020850191878901010162002691565b8452604082840101516001600160401b03811162000b325760208201603f828587010101121562000b3257602081848601010151916001600160401b0383116200152e578260051b6040519362000f5560208301866200277c565b84526020808501920160408285888a01010101116200152a576040838688010101915b60408285888a01010101831062001509575050505060208401526101009062000fa683820160600162003428565b604085015262000fbb6080828501016200512c565b606085015262000fd060a0828501016200512c565b9283608086015262000fe760c08383010162005141565b60a086015262000ffc60e08383010162005141565b60c08601520101519081880b820362001505578160e08401526001600160a01b03808716911603620007b8576200103890870b60643562003829565b506040519162001048836200270b565b825263ffffffff86166020830152821515604083015260608201526001600160401b036064351660808201526001600160401b03831660a08201526001600160a01b03841686525f8051602062005bb783398151915260205260408620815180518051906001600160401b03821162001438578190620010c9855462002c80565b601f8111620014c5575b50602090601f831160011462001458578b926200144c575b50508160011b915f199060031b1c19161782555b60208101518051906001600160401b038211620014385768010000000000000000821162001438576020906001850154836001870155808410620013e5575b500190600184018a5260208a20908a5b8160021c81106200139d5750600319811680820362001334575b50505050936001600160a01b03937f12cf3d04179e82c834f3ee7169a5df80651aa65530127f9ddb04c8cd82244353969360068460809860026001600160401b039701876040830151168154907bffffffffffffffffffffffffffffffffffffffff0000000000000000606085015160401b169163ffffffff60e01b16171790556003820190898b820151169082549174ff000000000000000000000000000000000000000060a083015160a01b16907fffffffffffffffffff000000000000000000000000000000000000000000000060e075ff00000000000000000000000000000000000000000060c086015160a81b1694015160b01b76ff00000000000000000000000000000000000000000000169416171717179055620012c5600482018760208601511688198254161781556040850151151568ff0000000000000000825491151560401b169068ff00000000000000001916179055565b6060830151600582015501908488820151166fffffffffffffffff000000000000000060a0845493015160401b16916fffffffffffffffffffffffffffffffff19161717905563ffffffff6040519816885215156020880152816064351660408801521660608601521692a280f35b928b938c5b81840381106200135d5750505060021c0155838360066001600160401b0362001168565b9091946020620013926001926001600160401b03895116908560031b60031b916001600160401b03809116831b921b19161790565b960192910162001339565b8b8c5b60048110620013b75750838201556001016200114e565b85519095916001916020916001600160401b0360068a901b81811b199092169216901b1792019501620013a0565b6200141890600187018d52838d20600380870160021c820192601888831b16806200141f575b500160021c019062004d8a565b5f6200113e565b5f1990818601918254918a03851b1c1690555f6200140b565b634e487b7160e01b8a52604160045260248afd5b015190505f80620010eb565b9250848b5260208b20908b935b601f1984168510620014a9576001945083601f1981161062001490575b505050811b018255620010ff565b01515f1960f88460031b161c191690555f808062001482565b8181015183556020948501946001909301929091019062001465565b620014f390868d5260208d20601f850160051c81019160208610620014fa575b601f0160051c019062004d8a565b5f620010d3565b9091508190620014e5565b8780fd5b906020806040936200151b8662003428565b81520193019290915062000f78565b8b80fd5b634e487b7160e01b8b52604160045260248bfd5b604051639b0c335d60e01b8152600490fd5b503462000248576040366003190112620002485762001572620025de565b6024356200157f62002d37565b826001600160a01b03807f000000000000000000000000000000000000000000000000000000000000000016931692803b156200161b5760405163f3fef3a360e01b81526001600160a01b0385166004820152602481018490529082908290604490829084905af180156200052f5762001603575b50506200023391339062003353565b6200160e90620026f7565b6200053a57825f620015f4565b5080fd5b5034620002485780600319360112620002485760206040517f5300fde95a5e446527bf6aa7c91bd6661bef5398afc77061d9bc87efb80b7ef68152f35b5034620002485780600319360112620002485760206040517fc56a3250645180a53cd9e196b2ee0a634a4f54e2edf59ea457f2083917e4d1008152f35b5034620002485760c03660031901126200024857620016b8366200279e565b7fc56a3250645180a53cd9e196b2ee0a634a4f54e2edf59ea457f2083917e4d100541562000236576200023390620046c4565b50346200024857602036600319011262000248576200170962002d37565b80808080600435335af16200171d62002cd7565b5015620002485780f35b503462000248576060366003190112620002485762001745620025f5565b6001600160a01b0360443581811692918382036200053e576200176762002e08565b620017728262002f58565b906200177e8462003257565b62000b045760408201511562000b04576001600160401b0391600183608083015116019083821162001a0e57620017bd60e082510151890b8362003829565b5094620017db602083019386855116908760a08601511691620038d2565b1562000b23576200181c906001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0560205260405f2090565b620018298682546200287c565b905587837f000000000000000000000000000000000000000000000000000000000000000016803b156200161b5760405163f3fef3a360e01b81526001600160a01b038a166004820152602481018890529082908290604490829084905af180156200052f57620019f0575b50509183826200192494620018bc60e062001913986024975116925101518c0b8962004da2565b508481620019d0575b505050511695604051916020830191169687825260408301526001606083015260608252620018f48262002744565b6040519485926280000360e11b60208501525180928585019062002691565b81010360048101845201826200277c565b733333333333333333333333333333333333333333803b156200053e576040516317938e1360e01b815260206004820152915f91839182908490829062001970906024830190620026b4565b03925af18015620019c557620019b3575b506040519081527f2b348084e891b20d449a69f90114c5ab7bf7c84d64c25445c8ab440d469a6b4d602060043592a480f35b620019be90620026f7565b5f62001981565b6040513d5f823e3d90fd5b620019df620019e79362002847565b168b62003353565b5f8084620018c5565b620019ff9094929394620026f7565b62001505579190875f62001895565b634e487b7160e01b5f52601160045260245ffd5b5034620002485760e0366003190112620002485762001a41366200279e565b7fc56a3250645180a53cd9e196b2ee0a634a4f54e2edf59ea457f2083917e4d100541562000236576001600160a01b0380606083015116907f000000000000000000000000000000000000000000000000000000000000000016145f1462001aae576200023390620046c4565b620002339060c4359062003a45565b50346200024857602036600319011262000248576020620003b362001ae1620025de565b6001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0260205260405f2090565b503462000248576040366003190112620002485762001b35620025f5565b336001600160a01b0382160362001b54576200043f90600435620039b1565b60405163334bd91960e11b8152600490fd5b50346200024857602036600319011262000248576001600160a01b0362001b8c620025de565b62001b9662002c45565b501681525f8051602062005bb783398151915260205260408120906040519162001bc0836200270b565b60405162001bce8162002727565b6040518254818562001be08362002c80565b808352926001811690811562001ef9575060011462001eb4575b62001c08925003826200277c565b815260405160018301805480835290855260208086209083019186915b81600384011062001e72579360069593819362001c73936001600160401b039997549181811062001e5e575b81811062001e47575b81811062001e30575b1062001e21575b5003826200277c565b60208201526001600160a01b036002830154858116604084015260401c16606082015260038201546001600160a01b038116608083015260ff8160a01c1660a083015260ff8160a81c1660c083015260b01c850b60e0820152855260ff6004820154848116602088015260401c1615156040860152600581015460608601520154818116608085015260401c1660a0830152604051906020825282519060c0602084015262001d31825161010060e08601526101e0850190620026b4565b60208084015185830360df190161010087015280518084529282019492910190835b81811062001e015750505083946001600160401b039260e08385604060a0960151166101208901526001600160a01b036060820151166101408901526001600160a01b0360808201511661016089015260ff858201511661018089015260ff60c0820151166101a08901520151900b6101c086015282602082015116604086015260408101511515606086015260608101516080860152826080820151168286015201511660c08301520390f35b82516001600160401b031686526020958601959092019160010162001d53565b60c01c81526020015f62001c6a565b9260206001918c8560801c16815201930162001c63565b9260206001918c8560401c16815201930162001c5a565b9260206001918c8516815201930162001c51565b9260016004918554906001600160401b03918281168252828160401c16602083015260809281841c16604083015260c01c606082015201940192019162001c25565b5090848652602086209086915b81831062001edc57505090602062001c089282010162001bfa565b602091935080600191548385880101520191019091839262001ec1565b6020925062001c0894915060ff191682840152151560051b82010162001bfa565b50346200024857604036600319011262000248576200043f60043562001f3f620025f5565b908084527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b62680060205262001f79600160408620015462002e64565b62003912565b503462000248576080366003190112620002485762001f9d620025de565b62001fa76200260c565b9062001fb262002623565b90606435926fffffffffffffffffffffffffffffffff84168094036200053e5762001fdc62002e08565b62001fe782620032e8565b856001600160a01b03928360808401511692833b156200053a57620020646101048492836040519586948593636f0d192560e11b855260048501906001600160a01b036080809263ffffffff8082511686526020820151151560208701528060408301511660408701526060820151166060860152015116910152565b6001600160401b038091169b8c60a485015216978860c48401528c60e48401525af180156200052f57620020cb575b5050907f500d805a349357fe5d4759fe052d79bd744b82c8452837f52a7456ec7d3d751c92604092835195865260208601521692a380f35b620020d990939293620026f7565b62000b045790855f62002093565b50346200024857602036600319011262000248576001604060209260043581527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268008452200154604051908152f35b5034620002485780600319360112620002485760206040517f880a9ba888678c7fe4e8c4f028c224f26ce12a3bed6e96025c61ef8a5db6312f8152f35b503462000248576060366003190112620002485762002190620025de565b6001600160401b036024358181116200052b57620021b39036906004016200265e565b91909360443591821162000248576020620021ec868686620021d936600489016200265e565b939092620021e662002e08565b6200288a565b604051908152f35b50346200053e5760403660031901126200053e5762002212620025de565b906024356200222062002d37565b6001600160a01b03928360806200223783620032e8565b015116803b156200053e576040516390a0827b60e01b81526001600160a01b038316600482015260248101849052905f908290604490829084905af18015620019c55762002290575b5062000233929333911662003353565b620002339350620022a190620026f7565b5f9262002280565b346200053e575f3660031901126200053e5760206040516305f5e1008152f35b346200053e5760a03660031901126200053e57620022e6620025de565b620022f06200263a565b620022fa6200264e565b6064359063ffffffff938483168093036200053e57608435908582168092036200053e57620023298162002f58565b506200233462002db1565b6001600160a01b03918260016200237b846001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0160205260405f2090565b0154168015620024bc575b8360018960405199620023998b620026db565b16988981526020810197151597888152604082018a81526060830190878252856080850197169d8e8852620023fe8a6001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0160205260405f2090565b9451166cffffffff00000000000000000068ffffffff000000000064ff0000000087549651151560201b16935160281b16935160481b16936cffffffffffffffffffffffffff19161717171781550191511673ffffffffffffffffffffffffffffffffffffffff19825416179055620024778762003257565b156200053e577ff0dcc8957a27613dd82c92382ad37254b9744169d0caa5f3873cfec7ba794eb9946080946040519788526020880152604087015260608601521692a3005b50620024c882620031ff565b6040516109ab8082018281106001600160401b03821117620025015782916200520c833903905ff58062002386576040513d5f823e3d90fd5b634e487b7160e01b5f52604160045260245ffd5b346200053e5760203660031901126200053e5760043563ffffffff60e01b81168091036200053e57602090637965db0b60e01b81149081156200255e575b506040519015158152f35b6301ffc9a760e01b1490508262002553565b346200053e5760403660031901126200053e57620025dc62002591620025de565b6001600160a01b03620025a36200260c565b91620025ae62002d37565b165f525f8051602062005bb78339815191526020526001600160401b03600460405f20015416339062002ea6565b005b600435906001600160a01b03821682036200053e57565b602435906001600160a01b03821682036200053e57565b602435906001600160401b03821682036200053e57565b604435906001600160401b03821682036200053e57565b6024359063ffffffff821682036200053e57565b6044359081151582036200053e57565b9181601f840112156200053e578235916001600160401b0383116200053e576020808501948460051b0101116200053e57565b5f5b838110620026a35750505f910152565b818101518382015260200162002693565b90602091620026cf8151809281855285808601910162002691565b601f01601f1916010190565b60a081019081106001600160401b038211176200250157604052565b6001600160401b0381116200250157604052565b60c081019081106001600160401b038211176200250157604052565b61010081019081106001600160401b038211176200250157604052565b608081019081106001600160401b038211176200250157604052565b604081019081106001600160401b038211176200250157604052565b90601f801991011681019081106001600160401b038211176200250157604052565b60c09060031901126200053e5760405190620027ba826200270b565b60043582526024356020830152816001600160a01b0360443581811681036200053e57604083015260643590811681036200053e576060820152608435608082015260a060a435910152565b9190811015620028175760051b0190565b634e487b7160e01b5f52603260045260245ffd5b9190916001600160401b038080941691160191821162001a0e57565b9073200000000000000000000000000000000000000091820180921162001a0e57565b906305f5e0ff820180921162001a0e57565b9190820180921162001a0e57565b919392935f945f948083036200053e57620028d5856001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0260205260405f2090565b544311156200053e57869796939291620028ef8662002f58565b926080620028fd88620032e8565b01906001600160a01b0392838351169460208701976200292a6001600160401b0397888b5116906200343d565b845f9b5b1062002b94575b50508c15925062002b8791505057436200297f886001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0260205260405f2090565b5582861662002993575b5050505050505050565b620029c195620029ab60e0865101515f0b8262003829565b85889992995116908660a08901511691620038d2565b1562002b4d575062002a03876001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0460205260405f2090565b62002a108782546200287c565b90558682167f00000000000000000000000000000000000000000000000000000000000000008316803b156200053e5760405163f3fef3a360e01b81526001600160a01b038316600482015260248101899052905f908290604490829084905af18015620019c55762002b29575b509062002a9587849360e096958451169062003353565b511693511691510151850b93823b1562000b0457604051639c45c34b60e01b81526001600160a01b0390911660048201526001600160401b03909116602482015260448101929092525f9290920b606482015290829082908183816084810103925af180156200052f5762002b11575b80808080808062002989565b62002b1d8291620026f7565b62000248578062002b05565b60e094939291995062002b3c90620026f7565b5f98929350909162002a9562002a7e565b8451608001516040516377e88bc960e11b81529084166001600160a01b031660048201526001600160401b03919091166024820152604490fd5b505f985050505050505050565b62002ba6859e9f809c95968462002806565b359062002bb58c858862002806565b359189831683036200053e578a62002bd2938a8a51169262003520565b9490911562002bfb5762002beb8f93926001926200282b565b9b019e9d8f95949192956200292e565b9a9e9d5062002935565b6040519062002c148262002727565b5f60e08360608152606060208201528260408201528260608201528260808201528260a08201528260c08201520152565b6040519062002c54826200270b565b5f60a08362002c6262002c05565b81528260208201528260408201528260608201528260808201520152565b90600182811c9216801562002cb0575b602083101462002c9c57565b634e487b7160e01b5f52602260045260245ffd5b91607f169162002c90565b6001600160401b0381116200250157601f01601f191660200190565b3d1562002d06573d9062002ceb8262002cbb565b9162002cfb60405193846200277c565b82523d5f602084013e565b606090565b6040519062002d1a82620026db565b5f6080838281528260208201528260408201528260608201520152565b335f9081527f176c3a4e1b4d1d61213187c0dde7f83e2e2c933a339cb5403b4090bc2f0aa49a60205260409020547f880a9ba888678c7fe4e8c4f028c224f26ce12a3bed6e96025c61ef8a5db6312f9060ff161562002d935750565b6044906040519063e2517d3f60e01b82523360048301526024820152fd5b335f9081527fb7db2dd08fcb62d0c9e08c51941cae53c267786a0b75803fb7960902fc8ef97d602052604090205460ff161562002dea57565b60405163e2517d3f60e01b81523360048201525f6024820152604490fd5b335f9081527fcf7720576cdb70176b3017d893bcb89ba0f36ed0261d4350fd0180e938b3bacd60205260409020547f5300fde95a5e446527bf6aa7c91bd6661bef5398afc77061d9bc87efb80b7ef69060ff161562002d935750565b805f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b62680060205260405f20335f5260205260ff60405f2054161562002d935750565b620019139262002eec92602492604051926001600160a01b0360208501931683526001600160401b03809216604085015216606083015260608252620018f48262002744565b733333333333333333333333333333333333333333803b156200053e576040516317938e1360e01b815260206004820152915f91839182908490829062002f38906024830190620026b4565b03925af18015620019c55762002f4b5750565b62002f5690620026f7565b565b9062002f6362002c45565b506001600160a01b038092165f5260205f8051602062005bb78339815191528152604092835f2084519062002f98826200270b565b85519562002fa68762002727565b80518254905f8162002fb88462002c80565b918282526001948a86821691825f14620031e05750506001146200319f575b62002fe5925003826200277c565b88528083019082519087829384928282549586815201915f52825f20945f5b8160038201106200315a57620030469654928583831062003140575b83831062003124575b83831062003107575b505010620030f8575b50905003826200277c565b858801526006600283015492856001600160401b0394858116858c0152841c1660608a015260ff60038201549760808b019a888a168c52828a60a01c1660a082015260c0810199838160a81c168b5260b01c5f0b60e0820152875260048301549086821690880152841c161515838601526005810154606086015201549082821660808501521c1660a082015293511615159081620030e9575b50156200053e57565b60ff9150511615155f620030e0565b60c01c81520188905f6200303b565b9091946001600160401b038560801c1681520193015f8562003032565b8192956001600160401b03868d1c168152019401908562003029565b8192956001600160401b0386168152019401908562003020565b86546001600160401b038082168652818b1c811696860196909652608081811c9096168a86015260c01c6060850152958201958795508c949093019260040162003004565b5050845f528188805f20855f915b858310620031c657505062002fe5935082010162002fd7565b8091929450548385880101520191018990858593620031ad565b60ff19168482015262002fe594151560051b840101915062002fd79050565b60405160208101913060601b83526bffffffffffffffffffffffff199060601b16603482015260288152606081018181106001600160401b03821117620025015760405251902090565b519081151582036200053e57565b5f80916040516001600160a01b036020820192168252602081526200327c8162002760565b51906108105afa6200328d62002cd7565b9015620032d6576020818051810103126200053e5760405190602082018281106001600160401b038211176200250157604052620032ce9060200162003249565b809152151590565b6040516313dd7ccd60e31b8152600490fd5b620032f79062000c4362002d0b565b906001600160a01b036001604051936200331185620026db565b805463ffffffff90818116875260ff8160201c1615156020880152818160281c16604088015260481c1660608601520154169182608082015291156200053e57565b60405163a9059cbb60e01b602082019081526001600160a01b039384166024830152604480830195909552938152620033ea9390925f92839291906200339b6064876200277c565b16937f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65646020604051620033ce8162002760565b8181520152519082855af1620033e362002cd7565b91620051de565b805190811591821562003402575b5050156200053e57565b81925090602091810103126200053e57602062003420910162003249565b5f80620033f8565b51906001600160401b03821682036200053e57565b604080516001600160a01b0392909216602083019081526001600160401b039390931682820152808252915f918291906200347a6060826200277c565b51906108015afa6200348b62002cd7565b9015620034f5576060818051810103126200053e5781516001600160401b0392606082018481118382101762002501578152620034ee6060620034d16020860162003428565b94858552620034e284820162003428565b60208601520162003428565b9101521690565b8151639d2c8fcb60e01b8152600490fd5b6001600160401b03918216908216039190821162001a0e57565b949192909394805f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0360205260405f20906001600160a01b039485835416958615620037ab57600284019788549860ff8a60481c166200379957600186015483608086510151168482160362003780576001600160401b039060a01c166001600160401b038b1660ff8c60401c165f146200371f575080926001600160401b03811682115f146200371657620035d79162003506565b995b620035e58b8d6200282b565b9b6001600160401b038d166001600160401b0385161162003704575069010000000000000000009069ff00000000000000000019161790556200362b8160019b62003506565b976001600160401b036020846200364b8d60e0895101515f0b9062003829565b50991695015116843b156200053e5760405163a703334f60e01b81526001600160401b0391821660048201526001600160a01b0392909216602483015282166044820152925f908490606490829084905af1918215620019c5577f53b9d5645f8b7ccd861ebd6036860fd21716451d1f238cb3720f12f3c49b0c4393604093620036f2575b5060018187541696015416956001600160401b038351921682526020820152a4565b620036fd90620026f7565b5f620036d0565b5f9c508c9b5099505050505050505050565b50505f620035d7565b9092809b93806001600160401b038316105f1462003773576200374f91620037479162003506565b809c6200282b565b816001600160401b038216115f146200376b57505b91620035d9565b905062003764565b50506200374f5f62003747565b60405163358d72d160e01b815260048101879052602490fd5b60405163f7348a7960e01b8152600490fd5b604051631a40316d60e01b8152600490fd5b60ff16604d811162001a0e57600a0a90565b9190820391821162001a0e57565b8115620037e8570490565b634e487b7160e01b5f52601260045260245ffd5b90620f42409182810292818404149015171562001a0e57565b8181029291811591840414171562001a0e57565b9190805f0b80155f14620038475750506001600160401b0382169190565b5f81131562003877575062003863620038739160ff16620037bd565b6001600160401b03841662003815565b9190565b9050607f19811462001a0e5762003893905f0360ff16620037bd565b91620038aa836001600160401b038093166200287c565b5f19810190811162001a0e57620038c684620038ce92620037dd565b938462003815565b1690565b6200390a6200390392936001600160401b0393836001600160a01b03620038fc8780971662002847565b166200343d565b946200282b565b169116101590565b90815f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800806020526001600160a01b0360405f20921691825f5260205260ff60405f205416155f14620039aa57825f5260205260405f20815f5260205260405f20600160ff1982541617905533917f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d5f80a4600190565b5050505f90565b90815f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800806020526001600160a01b0360405f20921691825f5260205260ff60405f2054165f14620039aa57825f5260205260405f20815f5260205260405f2060ff19815416905533917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b5f80a4600190565b9062003a5e6001600160a01b0360408401511662003257565b1562004618576001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000165f525f8051602062005bb783398151915260205260405f20906040519162003ab6836200270b565b60405162003ac48162002727565b6040518254815f62003ad68362002c80565b8083529260018116908115620045f75750600114620045b2575b62003afe925003826200277c565b8152600182016040519081602082549182815201915f5260205f20905f915b81600384011062004570579360069593819362003b67936001600160401b039997549181811062001e5e5781811062001e475781811062001e30571062001e21575003826200277c565b60208201526001600160a01b036002830154858116604084015260401c16606082015260038201546001600160a01b038116608083015260ff8160a01c1660a083015260ff8160a81c1660c083015260b01c5f0b60e0820152855260ff6004820154848116602088015260401c1615156040860152600581015460608601520154818116608085015260401c1660a08301526001600160a01b036060840151165f525f8051602062005bb783398151915260205260405f206040519062003c2e826200270b565b60405162003c3c8162002727565b6040518254815f62003c4e8362002c80565b80835292600181169081156200454f57506001146200450a575b62003c76925003826200277c565b8152600182016040519081602082549182815201915f5260205f20905f915b816003840110620044bd579360069593819362003cdf936001600160401b039997549181811062001e5e5781811062001e475781811062001e30571062001e21575003826200277c565b60208201526001600160a01b036002830154858116604084015260401c16606082015260038201546001600160a01b038116608083015260ff8160a01c1660a083015260ff8160a81c1660c083015260b01c5f0b60e0820152845260ff6004820154848116602087015260401c1615156040850152600581015460608501520154818116608084015260401c1660a082015262003d896001600160a01b03606086015116620032e8565b9184519062003dd662003dbb62003dac60a089015160808a01511515956200287c565b60e0885101515f0b9062004da2565b905060ff60c0885101511660ff60c087510151169162005165565b91156200448c5781925b5f8063ffffffff875116604051602081019182526020815262003e038162002760565b51906108085afa9062003e1562002cd7565b91156200447a576020828051810103126200053e576001600160401b0362003e44602062003e6d940162003428565b60208801519116919015620044665760ff60a062003e669251015116620037bd565b9062003815565b602085015115620044175763ffffffff62003e8e8160608801511662005026565b16620f4240818101811162001a0e576001600160401b039262003e6662003ec792620f42409463ffffffff60408c01511691016200287c565b0416906080870151908115155f146200440e5750905b602085015115620043c5576305f5e1008110156200437e57505f5b62003f2462003f0b60a0890151620037fc565b62003f1d895160a08b0151906200287c565b906200287c565b5f1981019190821162001a0e5762003f1d62003f539262003f4c8a5160a08c0151906200287c565b90620037dd565b606482028281046064148315171562001a0e578111620042df57505062003f84855160e0865101515f0b9062004da2565b92909562003faf6001600160401b03602088015116856001600160401b0360a08a01511691620038d2565b156200423b5791608093917f550576b2f9e0ac12dfd5dd2d5743b5b7f11f34302b5f6bec6ad60db81bd6a91860a0856001600160401b036001600160a01b036040819b990151168a606084015116908a8401511515916040519162004014836200270b565b82526020820152828816604082015282871660608201528a81019182528481015f815262004118602086019384515f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b03602052856060600260405f206001600160a01b0388511673ffffffffffffffffffffffffffffffffffffffff19825416178155600181016001600160a01b0360208a0151168154907bffffffffffffffff000000000000000000000000000000000000000060408c015160a01b169163ffffffff60e01b161717905501950151168619855416178455511515839068ff0000000000000000825491151560401b169068ff00000000000000001916179055565b51151569ff00000000000000000082549160481b169069ff00000000000000000019161790555194818b60408501511697818d6060870151169a87875197015160405197885260208801521660408601521660608401521688820152a4015116620041ae83826001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001662003353565b60e06001600160401b03602084015116925101515f0b90803b156200053e57604051639c45c34b60e01b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001660048201526001600160401b03909316602484015260448301939093525f90810b6064830152909182908183816084810162002f38565b94505050905062002f56925060208201516001600160401b03604051921682527f8d61555dcf69ba745e97fdea78e2652d9cdd464ca521faad482fe796f6d5841c60206001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001693a36001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016606082015262004e31565b9350949350505060208201516001600160a01b0360608401511691606481019081811162001a0e5760630190811162001a0e5760407fc8f90125c6a36c77a571201afc10310420481ab4895fadabb596d0ba71c22e3e9162002f569660648351920482526020820152a36001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000166060820152620046c4565b6001600160401b036305f5e0ff1982011162001a0e57620043be620043b86001600160401b036305f5e100936305f5e0ff190116620037fc565b6200286a565b0462003ef8565b6305f5e100811115620043da57505f62003ef8565b6001600160401b03816305f5e100031162001a0e57620043be620043b86001600160401b036305f5e10093840316620037fc565b90509062003edd565b63ffffffff6200442d8160608801511662005026565b16620f4240808281031162001a0e576001600160401b039262003e6662003ec792620f42409463ffffffff60408c0151169103620037cf565b5062003e6660ff60a08951015116620037bd565b604051635cffc5fb60e11b8152600490fd5b6127108181031162001a0e576001600160401b03612710620044b383820383861662003815565b0416919262003de0565b926001608060049286546001600160401b03811682526001600160401b038160401c1660208301526001600160401b0381841c16604083015260c01c606082015201940192019162003c95565b5090845f5260205f20905f915b8183106200453257505090602062003c769282010162003c68565b602091935080600191548385880101520191019091839262004517565b6020925062003c7694915060ff191682840152151560051b82010162003c68565b9260016004918554906001600160401b03918281168252828160401c16602083015260809281841c16604083015260c01c606082015201940192019162003b1d565b5090845f5260205f20905f915b818310620045da57505090602062003afe9282010162003af0565b6020919350806001915483858801015201910190918392620045bf565b6020925062003afe94915060ff191682840152151560051b82010162003af0565b506080810151909190156200464c5760246001600160a01b03604084015116604051906320a2097d60e11b82526004820152fd5b9080602062002f569201517f31604f5790fbed7a37471cea010a8f08483441a6e1235ac48bb59337439a630d60206001600160a01b03604085015116604051908152a26001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016606082015262004e31565b6060906060810151905f6001600160a01b0380931692835f526020945f8051602062005bb78339815191528652604091825f2096835162004705816200270b565b845193620047138562002727565b85518a5491905f81620047268562002c80565b918282526001958887821691825f1462004d6b57505060011462004d2a575b62004756925094939403826200277c565b8652808b019184885193898586948484549283815201935f52845f20965f915b83600384011062004cde575085620047b998549484841062004cc4575b84841062004ca7575b5083831062004c8a575b50501062004c7b5750905003826200277c565b82850152600289015495836001600160401b039788811688880152871c16606086015260038a0154946080958581168783015260ff8160a01c1660a083015260ff8160a81c1660c083015260b01c5f0b60e08201528252600660048b01549a60ff8585019c8d8b82169052891c1615158885015260058101546060850152015490878216868401528760a0840192881c168252868901976200485e868a511662003257565b1562004c0d57899a9b5f9a98999a508c612710620048958b519b8b6200488b60a083019e8f51906200287c565b9101519062003815565b04988a5199808b1162004c04575b508d8a8062004b2e575b620048c2620048d191620048e493516200287c565b60e08a5101515f0b9062004da2565b91909782878088511692511691620038d2565b1562004ae65750908c9695949392918a620049f9575b509260e0896200498f94620049c59b9997947fb021c853215aadb12b6fa8afa7b3158201517d9abf7f756cdbb67bd66abc5a1c9f9e9d9b99976200496e906001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0460205260405f2090565b6200497b8d82546200287c565b9055511696511694510151900b9062004da2565b9190928d84620049ca575b5050505050880151965116965192519351938493846040919493926060820195825260208201520152565b0390a4565b620049ee94620049e89188620049e08562002847565b169062003353565b62002ea6565b5f8080808d6200499a565b919293949596505087807f00000000000000000000000000000000000000000000000000000000000000001691875101511690803b156200053e578b5163f3fef3a360e01b81526001600160a01b03929092166004830152602482018a90525f908290604490829084905af1801562004adc578c948f91998098956200498f957fb021c853215aadb12b6fa8afa7b3158201517d9abf7f756cdbb67bd66abc5a1c9f9e9d9b9995620049c59d62004ac0575b509497995094509950939597999a9b620048fa565b60e0945062004acf90620026f7565b6200496e5f945062004aab565b8b513d5f823e3d90fd5b9a50505050955050505093957f8d61555dcf69ba745e97fdea78e2652d9cdd464ca521faad482fe796f6d5841c955084915062004b238162004e31565b0151945191168152a3565b62004b45908a8301518c858c51015116906200503f565b1562004b53575b8a620048ad565b509192939495968881999a50602492508389510151168c51928380926370a0823160e01b82528d7f00000000000000000000000000000000000000000000000000000000000000001660048301525afa90811562004adc57918e918e9a999897969594935f9162004bc7575b509962004b4c565b92505098508681813d831162004bfc575b62004be481836200277c565b810103126200053e57518c988e91620048d162004bbf565b503d62004bd8565b99505f620048a3565b505050509296509450839293015115155f1462004c405750505191516320a2097d60e11b81529116600482015260249150fd5b62002f56948294827f31604f5790fbed7a37471cea010a8f08483441a6e1235ac48bb59337439a630d9401519451169051908152a262004e31565b60c01c81520185905f6200303b565b9091946001600160401b038560801c1681520193015f85620047a6565b956001600160401b03868495981c16815201940190858e6200479c565b6001600160401b0386168752958101959282019262004793565b95509360049296509284919388546001600160401b039283821683528d8483831c169084015260809382851c169083015260c01c8782015201960191019386948c9296938a9562004776565b50508c5f528186805f20865f915b85831062004d5157505062004756935082010162004745565b809192945054838588010152019101879086859362004d38565b60ff1916848201526200475694151560051b8401019150620047459050565b81811062004d96575050565b5f815560010162004d8a565b9190805f0b9081155f1462004dc05750506001600160401b03821690565b5f82131562004e035762004dd8915060ff16620037bd565b918215620037e857620038ce62004dfb82856001600160401b03940690620037cf565b9384620037dd565b505f0380805f0b0362001a0e57620038ce62004e2a6001600160401b039260ff16620037bd565b8462003815565b805161271062004e5862004e4c60a08501938451906200287c565b60808501519062003815565b0481518181115f146200501d5750905b8190602084019182519162004e8f60608701956001600160a01b039485885116906200503f565b1562005014575b8162004f61575b620049c5837f4755f239bb1b047245415cb917deced72a3ca8baebcef109c396ff332ea6f50f9462004ed1858a516200287c565b9662004eeb828a51169860408c0199848b51169062003353565b62004f29828a51166001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0460205260405f2090565b62004f368782546200287c565b9055519651169651169651915192604051938493846040919493926060820195825260208201520152565b84518316927f00000000000000000000000000000000000000000000000000000000000000008116803b156200053e5760405163f3fef3a360e01b81526001600160a01b03959095166004860152602485018490525f908590604490829084905af1908115620019c5577f4755f239bb1b047245415cb917deced72a3ca8baebcef109c396ff332ea6f50f94620049c59262005002575b5093505062004e9d565b6200500d90620026f7565b5f62004ff8565b5f915062004e96565b90509062004e68565b90606463ffffffff8093160291821691820362001a0e57565b6040516370a0823160e01b81527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0390811660048301529092169392909190602082602481885afa918215620019c5575f92620050f2575b5080821094851595620050b2575b50505050565b7f5180f0ad9e9bd2296de2ee38c85d11c56613fa73f8ee66792f26ac318f1274749260609260405192835260208301526040820152a25f808080620050ac565b9091506020813d60201162005123575b8162005111602093836200277c565b810103126200053e5751905f6200509e565b3d915062005102565b51906001600160a01b03821682036200053e57565b519060ff821682036200053e57565b9060ff8091169116039060ff821162001a0e57565b919060ff821660ff82168181145f1462005180575050505090565b929391921015620051b857620038ce9192620051b0620051aa6001600160401b0395869362005150565b620037bd565b911662003815565b620038ce91620051d6620051aa6001600160401b0395869362005150565b9116620037dd565b9015620051fb57815115620051f1575090565b3b156200053e5790565b5080519081156200053e57602001fdfe60a080604052346100455733608052610961908161004a8239608051818181608b015281816101ac0152818161038e015281816104a20152818161051b015261069c0152f35b5f80fdfe60406080815260049081361015610014575f80fd5b5f915f3560e01c908163435354d3146104d757816390a0827b1461047b5781639c45c34b14610469578163a703334f1461033c578163de1a324a146100d457508063e94b77c1146100b35763eba61c0e1461006d575f80fd5b346100af57816003193601126100af57602090516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b5080fd5b82346100d1576100ce6100c5366105b5565b9291909161068d565b80f35b80fd5b91905034610338576101009136600319018381126103345760a01361033057815167ffffffffffffffff939060a081018581118282101761031d5784526101196105a2565b91828252602435918215158303610319576020810192835263ffffffff9360443585811681036102ce578783015260643585811681036102ce5760608301526084356001600160a01b03928382168203610315576080015260a4358881168091036102ce5760c435918983168093036102ce5760e435936fffffffffffffffffffffffffffffffff85168095036102ce577f000000000000000000000000000000000000000000000000000000000000000016330361031557866101dd9116610663565b9451151581156103055782156102f5578a6102e557885196602088019616865288870152606086015260808501525f60a0850152600260c085015260e084015260e08352820194828610908611176102d25761026891602491868652630100000160e01b6101208301526102598251809261012485019061079c565b81010383810186520184610641565b73333333333333333333333333333333333333333392833b156102ce576102a5935f928385518097819582946317938e1360e01b845283016107bd565b03925af19081156102c557506102b9575080f35b6102c391506105fd565b005b513d5f823e3d90fd5b5f80fd5b604183634e487b7160e01b5f525260245ffd5b88516376d4929560e11b81528890fd5b88516313c0a8df60e01b81528890fd5b885163017461b760e71b81528890fd5b8a80fd5b8780fd5b604184634e487b7160e01b5f525260245ffd5b8380fd5b8480fd5b8280fd5b919050346103385760603660031901126103385781359067ffffffffffffffff928383168093036102ce576024356001600160a01b03948582168092036102ce576044359081168091036102ce5786957f000000000000000000000000000000000000000000000000000000000000000016330361046557610259946103fc9260249286519260208401928352878401526060830152606082526103df82610625565b85519687926280000360e11b60208501525180928585019061079c565b73333333333333333333333333333333333333333392833b15610334576104399385928385518097819582946317938e1360e01b845283016107bd565b03925af190811561045c575061044c5750f35b610455906105fd565b6100d15780f35b513d84823e3d90fd5b8580fd5b83346100d1576100ce6100c5366105b5565b91905034610338573660031901126100af57356001600160a01b038082168092036102ce577f00000000000000000000000000000000000000000000000000000000000000001633036100af576100ce906024359033906107e9565b919050346102ce57806003193601126102ce576104f26105a2565b91602435926fffffffffffffffffffffffffffffffff84168094036102ce576001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001633036102ce5761054a90610663565b9282519063ffffffff602083019516855283820152828152606081019381851067ffffffffffffffff8611176102d25761026891602491868652630100000b60e01b608083015261025982518092608485019061079c565b6004359063ffffffff821682036102ce57565b60809060031901126102ce576004356001600160a01b03811681036102ce579060243567ffffffffffffffff811681036102ce579060443590606435805f0b81036102ce5790565b67ffffffffffffffff811161061157604052565b634e487b7160e01b5f52604160045260245ffd5b6080810190811067ffffffffffffffff82111761061157604052565b90601f8019910116810190811067ffffffffffffffff82111761061157604052565b9061271063ffffffff8093160191821161067957565b634e487b7160e01b5f52601160045260245ffd5b9092916001600160a01b0392837f00000000000000000000000000000000000000000000000000000000000000001633036102ce57805f0b80155f1461071c575050915b826106dd575b50505050565b67ffffffffffffffff732000000000000000000000000000000000000000941684018094116106795780610713941691166107e9565b5f8080806106d7565b5f81131561075c57506107319060ff166108f1565b8015610748578106810390811161067957916106d1565b634e487b7160e01b5f52601260045260245ffd5b90505f9391930380805f0b03610679576107789060ff166108f1565b9280938181810204149015176106d157634e487b7160e01b5f52601160045260245ffd5b5f5b8381106107ad5750505f910152565b818101518382015260200161079e565b604091602082526107dd815180928160208601526020868601910161079c565b601f01601f1916010190565b9190604051906020938483019363a9059cbb60e01b85526001600160a01b03809316602485015260448401526044835261082283610625565b169160405190604082019067ffffffffffffffff9383831085841117610611577f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564875f9586956040528181520152519082865af13d156108e4573d918211610611576108ac926040519261089f86601f19601f8401160185610641565b83523d5f8685013e610902565b80519182159182156108c4575b50509050156102ce57565b8092508193810103126102ce57015180151581036102ce57805f806108b9565b6108ac9260609250610902565b60ff16604d811161067957600a0a90565b901561091c57815115610913575090565b3b156102ce5790565b5080519081156102ce57602001fdfea2646970667358221220c909e7dbe656d7c973709e34eb673837a89b1ae88012e9262063134290e6f21f64736f6c634300081800336c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b00a2646970667358221220d7009235facd2afacf7dbd784aa03c97dd6444be46f4a33aa486147317b81a7764736f6c634300081800330000000000000000000000003a73033c0b1407574c76bdbac67f126f6b4a9aa9000000000000000000000000904861a24f30ec96ea7cfc3be9ea4b476d237e9800000000000000000000000090e2487764e5316a2e4109c2ed40a3b3ad423659000000000000000000000000b8ce59fc3717ada4c02eadf9682a9e934f625ebb0000000000000000000000000980d0f6799ca06c71ffafdc0e423cf2b0f20502", + "nonce": "0x24c", + "chainId": "0x3e7" + }, + "additionalContracts": [ + { + "transactionType": "CREATE", + "contractName": "HyperCoreFlowExecutor", + "address": "0xf7f570ebac4f4a25ff06b8a49a156f32c2d30e6f", + "initCode": "0x60c0346200010457601f62005d3e38819003918201601f19168301916001600160401b038311848410176200010857808492604094855283398101031262000104576200005a602062000052836200011c565b92016200011c565b6001600160a01b0390911660805260a052604051615c0c90816200013282396080518181816108b7015281816109ff0152818161158b0152818161182f01528181612a1701528181614a0501528181614b7c01528181614f68015261504e015260a051818181610670015281816107de01528181611a7b01528181613a6e01528181614188015281816141e401528181614288015281816142b40152818161435301526146990152f35b5f80fd5b634e487b7160e01b5f52604160045260245ffd5b51906001600160a01b0382168203620001045756fe6080604052600436101562000012575f80fd5b5f803560e01c806246912e146200257057806301ffc9a71462002515578063037a06a414620022c957806304c73f6014620022a9578063057f037014620021f45780631f74a0b5146200217257806321081d3c1462002135578063248a9ca314620020e75780632e748b211462001f7f5780632f2ff15d1462001f1a578063319adf9f1462001b6657806336568abe1462001b1757806337710e201462001abd57806339fff0981462001a225780633b1c6a0114620017275780633cf3a02514620016eb5780634265fe861462001699578063490e662f146200165c5780634b3b029b146200161f578063502a82e21462001554578063521c98ba1462000d9157806369b97ac71462000d7157806379c7b60b1462000d1757806379c7f2891462000c1557806390a0827b1462000bd457806391d148541462000b6657806396cc2cfb14620008f9578063a217fddf14620008db578063a4b672b61462000895578063af5de6f91462000802578063c55dae6314620007bc578063ccbedaec1462000542578063d06e28ed1462000443578063d547741f14620003da578063e38b73a914620003bc578063ea0aaf241462000359578063eb84e7f2146200024b5763ff3eae0014620001e2575f80fd5b34620002485760c0366003190112620002485762000200366200279e565b7fc56a3250645180a53cd9e196b2ee0a634a4f54e2edf59ea457f2083917e4d10054156200023657620002339062004e31565b80f35b60405163cd6d8f7d60e01b8152600490fd5b80fd5b503462000248576020366003190112620002485760408160c09260a0835162000274816200270b565b8281528260208201528285820152826060820152826080820152015260043581527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0360205220604051620002c8816200270b565b6001600160a01b039060028284541693848352600181015490602084019185811683526001600160401b0393849283604088019360a01c1683520154956060860193838816855260ff60a06080890198828b60401c1615158a52019860481c1615158852604051988952511660208801525116604086015251166060840152511515608083015251151560a0820152f35b50346200024857602036600319011262000248576020620003b36200037d620025de565b6001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0560205260405f2090565b54604051908152f35b50346200024857806003193601126200024857602060405160068152f35b50346200024857604036600319011262000248576200043f600435620003ff620025f5565b908084527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b62680060205262000439600160408620015462002e64565b620039b1565b5080f35b503462000248576040366003190112620002485762000461620025de565b602435906fffffffffffffffffffffffffffffffff82168092036200053e576200048a62002e08565b6200049581620032e8565b90836001600160a01b039263ffffffff84608083015116915116813b156200053a57829160448392604051948593849263435354d360e01b845260048401528a60248401525af180156200052f5762000513575b5050167f02366c0d102495be1ee805b749be7baebab4fc0710c6d3f38751f1a22bd711648380a380f35b6200051e90620026f7565b6200052b57835f620004e9565b8380fd5b6040513d84823e3d90fd5b8280fd5b5f80fd5b50346200024857606036600319011262000248578062000561620025de565b6200056b6200260c565b906200057662002623565b906200058162002d37565b620005bc816001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0260205260405f2090565b544311156200071a574362000601826001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0260205260405f2090565b556001600160a01b0380600162000648846001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0160205260405f2090565b01541693816001600160401b03938483166200071f575b5050508183166200066e578480f35b7f00000000000000000000000000000000000000000000000000000000000000001684525f8051602062005bb783398151915260205260046040852001541690823b156200071a5760405163a703334f60e01b81526001600160401b039283166004820152336024820152911660448201529082908290606490829084905af180156200052f5762000702575b8080808480f35b6200070d90620026f7565b6200024857805f620006fb565b505050fd5b1686525f8051602062005bb783398151915260205282600460408820015416853b15620007b85760405163a703334f60e01b81526001600160401b03918216600482015233602482015291166044820152858160648183895af1908115620007ad57869162000791575b82906200065f565b6200079c90620026f7565b620007a957845f62000789565b8480fd5b6040513d88823e3d90fd5b8680fd5b5034620002485780600319360112620002485760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b503462000248576020366003190112620002485760206001600160a01b03620008346200082e620025de565b620031ff565b6109ab60405162000848858301826200277c565b818152848101916200520c8339519020604051908482019260ff60f81b84523060601b60218401526035830152605582015260558152620008898162002744565b51902016604051908152f35b5034620002485780600319360112620002485760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b50346200024857806003193601126200024857602090604051908152f35b503462000248576040366003190112620002485762000917620025de565b906200092262002e08565b6200092d8262002f58565b6200093883620032e8565b926200094e60e083510151840b60243562004da2565b9460208401906001600160401b036200097381845116898360a08a01511691620038d2565b1562000b3657869750620009b7856001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0460205260405f2090565b620009c48582546200287c565b90556001600160a01b03808616807f410b9a8c926b6c439cdceb39c0bb8f829838a25bc5a26af9d4c263d1313cc46b6020604051898152a2817f000000000000000000000000000000000000000000000000000000000000000016803b1562000b325760405163f3fef3a360e01b81526001600160a01b038316600482015260248101889052908a908290604490829084905af1801562000b275787918b9162000b08575b505062000a8290608060e0960192848451169062003353565b511692511694510151850b93813b1562000b0457604051639c45c34b60e01b81526001600160a01b039490941660048501526001600160401b0316602484015260448301919091525f9290920b60648201529082908290608490829084905af180156200052f5762000af15750f35b62000afc90620026f7565b620002485780f35b8580fd5b62000b15919250620026f7565b62000b235785895f62000a69565b8880fd5b6040513d8c823e3d90fd5b8980fd5b6040516377e88bc960e11b81526001600160a01b03861660048201526001600160401b0389166024820152604490fd5b50346200024857604036600319011262000248576001600160a01b03604062000b8e620025f5565b9260043581527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268006020522091165f52602052602060ff60405f2054166040519015158152f35b50346200024857604036600319011262000248576200023362000bf6620025de565b62000c0062002d37565b602435906001600160a01b0333911662003353565b503462000248576020366003190112620002485760a062000c7a62000c39620025de565b62000c4362002d0b565b506001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0160205260405f2090565b6001600160a01b0360016040519262000c9384620026db565b805463ffffffff90818116865260ff8160201c1615156020870152818160281c16604087015260481c166060850152015416608082015262000d1560405180926001600160a01b036080809263ffffffff8082511686526020820151151560208701528060408301511660408701526060820151166060860152015116910152565bf35b50346200024857602036600319011262000248576020620003b362000d3b620025de565b6001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0460205260405f2090565b503462000248578060031936011262000248576020604051620f42408152f35b5034620002485760a0366003190112620002485762000daf620025de565b62000db96200263a565b9062000dc46200264e565b6001600160401b0360643516606435036200053e57608435906001600160401b03821682036200053e5762000df862002db1565b62000e0262002c05565b508480604051602081019063ffffffff881682526020815262000e258162002760565b519061080c5afa62000e3662002cd7565b901562001542578051810190602081830312620007b85760208101516001600160401b038111620015055761010081830184031262001505576040519262000e7e8462002727565b602082840101516001600160401b03811162000b325760208201603f828587010101121562000b32576020818486010101519062000ebc8262002cbb565b9162000ecc60405193846200277c565b8083526020840160408284888a01010101116200152a5762000efa9160406020850191878901010162002691565b8452604082840101516001600160401b03811162000b325760208201603f828587010101121562000b3257602081848601010151916001600160401b0383116200152e578260051b6040519362000f5560208301866200277c565b84526020808501920160408285888a01010101116200152a576040838688010101915b60408285888a01010101831062001509575050505060208401526101009062000fa683820160600162003428565b604085015262000fbb6080828501016200512c565b606085015262000fd060a0828501016200512c565b9283608086015262000fe760c08383010162005141565b60a086015262000ffc60e08383010162005141565b60c08601520101519081880b820362001505578160e08401526001600160a01b03808716911603620007b8576200103890870b60643562003829565b506040519162001048836200270b565b825263ffffffff86166020830152821515604083015260608201526001600160401b036064351660808201526001600160401b03831660a08201526001600160a01b03841686525f8051602062005bb783398151915260205260408620815180518051906001600160401b03821162001438578190620010c9855462002c80565b601f8111620014c5575b50602090601f831160011462001458578b926200144c575b50508160011b915f199060031b1c19161782555b60208101518051906001600160401b038211620014385768010000000000000000821162001438576020906001850154836001870155808410620013e5575b500190600184018a5260208a20908a5b8160021c81106200139d5750600319811680820362001334575b50505050936001600160a01b03937f12cf3d04179e82c834f3ee7169a5df80651aa65530127f9ddb04c8cd82244353969360068460809860026001600160401b039701876040830151168154907bffffffffffffffffffffffffffffffffffffffff0000000000000000606085015160401b169163ffffffff60e01b16171790556003820190898b820151169082549174ff000000000000000000000000000000000000000060a083015160a01b16907fffffffffffffffffff000000000000000000000000000000000000000000000060e075ff00000000000000000000000000000000000000000060c086015160a81b1694015160b01b76ff00000000000000000000000000000000000000000000169416171717179055620012c5600482018760208601511688198254161781556040850151151568ff0000000000000000825491151560401b169068ff00000000000000001916179055565b6060830151600582015501908488820151166fffffffffffffffff000000000000000060a0845493015160401b16916fffffffffffffffffffffffffffffffff19161717905563ffffffff6040519816885215156020880152816064351660408801521660608601521692a280f35b928b938c5b81840381106200135d5750505060021c0155838360066001600160401b0362001168565b9091946020620013926001926001600160401b03895116908560031b60031b916001600160401b03809116831b921b19161790565b960192910162001339565b8b8c5b60048110620013b75750838201556001016200114e565b85519095916001916020916001600160401b0360068a901b81811b199092169216901b1792019501620013a0565b6200141890600187018d52838d20600380870160021c820192601888831b16806200141f575b500160021c019062004d8a565b5f6200113e565b5f1990818601918254918a03851b1c1690555f6200140b565b634e487b7160e01b8a52604160045260248afd5b015190505f80620010eb565b9250848b5260208b20908b935b601f1984168510620014a9576001945083601f1981161062001490575b505050811b018255620010ff565b01515f1960f88460031b161c191690555f808062001482565b8181015183556020948501946001909301929091019062001465565b620014f390868d5260208d20601f850160051c81019160208610620014fa575b601f0160051c019062004d8a565b5f620010d3565b9091508190620014e5565b8780fd5b906020806040936200151b8662003428565b81520193019290915062000f78565b8b80fd5b634e487b7160e01b8b52604160045260248bfd5b604051639b0c335d60e01b8152600490fd5b503462000248576040366003190112620002485762001572620025de565b6024356200157f62002d37565b826001600160a01b03807f000000000000000000000000000000000000000000000000000000000000000016931692803b156200161b5760405163f3fef3a360e01b81526001600160a01b0385166004820152602481018490529082908290604490829084905af180156200052f5762001603575b50506200023391339062003353565b6200160e90620026f7565b6200053a57825f620015f4565b5080fd5b5034620002485780600319360112620002485760206040517f5300fde95a5e446527bf6aa7c91bd6661bef5398afc77061d9bc87efb80b7ef68152f35b5034620002485780600319360112620002485760206040517fc56a3250645180a53cd9e196b2ee0a634a4f54e2edf59ea457f2083917e4d1008152f35b5034620002485760c03660031901126200024857620016b8366200279e565b7fc56a3250645180a53cd9e196b2ee0a634a4f54e2edf59ea457f2083917e4d100541562000236576200023390620046c4565b50346200024857602036600319011262000248576200170962002d37565b80808080600435335af16200171d62002cd7565b5015620002485780f35b503462000248576060366003190112620002485762001745620025f5565b6001600160a01b0360443581811692918382036200053e576200176762002e08565b620017728262002f58565b906200177e8462003257565b62000b045760408201511562000b04576001600160401b0391600183608083015116019083821162001a0e57620017bd60e082510151890b8362003829565b5094620017db602083019386855116908760a08601511691620038d2565b1562000b23576200181c906001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0560205260405f2090565b620018298682546200287c565b905587837f000000000000000000000000000000000000000000000000000000000000000016803b156200161b5760405163f3fef3a360e01b81526001600160a01b038a166004820152602481018890529082908290604490829084905af180156200052f57620019f0575b50509183826200192494620018bc60e062001913986024975116925101518c0b8962004da2565b508481620019d0575b505050511695604051916020830191169687825260408301526001606083015260608252620018f48262002744565b6040519485926280000360e11b60208501525180928585019062002691565b81010360048101845201826200277c565b733333333333333333333333333333333333333333803b156200053e576040516317938e1360e01b815260206004820152915f91839182908490829062001970906024830190620026b4565b03925af18015620019c557620019b3575b506040519081527f2b348084e891b20d449a69f90114c5ab7bf7c84d64c25445c8ab440d469a6b4d602060043592a480f35b620019be90620026f7565b5f62001981565b6040513d5f823e3d90fd5b620019df620019e79362002847565b168b62003353565b5f8084620018c5565b620019ff9094929394620026f7565b62001505579190875f62001895565b634e487b7160e01b5f52601160045260245ffd5b5034620002485760e0366003190112620002485762001a41366200279e565b7fc56a3250645180a53cd9e196b2ee0a634a4f54e2edf59ea457f2083917e4d100541562000236576001600160a01b0380606083015116907f000000000000000000000000000000000000000000000000000000000000000016145f1462001aae576200023390620046c4565b620002339060c4359062003a45565b50346200024857602036600319011262000248576020620003b362001ae1620025de565b6001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0260205260405f2090565b503462000248576040366003190112620002485762001b35620025f5565b336001600160a01b0382160362001b54576200043f90600435620039b1565b60405163334bd91960e11b8152600490fd5b50346200024857602036600319011262000248576001600160a01b0362001b8c620025de565b62001b9662002c45565b501681525f8051602062005bb783398151915260205260408120906040519162001bc0836200270b565b60405162001bce8162002727565b6040518254818562001be08362002c80565b808352926001811690811562001ef9575060011462001eb4575b62001c08925003826200277c565b815260405160018301805480835290855260208086209083019186915b81600384011062001e72579360069593819362001c73936001600160401b039997549181811062001e5e575b81811062001e47575b81811062001e30575b1062001e21575b5003826200277c565b60208201526001600160a01b036002830154858116604084015260401c16606082015260038201546001600160a01b038116608083015260ff8160a01c1660a083015260ff8160a81c1660c083015260b01c850b60e0820152855260ff6004820154848116602088015260401c1615156040860152600581015460608601520154818116608085015260401c1660a0830152604051906020825282519060c0602084015262001d31825161010060e08601526101e0850190620026b4565b60208084015185830360df190161010087015280518084529282019492910190835b81811062001e015750505083946001600160401b039260e08385604060a0960151166101208901526001600160a01b036060820151166101408901526001600160a01b0360808201511661016089015260ff858201511661018089015260ff60c0820151166101a08901520151900b6101c086015282602082015116604086015260408101511515606086015260608101516080860152826080820151168286015201511660c08301520390f35b82516001600160401b031686526020958601959092019160010162001d53565b60c01c81526020015f62001c6a565b9260206001918c8560801c16815201930162001c63565b9260206001918c8560401c16815201930162001c5a565b9260206001918c8516815201930162001c51565b9260016004918554906001600160401b03918281168252828160401c16602083015260809281841c16604083015260c01c606082015201940192019162001c25565b5090848652602086209086915b81831062001edc57505090602062001c089282010162001bfa565b602091935080600191548385880101520191019091839262001ec1565b6020925062001c0894915060ff191682840152151560051b82010162001bfa565b50346200024857604036600319011262000248576200043f60043562001f3f620025f5565b908084527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b62680060205262001f79600160408620015462002e64565b62003912565b503462000248576080366003190112620002485762001f9d620025de565b62001fa76200260c565b9062001fb262002623565b90606435926fffffffffffffffffffffffffffffffff84168094036200053e5762001fdc62002e08565b62001fe782620032e8565b856001600160a01b03928360808401511692833b156200053a57620020646101048492836040519586948593636f0d192560e11b855260048501906001600160a01b036080809263ffffffff8082511686526020820151151560208701528060408301511660408701526060820151166060860152015116910152565b6001600160401b038091169b8c60a485015216978860c48401528c60e48401525af180156200052f57620020cb575b5050907f500d805a349357fe5d4759fe052d79bd744b82c8452837f52a7456ec7d3d751c92604092835195865260208601521692a380f35b620020d990939293620026f7565b62000b045790855f62002093565b50346200024857602036600319011262000248576001604060209260043581527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268008452200154604051908152f35b5034620002485780600319360112620002485760206040517f880a9ba888678c7fe4e8c4f028c224f26ce12a3bed6e96025c61ef8a5db6312f8152f35b503462000248576060366003190112620002485762002190620025de565b6001600160401b036024358181116200052b57620021b39036906004016200265e565b91909360443591821162000248576020620021ec868686620021d936600489016200265e565b939092620021e662002e08565b6200288a565b604051908152f35b50346200053e5760403660031901126200053e5762002212620025de565b906024356200222062002d37565b6001600160a01b03928360806200223783620032e8565b015116803b156200053e576040516390a0827b60e01b81526001600160a01b038316600482015260248101849052905f908290604490829084905af18015620019c55762002290575b5062000233929333911662003353565b620002339350620022a190620026f7565b5f9262002280565b346200053e575f3660031901126200053e5760206040516305f5e1008152f35b346200053e5760a03660031901126200053e57620022e6620025de565b620022f06200263a565b620022fa6200264e565b6064359063ffffffff938483168093036200053e57608435908582168092036200053e57620023298162002f58565b506200233462002db1565b6001600160a01b03918260016200237b846001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0160205260405f2090565b0154168015620024bc575b8360018960405199620023998b620026db565b16988981526020810197151597888152604082018a81526060830190878252856080850197169d8e8852620023fe8a6001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0160205260405f2090565b9451166cffffffff00000000000000000068ffffffff000000000064ff0000000087549651151560201b16935160281b16935160481b16936cffffffffffffffffffffffffff19161717171781550191511673ffffffffffffffffffffffffffffffffffffffff19825416179055620024778762003257565b156200053e577ff0dcc8957a27613dd82c92382ad37254b9744169d0caa5f3873cfec7ba794eb9946080946040519788526020880152604087015260608601521692a3005b50620024c882620031ff565b6040516109ab8082018281106001600160401b03821117620025015782916200520c833903905ff58062002386576040513d5f823e3d90fd5b634e487b7160e01b5f52604160045260245ffd5b346200053e5760203660031901126200053e5760043563ffffffff60e01b81168091036200053e57602090637965db0b60e01b81149081156200255e575b506040519015158152f35b6301ffc9a760e01b1490508262002553565b346200053e5760403660031901126200053e57620025dc62002591620025de565b6001600160a01b03620025a36200260c565b91620025ae62002d37565b165f525f8051602062005bb78339815191526020526001600160401b03600460405f20015416339062002ea6565b005b600435906001600160a01b03821682036200053e57565b602435906001600160a01b03821682036200053e57565b602435906001600160401b03821682036200053e57565b604435906001600160401b03821682036200053e57565b6024359063ffffffff821682036200053e57565b6044359081151582036200053e57565b9181601f840112156200053e578235916001600160401b0383116200053e576020808501948460051b0101116200053e57565b5f5b838110620026a35750505f910152565b818101518382015260200162002693565b90602091620026cf8151809281855285808601910162002691565b601f01601f1916010190565b60a081019081106001600160401b038211176200250157604052565b6001600160401b0381116200250157604052565b60c081019081106001600160401b038211176200250157604052565b61010081019081106001600160401b038211176200250157604052565b608081019081106001600160401b038211176200250157604052565b604081019081106001600160401b038211176200250157604052565b90601f801991011681019081106001600160401b038211176200250157604052565b60c09060031901126200053e5760405190620027ba826200270b565b60043582526024356020830152816001600160a01b0360443581811681036200053e57604083015260643590811681036200053e576060820152608435608082015260a060a435910152565b9190811015620028175760051b0190565b634e487b7160e01b5f52603260045260245ffd5b9190916001600160401b038080941691160191821162001a0e57565b9073200000000000000000000000000000000000000091820180921162001a0e57565b906305f5e0ff820180921162001a0e57565b9190820180921162001a0e57565b919392935f945f948083036200053e57620028d5856001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0260205260405f2090565b544311156200053e57869796939291620028ef8662002f58565b926080620028fd88620032e8565b01906001600160a01b0392838351169460208701976200292a6001600160401b0397888b5116906200343d565b845f9b5b1062002b94575b50508c15925062002b8791505057436200297f886001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0260205260405f2090565b5582861662002993575b5050505050505050565b620029c195620029ab60e0865101515f0b8262003829565b85889992995116908660a08901511691620038d2565b1562002b4d575062002a03876001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0460205260405f2090565b62002a108782546200287c565b90558682167f00000000000000000000000000000000000000000000000000000000000000008316803b156200053e5760405163f3fef3a360e01b81526001600160a01b038316600482015260248101899052905f908290604490829084905af18015620019c55762002b29575b509062002a9587849360e096958451169062003353565b511693511691510151850b93823b1562000b0457604051639c45c34b60e01b81526001600160a01b0390911660048201526001600160401b03909116602482015260448101929092525f9290920b606482015290829082908183816084810103925af180156200052f5762002b11575b80808080808062002989565b62002b1d8291620026f7565b62000248578062002b05565b60e094939291995062002b3c90620026f7565b5f98929350909162002a9562002a7e565b8451608001516040516377e88bc960e11b81529084166001600160a01b031660048201526001600160401b03919091166024820152604490fd5b505f985050505050505050565b62002ba6859e9f809c95968462002806565b359062002bb58c858862002806565b359189831683036200053e578a62002bd2938a8a51169262003520565b9490911562002bfb5762002beb8f93926001926200282b565b9b019e9d8f95949192956200292e565b9a9e9d5062002935565b6040519062002c148262002727565b5f60e08360608152606060208201528260408201528260608201528260808201528260a08201528260c08201520152565b6040519062002c54826200270b565b5f60a08362002c6262002c05565b81528260208201528260408201528260608201528260808201520152565b90600182811c9216801562002cb0575b602083101462002c9c57565b634e487b7160e01b5f52602260045260245ffd5b91607f169162002c90565b6001600160401b0381116200250157601f01601f191660200190565b3d1562002d06573d9062002ceb8262002cbb565b9162002cfb60405193846200277c565b82523d5f602084013e565b606090565b6040519062002d1a82620026db565b5f6080838281528260208201528260408201528260608201520152565b335f9081527f176c3a4e1b4d1d61213187c0dde7f83e2e2c933a339cb5403b4090bc2f0aa49a60205260409020547f880a9ba888678c7fe4e8c4f028c224f26ce12a3bed6e96025c61ef8a5db6312f9060ff161562002d935750565b6044906040519063e2517d3f60e01b82523360048301526024820152fd5b335f9081527fb7db2dd08fcb62d0c9e08c51941cae53c267786a0b75803fb7960902fc8ef97d602052604090205460ff161562002dea57565b60405163e2517d3f60e01b81523360048201525f6024820152604490fd5b335f9081527fcf7720576cdb70176b3017d893bcb89ba0f36ed0261d4350fd0180e938b3bacd60205260409020547f5300fde95a5e446527bf6aa7c91bd6661bef5398afc77061d9bc87efb80b7ef69060ff161562002d935750565b805f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b62680060205260405f20335f5260205260ff60405f2054161562002d935750565b620019139262002eec92602492604051926001600160a01b0360208501931683526001600160401b03809216604085015216606083015260608252620018f48262002744565b733333333333333333333333333333333333333333803b156200053e576040516317938e1360e01b815260206004820152915f91839182908490829062002f38906024830190620026b4565b03925af18015620019c55762002f4b5750565b62002f5690620026f7565b565b9062002f6362002c45565b506001600160a01b038092165f5260205f8051602062005bb78339815191528152604092835f2084519062002f98826200270b565b85519562002fa68762002727565b80518254905f8162002fb88462002c80565b918282526001948a86821691825f14620031e05750506001146200319f575b62002fe5925003826200277c565b88528083019082519087829384928282549586815201915f52825f20945f5b8160038201106200315a57620030469654928583831062003140575b83831062003124575b83831062003107575b505010620030f8575b50905003826200277c565b858801526006600283015492856001600160401b0394858116858c0152841c1660608a015260ff60038201549760808b019a888a168c52828a60a01c1660a082015260c0810199838160a81c168b5260b01c5f0b60e0820152875260048301549086821690880152841c161515838601526005810154606086015201549082821660808501521c1660a082015293511615159081620030e9575b50156200053e57565b60ff9150511615155f620030e0565b60c01c81520188905f6200303b565b9091946001600160401b038560801c1681520193015f8562003032565b8192956001600160401b03868d1c168152019401908562003029565b8192956001600160401b0386168152019401908562003020565b86546001600160401b038082168652818b1c811696860196909652608081811c9096168a86015260c01c6060850152958201958795508c949093019260040162003004565b5050845f528188805f20855f915b858310620031c657505062002fe5935082010162002fd7565b8091929450548385880101520191018990858593620031ad565b60ff19168482015262002fe594151560051b840101915062002fd79050565b60405160208101913060601b83526bffffffffffffffffffffffff199060601b16603482015260288152606081018181106001600160401b03821117620025015760405251902090565b519081151582036200053e57565b5f80916040516001600160a01b036020820192168252602081526200327c8162002760565b51906108105afa6200328d62002cd7565b9015620032d6576020818051810103126200053e5760405190602082018281106001600160401b038211176200250157604052620032ce9060200162003249565b809152151590565b6040516313dd7ccd60e31b8152600490fd5b620032f79062000c4362002d0b565b906001600160a01b036001604051936200331185620026db565b805463ffffffff90818116875260ff8160201c1615156020880152818160281c16604088015260481c1660608601520154169182608082015291156200053e57565b60405163a9059cbb60e01b602082019081526001600160a01b039384166024830152604480830195909552938152620033ea9390925f92839291906200339b6064876200277c565b16937f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65646020604051620033ce8162002760565b8181520152519082855af1620033e362002cd7565b91620051de565b805190811591821562003402575b5050156200053e57565b81925090602091810103126200053e57602062003420910162003249565b5f80620033f8565b51906001600160401b03821682036200053e57565b604080516001600160a01b0392909216602083019081526001600160401b039390931682820152808252915f918291906200347a6060826200277c565b51906108015afa6200348b62002cd7565b9015620034f5576060818051810103126200053e5781516001600160401b0392606082018481118382101762002501578152620034ee6060620034d16020860162003428565b94858552620034e284820162003428565b60208601520162003428565b9101521690565b8151639d2c8fcb60e01b8152600490fd5b6001600160401b03918216908216039190821162001a0e57565b949192909394805f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0360205260405f20906001600160a01b039485835416958615620037ab57600284019788549860ff8a60481c166200379957600186015483608086510151168482160362003780576001600160401b039060a01c166001600160401b038b1660ff8c60401c165f146200371f575080926001600160401b03811682115f146200371657620035d79162003506565b995b620035e58b8d6200282b565b9b6001600160401b038d166001600160401b0385161162003704575069010000000000000000009069ff00000000000000000019161790556200362b8160019b62003506565b976001600160401b036020846200364b8d60e0895101515f0b9062003829565b50991695015116843b156200053e5760405163a703334f60e01b81526001600160401b0391821660048201526001600160a01b0392909216602483015282166044820152925f908490606490829084905af1918215620019c5577f53b9d5645f8b7ccd861ebd6036860fd21716451d1f238cb3720f12f3c49b0c4393604093620036f2575b5060018187541696015416956001600160401b038351921682526020820152a4565b620036fd90620026f7565b5f620036d0565b5f9c508c9b5099505050505050505050565b50505f620035d7565b9092809b93806001600160401b038316105f1462003773576200374f91620037479162003506565b809c6200282b565b816001600160401b038216115f146200376b57505b91620035d9565b905062003764565b50506200374f5f62003747565b60405163358d72d160e01b815260048101879052602490fd5b60405163f7348a7960e01b8152600490fd5b604051631a40316d60e01b8152600490fd5b60ff16604d811162001a0e57600a0a90565b9190820391821162001a0e57565b8115620037e8570490565b634e487b7160e01b5f52601260045260245ffd5b90620f42409182810292818404149015171562001a0e57565b8181029291811591840414171562001a0e57565b9190805f0b80155f14620038475750506001600160401b0382169190565b5f81131562003877575062003863620038739160ff16620037bd565b6001600160401b03841662003815565b9190565b9050607f19811462001a0e5762003893905f0360ff16620037bd565b91620038aa836001600160401b038093166200287c565b5f19810190811162001a0e57620038c684620038ce92620037dd565b938462003815565b1690565b6200390a6200390392936001600160401b0393836001600160a01b03620038fc8780971662002847565b166200343d565b946200282b565b169116101590565b90815f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800806020526001600160a01b0360405f20921691825f5260205260ff60405f205416155f14620039aa57825f5260205260405f20815f5260205260405f20600160ff1982541617905533917f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d5f80a4600190565b5050505f90565b90815f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800806020526001600160a01b0360405f20921691825f5260205260ff60405f2054165f14620039aa57825f5260205260405f20815f5260205260405f2060ff19815416905533917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b5f80a4600190565b9062003a5e6001600160a01b0360408401511662003257565b1562004618576001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000165f525f8051602062005bb783398151915260205260405f20906040519162003ab6836200270b565b60405162003ac48162002727565b6040518254815f62003ad68362002c80565b8083529260018116908115620045f75750600114620045b2575b62003afe925003826200277c565b8152600182016040519081602082549182815201915f5260205f20905f915b81600384011062004570579360069593819362003b67936001600160401b039997549181811062001e5e5781811062001e475781811062001e30571062001e21575003826200277c565b60208201526001600160a01b036002830154858116604084015260401c16606082015260038201546001600160a01b038116608083015260ff8160a01c1660a083015260ff8160a81c1660c083015260b01c5f0b60e0820152855260ff6004820154848116602088015260401c1615156040860152600581015460608601520154818116608085015260401c1660a08301526001600160a01b036060840151165f525f8051602062005bb783398151915260205260405f206040519062003c2e826200270b565b60405162003c3c8162002727565b6040518254815f62003c4e8362002c80565b80835292600181169081156200454f57506001146200450a575b62003c76925003826200277c565b8152600182016040519081602082549182815201915f5260205f20905f915b816003840110620044bd579360069593819362003cdf936001600160401b039997549181811062001e5e5781811062001e475781811062001e30571062001e21575003826200277c565b60208201526001600160a01b036002830154858116604084015260401c16606082015260038201546001600160a01b038116608083015260ff8160a01c1660a083015260ff8160a81c1660c083015260b01c5f0b60e0820152845260ff6004820154848116602087015260401c1615156040850152600581015460608501520154818116608084015260401c1660a082015262003d896001600160a01b03606086015116620032e8565b9184519062003dd662003dbb62003dac60a089015160808a01511515956200287c565b60e0885101515f0b9062004da2565b905060ff60c0885101511660ff60c087510151169162005165565b91156200448c5781925b5f8063ffffffff875116604051602081019182526020815262003e038162002760565b51906108085afa9062003e1562002cd7565b91156200447a576020828051810103126200053e576001600160401b0362003e44602062003e6d940162003428565b60208801519116919015620044665760ff60a062003e669251015116620037bd565b9062003815565b602085015115620044175763ffffffff62003e8e8160608801511662005026565b16620f4240818101811162001a0e576001600160401b039262003e6662003ec792620f42409463ffffffff60408c01511691016200287c565b0416906080870151908115155f146200440e5750905b602085015115620043c5576305f5e1008110156200437e57505f5b62003f2462003f0b60a0890151620037fc565b62003f1d895160a08b0151906200287c565b906200287c565b5f1981019190821162001a0e5762003f1d62003f539262003f4c8a5160a08c0151906200287c565b90620037dd565b606482028281046064148315171562001a0e578111620042df57505062003f84855160e0865101515f0b9062004da2565b92909562003faf6001600160401b03602088015116856001600160401b0360a08a01511691620038d2565b156200423b5791608093917f550576b2f9e0ac12dfd5dd2d5743b5b7f11f34302b5f6bec6ad60db81bd6a91860a0856001600160401b036001600160a01b036040819b990151168a606084015116908a8401511515916040519162004014836200270b565b82526020820152828816604082015282871660608201528a81019182528481015f815262004118602086019384515f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b03602052856060600260405f206001600160a01b0388511673ffffffffffffffffffffffffffffffffffffffff19825416178155600181016001600160a01b0360208a0151168154907bffffffffffffffff000000000000000000000000000000000000000060408c015160a01b169163ffffffff60e01b161717905501950151168619855416178455511515839068ff0000000000000000825491151560401b169068ff00000000000000001916179055565b51151569ff00000000000000000082549160481b169069ff00000000000000000019161790555194818b60408501511697818d6060870151169a87875197015160405197885260208801521660408601521660608401521688820152a4015116620041ae83826001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001662003353565b60e06001600160401b03602084015116925101515f0b90803b156200053e57604051639c45c34b60e01b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001660048201526001600160401b03909316602484015260448301939093525f90810b6064830152909182908183816084810162002f38565b94505050905062002f56925060208201516001600160401b03604051921682527f8d61555dcf69ba745e97fdea78e2652d9cdd464ca521faad482fe796f6d5841c60206001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001693a36001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016606082015262004e31565b9350949350505060208201516001600160a01b0360608401511691606481019081811162001a0e5760630190811162001a0e5760407fc8f90125c6a36c77a571201afc10310420481ab4895fadabb596d0ba71c22e3e9162002f569660648351920482526020820152a36001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000166060820152620046c4565b6001600160401b036305f5e0ff1982011162001a0e57620043be620043b86001600160401b036305f5e100936305f5e0ff190116620037fc565b6200286a565b0462003ef8565b6305f5e100811115620043da57505f62003ef8565b6001600160401b03816305f5e100031162001a0e57620043be620043b86001600160401b036305f5e10093840316620037fc565b90509062003edd565b63ffffffff6200442d8160608801511662005026565b16620f4240808281031162001a0e576001600160401b039262003e6662003ec792620f42409463ffffffff60408c0151169103620037cf565b5062003e6660ff60a08951015116620037bd565b604051635cffc5fb60e11b8152600490fd5b6127108181031162001a0e576001600160401b03612710620044b383820383861662003815565b0416919262003de0565b926001608060049286546001600160401b03811682526001600160401b038160401c1660208301526001600160401b0381841c16604083015260c01c606082015201940192019162003c95565b5090845f5260205f20905f915b8183106200453257505090602062003c769282010162003c68565b602091935080600191548385880101520191019091839262004517565b6020925062003c7694915060ff191682840152151560051b82010162003c68565b9260016004918554906001600160401b03918281168252828160401c16602083015260809281841c16604083015260c01c606082015201940192019162003b1d565b5090845f5260205f20905f915b818310620045da57505090602062003afe9282010162003af0565b6020919350806001915483858801015201910190918392620045bf565b6020925062003afe94915060ff191682840152151560051b82010162003af0565b506080810151909190156200464c5760246001600160a01b03604084015116604051906320a2097d60e11b82526004820152fd5b9080602062002f569201517f31604f5790fbed7a37471cea010a8f08483441a6e1235ac48bb59337439a630d60206001600160a01b03604085015116604051908152a26001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016606082015262004e31565b6060906060810151905f6001600160a01b0380931692835f526020945f8051602062005bb78339815191528652604091825f2096835162004705816200270b565b845193620047138562002727565b85518a5491905f81620047268562002c80565b918282526001958887821691825f1462004d6b57505060011462004d2a575b62004756925094939403826200277c565b8652808b019184885193898586948484549283815201935f52845f20965f915b83600384011062004cde575085620047b998549484841062004cc4575b84841062004ca7575b5083831062004c8a575b50501062004c7b5750905003826200277c565b82850152600289015495836001600160401b039788811688880152871c16606086015260038a0154946080958581168783015260ff8160a01c1660a083015260ff8160a81c1660c083015260b01c5f0b60e08201528252600660048b01549a60ff8585019c8d8b82169052891c1615158885015260058101546060850152015490878216868401528760a0840192881c168252868901976200485e868a511662003257565b1562004c0d57899a9b5f9a98999a508c612710620048958b519b8b6200488b60a083019e8f51906200287c565b9101519062003815565b04988a5199808b1162004c04575b508d8a8062004b2e575b620048c2620048d191620048e493516200287c565b60e08a5101515f0b9062004da2565b91909782878088511692511691620038d2565b1562004ae65750908c9695949392918a620049f9575b509260e0896200498f94620049c59b9997947fb021c853215aadb12b6fa8afa7b3158201517d9abf7f756cdbb67bd66abc5a1c9f9e9d9b99976200496e906001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0460205260405f2090565b6200497b8d82546200287c565b9055511696511694510151900b9062004da2565b9190928d84620049ca575b5050505050880151965116965192519351938493846040919493926060820195825260208201520152565b0390a4565b620049ee94620049e89188620049e08562002847565b169062003353565b62002ea6565b5f8080808d6200499a565b919293949596505087807f00000000000000000000000000000000000000000000000000000000000000001691875101511690803b156200053e578b5163f3fef3a360e01b81526001600160a01b03929092166004830152602482018a90525f908290604490829084905af1801562004adc578c948f91998098956200498f957fb021c853215aadb12b6fa8afa7b3158201517d9abf7f756cdbb67bd66abc5a1c9f9e9d9b9995620049c59d62004ac0575b509497995094509950939597999a9b620048fa565b60e0945062004acf90620026f7565b6200496e5f945062004aab565b8b513d5f823e3d90fd5b9a50505050955050505093957f8d61555dcf69ba745e97fdea78e2652d9cdd464ca521faad482fe796f6d5841c955084915062004b238162004e31565b0151945191168152a3565b62004b45908a8301518c858c51015116906200503f565b1562004b53575b8a620048ad565b509192939495968881999a50602492508389510151168c51928380926370a0823160e01b82528d7f00000000000000000000000000000000000000000000000000000000000000001660048301525afa90811562004adc57918e918e9a999897969594935f9162004bc7575b509962004b4c565b92505098508681813d831162004bfc575b62004be481836200277c565b810103126200053e57518c988e91620048d162004bbf565b503d62004bd8565b99505f620048a3565b505050509296509450839293015115155f1462004c405750505191516320a2097d60e11b81529116600482015260249150fd5b62002f56948294827f31604f5790fbed7a37471cea010a8f08483441a6e1235ac48bb59337439a630d9401519451169051908152a262004e31565b60c01c81520185905f6200303b565b9091946001600160401b038560801c1681520193015f85620047a6565b956001600160401b03868495981c16815201940190858e6200479c565b6001600160401b0386168752958101959282019262004793565b95509360049296509284919388546001600160401b039283821683528d8483831c169084015260809382851c169083015260c01c8782015201960191019386948c9296938a9562004776565b50508c5f528186805f20865f915b85831062004d5157505062004756935082010162004745565b809192945054838588010152019101879086859362004d38565b60ff1916848201526200475694151560051b8401019150620047459050565b81811062004d96575050565b5f815560010162004d8a565b9190805f0b9081155f1462004dc05750506001600160401b03821690565b5f82131562004e035762004dd8915060ff16620037bd565b918215620037e857620038ce62004dfb82856001600160401b03940690620037cf565b9384620037dd565b505f0380805f0b0362001a0e57620038ce62004e2a6001600160401b039260ff16620037bd565b8462003815565b805161271062004e5862004e4c60a08501938451906200287c565b60808501519062003815565b0481518181115f146200501d5750905b8190602084019182519162004e8f60608701956001600160a01b039485885116906200503f565b1562005014575b8162004f61575b620049c5837f4755f239bb1b047245415cb917deced72a3ca8baebcef109c396ff332ea6f50f9462004ed1858a516200287c565b9662004eeb828a51169860408c0199848b51169062003353565b62004f29828a51166001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0460205260405f2090565b62004f368782546200287c565b9055519651169651169651915192604051938493846040919493926060820195825260208201520152565b84518316927f00000000000000000000000000000000000000000000000000000000000000008116803b156200053e5760405163f3fef3a360e01b81526001600160a01b03959095166004860152602485018490525f908590604490829084905af1908115620019c5577f4755f239bb1b047245415cb917deced72a3ca8baebcef109c396ff332ea6f50f94620049c59262005002575b5093505062004e9d565b6200500d90620026f7565b5f62004ff8565b5f915062004e96565b90509062004e68565b90606463ffffffff8093160291821691820362001a0e57565b6040516370a0823160e01b81527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0390811660048301529092169392909190602082602481885afa918215620019c5575f92620050f2575b5080821094851595620050b2575b50505050565b7f5180f0ad9e9bd2296de2ee38c85d11c56613fa73f8ee66792f26ac318f1274749260609260405192835260208301526040820152a25f808080620050ac565b9091506020813d60201162005123575b8162005111602093836200277c565b810103126200053e5751905f6200509e565b3d915062005102565b51906001600160a01b03821682036200053e57565b519060ff821682036200053e57565b9060ff8091169116039060ff821162001a0e57565b919060ff821660ff82168181145f1462005180575050505090565b929391921015620051b857620038ce9192620051b0620051aa6001600160401b0395869362005150565b620037bd565b911662003815565b620038ce91620051d6620051aa6001600160401b0395869362005150565b9116620037dd565b9015620051fb57815115620051f1575090565b3b156200053e5790565b5080519081156200053e57602001fdfe60a080604052346100455733608052610961908161004a8239608051818181608b015281816101ac0152818161038e015281816104a20152818161051b015261069c0152f35b5f80fdfe60406080815260049081361015610014575f80fd5b5f915f3560e01c908163435354d3146104d757816390a0827b1461047b5781639c45c34b14610469578163a703334f1461033c578163de1a324a146100d457508063e94b77c1146100b35763eba61c0e1461006d575f80fd5b346100af57816003193601126100af57602090516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b5080fd5b82346100d1576100ce6100c5366105b5565b9291909161068d565b80f35b80fd5b91905034610338576101009136600319018381126103345760a01361033057815167ffffffffffffffff939060a081018581118282101761031d5784526101196105a2565b91828252602435918215158303610319576020810192835263ffffffff9360443585811681036102ce578783015260643585811681036102ce5760608301526084356001600160a01b03928382168203610315576080015260a4358881168091036102ce5760c435918983168093036102ce5760e435936fffffffffffffffffffffffffffffffff85168095036102ce577f000000000000000000000000000000000000000000000000000000000000000016330361031557866101dd9116610663565b9451151581156103055782156102f5578a6102e557885196602088019616865288870152606086015260808501525f60a0850152600260c085015260e084015260e08352820194828610908611176102d25761026891602491868652630100000160e01b6101208301526102598251809261012485019061079c565b81010383810186520184610641565b73333333333333333333333333333333333333333392833b156102ce576102a5935f928385518097819582946317938e1360e01b845283016107bd565b03925af19081156102c557506102b9575080f35b6102c391506105fd565b005b513d5f823e3d90fd5b5f80fd5b604183634e487b7160e01b5f525260245ffd5b88516376d4929560e11b81528890fd5b88516313c0a8df60e01b81528890fd5b885163017461b760e71b81528890fd5b8a80fd5b8780fd5b604184634e487b7160e01b5f525260245ffd5b8380fd5b8480fd5b8280fd5b919050346103385760603660031901126103385781359067ffffffffffffffff928383168093036102ce576024356001600160a01b03948582168092036102ce576044359081168091036102ce5786957f000000000000000000000000000000000000000000000000000000000000000016330361046557610259946103fc9260249286519260208401928352878401526060830152606082526103df82610625565b85519687926280000360e11b60208501525180928585019061079c565b73333333333333333333333333333333333333333392833b15610334576104399385928385518097819582946317938e1360e01b845283016107bd565b03925af190811561045c575061044c5750f35b610455906105fd565b6100d15780f35b513d84823e3d90fd5b8580fd5b83346100d1576100ce6100c5366105b5565b91905034610338573660031901126100af57356001600160a01b038082168092036102ce577f00000000000000000000000000000000000000000000000000000000000000001633036100af576100ce906024359033906107e9565b919050346102ce57806003193601126102ce576104f26105a2565b91602435926fffffffffffffffffffffffffffffffff84168094036102ce576001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001633036102ce5761054a90610663565b9282519063ffffffff602083019516855283820152828152606081019381851067ffffffffffffffff8611176102d25761026891602491868652630100000b60e01b608083015261025982518092608485019061079c565b6004359063ffffffff821682036102ce57565b60809060031901126102ce576004356001600160a01b03811681036102ce579060243567ffffffffffffffff811681036102ce579060443590606435805f0b81036102ce5790565b67ffffffffffffffff811161061157604052565b634e487b7160e01b5f52604160045260245ffd5b6080810190811067ffffffffffffffff82111761061157604052565b90601f8019910116810190811067ffffffffffffffff82111761061157604052565b9061271063ffffffff8093160191821161067957565b634e487b7160e01b5f52601160045260245ffd5b9092916001600160a01b0392837f00000000000000000000000000000000000000000000000000000000000000001633036102ce57805f0b80155f1461071c575050915b826106dd575b50505050565b67ffffffffffffffff732000000000000000000000000000000000000000941684018094116106795780610713941691166107e9565b5f8080806106d7565b5f81131561075c57506107319060ff166108f1565b8015610748578106810390811161067957916106d1565b634e487b7160e01b5f52601260045260245ffd5b90505f9391930380805f0b03610679576107789060ff166108f1565b9280938181810204149015176106d157634e487b7160e01b5f52601160045260245ffd5b5f5b8381106107ad5750505f910152565b818101518382015260200161079e565b604091602082526107dd815180928160208601526020868601910161079c565b601f01601f1916010190565b9190604051906020938483019363a9059cbb60e01b85526001600160a01b03809316602485015260448401526044835261082283610625565b169160405190604082019067ffffffffffffffff9383831085841117610611577f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564875f9586956040528181520152519082865af13d156108e4573d918211610611576108ac926040519261089f86601f19601f8401160185610641565b83523d5f8685013e610902565b80519182159182156108c4575b50509050156102ce57565b8092508193810103126102ce57015180151581036102ce57805f806108b9565b6108ac9260609250610902565b60ff16604d811161067957600a0a90565b901561091c57815115610913575090565b3b156102ce5790565b5080519081156102ce57602001fdfea2646970667358221220c909e7dbe656d7c973709e34eb673837a89b1ae88012e9262063134290e6f21f64736f6c634300081800336c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b00a2646970667358221220d7009235facd2afacf7dbd784aa03c97dd6444be46f4a33aa486147317b81a7764736f6c6343000818003300000000000000000000000090e2487764e5316a2e4109c2ed40a3b3ad423659000000000000000000000000b8ce59fc3717ada4c02eadf9682a9e934f625ebb" + } + ], + "isFixedGasLimit": false + }, + { + "hash": "0xd74dec165080b5fbff6ccc35308056868c6bfe895b036c17ffe54a6efb5cdcc1", + "transactionType": "CALL", + "contractName": "DonationBox", + "contractAddress": "0x90e2487764e5316a2e4109c2ed40a3b3ad423659", + "function": "transferOwnership(address)", + "arguments": ["0x40153DdFAd90C49dbE3F5c9F96f2a5B25ec67461"], + "transaction": { + "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "to": "0x90e2487764e5316a2e4109c2ed40a3b3ad423659", + "gas": "0x9922", + "value": "0x0", + "input": "0xf2fde38b00000000000000000000000040153ddfad90c49dbe3f5c9f96f2a5b25ec67461", + "nonce": "0x24d", + "chainId": "0x3e7" + }, + "additionalContracts": [], + "isFixedGasLimit": false + }, + { + "hash": "0xafb3a40d29cb3ed22d77e0f87387aecc0cbef59680adee4e3439a7c6a9ef2751", + "transactionType": "CALL", + "contractName": "PermissionedMulticallHandler", + "contractAddress": "0x0980d0f6799ca06c71ffafdc0e423cf2b0f20502", + "function": "grantRole(bytes32,address)", + "arguments": [ + "0x69048ea73402a715065a3029b4059a4e97d1461c95fa4fabca1084b5f34f4abe", + "0x40153DdFAd90C49dbE3F5c9F96f2a5B25ec67461" + ], + "transaction": { + "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "to": "0x0980d0f6799ca06c71ffafdc0e423cf2b0f20502", + "gas": "0x11383", + "value": "0x0", + "input": "0x2f2ff15d69048ea73402a715065a3029b4059a4e97d1461c95fa4fabca1084b5f34f4abe00000000000000000000000040153ddfad90c49dbe3f5c9f96f2a5b25ec67461", + "nonce": "0x24e", + "chainId": "0x3e7" + }, + "additionalContracts": [], + "isFixedGasLimit": false + }, + { + "hash": "0xfbc1184185961f34d5eb882a59fd93483395a71af48f34cb177453d38e11a70b", + "transactionType": "CALL", + "contractName": "DstOFTHandler", + "contractAddress": "0x40153ddfad90c49dbe3f5c9f96f2a5b25ec67461", + "function": "setAuthorizedPeriphery(uint32,bytes32)", + "arguments": ["30101", "0x0000000000000000000000004607bceaf7b22cb0c46882ffc9fab3c6efe66e5a"], + "transaction": { + "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "to": "0x40153ddfad90c49dbe3f5c9f96f2a5b25ec67461", + "gas": "0x10e6b", + "value": "0x0", + "input": "0x52e12a1400000000000000000000000000000000000000000000000000000000000075950000000000000000000000004607bceaf7b22cb0c46882ffc9fab3c6efe66e5a", + "nonce": "0x24f", + "chainId": "0x3e7" + }, + "additionalContracts": [], + "isFixedGasLimit": false + }, + { + "hash": "0x8ffe6c78349baca66e5318acb553afe390cf87a9dca508aad5399946496939ab", + "transactionType": "CALL", + "contractName": "DstOFTHandler", + "contractAddress": "0x40153ddfad90c49dbe3f5c9f96f2a5b25ec67461", + "function": "setAuthorizedPeriphery(uint32,bytes32)", + "arguments": ["30110", "0x0000000000000000000000002ac5ee3796e027da274fbde84c82173a65868940"], + "transaction": { + "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "to": "0x40153ddfad90c49dbe3f5c9f96f2a5b25ec67461", + "gas": "0x10e6b", + "value": "0x0", + "input": "0x52e12a14000000000000000000000000000000000000000000000000000000000000759e0000000000000000000000002ac5ee3796e027da274fbde84c82173a65868940", + "nonce": "0x250", + "chainId": "0x3e7" + }, + "additionalContracts": [], + "isFixedGasLimit": false + } + ], + "receipts": [ + { + "status": "0x1", + "cumulativeGasUsed": "0x39da01", + "logs": [ + { + "address": "0x90e2487764e5316a2e4109c2ed40a3b3ad423659", + "topics": [ + "0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000009a8f92a830a5cb89a3816e3d267cb7791c16b04d" + ], + "data": "0x", + "blockHash": "0xba7536f79ed3b0766763cc1f0a6422fd88383b2ac58e0237f98f0c5cc80c2401", + "blockNumber": "0x13d9f96", + "blockTimestamp": "0x692f9ce8", + "transactionHash": "0xf72c3e798991af0e7123197bfd7da40585f936b4353b91159b749008ceac541a", + "transactionIndex": "0x2", + "logIndex": "0x2f", + "removed": false + } + ], + "logsBloom": "0x00000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000081000000000000000000000000000000000000020000000000000000000800000000000000000000000000040000480000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000020000001000000000000000000000000000000000000000000000000000000000000", + "type": "0x2", + "transactionHash": "0xf72c3e798991af0e7123197bfd7da40585f936b4353b91159b749008ceac541a", + "transactionIndex": "0x2", + "blockHash": "0xba7536f79ed3b0766763cc1f0a6422fd88383b2ac58e0237f98f0c5cc80c2401", + "blockNumber": "0x13d9f96", + "gasUsed": "0x3f377", + "effectiveGasPrice": "0x10437e16", + "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "to": null, + "contractAddress": "0x90e2487764e5316a2e4109c2ed40a3b3ad423659" + }, + { + "status": "0x1", + "cumulativeGasUsed": "0x4a8f16", + "logs": [ + { + "address": "0x0980d0f6799ca06c71ffafdc0e423cf2b0f20502", + "topics": [ + "0x2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000009a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "0x0000000000000000000000009a8f92a830a5cb89a3816e3d267cb7791c16b04d" + ], + "data": "0x", + "blockHash": "0xba7536f79ed3b0766763cc1f0a6422fd88383b2ac58e0237f98f0c5cc80c2401", + "blockNumber": "0x13d9f96", + "blockTimestamp": "0x692f9ce8", + "transactionHash": "0xee446c5d024fc2d807a387b7be9dc0e17f7f806efac526cc1db7bebd7fa5f7c7", + "transactionIndex": "0x3", + "logIndex": "0x30", + "removed": false + } + ], + "logsBloom": "0x00000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000004000000000000020000000000000000000800000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001002000000000000000000000000800000000000000000000000000000000100000000000020000001000000000000000000000000000000000000000000000000000000000000", + "type": "0x2", + "transactionHash": "0xee446c5d024fc2d807a387b7be9dc0e17f7f806efac526cc1db7bebd7fa5f7c7", + "transactionIndex": "0x3", + "blockHash": "0xba7536f79ed3b0766763cc1f0a6422fd88383b2ac58e0237f98f0c5cc80c2401", + "blockNumber": "0x13d9f96", + "gasUsed": "0x10b515", + "effectiveGasPrice": "0x10437e16", + "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "to": null, + "contractAddress": "0x0980d0f6799ca06c71ffafdc0e423cf2b0f20502" + }, + { + "status": "0x1", + "cumulativeGasUsed": "0xb2585e", + "logs": [ + { + "address": "0x40153ddfad90c49dbe3f5c9f96f2a5b25ec67461", + "topics": [ + "0xbd79b86ffe0ab8e8776151514217cd7cacd52c909f66475c3af44e129f0b00ff", + "0x5300fde95a5e446527bf6aa7c91bd6661bef5398afc77061d9bc87efb80b7ef6", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000" + ], + "data": "0x", + "blockHash": "0xba7536f79ed3b0766763cc1f0a6422fd88383b2ac58e0237f98f0c5cc80c2401", + "blockNumber": "0x13d9f96", + "blockTimestamp": "0x692f9ce8", + "transactionHash": "0xb29d240d7893c5e112a3407a86d1ae520674cb3799cc0fb88420b386dbf018c7", + "transactionIndex": "0x4", + "logIndex": "0x31", + "removed": false + }, + { + "address": "0x40153ddfad90c49dbe3f5c9f96f2a5b25ec67461", + "topics": [ + "0xbd79b86ffe0ab8e8776151514217cd7cacd52c909f66475c3af44e129f0b00ff", + "0x880a9ba888678c7fe4e8c4f028c224f26ce12a3bed6e96025c61ef8a5db6312f", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000" + ], + "data": "0x", + "blockHash": "0xba7536f79ed3b0766763cc1f0a6422fd88383b2ac58e0237f98f0c5cc80c2401", + "blockNumber": "0x13d9f96", + "blockTimestamp": "0x692f9ce8", + "transactionHash": "0xb29d240d7893c5e112a3407a86d1ae520674cb3799cc0fb88420b386dbf018c7", + "transactionIndex": "0x4", + "logIndex": "0x32", + "removed": false + }, + { + "address": "0x40153ddfad90c49dbe3f5c9f96f2a5b25ec67461", + "topics": [ + "0x2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000009a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "0x0000000000000000000000009a8f92a830a5cb89a3816e3d267cb7791c16b04d" + ], + "data": "0x", + "blockHash": "0xba7536f79ed3b0766763cc1f0a6422fd88383b2ac58e0237f98f0c5cc80c2401", + "blockNumber": "0x13d9f96", + "blockTimestamp": "0x692f9ce8", + "transactionHash": "0xb29d240d7893c5e112a3407a86d1ae520674cb3799cc0fb88420b386dbf018c7", + "transactionIndex": "0x4", + "logIndex": "0x33", + "removed": false + } + ], + "logsBloom": "0x00000004000000000800000000000000080080000000000000000080000000000100000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000020000002000000000000020000400000000000000800000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000800000000000000000000000000000000100000000000020100001000000000800000000000000000100000000000000000000000000000000", + "type": "0x2", + "transactionHash": "0xb29d240d7893c5e112a3407a86d1ae520674cb3799cc0fb88420b386dbf018c7", + "transactionIndex": "0x4", + "blockHash": "0xba7536f79ed3b0766763cc1f0a6422fd88383b2ac58e0237f98f0c5cc80c2401", + "blockNumber": "0x13d9f96", + "gasUsed": "0x67c948", + "effectiveGasPrice": "0x10437e16", + "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "to": null, + "contractAddress": "0x40153ddfad90c49dbe3f5c9f96f2a5b25ec67461" + }, + { + "status": "0x1", + "cumulativeGasUsed": "0xb2c73d", + "logs": [ + { + "address": "0x90e2487764e5316a2e4109c2ed40a3b3ad423659", + "topics": [ + "0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0", + "0x0000000000000000000000009a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "0x00000000000000000000000040153ddfad90c49dbe3f5c9f96f2a5b25ec67461" + ], + "data": "0x", + "blockHash": "0xba7536f79ed3b0766763cc1f0a6422fd88383b2ac58e0237f98f0c5cc80c2401", + "blockNumber": "0x13d9f96", + "blockTimestamp": "0x692f9ce8", + "transactionHash": "0xd74dec165080b5fbff6ccc35308056868c6bfe895b036c17ffe54a6efb5cdcc1", + "transactionIndex": "0x5", + "logIndex": "0x34", + "removed": false + } + ], + "logsBloom": "0x04000000000000000000000000000000000000000000000000800000800001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000081000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000480000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000", + "type": "0x2", + "transactionHash": "0xd74dec165080b5fbff6ccc35308056868c6bfe895b036c17ffe54a6efb5cdcc1", + "transactionIndex": "0x5", + "blockHash": "0xba7536f79ed3b0766763cc1f0a6422fd88383b2ac58e0237f98f0c5cc80c2401", + "blockNumber": "0x13d9f96", + "gasUsed": "0x6edf", + "effectiveGasPrice": "0x10437e16", + "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "to": "0x90e2487764e5316a2e4109c2ed40a3b3ad423659", + "contractAddress": null + }, + { + "status": "0x1", + "cumulativeGasUsed": "0xb38eb5", + "logs": [ + { + "address": "0x0980d0f6799ca06c71ffafdc0e423cf2b0f20502", + "topics": [ + "0x2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d", + "0x69048ea73402a715065a3029b4059a4e97d1461c95fa4fabca1084b5f34f4abe", + "0x00000000000000000000000040153ddfad90c49dbe3f5c9f96f2a5b25ec67461", + "0x0000000000000000000000009a8f92a830a5cb89a3816e3d267cb7791c16b04d" + ], + "data": "0x", + "blockHash": "0xba7536f79ed3b0766763cc1f0a6422fd88383b2ac58e0237f98f0c5cc80c2401", + "blockNumber": "0x13d9f96", + "blockTimestamp": "0x692f9ce8", + "transactionHash": "0xafb3a40d29cb3ed22d77e0f87387aecc0cbef59680adee4e3439a7c6a9ef2751", + "transactionIndex": "0x6", + "logIndex": "0x35", + "removed": false + } + ], + "logsBloom": "0x04000004000000000000000000000000000000000008000000000000800001000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000800000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001002000000000000000000000000800000000000000000000000000000000100200000000000000001000000000000000000000000000000000000000000000000000000000000", + "type": "0x2", + "transactionHash": "0xafb3a40d29cb3ed22d77e0f87387aecc0cbef59680adee4e3439a7c6a9ef2751", + "transactionIndex": "0x6", + "blockHash": "0xba7536f79ed3b0766763cc1f0a6422fd88383b2ac58e0237f98f0c5cc80c2401", + "blockNumber": "0x13d9f96", + "gasUsed": "0xc778", + "effectiveGasPrice": "0x10437e16", + "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "to": "0x0980d0f6799ca06c71ffafdc0e423cf2b0f20502", + "contractAddress": null + }, + { + "status": "0x1", + "cumulativeGasUsed": "0xb4527d", + "logs": [ + { + "address": "0x40153ddfad90c49dbe3f5c9f96f2a5b25ec67461", + "topics": ["0xf3adc8db618887d7b76838e244efb05fc99475bb5a904a914d939fbdc41b7e8d"], + "data": "0x00000000000000000000000000000000000000000000000000000000000075950000000000000000000000004607bceaf7b22cb0c46882ffc9fab3c6efe66e5a", + "blockHash": "0xba7536f79ed3b0766763cc1f0a6422fd88383b2ac58e0237f98f0c5cc80c2401", + "blockNumber": "0x13d9f96", + "blockTimestamp": "0x692f9ce8", + "transactionHash": "0xfbc1184185961f34d5eb882a59fd93483395a71af48f34cb177453d38e11a70b", + "transactionIndex": "0x7", + "logIndex": "0x36", + "removed": false + } + ], + "logsBloom": "0x00000000000000000000800000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000800000000000000400000000000000000000000000000000000", + "type": "0x2", + "transactionHash": "0xfbc1184185961f34d5eb882a59fd93483395a71af48f34cb177453d38e11a70b", + "transactionIndex": "0x7", + "blockHash": "0xba7536f79ed3b0766763cc1f0a6422fd88383b2ac58e0237f98f0c5cc80c2401", + "blockNumber": "0x13d9f96", + "gasUsed": "0xc3c8", + "effectiveGasPrice": "0x10437e16", + "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "to": "0x40153ddfad90c49dbe3f5c9f96f2a5b25ec67461", + "contractAddress": null + }, + { + "status": "0x1", + "cumulativeGasUsed": "0xb51645", + "logs": [ + { + "address": "0x40153ddfad90c49dbe3f5c9f96f2a5b25ec67461", + "topics": ["0xf3adc8db618887d7b76838e244efb05fc99475bb5a904a914d939fbdc41b7e8d"], + "data": "0x000000000000000000000000000000000000000000000000000000000000759e0000000000000000000000002ac5ee3796e027da274fbde84c82173a65868940", + "blockHash": "0xba7536f79ed3b0766763cc1f0a6422fd88383b2ac58e0237f98f0c5cc80c2401", + "blockNumber": "0x13d9f96", + "blockTimestamp": "0x692f9ce8", + "transactionHash": "0x8ffe6c78349baca66e5318acb553afe390cf87a9dca508aad5399946496939ab", + "transactionIndex": "0x8", + "logIndex": "0x37", + "removed": false + } + ], + "logsBloom": "0x00000000000000000000800000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000800000000000000400000000000000000000000000000000000", + "type": "0x2", + "transactionHash": "0x8ffe6c78349baca66e5318acb553afe390cf87a9dca508aad5399946496939ab", + "transactionIndex": "0x8", + "blockHash": "0xba7536f79ed3b0766763cc1f0a6422fd88383b2ac58e0237f98f0c5cc80c2401", + "blockNumber": "0x13d9f96", + "gasUsed": "0xc3c8", + "effectiveGasPrice": "0x10437e16", + "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "to": "0x40153ddfad90c49dbe3f5c9f96f2a5b25ec67461", + "contractAddress": null + } + ], + "libraries": [], + "pending": [], + "returns": {}, + "timestamp": 1764728130595, + "chain": 999, + "commit": "5d9f89e2" +} diff --git a/broadcast/DeployHyperliquidDepositHandler.s.sol/999/run-latest.json b/broadcast/DeployHyperliquidDepositHandler.s.sol/999/run-latest.json new file mode 100644 index 000000000..881273a0d --- /dev/null +++ b/broadcast/DeployHyperliquidDepositHandler.s.sol/999/run-latest.json @@ -0,0 +1,173 @@ +{ + "transactions": [ + { + "hash": "0x187b45f39be413aff2ff526946c64f76ed98763129e7e2ffb7d2d4c5bd997519", + "transactionType": "CREATE", + "contractName": "HyperliquidDepositHandler", + "contractAddress": "0x861e127036b28d32f3777b4676f6bbb9e007d195", + "function": null, + "arguments": null, + "transaction": { + "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "gas": "0x160360", + "value": "0x0", + "input": "0x60a080604052346100e15760015f5533156100cc5760018054336001600160a01b03198216811790925560405191906001600160a01b03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a36103a38181016001600160401b038111838210176100b8578291610ed2833903905ff080156100ad57608052604051610dec90816100e682396080518181816101b70152818161026a01526108080152f35b6040513d5f823e3d90fd5b634e487b7160e01b5f52604160045260245ffd5b631e4fbdf760e01b81525f6004820152602490fd5b5f80fdfe6040608081526004361015610012575f80fd5b5f905f3560e01c80633a5be8cb146105275780633eda20c61461045757806368c4ac26146103f2578063715018a61461038c5780637826a16e146102fa5780637bce887a146102385780638da5cb5b146102115780639b61d2d8146101db578063a4b672b614610198578063c48919d61461012c5763f2fde38b14610095575f80fd5b34610128576020366003190112610128576100ae6105df565b6100b66109f6565b6001600160a01b0380911691821561011157506001548273ffffffffffffffffffffffffffffffffffffffff19821617600155167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a380f35b51631e4fbdf760e01b815260048101849052602490fd5b5080fd5b8234610195576060366003190112610195576101466105df565b61014e610695565b6101566105f5565b61015e6109f6565b60025f54146101915767ffffffffffffffff602061018261018a9560025f55610a65565b015116610b1d565b6001815580f35b5f80fd5b80fd5b5034610128578160031936011261012857602090516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b8234610195576101ea366106ac565b6101f26109f6565b60025f5414610191576001600160a01b0361018a9360025f5516610a22565b50346101285781600319360112610128576020906001600160a01b03600154169051908152f35b50903461019157610248366106ac565b90916102526109f6565b60025f54146101915760025f556001600160a01b03807f000000000000000000000000000000000000000000000000000000000000000016911694813b1561019157805163f3fef3a360e01b81526001600160a01b038716600482015260248101859052915f908390604490829084905af19081156102f157506102dc575b5061018a9293610a22565b61018a93506102ea90610627565b5f926102d1565b513d5f823e3d90fd5b503461019157610309366106ac565b9160025f54146101915760025f5583516323b872dd60e01b60208201523360248201523060448201526064808201849052815260a08101949067ffffffffffffffff861181871017610378576103729561036d92526001600160a01b038316610c49565b610716565b60015f55005b634e487b7160e01b5f52604160045260245ffd5b34610191575f366003190112610191576103a46109f6565b5f6001600160a01b0360015473ffffffffffffffffffffffffffffffffffffffff198116600155167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3005b5034610191576020366003190112610191576080906001600160a01b0390816104196105df565b165f526002602052805f2080549167ffffffffffffffff600260018401549301545f0b938251958116865260a01c1660208501528301526060820152f35b5034610191576080366003190112610191576104716105df565b610479610695565b9060643592835f0b809403610191576002926104936109f6565b81519261049f8461060b565b6001600160a01b038091169283855267ffffffffffffffff6020860193168352808501936044358552606086019788525f52856020525f209351167bffffffffffffffff00000000000000000000000000000000000000008454925160a01b169163ffffffff60e01b161717825551600182015501905160ff198254169060ff161790555f80f35b5034610191576080366003190112610191576105416105df565b9061054a6105f5565b506064359067ffffffffffffffff8211610191573660238301121561019157816004013561058361057a82610679565b92519283610657565b8082526020820192366024838301011161019157815f9260246020930186378301015260025f54146101915760209060025f5580518101031261019157516001600160a01b038116809103610191576103729160243590610716565b600435906001600160a01b038216820361019157565b604435906001600160a01b038216820361019157565b6080810190811067ffffffffffffffff82111761037857604052565b67ffffffffffffffff811161037857604052565b6040810190811067ffffffffffffffff82111761037857604052565b90601f8019910116810190811067ffffffffffffffff82111761037857604052565b67ffffffffffffffff811161037857601f01601f191660200190565b6024359067ffffffffffffffff8216820361019157565b6060906003190112610191576001600160a01b03600435818116810361019157916024359160443590811681036101915790565b9073200000000000000000000000000000000000000091820180921161070257565b634e487b7160e01b5f52601160045260245ffd5b9161072083610a65565b67ffffffffffffffff926020908482840151169260608101515f0b926040805192828401985f806001600160a01b039b8c8c16978882528781526107638161063b565b51906108105afa610772610c0d565b90156109e557848180518101031261019157835191858301908111838210176103785784526107a2908501610c3c565b809152156107f3575b50505050906107b991610cef565b9390806107c9575b505050505050565b6107e895826107e3936107db866106e0565b169116610a22565b610b1d565b5f80808080806107c1565b810151916001830180931161070257898916937f00000000000000000000000000000000000000000000000000000000000000008a16803b1561019157835163f3fef3a360e01b81526001600160a01b038716600482015260248101869052905f908290604490829084905af180156109db576109cc575b506108768785610cef565b50806109b1575b50825191808301918252888484015260016060840152606083526108a08361060b565b6108d56024855180946108c5858301976280000360e11b895251809285850190610afc565b8101036004810185520183610657565b733333333333333333333333333333333333333333803b15610191575f92836044610924948851978896879586936317938e1360e01b8552600485015251809281602486015285850190610afc565b601f01601f191681010301925af180156109a757927f45b9d2d602535b50313ef0fa849df42dd31d8610fc42876e005a5b6806d3e8809261098b926107b998979695610998575b50516001600160a01b038a16815260208101919091529081906040820190565b0390a290915f80806107ab565b6109a190610627565b5f61096b565b82513d5f823e3d90fd5b6109c6908b6109bf8b6106e0565b1687610a22565b5f61087d565b6109d590610627565b5f61086b565b84513d5f823e3d90fd5b83516313dd7ccd60e31b8152600490fd5b6001600160a01b03600154163303610a0a57565b60405163118cdaa760e01b8152336004820152602490fd5b60405163a9059cbb60e01b60208201526001600160a01b03929092166024830152604480830193909352918152610a6391610a5e606483610657565b610c49565b565b60405f60608251610a758161060b565b828152826020820152828482015201526001600160a01b03809216805f52600260205282825f20541615610aeb57906002915f5281602052805f209067ffffffffffffffff815194610ac68661060b565b8354908116865260a01c16602085015260018201549084015201545f0b606082015290565b8151633dd1b30560e01b8152600490fd5b5f5b838110610b0d5750505f910152565b8181015183820152602001610afe565b604051926001600160a01b03602085019316835267ffffffffffffffff809216604085015216606083015260608252610b558261060b565b610b8c60246040518093610b7c60208301966280000360e11b885251809285850190610afc565b8101036004810184520182610657565b73333333333333333333333333333333333333333390813b15610191575f91610bde918360446040518097819682956317938e1360e01b84526020600485015251809281602486015285850190610afc565b601f01601f191681010301925af18015610c0257610bf95750565b610a6390610627565b6040513d5f823e3d90fd5b3d15610c37573d90610c1e82610679565b91610c2c6040519384610657565b82523d5f602084013e565b606090565b5190811515820361019157565b905f806001600160a01b03610ca69416927f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65646020604051610c898161063b565b818152015260208151910182855af1610ca0610c0d565b91610d8d565b8051908115918215610cbc575b50501561019157565b8192509060209181010312610191576020610cd79101610c3c565b5f80610cb3565b60ff16604d811161070257600a0a90565b9190805f0b9081155f14610d0d57505067ffffffffffffffff821690565b5f821315610d5757610d22915060ff16610cde565b8015610d4357808306830392831161070257820467ffffffffffffffff1690565b634e487b7160e01b5f52601260045260245ffd5b505f0380805f0b0361070257610d6f9060ff16610cde565b828181029181830414901517156107025767ffffffffffffffff1690565b9015610da757815115610d9e575090565b3b156101915790565b50805190811561019157602001fdfea2646970667358221220c0cba36db139425c71e4049e05ab727a287009647e4f3ca742370867c5d7d0ff64736f6c634300081800336080806040523461005a575f8054336001600160a01b0319821681178355916001600160a01b03909116907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09080a3610344908161005f8239f35b5f80fdfe608060409080825260049081361015610016575f80fd5b5f3560e01c908163715018a61461026e5781638da5cb5b1461024c57508063f2fde38b146101d45763f3fef3a31461004c575f80fd5b346101545781600319360112610154578035916001600160a01b0383168093036101545760249261007b6102d2565b8151916020830163a9059cbb60e01b815233868501528535604485015260448452608084019367ffffffffffffffff94818110868211176101c25760c08201818110878211176101b0578452602090527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c656460a0820152515f9182919082865af1923d1561019f573d9181831161018d57805195601f8401601f19908116603f011687019283118784101761017b575052835261013e93503d5f602085013e6102e5565b8051908115918215610158575b50501561015457005b5f80fd5b819250906020918101031261015457602001518015158103610154575f8061014b565b60418891634e487b7160e01b5f52525ffd5b86604187634e487b7160e01b5f52525ffd5b5050915061013e92506060916102e5565b88604189634e487b7160e01b5f52525ffd5b87604188634e487b7160e01b5f52525ffd5b503461015457602036600319011261015457356001600160a01b03808216809203610154576102016102d2565b8115610154575f548273ffffffffffffffffffffffffffffffffffffffff198216175f55167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a3005b34610154575f366003190112610154576020906001600160a01b035f54168152f35b34610154575f366003190112610154576102866102d2565b5f6001600160a01b03815473ffffffffffffffffffffffffffffffffffffffff1981168355167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3005b6001600160a01b035f5416330361015457565b90156102ff578151156102f6575090565b3b156101545790565b50805190811561015457602001fdfea2646970667358221220c55691de465342d56d68c31160f5d7d661422ef39ebccaa5223872c823ad6d6964736f6c63430008180033", + "nonce": "0x206", + "chainId": "0x3e7" + }, + "additionalContracts": [ + { + "transactionType": "CREATE", + "address": "0x59ee1342867c200fa8ac052faa5f3df8eef21a67", + "initCode": "0x6080806040523461005a575f8054336001600160a01b0319821681178355916001600160a01b03909116907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09080a3610344908161005f8239f35b5f80fdfe608060409080825260049081361015610016575f80fd5b5f3560e01c908163715018a61461026e5781638da5cb5b1461024c57508063f2fde38b146101d45763f3fef3a31461004c575f80fd5b346101545781600319360112610154578035916001600160a01b0383168093036101545760249261007b6102d2565b8151916020830163a9059cbb60e01b815233868501528535604485015260448452608084019367ffffffffffffffff94818110868211176101c25760c08201818110878211176101b0578452602090527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c656460a0820152515f9182919082865af1923d1561019f573d9181831161018d57805195601f8401601f19908116603f011687019283118784101761017b575052835261013e93503d5f602085013e6102e5565b8051908115918215610158575b50501561015457005b5f80fd5b819250906020918101031261015457602001518015158103610154575f8061014b565b60418891634e487b7160e01b5f52525ffd5b86604187634e487b7160e01b5f52525ffd5b5050915061013e92506060916102e5565b88604189634e487b7160e01b5f52525ffd5b87604188634e487b7160e01b5f52525ffd5b503461015457602036600319011261015457356001600160a01b03808216809203610154576102016102d2565b8115610154575f548273ffffffffffffffffffffffffffffffffffffffff198216175f55167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a3005b34610154575f366003190112610154576020906001600160a01b035f54168152f35b34610154575f366003190112610154576102866102d2565b5f6001600160a01b03815473ffffffffffffffffffffffffffffffffffffffff1981168355167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3005b6001600160a01b035f5416330361015457565b90156102ff578151156102f6575090565b3b156101545790565b50805190811561015457602001fdfea2646970667358221220c55691de465342d56d68c31160f5d7d661422ef39ebccaa5223872c823ad6d6964736f6c63430008180033" + } + ], + "isFixedGasLimit": false + }, + { + "hash": "0xafaa7c3ef97727b20d46e5cb7f6478ab2e3597d7426aa0b007e52e016aa0cb8f", + "transactionType": "CALL", + "contractName": null, + "contractAddress": "0x3333333333333333333333333333333333333333", + "function": "sendRawAction(bytes)", + "arguments": [ + "0x01000006000000000000000000000000861e127036b28d32f3777b4676f6bbb9e007d19500000000000000000000000000000000000000000000000000000000000001680000000000000000000000000000000000000000000000000000000000000001" + ], + "transaction": { + "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "to": "0x3333333333333333333333333333333333333333", + "gas": "0x1039d", + "value": "0x0", + "input": "0x17938e130000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000006401000006000000000000000000000000861e127036b28d32f3777b4676f6bbb9e007d1950000000000000000000000000000000000000000000000000000000000000168000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000", + "nonce": "0x207", + "chainId": "0x3e7" + }, + "additionalContracts": [], + "isFixedGasLimit": false + }, + { + "hash": "0xea620a26dd14c70920861794ebb4ad4ed9e20eaa0a9e4a8dd076bc5e6e978c12", + "transactionType": "CALL", + "contractName": "HyperliquidDepositHandler", + "contractAddress": "0x861e127036b28d32f3777b4676f6bbb9e007d195", + "function": "addSupportedToken(address,uint64,uint256,int8)", + "arguments": ["0x111111a1a0667d36bD57c0A9f569b98057111111", "360", "1000000", "-2"], + "transaction": { + "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "to": "0x861e127036b28d32f3777b4676f6bbb9e007d195", + "gas": "0x1ed4c", + "value": "0x0", + "input": "0x3eda20c6000000000000000000000000111111a1a0667d36bd57c0a9f569b98057111111000000000000000000000000000000000000000000000000000000000000016800000000000000000000000000000000000000000000000000000000000f4240fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe", + "nonce": "0x208", + "chainId": "0x3e7" + }, + "additionalContracts": [], + "isFixedGasLimit": false + } + ], + "receipts": [ + { + "status": "0x1", + "cumulativeGasUsed": "0x10eee8", + "logs": [ + { + "address": "0x861e127036b28d32f3777b4676f6bbb9e007d195", + "topics": [ + "0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000009a8f92a830a5cb89a3816e3d267cb7791c16b04d" + ], + "data": "0x", + "blockHash": "0x0a8f06eb397fdab2e4392e16237ee54d6f6fe244e7ae4c021885998578c4cfe0", + "blockNumber": "0x135c76f", + "blockTimestamp": "0x6927e55c", + "transactionHash": "0x187b45f39be413aff2ff526946c64f76ed98763129e7e2ffb7d2d4c5bd997519", + "transactionIndex": "0x0", + "logIndex": "0x0", + "removed": false + }, + { + "address": "0x59ee1342867c200fa8ac052faa5f3df8eef21a67", + "topics": [ + "0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x000000000000000000000000861e127036b28d32f3777b4676f6bbb9e007d195" + ], + "data": "0x", + "blockHash": "0x0a8f06eb397fdab2e4392e16237ee54d6f6fe244e7ae4c021885998578c4cfe0", + "blockNumber": "0x135c76f", + "blockTimestamp": "0x6927e55c", + "transactionHash": "0x187b45f39be413aff2ff526946c64f76ed98763129e7e2ffb7d2d4c5bd997519", + "transactionIndex": "0x0", + "logIndex": "0x1", + "removed": false + } + ], + "logsBloom": "0x00000000000000000000000000000000000000000000000000800000000020000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000001000000000000000040000002000000000080020000000020000000000800000000000000000000000000040000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000080000000100008000000000000020000001000000000000000000000000000000000000000000000000000000000000", + "type": "0x2", + "transactionHash": "0x187b45f39be413aff2ff526946c64f76ed98763129e7e2ffb7d2d4c5bd997519", + "transactionIndex": "0x0", + "blockHash": "0x0a8f06eb397fdab2e4392e16237ee54d6f6fe244e7ae4c021885998578c4cfe0", + "blockNumber": "0x135c76f", + "gasUsed": "0x10eee8", + "effectiveGasPrice": "0x23c34600", + "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "to": null, + "contractAddress": "0x861e127036b28d32f3777b4676f6bbb9e007d195" + }, + { + "status": "0x1", + "cumulativeGasUsed": "0x11aadd", + "logs": [ + { + "address": "0x3333333333333333333333333333333333333333", + "topics": [ + "0x8c7f585fb295f7eb1e6aeb8fba61b23a4fe60beda405f0045073b185c74412e3", + "0x0000000000000000000000009a8f92a830a5cb89a3816e3d267cb7791c16b04d" + ], + "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000006401000006000000000000000000000000861e127036b28d32f3777b4676f6bbb9e007d1950000000000000000000000000000000000000000000000000000000000000168000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000", + "blockHash": "0x0a8f06eb397fdab2e4392e16237ee54d6f6fe244e7ae4c021885998578c4cfe0", + "blockNumber": "0x135c76f", + "blockTimestamp": "0x6927e55c", + "transactionHash": "0xafaa7c3ef97727b20d46e5cb7f6478ab2e3597d7426aa0b007e52e016aa0cb8f", + "transactionIndex": "0x1", + "logIndex": "0x2", + "removed": false + } + ], + "logsBloom": "0x00000000020000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000004000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000004000000000000000010000000000001000000000000000000000000000000000000000000000000000000000000", + "type": "0x2", + "transactionHash": "0xafaa7c3ef97727b20d46e5cb7f6478ab2e3597d7426aa0b007e52e016aa0cb8f", + "transactionIndex": "0x1", + "blockHash": "0x0a8f06eb397fdab2e4392e16237ee54d6f6fe244e7ae4c021885998578c4cfe0", + "blockNumber": "0x135c76f", + "gasUsed": "0xbbf5", + "effectiveGasPrice": "0x23c34600", + "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "to": "0x3333333333333333333333333333333333333333", + "contractAddress": null + }, + { + "status": "0x1", + "cumulativeGasUsed": "0x131001", + "logs": [], + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "type": "0x2", + "transactionHash": "0xea620a26dd14c70920861794ebb4ad4ed9e20eaa0a9e4a8dd076bc5e6e978c12", + "transactionIndex": "0x2", + "blockHash": "0x0a8f06eb397fdab2e4392e16237ee54d6f6fe244e7ae4c021885998578c4cfe0", + "blockNumber": "0x135c76f", + "gasUsed": "0x16524", + "effectiveGasPrice": "0x23c34600", + "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "to": "0x861e127036b28d32f3777b4676f6bbb9e007d195", + "contractAddress": null + } + ], + "libraries": [], + "pending": [], + "returns": {}, + "timestamp": 1764222354052, + "chain": 999, + "commit": "1397709" +} diff --git a/broadcast/DeploySrcPeriphery.s.sol/1/run-latest.json b/broadcast/DeploySrcPeriphery.s.sol/1/run-latest.json new file mode 100644 index 000000000..46c83f498 --- /dev/null +++ b/broadcast/DeploySrcPeriphery.s.sol/1/run-latest.json @@ -0,0 +1,68 @@ +{ + "transactions": [ + { + "hash": "0x644921ba43415194748b1054d7610bc78e0b518b93cc703afc1b1f10af87fb11", + "transactionType": "CREATE", + "contractName": "SponsoredOFTSrcPeriphery", + "contractAddress": "0x4607bceaf7b22cb0c46882ffc9fab3c6efe66e5a", + "function": null, + "arguments": [ + "0xdAC17F958D2ee523a2206206994597C13D831ec7", + "0x6C96dE32CEa08842dcc4058c14d3aaAD7Fa41dee", + "30101", + "0x9A8f92a830A5cB89a3816e3D267CB7791c16b04D" + ], + "transaction": { + "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "gas": "0x198065", + "value": "0x0", + "input": "0x60e0604090808252346200020c57608081620018158038038091620000258285620002be565b8339810103126200020c576200003b81620002f6565b906020906200004c828201620002f6565b91620000686060620000608785016200030b565b9301620002f6565b935f549260018060a01b03938460018060a01b03199633888416175f55895192823391167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a3846080528060a0528360c0521691635e280f1160e01b82528482600481865afa9182156200027257869286915f916200027c575b5060048b518095819363416ecebf60e01b8352165afa91821562000272575f9262000233575b5063ffffffff8091169116036200022257908260049392885194858092637e062a3560e11b82525afa928315620002185784925f94620001d1575b50508116911603620001c0577fbbe623e022cc184bd276c9a778810da1531bdd4c0bac9d86069eb499aa2eb500921690825416179055516114f790816200031e823960805181818161025401526107ee015260a05181818161012c0152818161082d0152610d75015260c0518181816102b90152610a3b0152f35b8351633722464560e11b8152600490fd5b809294508193503d831162000210575b620001ed8183620002be565b810103126200020c5782620002038192620002f6565b92905f62000145565b5f80fd5b503d620001e1565b87513d5f823e3d90fd5b8651637c68382b60e01b8152600490fd5b9091508481813d83116200026a575b6200024e8183620002be565b810103126200020c5762000262906200030b565b905f6200010a565b503d62000242565b89513d5f823e3d90fd5b9293505081813d8311620002b6575b620002978183620002be565b810103126200020c57519085821682036200020c578486925f620000e4565b503d6200028b565b601f909101601f19168101906001600160401b03821190821017620002e257604052565b634e487b7160e01b5f52604160045260245ffd5b51906001600160a01b03821682036200020c57565b519063ffffffff821682036200020c5756fe60806040526004361015610011575f80fd5b5f3560e01c8063238ac933146100c45780635b9cae35146100bf5780636c19e783146100ba578063715018a6146100b557806382bfefc8146100b05780638da5cb5b146100ab578063c9279a74146100a6578063f2fde38b146100a1578063f731ce5f1461009c578063fcc5b1e3146100975763feb6172414610092575f80fd5b610516565b6104a8565b610475565b6102dd565b61029d565b610278565b610235565b6101d1565b610161565b61010d565b34610109575f3660031901126101095760206001600160a01b037fbbe623e022cc184bd276c9a778810da1531bdd4c0bac9d86069eb499aa2eb5005416604051908152f35b5f80fd5b34610109575f3660031901126101095760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b6001600160a01b0381160361010957565b346101095760203660031901126101095760043561017e81610150565b610186610997565b6001600160a01b037fbbe623e022cc184bd276c9a778810da1531bdd4c0bac9d86069eb499aa2eb500911673ffffffffffffffffffffffffffffffffffffffff198254161790555f80f35b34610109575f366003190112610109576101e9610997565b5f6001600160a01b03815473ffffffffffffffffffffffffffffffffffffffff1981168355167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3005b34610109575f3660031901126101095760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b34610109575f3660031901126101095760206001600160a01b035f5416604051908152f35b34610109575f36600319011261010957602060405163ffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b34610109576020366003190112610109576004356102fa81610150565b610302610997565b6001600160a01b03809116908115610109575f548273ffffffffffffffffffffffffffffffffffffffff198216175f55167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a3005b634e487b7160e01b5f52604160045260245ffd5b6040810190811067ffffffffffffffff82111761038957604052565b610359565b6080810190811067ffffffffffffffff82111761038957604052565b90601f8019910116810190811067ffffffffffffffff82111761038957604052565b6040519060e0820182811067ffffffffffffffff82111761038957604052565b67ffffffffffffffff811161038957601f01601f191660200190565b6040516020810181811067ffffffffffffffff821117610389576040525f8152905f368137565b5f5b8381106104405750505f910152565b8181015183820152602001610431565b906020916104698151809281855285808601910161042f565b601f01601f1916010190565b34610109575f366003190112610109576104a4610490610408565b604051918291602083526020830190610450565b0390f35b600319604036820112610109576004359067ffffffffffffffff908183116101095760609083360301126101095760243591818311610109573660238401121561010957826004013591821161010957366024838501011161010957602461051493019060040161075d565b005b34610109576020366003190112610109576004355f527fbbe623e022cc184bd276c9a778810da1531bdd4c0bac9d86069eb499aa2eb501602052602060ff60405f2054166040519015158152f35b1561010957565b90359061019e1981360301821215610109570190565b634e487b7160e01b5f52601160045260245ffd5b919082039182116105a257565b610581565b3d156105d1573d906105b8826103ec565b916105c660405193846103aa565b82523d5f602084013e565b606090565b9190826040910312610109576040516105ee8161036d565b6020808294805184520151910152565b919082810360c0811261010957608013610109576040519067ffffffffffffffff60608301818111848210176103895760405284518352602085015190811681036101095782608091602061066895015261065c83604088016105d6565b604082015294016105d6565b90565b6106689163ffffffff825116815260208201516020820152604082015160408201526060820151606082015260c06106c76106b5608085015160e0608086015260e0850190610450565b60a085015184820360a0860152610450565b9201519060c0818403910152610450565b9193926001600160a01b039060206106fa60609460808752608087019061066b565b968051828701520151604085015216910152565b6040513d5f823e3d90fd5b959287959260e09895928852602088015260408701526060860152608085015260c060a08501528160c0850152848401375f828201840152601f01601f1916010190565b909161076a8184846109bb565b6107b86107ab608061077c858061056b565b01355f527fbbe623e022cc184bd276c9a778810da1531bdd4c0bac9d86069eb499aa2eb50160205260405f2090565b805460ff19166001179055565b6107c182610bd8565b815134106109855761088292826107db60c0945134610595565b8061095d575b506001600160a01b0391827f00000000000000000000000000000000000000000000000000000000000000001692610828606061081e8b8061056b565b0135303387610def565b6108627f00000000000000000000000000000000000000000000000000000000000000009485606061085a8d8061056b565b013591610e47565b8251916040519788968795869363c7c7f5b360e01b8552600485016106d8565b0393165af180156109585761092a575b507f8fb515a2e89f5acfca1124e69e331c2cded0ca216b578ba531720f6841139dbf60806108c0848061056b565b01359160e06108cf858061056b565b01359461092560406108e1878061056b565b01359260a06108f0888061056b565b01359260c06108ff898061056b565b01359461010061090f8a8061056b565b0135906040519687966040339c01359288610719565b0390a4565b61094b9060c03d60c011610951575b61094381836103aa565b8101906105fe565b50610892565b503d610939565b61070e565b5f80808061097f946001600160a01b0389165af16109796105a7565b50610564565b5f6107e1565b604051639c92bdfb60e01b8152600490fd5b6001600160a01b035f5416330361010957565b3563ffffffff811681036101095790565b91610a0590610a09926109f57fbbe623e022cc184bd276c9a778810da1531bdd4c0bac9d86069eb499aa2eb500546001600160a01b031690565b6109ff868061056b565b90610f41565b1590565b610ab65760a0610a19828061056b565b01354211610aa457610a33610a2e828061056b565b6109aa565b63ffffffff807f000000000000000000000000000000000000000000000000000000000000000016911603610a9257610a74608061077c83610a7b9461056b565b5460ff1690565b610a8157565b604051623f613760e71b8152600490fd5b604051637c68382b60e01b8152600490fd5b604051638727a7f960e01b8152600490fd5b60405163c1606c2f60e01b8152600490fd5b6040519060e0820182811067ffffffffffffffff82111761038957604052606060c0835f81525f60208201525f60408201525f838201528260808201528260a08201520152565b60405190610b1c8261036d565b5f6020838281520152565b3560ff811681036101095790565b903590601e1981360301821215610109570180359067ffffffffffffffff82116101095760200191813603831361010957565b929192610b74826103ec565b91610b8260405193846103aa565b829481845281830111610109578281602093845f960137010152565b9060408282031261010957610668916105d6565b91906020610bc95f9260408652604086019061066b565b930152565b3561066881610150565b610be0610ac8565b50610be9610b0f565b506080610bf6828061056b565b013591610c7d60a0610c08848061056b565b013560c0610c16858061056b565b013560409560e0610c27878061056b565b0135610100610c36888061056b565b013591610c4f610160610c498a8061056b565b01610b27565b93610c72610c6b610c608b8061056b565b610180810190610b35565b3691610b68565b958a8a013592611058565b91610ccf610cb9610c8c6110a3565b610cb3610120610c9c868061056b565b01356fffffffffffffffffffffffffffffffff1690565b906110bd565b610cc9610140610c9c858061056b565b906111b9565b610ce46020610cde848061056b565b016109aa565b9385610cf0848061056b565b0135916060610cff858061056b565b01356060610d0d868061056b565b013590610d18610408565b94610d30610d246103cc565b63ffffffff909a168a52565b6020890152888801526060870152608086015260a085015260c08401528351633b6f743b60e01b8152848180610d698760048301610bb2565b03816001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000165afa948515610958575f95610db9575b50506020610db39101610bce565b91929190565b610db392955060209181610de192903d10610de8575b610dd981836103aa565b810190610b9e565b9491610da5565b503d610dcf565b9290604051926323b872dd60e01b60208501526001600160a01b03809216602485015216604483015260648201526064815260a081019181831067ffffffffffffffff84111761038957610e45926040526112b8565b565b91909160405191602083015f8063095ea7b360e01b938484526001600160a01b03908189166024890152604488015260448752610e838761038e565b85169286519082855af190610e966105a7565b82610f0f575b5081610f04575b5015610eb0575b50505050565b60405160208101919091526001600160a01b0390931660248401525f6044840152610efb92610ef690610ef081606481015b03601f1981018352826103aa565b826112b8565b6112b8565b5f808080610eaa565b90503b15155f610ea3565b80519192508115918215610f27575b5050905f610e9c565b610f3a92506020809183010191016112a0565b5f80610f1e565b92906110479261104191610f54816109aa565b90611036610f64602083016109aa565b610ee2610f746101608501610b27565b610f85610c6b610180870187610b35565b60208151910120906040519586946020860198610140830135928a610120820135936101008301359360e08401359360c08101359360a08201359360808301359360406060850135940135929a98969492909d9c9b99979593919d6101a08c019e63ffffffff8092168d521660208c015260408b015260608a0152608089015260a088015260c087015260e086015261010085015261012084015261014083015260ff166101608201526101800152565b519020923691610b68565b90611367565b6001600160a01b0390811691161490565b94610ee2946106689792989460ff946040519a8b9960208b015260408a01526060890152608088015260a087015260c08601521660e084015261010080840152610120830190610450565b604051600360f01b6020820152600281526106688161036d565b9061ffff6003816110cd85611345565b16036111ad576040519260208401926fffffffffffffffffffffffffffffffff199060801b168352601084526111028461036d565b60038261110e83611345565b160361118b5783518281116101095782166001019182116105a2576106689260249261117c604051968461114c89965180926020808a01910161042f565b850192600160f81b9081602086015261ffff60f01b9060f01b16602185015260238401525180938684019061042f565b010360048101845201826103aa565b90611197602492611345565b604051633a51740d60e01b815291166004820152fd5b60249061119784611345565b9061ffff6003816111c985611345565b16036111ad576040519260208401925f84526fffffffffffffffffffffffffffffffff199060801b166022850152601284526112048461036d565b60038261121083611345565b160361118b5783518281116101095782166001019182116105a2576106689260249261117c604051968461124e89965180926020808a01910161042f565b600160f81b60209187019182015260f09390931b7fffff000000000000000000000000000000000000000000000000000000000000166021840152600360f81b6023840152519283908684019061042f565b90816020910312610109575180151581036101095790565b905f806001600160a01b036113159416927f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c656460206040516112f88161036d565b818152015260208151910182855af161130f6105a7565b9161141d565b805190811591821561132b575b50501561010957565b61133e92506020809183010191016112a0565b5f80611322565b6002815110611355576002015190565b604051632d0483c560e21b8152600490fd5b610668916113749161137c565b9190916113cf565b9060418151145f146113a8576113a491602082015190606060408401519301515f1a90611446565b9091565b50505f90600290565b600511156113bb57565b634e487b7160e01b5f52602160045260245ffd5b6113d8816113b1565b806113e05750565b6113e9816113b1565b600181036113f5575f80fd5b6113fe816113b1565b6002810361140a575f80fd5b806114166003926113b1565b1461010957565b90156114375781511561142e575090565b3b156101095790565b50805190811561010957602001fd5b7f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a084116114b6576020935f9360ff60809460405194855216868401526040830152606082015282805260015afa15610958575f516001600160a01b038116156114ae57905f90565b505f90600190565b505050505f9060039056fea264697066735822122051769784125192cb0975f21ea1bbee4a627e7578e6b15669655ba40a10d38d8b64736f6c63430008180033000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec70000000000000000000000006c96de32cea08842dcc4058c14d3aaad7fa41dee00000000000000000000000000000000000000000000000000000000000075950000000000000000000000009a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "nonce": "0x2ce1", + "chainId": "0x1" + }, + "additionalContracts": [], + "isFixedGasLimit": false + } + ], + "receipts": [ + { + "status": "0x1", + "cumulativeGasUsed": "0x2044cc3", + "logs": [ + { + "address": "0x4607bceaf7b22cb0c46882ffc9fab3c6efe66e5a", + "topics": [ + "0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000009a8f92a830a5cb89a3816e3d267cb7791c16b04d" + ], + "data": "0x", + "blockHash": "0x551db39887921a6406e0fb12e7e5a64728b98e43b4d55dd49077622b1cc56adb", + "blockNumber": "0x16d23fd", + "blockTimestamp": "0x692f9bdf", + "transactionHash": "0x644921ba43415194748b1054d7610bc78e0b518b93cc703afc1b1f10af87fb11", + "transactionIndex": "0xb8", + "logIndex": "0x1a4", + "removed": false + } + ], + "logsBloom": "0x00000000000000000000000000000000000000000000000000800000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000020000000000000000000800000000000000000080000000040000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000800000000000000000000000000000000000000000000020000001000000000000000000000000000000000000000000000000000000000000", + "type": "0x2", + "transactionHash": "0x644921ba43415194748b1054d7610bc78e0b518b93cc703afc1b1f10af87fb11", + "transactionIndex": "0xb8", + "blockHash": "0x551db39887921a6406e0fb12e7e5a64728b98e43b4d55dd49077622b1cc56adb", + "blockNumber": "0x16d23fd", + "gasUsed": "0x139dd8", + "effectiveGasPrice": "0x24606e1", + "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "to": null, + "contractAddress": "0x4607bceaf7b22cb0c46882ffc9fab3c6efe66e5a" + } + ], + "libraries": [], + "pending": [], + "returns": {}, + "timestamp": 1764727805465, + "chain": 1, + "commit": "5d9f89e2" +} diff --git a/broadcast/DeploySrcPeriphery.s.sol/42161/run-latest.json b/broadcast/DeploySrcPeriphery.s.sol/42161/run-latest.json new file mode 100644 index 000000000..0e4475cbb --- /dev/null +++ b/broadcast/DeploySrcPeriphery.s.sol/42161/run-latest.json @@ -0,0 +1,71 @@ +{ + "transactions": [ + { + "hash": "0xaf2427171b1d55e27dc75985001ce5a6be8c89a817f51b479f223b4c9799c7eb", + "transactionType": "CREATE", + "contractName": "SponsoredOFTSrcPeriphery", + "contractAddress": "0x2ac5ee3796e027da274fbde84c82173a65868940", + "function": null, + "arguments": [ + "0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9", + "0x14E4A1B13bf7F943c8ff7C51fb60FA964A298D92", + "30110", + "0x9A8f92a830A5cB89a3816e3D267CB7791c16b04D" + ], + "transaction": { + "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "gas": "0x19d578", + "value": "0x0", + "input": "0x60e0604090808252346200020c57608081620018158038038091620000258285620002be565b8339810103126200020c576200003b81620002f6565b906020906200004c828201620002f6565b91620000686060620000608785016200030b565b9301620002f6565b935f549260018060a01b03938460018060a01b03199633888416175f55895192823391167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a3846080528060a0528360c0521691635e280f1160e01b82528482600481865afa9182156200027257869286915f916200027c575b5060048b518095819363416ecebf60e01b8352165afa91821562000272575f9262000233575b5063ffffffff8091169116036200022257908260049392885194858092637e062a3560e11b82525afa928315620002185784925f94620001d1575b50508116911603620001c0577fbbe623e022cc184bd276c9a778810da1531bdd4c0bac9d86069eb499aa2eb500921690825416179055516114f790816200031e823960805181818161025401526107ee015260a05181818161012c0152818161082d0152610d75015260c0518181816102b90152610a3b0152f35b8351633722464560e11b8152600490fd5b809294508193503d831162000210575b620001ed8183620002be565b810103126200020c5782620002038192620002f6565b92905f62000145565b5f80fd5b503d620001e1565b87513d5f823e3d90fd5b8651637c68382b60e01b8152600490fd5b9091508481813d83116200026a575b6200024e8183620002be565b810103126200020c5762000262906200030b565b905f6200010a565b503d62000242565b89513d5f823e3d90fd5b9293505081813d8311620002b6575b620002978183620002be565b810103126200020c57519085821682036200020c578486925f620000e4565b503d6200028b565b601f909101601f19168101906001600160401b03821190821017620002e257604052565b634e487b7160e01b5f52604160045260245ffd5b51906001600160a01b03821682036200020c57565b519063ffffffff821682036200020c5756fe60806040526004361015610011575f80fd5b5f3560e01c8063238ac933146100c45780635b9cae35146100bf5780636c19e783146100ba578063715018a6146100b557806382bfefc8146100b05780638da5cb5b146100ab578063c9279a74146100a6578063f2fde38b146100a1578063f731ce5f1461009c578063fcc5b1e3146100975763feb6172414610092575f80fd5b610516565b6104a8565b610475565b6102dd565b61029d565b610278565b610235565b6101d1565b610161565b61010d565b34610109575f3660031901126101095760206001600160a01b037fbbe623e022cc184bd276c9a778810da1531bdd4c0bac9d86069eb499aa2eb5005416604051908152f35b5f80fd5b34610109575f3660031901126101095760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b6001600160a01b0381160361010957565b346101095760203660031901126101095760043561017e81610150565b610186610997565b6001600160a01b037fbbe623e022cc184bd276c9a778810da1531bdd4c0bac9d86069eb499aa2eb500911673ffffffffffffffffffffffffffffffffffffffff198254161790555f80f35b34610109575f366003190112610109576101e9610997565b5f6001600160a01b03815473ffffffffffffffffffffffffffffffffffffffff1981168355167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3005b34610109575f3660031901126101095760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b34610109575f3660031901126101095760206001600160a01b035f5416604051908152f35b34610109575f36600319011261010957602060405163ffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b34610109576020366003190112610109576004356102fa81610150565b610302610997565b6001600160a01b03809116908115610109575f548273ffffffffffffffffffffffffffffffffffffffff198216175f55167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a3005b634e487b7160e01b5f52604160045260245ffd5b6040810190811067ffffffffffffffff82111761038957604052565b610359565b6080810190811067ffffffffffffffff82111761038957604052565b90601f8019910116810190811067ffffffffffffffff82111761038957604052565b6040519060e0820182811067ffffffffffffffff82111761038957604052565b67ffffffffffffffff811161038957601f01601f191660200190565b6040516020810181811067ffffffffffffffff821117610389576040525f8152905f368137565b5f5b8381106104405750505f910152565b8181015183820152602001610431565b906020916104698151809281855285808601910161042f565b601f01601f1916010190565b34610109575f366003190112610109576104a4610490610408565b604051918291602083526020830190610450565b0390f35b600319604036820112610109576004359067ffffffffffffffff908183116101095760609083360301126101095760243591818311610109573660238401121561010957826004013591821161010957366024838501011161010957602461051493019060040161075d565b005b34610109576020366003190112610109576004355f527fbbe623e022cc184bd276c9a778810da1531bdd4c0bac9d86069eb499aa2eb501602052602060ff60405f2054166040519015158152f35b1561010957565b90359061019e1981360301821215610109570190565b634e487b7160e01b5f52601160045260245ffd5b919082039182116105a257565b610581565b3d156105d1573d906105b8826103ec565b916105c660405193846103aa565b82523d5f602084013e565b606090565b9190826040910312610109576040516105ee8161036d565b6020808294805184520151910152565b919082810360c0811261010957608013610109576040519067ffffffffffffffff60608301818111848210176103895760405284518352602085015190811681036101095782608091602061066895015261065c83604088016105d6565b604082015294016105d6565b90565b6106689163ffffffff825116815260208201516020820152604082015160408201526060820151606082015260c06106c76106b5608085015160e0608086015260e0850190610450565b60a085015184820360a0860152610450565b9201519060c0818403910152610450565b9193926001600160a01b039060206106fa60609460808752608087019061066b565b968051828701520151604085015216910152565b6040513d5f823e3d90fd5b959287959260e09895928852602088015260408701526060860152608085015260c060a08501528160c0850152848401375f828201840152601f01601f1916010190565b909161076a8184846109bb565b6107b86107ab608061077c858061056b565b01355f527fbbe623e022cc184bd276c9a778810da1531bdd4c0bac9d86069eb499aa2eb50160205260405f2090565b805460ff19166001179055565b6107c182610bd8565b815134106109855761088292826107db60c0945134610595565b8061095d575b506001600160a01b0391827f00000000000000000000000000000000000000000000000000000000000000001692610828606061081e8b8061056b565b0135303387610def565b6108627f00000000000000000000000000000000000000000000000000000000000000009485606061085a8d8061056b565b013591610e47565b8251916040519788968795869363c7c7f5b360e01b8552600485016106d8565b0393165af180156109585761092a575b507f8fb515a2e89f5acfca1124e69e331c2cded0ca216b578ba531720f6841139dbf60806108c0848061056b565b01359160e06108cf858061056b565b01359461092560406108e1878061056b565b01359260a06108f0888061056b565b01359260c06108ff898061056b565b01359461010061090f8a8061056b565b0135906040519687966040339c01359288610719565b0390a4565b61094b9060c03d60c011610951575b61094381836103aa565b8101906105fe565b50610892565b503d610939565b61070e565b5f80808061097f946001600160a01b0389165af16109796105a7565b50610564565b5f6107e1565b604051639c92bdfb60e01b8152600490fd5b6001600160a01b035f5416330361010957565b3563ffffffff811681036101095790565b91610a0590610a09926109f57fbbe623e022cc184bd276c9a778810da1531bdd4c0bac9d86069eb499aa2eb500546001600160a01b031690565b6109ff868061056b565b90610f41565b1590565b610ab65760a0610a19828061056b565b01354211610aa457610a33610a2e828061056b565b6109aa565b63ffffffff807f000000000000000000000000000000000000000000000000000000000000000016911603610a9257610a74608061077c83610a7b9461056b565b5460ff1690565b610a8157565b604051623f613760e71b8152600490fd5b604051637c68382b60e01b8152600490fd5b604051638727a7f960e01b8152600490fd5b60405163c1606c2f60e01b8152600490fd5b6040519060e0820182811067ffffffffffffffff82111761038957604052606060c0835f81525f60208201525f60408201525f838201528260808201528260a08201520152565b60405190610b1c8261036d565b5f6020838281520152565b3560ff811681036101095790565b903590601e1981360301821215610109570180359067ffffffffffffffff82116101095760200191813603831361010957565b929192610b74826103ec565b91610b8260405193846103aa565b829481845281830111610109578281602093845f960137010152565b9060408282031261010957610668916105d6565b91906020610bc95f9260408652604086019061066b565b930152565b3561066881610150565b610be0610ac8565b50610be9610b0f565b506080610bf6828061056b565b013591610c7d60a0610c08848061056b565b013560c0610c16858061056b565b013560409560e0610c27878061056b565b0135610100610c36888061056b565b013591610c4f610160610c498a8061056b565b01610b27565b93610c72610c6b610c608b8061056b565b610180810190610b35565b3691610b68565b958a8a013592611058565b91610ccf610cb9610c8c6110a3565b610cb3610120610c9c868061056b565b01356fffffffffffffffffffffffffffffffff1690565b906110bd565b610cc9610140610c9c858061056b565b906111b9565b610ce46020610cde848061056b565b016109aa565b9385610cf0848061056b565b0135916060610cff858061056b565b01356060610d0d868061056b565b013590610d18610408565b94610d30610d246103cc565b63ffffffff909a168a52565b6020890152888801526060870152608086015260a085015260c08401528351633b6f743b60e01b8152848180610d698760048301610bb2565b03816001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000165afa948515610958575f95610db9575b50506020610db39101610bce565b91929190565b610db392955060209181610de192903d10610de8575b610dd981836103aa565b810190610b9e565b9491610da5565b503d610dcf565b9290604051926323b872dd60e01b60208501526001600160a01b03809216602485015216604483015260648201526064815260a081019181831067ffffffffffffffff84111761038957610e45926040526112b8565b565b91909160405191602083015f8063095ea7b360e01b938484526001600160a01b03908189166024890152604488015260448752610e838761038e565b85169286519082855af190610e966105a7565b82610f0f575b5081610f04575b5015610eb0575b50505050565b60405160208101919091526001600160a01b0390931660248401525f6044840152610efb92610ef690610ef081606481015b03601f1981018352826103aa565b826112b8565b6112b8565b5f808080610eaa565b90503b15155f610ea3565b80519192508115918215610f27575b5050905f610e9c565b610f3a92506020809183010191016112a0565b5f80610f1e565b92906110479261104191610f54816109aa565b90611036610f64602083016109aa565b610ee2610f746101608501610b27565b610f85610c6b610180870187610b35565b60208151910120906040519586946020860198610140830135928a610120820135936101008301359360e08401359360c08101359360a08201359360808301359360406060850135940135929a98969492909d9c9b99979593919d6101a08c019e63ffffffff8092168d521660208c015260408b015260608a0152608089015260a088015260c087015260e086015261010085015261012084015261014083015260ff166101608201526101800152565b519020923691610b68565b90611367565b6001600160a01b0390811691161490565b94610ee2946106689792989460ff946040519a8b9960208b015260408a01526060890152608088015260a087015260c08601521660e084015261010080840152610120830190610450565b604051600360f01b6020820152600281526106688161036d565b9061ffff6003816110cd85611345565b16036111ad576040519260208401926fffffffffffffffffffffffffffffffff199060801b168352601084526111028461036d565b60038261110e83611345565b160361118b5783518281116101095782166001019182116105a2576106689260249261117c604051968461114c89965180926020808a01910161042f565b850192600160f81b9081602086015261ffff60f01b9060f01b16602185015260238401525180938684019061042f565b010360048101845201826103aa565b90611197602492611345565b604051633a51740d60e01b815291166004820152fd5b60249061119784611345565b9061ffff6003816111c985611345565b16036111ad576040519260208401925f84526fffffffffffffffffffffffffffffffff199060801b166022850152601284526112048461036d565b60038261121083611345565b160361118b5783518281116101095782166001019182116105a2576106689260249261117c604051968461124e89965180926020808a01910161042f565b600160f81b60209187019182015260f09390931b7fffff000000000000000000000000000000000000000000000000000000000000166021840152600360f81b6023840152519283908684019061042f565b90816020910312610109575180151581036101095790565b905f806001600160a01b036113159416927f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c656460206040516112f88161036d565b818152015260208151910182855af161130f6105a7565b9161141d565b805190811591821561132b575b50501561010957565b61133e92506020809183010191016112a0565b5f80611322565b6002815110611355576002015190565b604051632d0483c560e21b8152600490fd5b610668916113749161137c565b9190916113cf565b9060418151145f146113a8576113a491602082015190606060408401519301515f1a90611446565b9091565b50505f90600290565b600511156113bb57565b634e487b7160e01b5f52602160045260245ffd5b6113d8816113b1565b806113e05750565b6113e9816113b1565b600181036113f5575f80fd5b6113fe816113b1565b6002810361140a575f80fd5b806114166003926113b1565b1461010957565b90156114375781511561142e575090565b3b156101095790565b50805190811561010957602001fd5b7f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a084116114b6576020935f9360ff60809460405194855216868401526040830152606082015282805260015afa15610958575f516001600160a01b038116156114ae57905f90565b505f90600190565b505050505f9060039056fea264697066735822122051769784125192cb0975f21ea1bbee4a627e7578e6b15669655ba40a10d38d8b64736f6c63430008180033000000000000000000000000fd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb900000000000000000000000014e4a1b13bf7f943c8ff7c51fb60fa964a298d92000000000000000000000000000000000000000000000000000000000000759e0000000000000000000000009a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "nonce": "0x156f", + "chainId": "0xa4b1" + }, + "additionalContracts": [], + "isFixedGasLimit": false + } + ], + "receipts": [ + { + "status": "0x1", + "cumulativeGasUsed": "0x14ebe6", + "logs": [ + { + "address": "0x2ac5ee3796e027da274fbde84c82173a65868940", + "topics": [ + "0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000009a8f92a830a5cb89a3816e3d267cb7791c16b04d" + ], + "data": "0x", + "blockHash": "0xe8654d6f96063f64d25fde9ed46790b3ff86f62251bf1043b0067a27409af110", + "blockNumber": "0x183c08e6", + "blockTimestamp": "0x692f9b49", + "transactionHash": "0xaf2427171b1d55e27dc75985001ce5a6be8c89a817f51b479f223b4c9799c7eb", + "transactionIndex": "0x3", + "logIndex": "0x1", + "removed": false + } + ], + "logsBloom": "0x00000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000001000000000000000000000000000000000000020000000000000000000800002000000000000000000000040000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000020000001000000000000000000000000000000000000000000000080000000000000", + "type": "0x2", + "transactionHash": "0xaf2427171b1d55e27dc75985001ce5a6be8c89a817f51b479f223b4c9799c7eb", + "transactionIndex": "0x3", + "blockHash": "0xe8654d6f96063f64d25fde9ed46790b3ff86f62251bf1043b0067a27409af110", + "blockNumber": "0x183c08e6", + "gasUsed": "0x13ae42", + "effectiveGasPrice": "0xd570a0", + "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "to": null, + "contractAddress": "0x2ac5ee3796e027da274fbde84c82173a65868940", + "gasUsedForL1": "0x106a", + "l1BlockNumber": "0x16d23ef", + "timeboosted": false + } + ], + "libraries": [], + "pending": [], + "returns": {}, + "timestamp": 1764727648975, + "chain": 42161, + "commit": "5d9f89e2" +} diff --git a/broadcast/UpdateAuthorizedPeripheries.s.sol/999/run-latest.json b/broadcast/UpdateAuthorizedPeripheries.s.sol/999/run-latest.json new file mode 100644 index 000000000..481152b0b --- /dev/null +++ b/broadcast/UpdateAuthorizedPeripheries.s.sol/999/run-latest.json @@ -0,0 +1,60 @@ +{ + "transactions": [ + { + "hash": "0x19ad09c2ffb53f04a2b5e9186ec23f981897b37feb3e30a7a49f300ee25221f6", + "transactionType": "CALL", + "contractName": null, + "contractAddress": "0xd9f40794367a2ecb0b409ca8dbc55345c0db6e9f", + "function": "setAuthorizedPeriphery(uint32,bytes32)", + "arguments": ["30101", "0x000000000000000000000000e35d1205a523b699785967fffe99b72059b46707"], + "transaction": { + "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "to": "0xd9f40794367a2ecb0b409ca8dbc55345c0db6e9f", + "gas": "0xbcaa", + "value": "0x0", + "input": "0x52e12a140000000000000000000000000000000000000000000000000000000000007595000000000000000000000000e35d1205a523b699785967fffe99b72059b46707", + "nonce": "0x13f", + "chainId": "0x3e7" + }, + "additionalContracts": [], + "isFixedGasLimit": false + } + ], + "receipts": [ + { + "status": "0x1", + "cumulativeGasUsed": "0x8101", + "logs": [ + { + "address": "0xd9f40794367a2ecb0b409ca8dbc55345c0db6e9f", + "topics": ["0xf3adc8db618887d7b76838e244efb05fc99475bb5a904a914d939fbdc41b7e8d"], + "data": "0x0000000000000000000000000000000000000000000000000000000000007595000000000000000000000000e35d1205a523b699785967fffe99b72059b46707", + "blockHash": "0x57cf2f4386edc7e19f09a4c0abb92715c71526d2750d1ee34f37fd3ea75e2462", + "blockNumber": "0x11ab67e", + "blockTimestamp": "0x690d4508", + "transactionHash": "0x19ad09c2ffb53f04a2b5e9186ec23f981897b37feb3e30a7a49f300ee25221f6", + "transactionIndex": "0x0", + "logIndex": "0x0", + "removed": false + } + ], + "logsBloom": "0x00000000000000000000800000000000000000000000200000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000020000000000000000000000000400000000000000000000000000000000000", + "type": "0x2", + "transactionHash": "0x19ad09c2ffb53f04a2b5e9186ec23f981897b37feb3e30a7a49f300ee25221f6", + "transactionIndex": "0x0", + "blockHash": "0x57cf2f4386edc7e19f09a4c0abb92715c71526d2750d1ee34f37fd3ea75e2462", + "blockNumber": "0x11ab67e", + "gasUsed": "0x8101", + "effectiveGasPrice": "0x3a7dfcb4", + "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "to": "0xd9f40794367a2ecb0b409ca8dbc55345c0db6e9f", + "contractAddress": null + } + ], + "libraries": [], + "pending": [], + "returns": {}, + "timestamp": 1762477320670, + "chain": 999, + "commit": "d93c7b1f" +} diff --git a/broadcast/deployed-addresses.json b/broadcast/deployed-addresses.json index 5ee37f3ec..4d72aa918 100644 --- a/broadcast/deployed-addresses.json +++ b/broadcast/deployed-addresses.json @@ -176,6 +176,11 @@ "address": "0x0Bf07B2e415F02711fFBB32491f8ec9e5489B2e7", "block_number": 19084679, "transaction_hash": "0xa2a7b2c6812fb8ae34539fb04cd5f2a9112da1c7f6ffce0ddcf1fee7e43acf48" + }, + "SponsoredOFTSrcPeriphery": { + "address": "0x4607bceaf7b22cb0c46882ffc9fab3c6efe66e5a", + "block_number": 23929853, + "transaction_hash": "0x644921ba43415194748b1054d7610bc78e0b518b93cc703afc1b1f10af87fb11" } } }, @@ -472,6 +477,31 @@ "SpokePoolPeriphery": { "address": "0xF1BF00D947267Da5cC63f8c8A60568c59FA31bCb", "block_number": 15142204 + }, + "DonationBox": { + "address": "0x90e2487764e5316a2e4109c2ed40a3b3ad423659", + "block_number": 20815766, + "transaction_hash": "0xf72c3e798991af0e7123197bfd7da40585f936b4353b91159b749008ceac541a" + }, + "SponsoredCCTPDstPeriphery": { + "address": "0x83e245941befbde29682df068bcda006a804eb0c", + "block_number": 20793257, + "transaction_hash": "0xb08e0d23618a1447781b9071792c660bfd76db006b105822ea41d65e0ddc2802" + }, + "PermissionedMulticallHandler": { + "address": "0x0980d0f6799ca06c71ffafdc0e423cf2b0f20502", + "block_number": 20815766, + "transaction_hash": "0xee446c5d024fc2d807a387b7be9dc0e17f7f806efac526cc1db7bebd7fa5f7c7" + }, + "DstOFTHandler": { + "address": "0x40153ddfad90c49dbe3f5c9f96f2a5b25ec67461", + "block_number": 20815766, + "transaction_hash": "0xb29d240d7893c5e112a3407a86d1ae520674cb3799cc0fb88420b386dbf018c7" + }, + "HyperliquidDepositHandler": { + "address": "0x861e127036b28d32f3777b4676f6bbb9e007d195", + "block_number": 20301679, + "transaction_hash": "0x187b45f39be413aff2ff526946c64f76ed98763129e7e2ffb7d2d4c5bd997519" } } }, @@ -575,6 +605,11 @@ "address": "0x2eb5def5cf9635f9d926788a26d424287a045b92", "block_number": 36361964, "transaction_hash": "0x6ec337bdf00c331fabbfb0e98862aa2b766c613b6d6002c44b53668b7240989c" + }, + "SponsoredCCTPSrcPeriphery": { + "address": "0xa7a8d1efc1ee3e69999d370380949092251a5c20", + "block_number": 38957833, + "transaction_hash": "0xa7a480cad9b735b44890b80945b33b22d171820c6343349343813bb25e5be017" } } }, @@ -673,6 +708,16 @@ "address": "0x33d52d76d617126648067401c106923e4a34dbe1", "block_number": 385694983, "transaction_hash": "0x3a645809d7a2d2a0176afc87f8e565419f94547c9b1521b537b6f233c3bb412c" + }, + "SponsoredCCTPSrcPeriphery": { + "address": "0xce1ffe01ebb4f8521c12e74363a396ee3d337e1b", + "block_number": 406495696, + "transaction_hash": "0x6c4f6d7537530911757ecc317e0b5a39b5caf7089ab5549cb4299c830a9d854c" + }, + "SponsoredOFTSrcPeriphery": { + "address": "0x2ac5ee3796e027da274fbde84c82173a65868940", + "block_number": 406587622, + "transaction_hash": "0xaf2427171b1d55e27dc75985001ce5a6be8c89a817f51b479f223b4c9799c7eb" } } }, diff --git a/broadcast/deployed-addresses.md b/broadcast/deployed-addresses.md index 38bb47222..5f7a39ee1 100644 --- a/broadcast/deployed-addresses.md +++ b/broadcast/deployed-addresses.md @@ -46,6 +46,7 @@ This file contains the latest deployed smart contract addresses from the broadca | SpokePool | [0x5c7BCd6E7De5423a257D81B442095A1a6ced35C5](https://etherscan.io/address/0x5c7BCd6E7De5423a257D81B442095A1a6ced35C5) | | SpokePoolPeriphery | [0x89415a82d909a7238d69094C3Dd1dCC1aCbDa85C](https://etherscan.io/address/0x89415a82d909a7238d69094C3Dd1dCC1aCbDa85C) | | SpokePoolVerifier | [0x3Fb9cED51E968594C87963a371Ed90c39519f65A](https://etherscan.io/address/0x3Fb9cED51E968594C87963a371Ed90c39519f65A) | +| SponsoredOFTSrcPeriphery | [0x4607BceaF7b22cb0c46882FFc9fAB3c6efe66e5a](https://etherscan.io/address/0x4607BceaF7b22cb0c46882FFc9fAB3c6efe66e5a) | | Universal_Adapter_143 | [0xC29a3Ba0fBf477F16Fd53d2C438Eade024FD8452](https://etherscan.io/address/0xC29a3Ba0fBf477F16Fd53d2C438Eade024FD8452) | | Universal_Adapter_56 | [0x6f1C9d3bcDF51316E7b515a62C02F601500b084b](https://etherscan.io/address/0x6f1C9d3bcDF51316E7b515a62C02F601500b084b) | | Universal_Adapter_9745 | [0xb47fD69FE25878F4E43aAF2F9ad7D0A3A0B22363](https://etherscan.io/address/0xb47fD69FE25878F4E43aAF2F9ad7D0A3A0B22363) | @@ -157,13 +158,19 @@ This file contains the latest deployed smart contract addresses from the broadca ## HyperEVM (999) -| Contract Name | Address | -| ------------------ | ------------------------------------------------------------------------------------------------------------------------- | -| Helios | [0xc19B7EF43a6eBd393446F401d1eCFac01B181ac0](https://hyperevmscan.io//address/0xc19B7EF43a6eBd393446F401d1eCFac01B181ac0) | -| MulticallHandler | [0x5E7840E06fAcCb6d1c3b5F5E0d1d3d07F2829bba](https://hyperevmscan.io//address/0x5E7840E06fAcCb6d1c3b5F5E0d1d3d07F2829bba) | -| SpokePool | [0x35E63eA3eb0fb7A3bc543C71FB66412e1F6B0E04](https://hyperevmscan.io//address/0x35E63eA3eb0fb7A3bc543C71FB66412e1F6B0E04) | -| SpokePoolPeriphery | [0xF1BF00D947267Da5cC63f8c8A60568c59FA31bCb](https://hyperevmscan.io//address/0xF1BF00D947267Da5cC63f8c8A60568c59FA31bCb) | -| SpokePoolVerifier | [0x3Fb9cED51E968594C87963a371Ed90c39519f65A](https://hyperevmscan.io//address/0x3Fb9cED51E968594C87963a371Ed90c39519f65A) | +| Contract Name | Address | +| ---------------------------- | ------------------------------------------------------------------------------------------------------------------------- | +| DonationBox | [0x002E76DC036A1efF1488ee5435eE66C6aBF32674](https://hyperevmscan.io//address/0x002E76DC036A1efF1488ee5435eE66C6aBF32674) | +| DonationBox | [0x90E2487764E5316a2e4109c2Ed40A3B3ad423659](https://hyperevmscan.io//address/0x90E2487764E5316a2e4109c2Ed40A3B3ad423659) | +| DstOFTHandler | [0x40153DdFAd90C49dbE3F5c9F96f2a5B25ec67461](https://hyperevmscan.io//address/0x40153DdFAd90C49dbE3F5c9F96f2a5B25ec67461) | +| Helios | [0xc19B7EF43a6eBd393446F401d1eCFac01B181ac0](https://hyperevmscan.io//address/0xc19B7EF43a6eBd393446F401d1eCFac01B181ac0) | +| HyperliquidDepositHandler | [0x861E127036B28D32f3777B4676F6bbb9e007d195](https://hyperevmscan.io//address/0x861E127036B28D32f3777B4676F6bbb9e007d195) | +| MulticallHandler | [0x5E7840E06fAcCb6d1c3b5F5E0d1d3d07F2829bba](https://hyperevmscan.io//address/0x5E7840E06fAcCb6d1c3b5F5E0d1d3d07F2829bba) | +| PermissionedMulticallHandler | [0x0980D0F6799CA06C71fFAFdc0E423cF2B0f20502](https://hyperevmscan.io//address/0x0980D0F6799CA06C71fFAFdc0E423cF2B0f20502) | +| SpokePool | [0x35E63eA3eb0fb7A3bc543C71FB66412e1F6B0E04](https://hyperevmscan.io//address/0x35E63eA3eb0fb7A3bc543C71FB66412e1F6B0E04) | +| SpokePoolPeriphery | [0xF1BF00D947267Da5cC63f8c8A60568c59FA31bCb](https://hyperevmscan.io//address/0xF1BF00D947267Da5cC63f8c8A60568c59FA31bCb) | +| SpokePoolVerifier | [0x3Fb9cED51E968594C87963a371Ed90c39519f65A](https://hyperevmscan.io//address/0x3Fb9cED51E968594C87963a371Ed90c39519f65A) | +| SponsoredCCTPDstPeriphery | [0x83e245941BefbDe29682dF068Bcda006A804eb0C](https://hyperevmscan.io//address/0x83e245941BefbDe29682dF068Bcda006A804eb0C) | ## Lisk (1135) @@ -186,14 +193,15 @@ This file contains the latest deployed smart contract addresses from the broadca ## Base (8453) -| Contract Name | Address | -| ----------------------- | --------------------------------------------------------------------------------------------------------------------- | -| 1inch_SwapAndBridge | [0x7CFaBF2eA327009B39f40078011B0Fb714b65926](https://basescan.org/address/0x7CFaBF2eA327009B39f40078011B0Fb714b65926) | -| Base_SpokePool | [0x2eb5def5cF9635F9D926788a26D424287a045b92](https://basescan.org/address/0x2eb5def5cF9635F9D926788a26D424287a045b92) | -| MulticallHandler | [0x0F7Ae28dE1C8532170AD4ee566B5801485c13a0E](https://basescan.org/address/0x0F7Ae28dE1C8532170AD4ee566B5801485c13a0E) | -| SpokePool | [0x09aea4b2242abC8bb4BB78D537A67a245A7bEC64](https://basescan.org/address/0x09aea4b2242abC8bb4BB78D537A67a245A7bEC64) | -| SpokePoolVerifier | [0x3Fb9cED51E968594C87963a371Ed90c39519f65A](https://basescan.org/address/0x3Fb9cED51E968594C87963a371Ed90c39519f65A) | -| UniswapV3_SwapAndBridge | [0xbcfbCE9D92A516e3e7b0762AE218B4194adE34b4](https://basescan.org/address/0xbcfbCE9D92A516e3e7b0762AE218B4194adE34b4) | +| Contract Name | Address | +| ------------------------- | --------------------------------------------------------------------------------------------------------------------- | +| 1inch_SwapAndBridge | [0x7CFaBF2eA327009B39f40078011B0Fb714b65926](https://basescan.org/address/0x7CFaBF2eA327009B39f40078011B0Fb714b65926) | +| Base_SpokePool | [0x2eb5def5cF9635F9D926788a26D424287a045b92](https://basescan.org/address/0x2eb5def5cF9635F9D926788a26D424287a045b92) | +| MulticallHandler | [0x0F7Ae28dE1C8532170AD4ee566B5801485c13a0E](https://basescan.org/address/0x0F7Ae28dE1C8532170AD4ee566B5801485c13a0E) | +| SpokePool | [0x09aea4b2242abC8bb4BB78D537A67a245A7bEC64](https://basescan.org/address/0x09aea4b2242abC8bb4BB78D537A67a245A7bEC64) | +| SpokePoolVerifier | [0x3Fb9cED51E968594C87963a371Ed90c39519f65A](https://basescan.org/address/0x3Fb9cED51E968594C87963a371Ed90c39519f65A) | +| SponsoredCCTPSrcPeriphery | [0xA7A8d1efC1EE3E69999D370380949092251a5c20](https://basescan.org/address/0xA7A8d1efC1EE3E69999D370380949092251a5c20) | +| UniswapV3_SwapAndBridge | [0xbcfbCE9D92A516e3e7b0762AE218B4194adE34b4](https://basescan.org/address/0xbcfbCE9D92A516e3e7b0762AE218B4194adE34b4) | ## Plasma (9745) @@ -217,15 +225,17 @@ This file contains the latest deployed smart contract addresses from the broadca ## Arbitrum One (42161) -| Contract Name | Address | -| ----------------------- | -------------------------------------------------------------------------------------------------------------------- | -| 1inch_SwapAndBridge | [0xC456398D5eE3B93828252e48beDEDbc39e03368E](https://arbiscan.io/address/0xC456398D5eE3B93828252e48beDEDbc39e03368E) | -| Arbitrum_SpokePool | [0x33d52D76d617126648067401C106923e4A34dbe1](https://arbiscan.io/address/0x33d52D76d617126648067401C106923e4A34dbe1) | -| MulticallHandler | [0x0F7Ae28dE1C8532170AD4ee566B5801485c13a0E](https://arbiscan.io/address/0x0F7Ae28dE1C8532170AD4ee566B5801485c13a0E) | -| SpokePool | [0xe35e9842fceaCA96570B734083f4a58e8F7C5f2A](https://arbiscan.io/address/0xe35e9842fceaCA96570B734083f4a58e8F7C5f2A) | -| SpokePoolPeriphery | [0x89415a82d909a7238d69094C3Dd1dCC1aCbDa85C](https://arbiscan.io/address/0x89415a82d909a7238d69094C3Dd1dCC1aCbDa85C) | -| SpokePoolVerifier | [0x3Fb9cED51E968594C87963a371Ed90c39519f65A](https://arbiscan.io/address/0x3Fb9cED51E968594C87963a371Ed90c39519f65A) | -| UniswapV3_SwapAndBridge | [0xF633b72A4C2Fb73b77A379bf72864A825aD35b6D](https://arbiscan.io/address/0xF633b72A4C2Fb73b77A379bf72864A825aD35b6D) | +| Contract Name | Address | +| ------------------------- | -------------------------------------------------------------------------------------------------------------------- | +| 1inch_SwapAndBridge | [0xC456398D5eE3B93828252e48beDEDbc39e03368E](https://arbiscan.io/address/0xC456398D5eE3B93828252e48beDEDbc39e03368E) | +| Arbitrum_SpokePool | [0x33d52D76d617126648067401C106923e4A34dbe1](https://arbiscan.io/address/0x33d52D76d617126648067401C106923e4A34dbe1) | +| MulticallHandler | [0x0F7Ae28dE1C8532170AD4ee566B5801485c13a0E](https://arbiscan.io/address/0x0F7Ae28dE1C8532170AD4ee566B5801485c13a0E) | +| SpokePool | [0xe35e9842fceaCA96570B734083f4a58e8F7C5f2A](https://arbiscan.io/address/0xe35e9842fceaCA96570B734083f4a58e8F7C5f2A) | +| SpokePoolPeriphery | [0x89415a82d909a7238d69094C3Dd1dCC1aCbDa85C](https://arbiscan.io/address/0x89415a82d909a7238d69094C3Dd1dCC1aCbDa85C) | +| SpokePoolVerifier | [0x3Fb9cED51E968594C87963a371Ed90c39519f65A](https://arbiscan.io/address/0x3Fb9cED51E968594C87963a371Ed90c39519f65A) | +| SponsoredCCTPSrcPeriphery | [0xce1FFE01eBB4f8521C12e74363A396ee3d337E1B](https://arbiscan.io/address/0xce1FFE01eBB4f8521C12e74363A396ee3d337E1B) | +| SponsoredOFTSrcPeriphery | [0x2ac5Ee3796E027dA274fbDe84c82173a65868940](https://arbiscan.io/address/0x2ac5Ee3796E027dA274fbDe84c82173a65868940) | +| UniswapV3_SwapAndBridge | [0xF633b72A4C2Fb73b77A379bf72864A825aD35b6D](https://arbiscan.io/address/0xF633b72A4C2Fb73b77A379bf72864A825aD35b6D) | ## Ink (57073) diff --git a/contracts/chain-adapters/OP_Adapter.sol b/contracts/chain-adapters/OP_Adapter.sol index 0fcf1a975..e2f5c8818 100644 --- a/contracts/chain-adapters/OP_Adapter.sol +++ b/contracts/chain-adapters/OP_Adapter.sol @@ -35,31 +35,43 @@ contract OP_Adapter is CrossDomainEnabled, AdapterInterface, CircleCCTPAdapter { IL1StandardBridge public immutable L1_STANDARD_BRIDGE; IOpUSDCBridgeAdapter public immutable L1_OP_USDC_BRIDGE; + error InvalidBridgeConfig(); + /** * @notice Constructs new Adapter. * @param _l1Weth WETH address on L1. + * @param _l1Usdc USDC address on L1. * @param _crossDomainMessenger XDomainMessenger Destination chain system contract. * @param _l1StandardBridge Standard bridge contract. - * @param _l1Usdc USDC address on L1. + * @param _l1USDCBridge OP USDC bridge contract. + * @param _cctpTokenMessenger CCTP token messenger contract. + * @param _recipientCircleDomainId Circle domain ID of the destination chain. */ constructor( WETH9Interface _l1Weth, IERC20 _l1Usdc, address _crossDomainMessenger, IL1StandardBridge _l1StandardBridge, - IOpUSDCBridgeAdapter _l1USDCBridge + IOpUSDCBridgeAdapter _l1USDCBridge, + ITokenMessenger _cctpTokenMessenger, + uint32 _recipientCircleDomainId ) CrossDomainEnabled(_crossDomainMessenger) - CircleCCTPAdapter( - _l1Usdc, - // Hardcode cctp messenger to 0x0 to disable CCTP bridging. - ITokenMessenger(address(0)), - CircleDomainIds.UNINITIALIZED - ) + CircleCCTPAdapter(_l1Usdc, _cctpTokenMessenger, _recipientCircleDomainId) { L1_WETH = _l1Weth; L1_STANDARD_BRIDGE = _l1StandardBridge; L1_OP_USDC_BRIDGE = _l1USDCBridge; + + address zero = address(0); + if (address(_l1Usdc) != zero) { + bool opUSDCBridgeDisabled = address(_l1USDCBridge) == zero; + bool cctpUSDCBridgeDisabled = address(_cctpTokenMessenger) == zero; + // Bridged and Native USDC are mutually exclusive. + if (opUSDCBridgeDisabled == cctpUSDCBridgeDisabled) { + revert InvalidBridgeConfig(); + } + } } /** diff --git a/contracts/external/interfaces/CCTPInterfaces.sol b/contracts/external/interfaces/CCTPInterfaces.sol index 4eb89740b..9ac256f90 100644 --- a/contracts/external/interfaces/CCTPInterfaces.sol +++ b/contracts/external/interfaces/CCTPInterfaces.sol @@ -57,8 +57,8 @@ interface ITokenMessenger { function localMinter() external view returns (ITokenMinter minter); } -// Source: https://github.com/circlefin/evm-cctp-contracts/blob/63ab1f0ac06ce0793c0bbfbb8d09816bc211386d/src/v2/TokenMessengerV2.sol#L138C1-L166C15 interface ITokenMessengerV2 { + // Source: https://github.com/circlefin/evm-cctp-contracts/blob/63ab1f0ac06ce0793c0bbfbb8d09816bc211386d/src/v2/TokenMessengerV2.sol#L138C1-L166C15 /** * @notice Deposits and burns tokens from sender to be minted on destination domain. * Emits a `DepositForBurn` event. @@ -88,6 +88,39 @@ interface ITokenMessengerV2 { uint256 maxFee, uint32 minFinalityThreshold ) external; + + // Source: https://github.com/circlefin/evm-cctp-contracts/blob/63ab1f0ac06ce0793c0bbfbb8d09816bc211386d/src/v2/TokenMessengerV2.sol#L180C1-L210C15 + /** + * @notice Deposits and burns tokens from sender to be minted on destination domain. + * Emits a `DepositForBurn` event. + * @dev reverts if: + * - `hookData` is zero-length + * - `burnToken` is not supported + * - `destinationDomain` has no TokenMessenger registered + * - transferFrom() reverts. For example, if sender's burnToken balance or approved allowance + * to this contract is less than `amount`. + * - burn() reverts. For example, if `amount` is 0. + * - maxFee is greater than or equal to `amount`. + * - MessageTransmitterV2#sendMessage reverts. + * @param amount amount of tokens to burn + * @param destinationDomain destination domain to receive message on + * @param mintRecipient address of mint recipient on destination domain, as bytes32 + * @param burnToken token to burn `amount` of, on local domain + * @param destinationCaller authorized caller on the destination domain, as bytes32. If equal to bytes32(0), + * any address can broadcast the message. + * @param maxFee maximum fee to pay on the destination domain, specified in units of burnToken + * @param hookData hook data to append to burn message for interpretation on destination domain + */ + function depositForBurnWithHook( + uint256 amount, + uint32 destinationDomain, + bytes32 mintRecipient, + address burnToken, + bytes32 destinationCaller, + uint256 maxFee, + uint32 minFinalityThreshold, + bytes calldata hookData + ) external; } /** @@ -128,3 +161,38 @@ interface IMessageTransmitter { bytes calldata messageBody ) external returns (uint64); } + +interface IMessageTransmitterV2 { + // Source: https://github.com/circlefin/evm-cctp-contracts/blob/63ab1f0ac06ce0793c0bbfbb8d09816bc211386d/src/v2/MessageTransmitterV2.sol#L176C1-L209C61 + /** + * @notice Receive a message. Messages can only be broadcast once for a given nonce. + * The message body of a valid message is passed to the specified recipient for further processing. + * + * @dev Attestation format: + * A valid attestation is the concatenated 65-byte signature(s) of exactly + * `thresholdSignature` signatures, in increasing order of attester address. + * ***If the attester addresses recovered from signatures are not in + * increasing order, signature verification will fail.*** + * If incorrect number of signatures or duplicate signatures are supplied, + * signature verification will fail. + * + * Message Format: + * + * Field Bytes Type Index + * version 4 uint32 0 + * sourceDomain 4 uint32 4 + * destinationDomain 4 uint32 8 + * nonce 32 bytes32 12 + * sender 32 bytes32 44 + * recipient 32 bytes32 76 + * destinationCaller 32 bytes32 108 + * minFinalityThreshold 4 uint32 140 + * finalityThresholdExecuted 4 uint32 144 + * messageBody dynamic bytes 148 + * @param message Message bytes + * @param attestation Concatenated 65-byte signature(s) of `message`, in increasing order + * of the attester address recovered from signatures. + * @return success True, if successful; false, if not + */ + function receiveMessage(bytes calldata message, bytes calldata attestation) external returns (bool success); +} diff --git a/contracts/external/interfaces/ICoreDepositWallet.sol b/contracts/external/interfaces/ICoreDepositWallet.sol new file mode 100644 index 000000000..b92a4c9e4 --- /dev/null +++ b/contracts/external/interfaces/ICoreDepositWallet.sol @@ -0,0 +1,32 @@ +/* + * Copyright 2025 Circle Internet Group, Inc. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +pragma solidity ^0.8.0; + +/** + * @title ICoreDepositWallet + * @notice Interface for the core deposit wallet + * @dev Source: https://developers.circle.com/cctp/coredepositwallet-contract-interface#deposit-function + */ +interface ICoreDepositWallet { + /** + * @notice Deposits tokens for the sender. + * @param amount The amount of tokens being deposited. + * @param destinationDex The destination dex on HyperCore. + */ + function deposit(uint256 amount, uint32 destinationDex) external; +} diff --git a/contracts/external/interfaces/ILayerZeroComposer.sol b/contracts/external/interfaces/ILayerZeroComposer.sol new file mode 100644 index 000000000..1fdd962d6 --- /dev/null +++ b/contracts/external/interfaces/ILayerZeroComposer.sol @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: MIT + +pragma solidity >=0.8.0; + +/** + * @title ILayerZeroComposer + * @dev Copied over from https://github.com/LayerZero-Labs/LayerZero-v2/blob/2ff4988f85b5c94032eb71bbc4073e69c078179d/packages/layerzero-v2/evm/protocol/contracts/interfaces/ILayerZeroComposer.sol#L8 + */ +interface ILayerZeroComposer { + /** + * @notice Composes a LayerZero message from an OApp. + * @param _from The address initiating the composition, typically the OApp where the lzReceive was called. + * @param _guid The unique identifier for the corresponding LayerZero src/dst tx. + * @param _message The composed message payload in bytes. NOT necessarily the same payload passed via lzReceive. + * @param _executor The address of the executor for the composed message. + * @param _extraData Additional arbitrary data in bytes passed by the entity who executes the lzCompose. + */ + function lzCompose( + address _from, + bytes32 _guid, + bytes calldata _message, + address _executor, + bytes calldata _extraData + ) external payable; +} diff --git a/contracts/external/libraries/BytesLib.sol b/contracts/external/libraries/BytesLib.sol new file mode 100644 index 000000000..03d38d7ca --- /dev/null +++ b/contracts/external/libraries/BytesLib.sol @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.0; + +import { Bytes } from "@openzeppelin/contracts/utils/Bytes.sol"; + +library BytesLib { + /************************************** + * ERRORS * + **************************************/ + error OutOfBounds(); + + /************************************** + * FUNCTIONS * + **************************************/ + + // The following 4 functions are copied from solidity-bytes-utils library + // https://github.com/GNSPS/solidity-bytes-utils/blob/fc502455bb2a7e26a743378df042612dd50d1eb9/contracts/BytesLib.sol#L323C5-L398C6 + // Code was copied, and slightly modified to use revert instead of require + + /** + * @notice Reads a uint16 from a bytes array at a given start index + * @param _bytes The bytes array to convert + * @param _start The start index of the uint16 + * @return result The uint16 result + */ + function toUint16(bytes memory _bytes, uint256 _start) internal pure returns (uint16 result) { + if (_bytes.length < _start + 2) { + revert OutOfBounds(); + } + + // solhint-disable-next-line no-inline-assembly + assembly { + result := mload(add(add(_bytes, 0x2), _start)) + } + } + + /** + * @notice Reads a uint32 from a bytes array at a given start index + * @param _bytes The bytes array to convert + * @param _start The start index of the uint32 + * @return result The uint32 result + */ + function toUint32(bytes memory _bytes, uint256 _start) internal pure returns (uint32 result) { + if (_bytes.length < _start + 4) { + revert OutOfBounds(); + } + + // solhint-disable-next-line no-inline-assembly + assembly { + result := mload(add(add(_bytes, 0x4), _start)) + } + } + + /** + * @notice Reads a uint256 from a bytes array at a given start index + * @param _bytes The bytes array to convert + * @param _start The start index of the uint256 + * @return result The uint256 result + */ + function toUint256(bytes memory _bytes, uint256 _start) internal pure returns (uint256 result) { + if (_bytes.length < _start + 32) { + revert OutOfBounds(); + } + + // solhint-disable-next-line no-inline-assembly + assembly { + result := mload(add(add(_bytes, 0x20), _start)) + } + } + + /** + * @notice Reads a bytes32 from a bytes array at a given start index + * @param _bytes The bytes array to convert + * @param _start The start index of the bytes32 + * @return result The bytes32 result + */ + function toBytes32(bytes memory _bytes, uint256 _start) internal pure returns (bytes32 result) { + if (_bytes.length < _start + 32) { + revert OutOfBounds(); + } + + // solhint-disable-next-line no-inline-assembly + assembly { + result := mload(add(add(_bytes, 0x20), _start)) + } + } + + /** + * @notice Reads a bytes array from a bytes array at a given start index and length + * Source: OpenZeppelin Contracts v5 (utils/Bytes.sol) + * @param _bytes The bytes array to convert + * @param _start The start index of the bytes array + * @param _end The end index of the bytes array + * @return result The bytes array result + */ + function slice(bytes memory _bytes, uint256 _start, uint256 _end) internal pure returns (bytes memory result) { + return Bytes.slice(_bytes, _start, _end); + } +} diff --git a/contracts/external/libraries/MinimalLZOptions.sol b/contracts/external/libraries/MinimalLZOptions.sol new file mode 100644 index 000000000..36f1fdc90 --- /dev/null +++ b/contracts/external/libraries/MinimalLZOptions.sol @@ -0,0 +1,126 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.23; + +import { BytesLib } from "../../external/libraries/BytesLib.sol"; +import { SafeCast } from "@openzeppelin/contracts-v4/utils/math/SafeCast.sol"; + +/** + * @title MinimalExecutorOptions + * @notice This library is used to provide minimal required functionality of + * https://github.com/LayerZero-Labs/LayerZero-v2/blob/2ff4988f85b5c94032eb71bbc4073e69c078179d/packages/layerzero-v2/evm/messagelib/contracts/libs/ExecutorOptions.sol#L7 + * Code was copied, was not modified + */ +library MinimalExecutorOptions { + uint8 internal constant WORKER_ID = 1; + + uint8 internal constant OPTION_TYPE_LZRECEIVE = 1; + uint8 internal constant OPTION_TYPE_LZCOMPOSE = 3; + + function encodeLzReceiveOption(uint128 _gas, uint128 _value) internal pure returns (bytes memory) { + return _value == 0 ? abi.encodePacked(_gas) : abi.encodePacked(_gas, _value); + } + + function encodeLzComposeOption(uint16 _index, uint128 _gas, uint128 _value) internal pure returns (bytes memory) { + return _value == 0 ? abi.encodePacked(_index, _gas) : abi.encodePacked(_index, _gas, _value); + } +} + +/** + * @title MinimalLZOptions + * @notice This library is used to provide minimal functionality of + * https://github.com/LayerZero-Labs/devtools/blob/52ad590ab249f660f803ae3aafcbf7115733359c/packages/oapp-evm/contracts/oapp/libs/OptionsBuilder.sol + * Code was copied, was not modified + */ +library MinimalLZOptions { + // @dev Only used in `onlyType3` modifier + using BytesLib for bytes; + // @dev Only used in `addExecutorOption` + using SafeCast for uint256; + + // Constants for options types + uint16 internal constant TYPE_1 = 1; // legacy options type 1 + uint16 internal constant TYPE_2 = 2; // legacy options type 2 + uint16 internal constant TYPE_3 = 3; + + // Custom error message + error InvalidSize(uint256 max, uint256 actual); + error InvalidOptionType(uint16 optionType); + + // Modifier to ensure only options of type 3 are used + modifier onlyType3(bytes memory _options) { + if (_options.toUint16(0) != TYPE_3) revert InvalidOptionType(_options.toUint16(0)); + _; + } + + /** + * @dev Creates a new options container with type 3. + * @return options The newly created options container. + */ + function newOptions() internal pure returns (bytes memory) { + return abi.encodePacked(TYPE_3); + } + + /** + * @dev Adds an executor LZ receive option to the existing options. + * @param _options The existing options container. + * @param _gas The gasLimit used on the lzReceive() function in the OApp. + * @param _value The msg.value passed to the lzReceive() function in the OApp. + * @return options The updated options container. + * + * @dev When multiples of this option are added, they are summed by the executor + * eg. if (_gas: 200k, and _value: 1 ether) AND (_gas: 100k, _value: 0.5 ether) are sent in an option to the LayerZeroEndpoint, + * that becomes (300k, 1.5 ether) when the message is executed on the remote lzReceive() function. + */ + function addExecutorLzReceiveOption( + bytes memory _options, + uint128 _gas, + uint128 _value + ) internal pure onlyType3(_options) returns (bytes memory) { + bytes memory option = MinimalExecutorOptions.encodeLzReceiveOption(_gas, _value); + return addExecutorOption(_options, MinimalExecutorOptions.OPTION_TYPE_LZRECEIVE, option); + } + + /** + * @dev Adds an executor LZ compose option to the existing options. + * @param _options The existing options container. + * @param _index The index for the lzCompose() function call. + * @param _gas The gasLimit for the lzCompose() function call. + * @param _value The msg.value for the lzCompose() function call. + * @return options The updated options container. + * + * @dev When multiples of this option are added, they are summed PER index by the executor on the remote chain. + * @dev If the OApp sends N lzCompose calls on the remote, you must provide N incremented indexes starting with 0. + * ie. When your remote OApp composes (N = 3) messages, you must set this option for index 0,1,2 + */ + function addExecutorLzComposeOption( + bytes memory _options, + uint16 _index, + uint128 _gas, + uint128 _value + ) internal pure onlyType3(_options) returns (bytes memory) { + bytes memory option = MinimalExecutorOptions.encodeLzComposeOption(_index, _gas, _value); + return addExecutorOption(_options, MinimalExecutorOptions.OPTION_TYPE_LZCOMPOSE, option); + } + + /** + * @dev Adds an executor option to the existing options. + * @param _options The existing options container. + * @param _optionType The type of the executor option. + * @param _option The encoded data for the executor option. + * @return options The updated options container. + */ + function addExecutorOption( + bytes memory _options, + uint8 _optionType, + bytes memory _option + ) internal pure onlyType3(_options) returns (bytes memory) { + return + abi.encodePacked( + _options, + MinimalExecutorOptions.WORKER_ID, + _option.length.toUint16() + 1, // +1 for optionType + _optionType, + _option + ); + } +} diff --git a/contracts/external/libraries/OFTComposeMsgCodec.sol b/contracts/external/libraries/OFTComposeMsgCodec.sol new file mode 100644 index 000000000..1ed1b2a46 --- /dev/null +++ b/contracts/external/libraries/OFTComposeMsgCodec.sol @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.20; + +/** + * @title OFTComposeMsgCodec + * @notice Copied from LZ implementation here: + * https://github.com/LayerZero-Labs/devtools/blob/608915a7e260d995ce28e41c4e4877db9b18613b/packages/oft-evm/contracts/libs/OFTComposeMsgCodec.sol#L5 + * Code was copied, was not modified + */ +library OFTComposeMsgCodec { + // Offset constants for decoding composed messages + uint8 private constant NONCE_OFFSET = 8; + uint8 private constant SRC_EID_OFFSET = 12; + uint8 private constant AMOUNT_LD_OFFSET = 44; + uint8 private constant COMPOSE_FROM_OFFSET = 76; + + /** + * @dev Encodes a OFT composed message. + * @param _nonce The nonce value. + * @param _srcEid The source endpoint ID. + * @param _amountLD The amount in local decimals. + * @param _composeMsg The composed message. + * @return _msg The encoded Composed message. + */ + function encode( + uint64 _nonce, + uint32 _srcEid, + uint256 _amountLD, + bytes memory _composeMsg // 0x[composeFrom][composeMsg] + ) internal pure returns (bytes memory _msg) { + _msg = abi.encodePacked(_nonce, _srcEid, _amountLD, _composeMsg); + } + + /** + * @dev Retrieves the nonce for the composed message. + * @param _msg The message. + * @return The nonce value. + */ + function nonce(bytes calldata _msg) internal pure returns (uint64) { + return uint64(bytes8(_msg[:NONCE_OFFSET])); + } + + /** + * @dev Retrieves the source endpoint ID for the composed message. + * @param _msg The message. + * @return The source endpoint ID. + */ + function srcEid(bytes calldata _msg) internal pure returns (uint32) { + return uint32(bytes4(_msg[NONCE_OFFSET:SRC_EID_OFFSET])); + } + + /** + * @dev Retrieves the amount in local decimals from the composed message. + * @param _msg The message. + * @return The amount in local decimals. + */ + function amountLD(bytes calldata _msg) internal pure returns (uint256) { + return uint256(bytes32(_msg[SRC_EID_OFFSET:AMOUNT_LD_OFFSET])); + } + + /** + * @dev Retrieves the composeFrom value from the composed message. + * @param _msg The message. + * @return The composeFrom value. + */ + function composeFrom(bytes calldata _msg) internal pure returns (bytes32) { + return bytes32(_msg[AMOUNT_LD_OFFSET:COMPOSE_FROM_OFFSET]); + } + + /** + * @dev Retrieves the composed message. + * @param _msg The message. + * @return The composed message. + */ + function composeMsg(bytes calldata _msg) internal pure returns (bytes memory) { + return _msg[COMPOSE_FROM_OFFSET:]; + } + + /** + * @dev Converts an address to bytes32. + * @param _addr The address to convert. + * @return The bytes32 representation of the address. + */ + function addressToBytes32(address _addr) internal pure returns (bytes32) { + return bytes32(uint256(uint160(_addr))); + } + + /** + * @dev Converts bytes32 to an address. + * @param _b The bytes32 value to convert. + * @return The address representation of bytes32. + */ + function bytes32ToAddress(bytes32 _b) internal pure returns (address) { + return address(uint160(uint256(_b))); + } +} diff --git a/contracts/handlers/HyperliquidDepositHandler.sol b/contracts/handlers/HyperliquidDepositHandler.sol new file mode 100644 index 000000000..a7ece2200 --- /dev/null +++ b/contracts/handlers/HyperliquidDepositHandler.sol @@ -0,0 +1,251 @@ +// SPDX-License-Identifier: GPL-3.0-only +pragma solidity ^0.8.0; + +import "../interfaces/SpokePoolMessageHandler.sol"; +import "@openzeppelin/contracts-v4/token/ERC20/IERC20.sol"; +import "@openzeppelin/contracts-v4/token/ERC20/utils/SafeERC20.sol"; +import "@openzeppelin/contracts-v4/security/ReentrancyGuard.sol"; +import { ECDSA } from "@openzeppelin/contracts-v4/utils/cryptography/ECDSA.sol"; +import { HyperCoreLib } from "../libraries/HyperCoreLib.sol"; +import { Ownable } from "@openzeppelin/contracts-v4/access/Ownable.sol"; +import { DonationBox } from "../chain-adapters/DonationBox.sol"; + +/** + * @title Allows caller to bridge tokens from HyperEVM to Hypercore and send them to an end user's account + * on Hypercore. + * @dev This contract should only be deployed on HyperEVM. + * @dev This contract can replace a MulticallHandler on HyperEVM if the intent only wants to deposit tokens into + * Hypercore and bypass the other complex arbitrary calldata logic. + * @dev This contract can also be called directly to deposit tokens into Hypercore on behalf of an end user. + */ +contract HyperliquidDepositHandler is AcrossMessageHandler, ReentrancyGuard, Ownable { + using SafeERC20 for IERC20; + struct TokenInfo { + // HyperEVM token address. + address evmAddress; + // Hypercore token index. + uint64 tokenId; + // Activation fee in EVM units. e.g. 1000000 ($1) for USDH. + uint256 activationFeeEvm; + // coreDecimals - evmDecimals. e.g. -2 for USDH. + int8 decimalDiff; + } + + // Stores hardcoded Hypercore configurations for tokens that this handler supports. + mapping(address => TokenInfo) public supportedTokens; + + // Donation box contract to store funds for account activation fees. + DonationBox public immutable donationBox; + + // Address of the signer that will sign the payloads used for calling handleV3AcrossMessage. This signer + // should be one controlled by the Across API to prevent griefing attacks that attempt to drain the Donation Box. + address public signer; + + // Address of the SpokePool contract that can call handleV3AcrossMessage. + address public spokePool; + + // Track which accounts we have already sponsored for activation. Used to prevent griefing attacks when the same account is activated multiple times + // due to Hyperliquid's policy of removing dust from small accounts which technically could be taken advantage of by a griefer. + mapping(address => bool) public accountsActivated; + + error InsufficientEvmAmountForActivation(); + error TokenNotSupported(); + error InvalidSignature(); + error NotSpokePool(); + error AccountAlreadyActivated(); + + event UserAccountActivated(address user, address indexed token, uint256 amountRequiredToActivate); + event AddedSupportedToken(address evmAddress, uint64 tokenId, uint256 activationFeeEvm, int8 decimalDiff); + event SignerSet(address signer); + event SpokePoolSet(address spokePool); + + /** + * @notice Constructor. + * @dev Creates a new donation box contract owned by this contract. + * @param _signer Address of the signer that will sign the payloads used for calling handleV3AcrossMessage. This signer + * should be one controlled by the Across API to prevent griefing attacks that attempt to drain the Donation Box. + * @param _spokePool Address of the SpokePool contract that can call handleV3AcrossMessage. + */ + constructor(address _signer, address _spokePool) { + donationBox = new DonationBox(); + signer = _signer; + spokePool = _spokePool; + } + + modifier onlySpokePool() { + if (msg.sender != spokePool) revert NotSpokePool(); + _; + } + + /// ------------------------------------------------------------------------------------------------------------- + /// - PUBLIC FUNCTIONS - + /// ------------------------------------------------------------------------------------------------------------- + + /** + * @notice Bridges tokens from HyperEVM to Hypercore and sends them to the end user's account on Hypercore. + * @dev Requires msg.sender to have approved this contract to spend the tokens. + * @param token The address of the token to deposit. + * @param amount The amount of tokens on HyperEVM to deposit. + * @param user The address of the user on Hypercore to send the tokens to. + * @param signature Encoded signed message containing the end user address. The payload is designed to be signed + * by the Across API to prevent griefing attacks that attempt to drain the Donation Box. + */ + function depositToHypercore( + address token, + uint256 amount, + address user, + bytes memory signature + ) external nonReentrant { + _verifySignature(user, signature); + IERC20(token).safeTransferFrom(msg.sender, address(this), amount); + _depositToHypercore(token, amount, user); + } + + /** + * @notice Entrypoint function if this contract is called by the SpokePool contract following an intent fill. + * @dev Deposits tokens into Hypercore and sends them to the end user's account on Hypercore. + * @dev Requires msg.sender to be the SpokePool contract. This prevents someone from calling this function + * to drain funds that were accidentally dropped onto this contract. + * @param token The address of the token sent. + * @param amount The amount of tokens received by this contract. + * @param message Encoded signed message containing the end user address. The payload is designed to be signed + * by the Across API to prevent griefing attacks that attempt to drain the Donation Box. + */ + function handleV3AcrossMessage( + address token, + uint256 amount, + address /* relayer */, + bytes memory message + ) external nonReentrant onlySpokePool { + (address user, bytes memory signature) = abi.decode(message, (address, bytes)); + _verifySignature(user, signature); + _depositToHypercore(token, amount, user); + } + + /// ------------------------------------------------------------------------------------------------------------- + /// - ONLY OWNER FUNCTIONS - + /// ------------------------------------------------------------------------------------------------------------- + + /** + * @notice Sets the address of the signer that will sign the payloads used for calling handleV3AcrossMessage. + * @dev Caller must be owner of this contract. + * @param _signer Address of the signer that will sign the payloads used for calling handleV3AcrossMessage. This signer + * should be one controlled by the Across API to prevent griefing attacks that attempt to drain the Donation Box. + */ + function setSigner(address _signer) external onlyOwner { + signer = _signer; + emit SignerSet(signer); + } + + /** + * @notice Sets the address of the SpokePool contract that can call handleV3AcrossMessage. + * @dev Caller must be owner of this contract. + * @param _spokePool Address of the SpokePool contract that can call handleV3AcrossMessage. + */ + function setSpokePool(address _spokePool) external onlyOwner { + spokePool = _spokePool; + emit SpokePoolSet(spokePool); + } + + /** + * @notice Adds a new token to the supported tokens list. + * @dev Caller must be owner of this contract. + * @param evmAddress The address of the EVM token. + * @param tokenId The index of the Hypercore token. + * @param activationFeeEvm The activation fee in EVM units. + * @param decimalDiff The difference in decimals between the EVM and Hypercore tokens. + */ + function addSupportedToken( + address evmAddress, + uint64 tokenId, + uint256 activationFeeEvm, + int8 decimalDiff + ) external onlyOwner { + supportedTokens[evmAddress] = TokenInfo({ + evmAddress: evmAddress, + tokenId: tokenId, + activationFeeEvm: activationFeeEvm, + decimalDiff: decimalDiff + }); + emit AddedSupportedToken(evmAddress, tokenId, activationFeeEvm, decimalDiff); + } + + /** + * @notice Send Hypercore funds to a user from this contract's Hypercore account + * @dev The coreAmount parameter is specified in Hypercore units which often differs from the EVM units for the + * same token. + * @param token The token address + * @param coreAmount The amount of tokens on Hypercore to sweep + * @param user The address of the user to send the tokens to + */ + function sweepCoreFundsToUser(address token, uint64 coreAmount, address user) external onlyOwner nonReentrant { + uint64 tokenIndex = _getTokenInfo(token).tokenId; + HyperCoreLib.transferERC20CoreToCore(tokenIndex, user, coreAmount); + } + + /** + * @notice Send donation box funds to a user from this contract's address on HyperEVM + * @param token The token address + * @param amount The amount of tokens to sweep + * @param user The address of the user to send the tokens to + */ + function sweepDonationBoxFundsToUser(address token, uint256 amount, address user) external onlyOwner nonReentrant { + donationBox.withdraw(IERC20(token), amount); + IERC20(token).safeTransfer(user, amount); + } + + /** + * @notice Send ERC20 tokens to a user from this contract's address on HyperEVM + * @param token The token address + * @param evmAmount The amount of tokens to sweep + * @param user The address of the user to send the tokens to + */ + function sweepERC20ToUser(address token, uint256 evmAmount, address user) external onlyOwner nonReentrant { + IERC20(token).safeTransfer(user, evmAmount); + } + + /// ------------------------------------------------------------------------------------------------------------- + /// - INTERNAL FUNCTIONS - + /// ------------------------------------------------------------------------------------------------------------- + + function _depositToHypercore(address token, uint256 evmAmount, address user) internal { + TokenInfo memory tokenInfo = _getTokenInfo(token); + uint64 tokenIndex = tokenInfo.tokenId; + int8 decimalDiff = tokenInfo.decimalDiff; + + bool userExists = HyperCoreLib.coreUserExists(user); + if (!userExists) { + if (accountsActivated[user]) revert AccountAlreadyActivated(); + accountsActivated[user] = true; + // To activate an account, we must pay the activation fee from this contract's core account and then send 1 + // wei to the user's account, so we pull the activation fee + 1 wei from the donation box. This contract + // does not allow the end user subtracting part of their received amount to use for the activation fee. + uint256 activationFee = tokenInfo.activationFeeEvm; + uint256 amountRequiredToActivate = activationFee + 1; + donationBox.withdraw(IERC20(token), amountRequiredToActivate); + // Deposit the activation fee + 1 wei into this contract's core account to pay for the user's + // account activation. + HyperCoreLib.transferERC20EVMToSelfOnCore(token, tokenIndex, amountRequiredToActivate, decimalDiff); + HyperCoreLib.transferERC20CoreToCore(tokenIndex, user, 1); + emit UserAccountActivated(user, token, amountRequiredToActivate); + } + + HyperCoreLib.transferERC20EVMToCore(token, tokenIndex, user, evmAmount, decimalDiff); + } + + function _verifySignature(address expectedUser, bytes memory signature) internal view returns (bool) { + /// @dev There is no nonce in this signature because an account on Hypercore can only be activated once + /// by this contract, so reusing a signature cannot be used to grief the DonationBox. + bytes32 expectedHash = keccak256(abi.encode(expectedUser)); + if (ECDSA.recover(expectedHash, signature) != signer) revert InvalidSignature(); + } + + function _getTokenInfo(address evmAddress) internal view returns (TokenInfo memory) { + if (supportedTokens[evmAddress].evmAddress == address(0)) { + revert TokenNotSupported(); + } + return supportedTokens[evmAddress]; + } + + // Native tokens are not supported by this contract, so there is no fallback function. +} diff --git a/contracts/handlers/MulticallHandler.sol b/contracts/handlers/MulticallHandler.sol index 47d2841fe..239a3287e 100644 --- a/contracts/handlers/MulticallHandler.sol +++ b/contracts/handlers/MulticallHandler.sol @@ -58,10 +58,11 @@ contract MulticallHandler is AcrossMessageHandler, ReentrancyGuard { * @dev This will execute all calls encoded in the msg. The caller is responsible for making sure all tokens are * drained from this contract by the end of the series of calls. If not, they can be stolen. * A drainLeftoverTokens call can be included as a way to drain any remaining tokens from this contract. + * @param token The token address that was received from the relay * @param message abi encoded array of Call structs, containing a target, callData, and value for each call that * the contract should make. */ - function handleV3AcrossMessage(address token, uint256, address, bytes memory message) external nonReentrant { + function handleV3AcrossMessage(address token, uint256, address, bytes memory message) public virtual nonReentrant { Instructions memory instructions = abi.decode(message, (Instructions)); // If there is no fallback recipient, call and revert if the inner call fails. @@ -130,7 +131,7 @@ contract MulticallHandler is AcrossMessageHandler, ReentrancyGuard { uint256 value, Replacement[] calldata replacement ) external onlySelf { - for (uint256 i = 0; i < replacement.length; i++) { + for (uint256 i = 0; i < replacement.length; ++i) { uint256 bal = 0; if (replacement[i].token != address(0)) { bal = IERC20(replacement[i].token).balanceOf(address(this)); diff --git a/contracts/handlers/PermissionedMulticallHandler.sol b/contracts/handlers/PermissionedMulticallHandler.sol new file mode 100644 index 000000000..5b9a4cc4c --- /dev/null +++ b/contracts/handlers/PermissionedMulticallHandler.sol @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: GPL-3.0-only +pragma solidity ^0.8.0; + +import { MulticallHandler } from "./MulticallHandler.sol"; +import { AccessControl } from "@openzeppelin/contracts-v4/access/AccessControl.sol"; + +/** + * @title PermissionedMulticallHandler + * @notice Extension of MulticallHandler that restricts access to whitelisted callers + * @dev Uses OpenZeppelin's AccessControl for caller permission management. + * Only addresses with the WHITELISTED_CALLER_ROLE can call handleV3AcrossMessage. + */ +contract PermissionedMulticallHandler is MulticallHandler, AccessControl { + /// @notice Role identifier for whitelisted callers + bytes32 public constant WHITELISTED_CALLER_ROLE = keccak256("WHITELISTED_CALLER_ROLE"); + + /** + * @notice Constructor that sets up the initial admin + * @param admin Address that will have DEFAULT_ADMIN_ROLE + */ + constructor(address admin) { + _grantRole(DEFAULT_ADMIN_ROLE, admin); + } + + /** + * @notice Overrides handleV3AcrossMessage to add caller whitelist check + * @dev Only addresses with WHITELISTED_CALLER_ROLE can call this function + * @param token The token being transferred + * @param amount The amount of tokens + * @param relayer The relayer address + * @param message The encoded Instructions struct + */ + function handleV3AcrossMessage( + address token, + uint256 amount, + address relayer, + bytes memory message + ) public override onlyRole(WHITELISTED_CALLER_ROLE) { + // Call parent implementation + super.handleV3AcrossMessage(token, amount, relayer, message); + } +} diff --git a/contracts/interfaces/IOFT.sol b/contracts/interfaces/IOFT.sol index 297229143..28ac0ba1d 100644 --- a/contracts/interfaces/IOFT.sol +++ b/contracts/interfaces/IOFT.sol @@ -87,3 +87,15 @@ interface IOFT { address _refundAddress ) external payable returns (MessagingReceipt memory, OFTReceipt memory); } + +interface IEndpoint { + function eid() external view returns (uint32); +} + +interface IOAppCore { + /** + * @notice Retrieves the LayerZero endpoint associated with the OApp. + * @return iEndpoint The LayerZero endpoint as an interface. + */ + function endpoint() external view returns (IEndpoint iEndpoint); +} diff --git a/contracts/interfaces/SponsoredCCTPInterface.sol b/contracts/interfaces/SponsoredCCTPInterface.sol new file mode 100644 index 000000000..0eec4b37c --- /dev/null +++ b/contracts/interfaces/SponsoredCCTPInterface.sol @@ -0,0 +1,86 @@ +//SPDX-License-Identifier: Unlicense +pragma solidity ^0.8.0; + +/** + * @title SponsoredCCTPInterface + * @notice Interface for the SponsoredCCTP contract + * @custom:security-contact bugs@across.to + */ +interface SponsoredCCTPInterface { + // Error thrown when the signature is invalid. + error InvalidSignature(); + + // Error thrown when the nonce is invalid. + error InvalidNonce(); + + // Error thrown when the deadline is invalid. + error InvalidDeadline(); + + // Error thrown when the source domain is invalid. + error InvalidSourceDomain(); + + // Error thrown when the CCTP message transmitter receive message fails. + error CCTPMessageTransmitterFailed(); + + event SponsoredDepositForBurn( + bytes32 indexed quoteNonce, + address indexed originSender, + bytes32 indexed finalRecipient, + uint256 quoteDeadline, + uint256 maxBpsToSponsor, + uint256 maxUserSlippageBps, + bytes32 finalToken, + bytes signature + ); + + // Event when emergency receive is called + event EmergencyReceiveMessage(bytes32 nonce, address finalRecipent, address finalToken, uint256 amount); + + // Execution modes for the sponsored CCTP flow + enum ExecutionMode { + // Send to core and perform swap (if needed) there. + DirectToCore, + // Execute arbitrary actions (like a swap) on HyperEVM, then transfer to HyperCore + ArbitraryActionsToCore, + // Execute arbitrary actions on HyperEVM only (no HyperCore transfer) + ArbitraryActionsToEVM + } + + // Params that will be used to create a sponsored CCTP quote and deposit for burn. + struct SponsoredCCTPQuote { + // The domain ID of the source chain. + uint32 sourceDomain; + // The domain ID of the destination chain. + uint32 destinationDomain; + // The recipient of the minted USDC on the destination chain. + bytes32 mintRecipient; + // The amount that the user pays on the source chain. + uint256 amount; + // The token that will be burned on the source chain. + bytes32 burnToken; + // The caller of the destination chain. + bytes32 destinationCaller; + // Maximum fee to pay on the destination domain, specified in units of burnToken + uint256 maxFee; + // Minimum finality threshold before allowed to attest + uint32 minFinalityThreshold; + // Nonce is used to prevent replay attacks. + bytes32 nonce; + // Timestamp of the quote after which it can no longer be used. + uint256 deadline; + // The maximum basis points of the amount that can be sponsored. + uint256 maxBpsToSponsor; + // Slippage tolerance for the fees on the destination. Used in swap flow, enforced on destination + uint256 maxUserSlippageBps; + // The final recipient of the sponsored deposit. This is needed as the mintRecipient will be the + // handler contract address instead of the final recipient. + bytes32 finalRecipient; + // The final token that final recipient will receive. This is needed as it can be different from the burnToken + // in which case we perform a swap on the destination chain. + bytes32 finalToken; + // Execution mode: DirectToCore, ArbitraryActionsToCore, or ArbitraryActionsToEVM + uint8 executionMode; + // Encoded action data for arbitrary execution. Empty for DirectToCore mode. + bytes actionData; + } +} diff --git a/contracts/libraries/AddressConverters.sol b/contracts/libraries/AddressConverters.sol index 6b52ff479..d2f9ac2c4 100644 --- a/contracts/libraries/AddressConverters.sol +++ b/contracts/libraries/AddressConverters.sol @@ -17,10 +17,14 @@ library Bytes32ToAddress { } function checkAddress(bytes32 _bytes32) internal pure { - if (uint256(_bytes32) >> 160 != 0) { + if (!isValidAddress(_bytes32)) { revert InvalidBytes32(); } } + + function isValidAddress(bytes32 _bytes32) internal pure returns (bool) { + return uint256(_bytes32) >> 160 == 0; + } } library AddressToBytes32 { diff --git a/contracts/libraries/HyperCoreLib.sol b/contracts/libraries/HyperCoreLib.sol new file mode 100644 index 000000000..0c5828041 --- /dev/null +++ b/contracts/libraries/HyperCoreLib.sol @@ -0,0 +1,376 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import { IERC20 } from "@openzeppelin/contracts-v4/token/ERC20/IERC20.sol"; +import { SafeERC20 } from "@openzeppelin/contracts-v4/token/ERC20/utils/SafeERC20.sol"; +import { ICoreDepositWallet } from "../external/interfaces/ICoreDepositWallet.sol"; + +interface ICoreWriter { + function sendRawAction(bytes calldata data) external; +} + +library HyperCoreLib { + using SafeERC20 for IERC20; + + // Time-in-Force order types + enum Tif { + None, // invalid + ALO, // Add Liquidity Only + GTC, // Good-Till-Cancel + IOC // Immediate-or-Cancel + } + + struct SpotBalance { + uint64 total; + uint64 hold; // Unused in this implementation + uint64 entryNtl; // Unused in this implementation + } + + struct TokenInfo { + string name; + uint64[] spots; + uint64 deployerTradingFeeShare; + address deployer; + address evmContract; + uint8 szDecimals; + uint8 weiDecimals; + int8 evmExtraWeiDecimals; + } + + struct CoreUserExists { + bool exists; + } + + // Base asset bridge addresses + address public constant BASE_ASSET_BRIDGE_ADDRESS = 0x2000000000000000000000000000000000000000; + uint256 public constant BASE_ASSET_BRIDGE_ADDRESS_UINT256 = uint256(uint160(BASE_ASSET_BRIDGE_ADDRESS)); + + // Precompile addresses + address public constant SPOT_BALANCE_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000000801; + address public constant SPOT_PX_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000000808; + address public constant CORE_USER_EXISTS_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000000810; + address public constant TOKEN_INFO_PRECOMPILE_ADDRESS = 0x000000000000000000000000000000000000080C; + address public constant CORE_WRITER_PRECOMPILE_ADDRESS = 0x3333333333333333333333333333333333333333; + + // USDC + address public constant USDC_CORE_DEPOSIT_WALLET_ADDRESS = 0x6B9E773128f453f5c2C60935Ee2DE2CBc5390A24; + uint64 public constant USDC_CORE_INDEX = 0; + + // CoreWriter action headers + bytes4 public constant LIMIT_ORDER_HEADER = 0x01000001; // version=1, action=1 + bytes4 public constant SPOT_SEND_HEADER = 0x01000006; // version=1, action=6 + bytes4 public constant CANCEL_BY_CLOID_HEADER = 0x0100000B; // version=1, action=11 + + // HyperCore protocol constants + uint32 private constant CORE_SPOT_DEX_ID = type(uint32).max; + + // Errors + error LimitPxIsZero(); + error OrderSizeIsZero(); + error InvalidTif(); + error SpotBalancePrecompileCallFailed(); + error CoreUserExistsPrecompileCallFailed(); + error TokenInfoPrecompileCallFailed(); + error SpotPxPrecompileCallFailed(); + + /** + * @notice Transfer `amountEVM` from HyperEVM to `to` on HyperCore. + * @dev Returns the amount credited on Core in Core units (post conversion). + * @param erc20EVMAddress The address of the ERC20 token on HyperEVM + * @param erc20CoreIndex The HyperCore index id of the token to transfer + * @param to The address to receive tokens on HyperCore + * @param amountEVM The amount to transfer on HyperEVM + * @param decimalDiff The decimal difference of evmDecimals - coreDecimals + * @return amountEVMSent The amount sent on HyperEVM + * @return amountCoreToReceive The amount credited on Core in Core units (post conversion) + */ + function transferERC20EVMToCore( + address erc20EVMAddress, + uint64 erc20CoreIndex, + address to, + uint256 amountEVM, + int8 decimalDiff + ) internal returns (uint256 amountEVMSent, uint64 amountCoreToReceive) { + // if the transfer amount exceeds the bridge balance, this wil revert + (uint256 _amountEVMToSend, uint64 _amountCoreToReceive) = maximumEVMSendAmountToAmounts(amountEVM, decimalDiff); + + if (_amountEVMToSend != 0) { + transferToCore(erc20EVMAddress, erc20CoreIndex, _amountEVMToSend); + // Transfer the tokens from this contract on HyperCore to the `to` address on HyperCore + transferERC20CoreToCore(erc20CoreIndex, to, _amountCoreToReceive); + } + + return (_amountEVMToSend, _amountCoreToReceive); + } + + /** + * @notice Bridges `amountEVM` of `erc20` from this address on HyperEVM to this address on HyperCore. + * @dev Returns the amount credited on Core in Core units (post conversion). + * @dev The decimal difference is evmDecimals - coreDecimals + * @param erc20EVMAddress The address of the ERC20 token on HyperEVM + * @param erc20CoreIndex The HyperCore index id of the token to transfer + * @param amountEVM The amount to transfer on HyperEVM + * @param decimalDiff The decimal difference of evmDecimals - coreDecimals + * @return amountEVMSent The amount sent on HyperEVM + * @return amountCoreToReceive The amount credited on Core in Core units (post conversion) + */ + function transferERC20EVMToSelfOnCore( + address erc20EVMAddress, + uint64 erc20CoreIndex, + uint256 amountEVM, + int8 decimalDiff + ) internal returns (uint256 amountEVMSent, uint64 amountCoreToReceive) { + (uint256 _amountEVMToSend, uint64 _amountCoreToReceive) = maximumEVMSendAmountToAmounts(amountEVM, decimalDiff); + + if (_amountEVMToSend != 0) { + transferToCore(erc20EVMAddress, erc20CoreIndex, _amountEVMToSend); + } + + return (_amountEVMToSend, _amountCoreToReceive); + } + + /** + * @notice Transfers tokens from this contract on HyperCore to the `to` address on HyperCore + * @param erc20CoreIndex The HyperCore index id of the token + * @param to The address to receive tokens on HyperCore + * @param amountCore The amount to transfer on HyperCore + */ + function transferERC20CoreToCore(uint64 erc20CoreIndex, address to, uint64 amountCore) internal { + bytes memory action = abi.encode(to, erc20CoreIndex, amountCore); + bytes memory payload = abi.encodePacked(SPOT_SEND_HEADER, action); + + ICoreWriter(CORE_WRITER_PRECOMPILE_ADDRESS).sendRawAction(payload); + } + + /** + * @notice Transfers tokens from this contract on HyperEVM to this contract's address on HyperCore + * @param erc20EVMAddress The address of the ERC20 token on HyperEVM + * @param erc20CoreIndex The HyperCore index id of the token to transfer + * @param amountEVMToSend The amount to transfer on HyperEVM + */ + function transferToCore(address erc20EVMAddress, uint64 erc20CoreIndex, uint256 amountEVMToSend) internal { + // USDC requires a special transfer to core + if (erc20CoreIndex == USDC_CORE_INDEX) { + IERC20(erc20EVMAddress).forceApprove(USDC_CORE_DEPOSIT_WALLET_ADDRESS, amountEVMToSend); + ICoreDepositWallet(USDC_CORE_DEPOSIT_WALLET_ADDRESS).deposit(amountEVMToSend, CORE_SPOT_DEX_ID); + } else { + // For all other tokens, transfer to the asset bridge address on HyperCore + IERC20(erc20EVMAddress).safeTransfer(toAssetBridgeAddress(erc20CoreIndex), amountEVMToSend); + } + } + + /** + * @notice Submit a limit order on HyperCore. + * @dev Expects price & size already scaled by 1e8 per HyperCore spec. + * @param asset The asset index of the order + * @param isBuy Whether the order is a buy order + * @param limitPriceX1e8 The limit price of the order scaled by 1e8 + * @param sizeX1e8 The size of the order scaled by 1e8 + * @param reduceOnly If true, only reduce existing position rather than opening a new opposing order + * @param tif Time-in-Force: ALO, GTC, IOC (None invalid) + * @param cloid The client order id of the order, 0 means no cloid + */ + function submitLimitOrder( + uint32 asset, + bool isBuy, + uint64 limitPriceX1e8, + uint64 sizeX1e8, + bool reduceOnly, + Tif tif, + uint128 cloid + ) internal { + // Basic sanity checks + if (limitPriceX1e8 == 0) revert LimitPxIsZero(); + if (sizeX1e8 == 0) revert OrderSizeIsZero(); + if (tif == Tif.None || uint8(tif) > uint8(type(Tif).max)) revert InvalidTif(); + + // Encode the action + bytes memory encodedAction = abi.encode(asset, isBuy, limitPriceX1e8, sizeX1e8, reduceOnly, uint8(tif), cloid); + + // Prefix with the limit-order header + bytes memory data = abi.encodePacked(LIMIT_ORDER_HEADER, encodedAction); + + // Enqueue limit order to HyperCore via CoreWriter precompile + ICoreWriter(CORE_WRITER_PRECOMPILE_ADDRESS).sendRawAction(data); + } + + /** + * @notice Enqueue a cancel-order-by-CLOID for a given asset. + * @param asset The asset index of the order + * @param cloid The client order id of the order + */ + function cancelOrderByCloid(uint32 asset, uint128 cloid) internal { + // Encode the action + bytes memory encodedAction = abi.encode(asset, cloid); + + // Prefix with the cancel-by-cloid header + bytes memory data = abi.encodePacked(CANCEL_BY_CLOID_HEADER, encodedAction); + + // Enqueue cancel order by CLOID to HyperCore via CoreWriter precompile + ICoreWriter(CORE_WRITER_PRECOMPILE_ADDRESS).sendRawAction(data); + } + + /** + * @notice Get the balance of the specified ERC20 for `account` on HyperCore. + * @param account The address of the account to get the balance of + * @param token The token to get the balance of + * @return balance The balance of the specified ERC20 for `account` on HyperCore + */ + function spotBalance(address account, uint64 token) internal view returns (uint64 balance) { + (bool success, bytes memory result) = SPOT_BALANCE_PRECOMPILE_ADDRESS.staticcall(abi.encode(account, token)); + if (!success) revert SpotBalancePrecompileCallFailed(); + SpotBalance memory _spotBalance = abi.decode(result, (SpotBalance)); + return _spotBalance.total; + } + + /** + * @notice Checks if the user exists / has been activated on HyperCore. + * @param user The address of the user to check if they exist on HyperCore + * @return exists True if the user exists on HyperCore, false otherwise + */ + function coreUserExists(address user) internal view returns (bool) { + (bool success, bytes memory result) = CORE_USER_EXISTS_PRECOMPILE_ADDRESS.staticcall(abi.encode(user)); + if (!success) revert CoreUserExistsPrecompileCallFailed(); + CoreUserExists memory _coreUserExists = abi.decode(result, (CoreUserExists)); + return _coreUserExists.exists; + } + + /** + * @notice Get the spot price of the specified asset on HyperCore. + * @param index The asset index to get the spot price of + * @return spotPx The spot price of the specified asset on HyperCore scaled by 1e8 + */ + function spotPx(uint32 index) internal view returns (uint64) { + (bool success, bytes memory result) = SPOT_PX_PRECOMPILE_ADDRESS.staticcall(abi.encode(index)); + if (!success) revert SpotPxPrecompileCallFailed(); + return abi.decode(result, (uint64)); + } + + /** + * @notice Get the info of the specified token on HyperCore. + * @param erc20CoreIndex The token to get the info of + * @return tokenInfo The info of the specified token on HyperCore + */ + function tokenInfo(uint32 erc20CoreIndex) internal view returns (TokenInfo memory) { + (bool success, bytes memory result) = TOKEN_INFO_PRECOMPILE_ADDRESS.staticcall(abi.encode(erc20CoreIndex)); + if (!success) revert TokenInfoPrecompileCallFailed(); + TokenInfo memory _tokenInfo = abi.decode(result, (TokenInfo)); + return _tokenInfo; + } + + /** + * @notice Checks if an amount is safe to bridge from HyperEVM to HyperCore + * @dev Verifies that the asset bridge has sufficient balance to cover the amount plus a buffer + * @param erc20CoreIndex The HyperCore index id of the token + * @param coreAmount The amount that the bridging should result in on HyperCore + * @param coreBufferAmount The minimum buffer amount that should remain on HyperCore after bridging + * @return True if the bridge has enough balance to safely bridge the amount, false otherwise + */ + function isCoreAmountSafeToBridge( + uint64 erc20CoreIndex, + uint64 coreAmount, + uint64 coreBufferAmount + ) internal view returns (bool) { + address bridgeAddress = toAssetBridgeAddress(erc20CoreIndex); + uint64 currentBridgeBalance = spotBalance(bridgeAddress, erc20CoreIndex); + + // Return true if currentBridgeBalance >= coreAmount + coreBufferAmount + return currentBridgeBalance >= coreAmount + coreBufferAmount; + } + + /** + * @notice Converts a core index id to an asset bridge address + * @param erc20CoreIndex The core token index id to convert + * @return assetBridgeAddress The asset bridge address + */ + function toAssetBridgeAddress(uint64 erc20CoreIndex) internal pure returns (address) { + return address(uint160(BASE_ASSET_BRIDGE_ADDRESS_UINT256 + erc20CoreIndex)); + } + + /** + * @notice Converts an asset bridge address to a core index id + * @param assetBridgeAddress The asset bridge address to convert + * @return erc20CoreIndex The core token index id + */ + function toTokenId(address assetBridgeAddress) internal pure returns (uint64) { + return uint64(uint160(assetBridgeAddress) - BASE_ASSET_BRIDGE_ADDRESS_UINT256); + } + + /** + * @notice Returns an amount to send on HyperEVM to receive AT LEAST the minimumCoreReceiveAmount on HyperCore + * @param minimumCoreReceiveAmount The minimum amount desired to receive on HyperCore + * @param decimalDiff The decimal difference of evmDecimals - coreDecimals + * @return amountEVMToSend The amount to send on HyperEVM to receive at least minimumCoreReceiveAmount on HyperCore + * @return amountCoreToReceive The amount that will be received on core if the amountEVMToSend is sent from HyperEVM + */ + function minimumCoreReceiveAmountToAmounts( + uint64 minimumCoreReceiveAmount, + int8 decimalDiff + ) internal pure returns (uint256 amountEVMToSend, uint64 amountCoreToReceive) { + if (decimalDiff == 0) { + // Same decimals between HyperEVM and HyperCore + amountEVMToSend = uint256(minimumCoreReceiveAmount); + amountCoreToReceive = minimumCoreReceiveAmount; + } else if (decimalDiff > 0) { + // EVM token has more decimals than Core + // Scale up to represent the same value in higher-precision EVM units + amountEVMToSend = uint256(minimumCoreReceiveAmount) * (10 ** uint8(decimalDiff)); + amountCoreToReceive = minimumCoreReceiveAmount; + } else { + // Core token has more decimals than EVM + // Scale down, rounding UP to avoid shortfall on Core + uint256 scaleDivisor = 10 ** uint8(-decimalDiff); + amountEVMToSend = (uint256(minimumCoreReceiveAmount) + scaleDivisor - 1) / scaleDivisor; // ceil division + amountCoreToReceive = uint64(amountEVMToSend * scaleDivisor); + } + } + + /** + * @notice Converts a maximum EVM amount to send into an EVM amount to send to avoid loss to dust, + * @notice and the corresponding amount that will be recieved on Core. + * @param maximumEVMSendAmount The maximum amount to send on HyperEVM + * @param decimalDiff The decimal difference of evmDecimals - coreDecimals + * @return amountEVMToSend The amount to send on HyperEVM + * @return amountCoreToReceive The amount that will be received on HyperCore if the amountEVMToSend is sent + */ + function maximumEVMSendAmountToAmounts( + uint256 maximumEVMSendAmount, + int8 decimalDiff + ) internal pure returns (uint256 amountEVMToSend, uint64 amountCoreToReceive) { + /// @dev HyperLiquid decimal conversion: Scale EVM (u256,evmDecimals) -> Core (u64,coreDecimals) + /// @dev Core amount is guaranteed to be within u64 range. + if (decimalDiff == 0) { + amountEVMToSend = maximumEVMSendAmount; + amountCoreToReceive = uint64(amountEVMToSend); + } else if (decimalDiff > 0) { + // EVM token has more decimals than Core + uint256 scale = 10 ** uint8(decimalDiff); + amountEVMToSend = maximumEVMSendAmount - (maximumEVMSendAmount % scale); // Safe: dustAmount = maximumEVMSendAmount % scale, so dust <= maximumEVMSendAmount + + /// @dev Safe: Guaranteed to be in the range of [0, u64.max] because it is upperbounded by uint64 maxAmt + amountCoreToReceive = uint64(amountEVMToSend / scale); + } else { + // Core token has more decimals than EVM + uint256 scale = 10 ** uint8(-1 * decimalDiff); + amountEVMToSend = maximumEVMSendAmount; + + /// @dev Safe: Guaranteed to be in the range of [0, u64.max] because it is upperbounded by uint64 maxAmt + amountCoreToReceive = uint64(amountEVMToSend * scale); + } + } + + function convertCoreDecimalsSimple( + uint64 amountDecimalsFrom, + uint8 decimalsFrom, + uint8 decimalsTo + ) internal pure returns (uint64) { + if (decimalsFrom == decimalsTo) { + return amountDecimalsFrom; + } else if (decimalsFrom < decimalsTo) { + return uint64(amountDecimalsFrom * 10 ** (decimalsTo - decimalsFrom)); + } else { + // round down + return uint64(amountDecimalsFrom / 10 ** (decimalsFrom - decimalsTo)); + } + } +} diff --git a/contracts/libraries/SponsoredCCTPQuoteLib.sol b/contracts/libraries/SponsoredCCTPQuoteLib.sol new file mode 100644 index 000000000..199f93761 --- /dev/null +++ b/contracts/libraries/SponsoredCCTPQuoteLib.sol @@ -0,0 +1,197 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.0; + +import { SignatureChecker } from "@openzeppelin/contracts-v4/utils/cryptography/SignatureChecker.sol"; + +import { SponsoredCCTPInterface } from "../interfaces/SponsoredCCTPInterface.sol"; +import { BytesLib } from "../external/libraries/BytesLib.sol"; +import { Bytes32ToAddress } from "./AddressConverters.sol"; + +/** + * @title SponsoredCCTPQuoteLib + * @notice Library that contains the functions to get the data from the quotes and validate the signatures. + */ +library SponsoredCCTPQuoteLib { + using BytesLib for bytes; + using Bytes32ToAddress for bytes32; + + /// @dev Indices of each field in message that we get from CCTP + /// Source: https://github.com/circlefin/evm-cctp-contracts/blob/4061786a5726bc05f99fcdb53b0985599f0dbaf7/src/messages/v2/MessageV2.sol#L52-L61 + uint256 private constant VERSION_INDEX = 0; + uint256 private constant SOURCE_DOMAIN_INDEX = 4; + uint256 private constant DESTINATION_DOMAIN_INDEX = 8; + uint256 private constant NONCE_INDEX = 12; + uint256 private constant SENDER_INDEX = 44; + uint256 private constant RECIPIENT_INDEX = 76; + uint256 private constant DESTINATION_CALLER_INDEX = 108; + uint256 private constant MIN_FINALITY_THRESHOLD_INDEX = 140; + uint256 private constant FINALITY_THRESHOLD_EXECUTED_INDEX = 144; + uint256 private constant MESSAGE_BODY_INDEX = 148; + + /// @dev Indices of each field in message body that is extracted from message + /// Source: https://github.com/circlefin/evm-cctp-contracts/blob/4061786a5726bc05f99fcdb53b0985599f0dbaf7/src/messages/v2/BurnMessageV2.sol#L48-L52 + uint256 private constant BURN_TOKEN_INDEX = 4; + uint256 private constant MINT_RECIPIENT_INDEX = 36; + uint256 private constant AMOUNT_INDEX = 68; + uint256 private constant MAX_FEE_INDEX = 132; + uint256 private constant FEE_EXECUTED_INDEX = 164; + uint256 private constant HOOK_DATA_INDEX = 228; + + // Minimum length of the message body (can be longer due to variable actionData) + uint256 private constant MIN_MSG_BYTES_LENGTH = 664; + + /** + * @notice Gets the data for the deposit for burn. + * @param quote The quote that contains the data for the deposit. + * @return amount The amount of tokens to deposit for burn. + * @return destinationDomain The destination domain ID for the chain that the tokens are being deposited to. + * @return mintRecipient The recipent of the minted tokens. This would be the destination periphery contract. + * @return burnToken The address of the token to burn. + * @return destinationCaller The address that will call the CCTP receiveMessage function. This would be the destination periphery contract. + * @return maxFee The maximum fee that can be paid for the deposit. + * @return minFinalityThreshold The minimum finality threshold for the deposit. + * @return hookData The hook data for the deposit. Contrains additional data to be used by the destination periphery contract. + */ + function getDepositForBurnData( + SponsoredCCTPInterface.SponsoredCCTPQuote memory quote + ) + internal + pure + returns ( + uint256 amount, + uint32 destinationDomain, + bytes32 mintRecipient, + address burnToken, + bytes32 destinationCaller, + uint256 maxFee, + uint32 minFinalityThreshold, + bytes memory hookData + ) + { + amount = quote.amount; + destinationDomain = quote.destinationDomain; + mintRecipient = quote.mintRecipient; + burnToken = quote.burnToken.toAddress(); + destinationCaller = quote.destinationCaller; + maxFee = quote.maxFee; + minFinalityThreshold = quote.minFinalityThreshold; + hookData = abi.encode( + quote.nonce, + quote.deadline, + quote.maxBpsToSponsor, + quote.maxUserSlippageBps, + quote.finalRecipient, + quote.finalToken, + quote.executionMode, + quote.actionData + ); + } + + /** + * @notice Validates the message that is received from CCTP. If this checks fails, then the quote on source chain was invalid + * and we are unable to retrieve user's address to send the funds to. In that case the funds will stay in this contract. + * @param message The message that is received from CCTP. + * @return isValid True if the message is valid, false otherwise. + */ + function validateMessage(bytes memory message) internal view returns (bool) { + // Message must be at least the minimum length (can be longer due to variable actionData) + if (message.length < MIN_MSG_BYTES_LENGTH) { + return false; + } + + // Mint recipient should be this contract + if (message.toBytes32(MESSAGE_BODY_INDEX + MINT_RECIPIENT_INDEX).toAddress() != address(this)) { + return false; + } + + // Validate that finalRecipient and finalToken addresses are valid + bytes memory messageBody = message.slice(MESSAGE_BODY_INDEX, message.length); + bytes memory hookData = messageBody.slice(HOOK_DATA_INDEX, messageBody.length); + + // Decode to check address validity + (, , , , bytes32 finalRecipient, bytes32 finalToken, , ) = abi.decode( + hookData, + (bytes32, uint256, uint256, uint256, bytes32, bytes32, uint8, bytes) + ); + + return finalRecipient.isValidAddress() && finalToken.isValidAddress(); + } + + /** + * @notice Returns the quote and the fee that was executed from the CCTP message. + * @param message The message that is received from CCTP. + * @return quote The quote that contains the data of the deposit. + * @return feeExecuted The fee that was executed for the deposit. This is the fee that was paid to the CCTP message transmitter. + */ + function getSponsoredCCTPQuoteData( + bytes memory message + ) internal pure returns (SponsoredCCTPInterface.SponsoredCCTPQuote memory quote, uint256 feeExecuted) { + quote.sourceDomain = message.toUint32(SOURCE_DOMAIN_INDEX); + quote.destinationDomain = message.toUint32(DESTINATION_DOMAIN_INDEX); + quote.destinationCaller = message.toBytes32(DESTINATION_CALLER_INDEX); + quote.minFinalityThreshold = message.toUint32(MIN_FINALITY_THRESHOLD_INDEX); + + // first need to extract the message body from the message + bytes memory messageBody = message.slice(MESSAGE_BODY_INDEX, message.length); + quote.mintRecipient = messageBody.toBytes32(MINT_RECIPIENT_INDEX); + quote.amount = messageBody.toUint256(AMOUNT_INDEX); + quote.burnToken = messageBody.toBytes32(BURN_TOKEN_INDEX); + quote.maxFee = messageBody.toUint256(MAX_FEE_INDEX); + feeExecuted = messageBody.toUint256(FEE_EXECUTED_INDEX); + + bytes memory hookData = messageBody.slice(HOOK_DATA_INDEX, messageBody.length); + ( + quote.nonce, + quote.deadline, + quote.maxBpsToSponsor, + quote.maxUserSlippageBps, + quote.finalRecipient, + quote.finalToken, + quote.executionMode, + quote.actionData + ) = abi.decode(hookData, (bytes32, uint256, uint256, uint256, bytes32, bytes32, uint8, bytes)); + } + + /** + * @notice Validates the signature against the quote. + * @param signer The signer address that was used to sign the quote. + * @param quote The quote that contains the data of the deposit. + * @param signature The signature of the quote. + * @return isValid True if the signature is valid, false otherwise. + */ + function validateSignature( + address signer, + SponsoredCCTPInterface.SponsoredCCTPQuote memory quote, + bytes memory signature + ) internal view returns (bool) { + // Need to split the hash into two parts to avoid stack too deep error + bytes32 hash1 = keccak256( + abi.encode( + quote.sourceDomain, + quote.destinationDomain, + quote.mintRecipient, + quote.amount, + quote.burnToken, + quote.destinationCaller, + quote.maxFee, + quote.minFinalityThreshold + ) + ); + + bytes32 hash2 = keccak256( + abi.encode( + quote.nonce, + quote.deadline, + quote.maxBpsToSponsor, + quote.maxUserSlippageBps, + quote.finalRecipient, + quote.finalToken, + quote.executionMode, + keccak256(quote.actionData) // Hash the actionData to keep signature size reasonable + ) + ); + + bytes32 typedDataHash = keccak256(abi.encode(hash1, hash2)); + return SignatureChecker.isValidSignatureNow(signer, typedDataHash, signature); + } +} diff --git a/contracts/periphery/mintburn/ArbitraryEVMFlowExecutor.sol b/contracts/periphery/mintburn/ArbitraryEVMFlowExecutor.sol new file mode 100644 index 000000000..1ab40f3cd --- /dev/null +++ b/contracts/periphery/mintburn/ArbitraryEVMFlowExecutor.sol @@ -0,0 +1,180 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.0; + +import { IERC20 } from "@openzeppelin/contracts-v4/token/ERC20/IERC20.sol"; +import { SafeERC20 } from "@openzeppelin/contracts-v4/token/ERC20/utils/SafeERC20.sol"; + +// Import MulticallHandler +import { MulticallHandler } from "../../handlers/MulticallHandler.sol"; +import { EVMFlowParams, CommonFlowParams } from "./Structs.sol"; + +/** + * @title ArbitraryEVMFlowExecutor + * @notice Base contract for executing arbitrary action sequences using MulticallHandler + * @dev This contract provides shared functionality for both OFT and CCTP handlers to execute + * arbitrary actions on HyperEVM via MulticallHandler, returning information about the resulting token amount + * @custom:security-contact bugs@across.to + */ +abstract contract ArbitraryEVMFlowExecutor { + using SafeERC20 for IERC20; + + /// @notice Compressed call struct (no value field to save gas) + struct CompressedCall { + address target; + bytes callData; + } + + /// @notice MulticallHandler contract instance + address public immutable multicallHandler; + + /** + * @notice Emitted when arbitrary actions are executed successfully + * @param quoteNonce Unique identifier for this quote/transaction + * @param initialToken The token address received before executing actions + * @param initialAmount The amount of initial token received + * @param finalToken The token address after executing actions + * @param finalAmount The amount of final token after executing actions + */ + event ArbitraryActionsExecuted( + bytes32 indexed quoteNonce, + address indexed initialToken, + uint256 initialAmount, + address indexed finalToken, + uint256 finalAmount + ); + + uint256 private constant BPS_TOTAL_PRECISION = 18; + uint256 private constant BPS_PRECISION_SCALAR = 10 ** BPS_TOTAL_PRECISION; + + constructor(address _multicallHandler) { + multicallHandler = _multicallHandler; + } + + /** + * @notice Executes arbitrary actions by transferring tokens to MulticallHandler + * @dev Decompresses CompressedCall[] to MulticallHandler.Call[] format (adds value: 0) + * @param params Parameters of HyperEVM execution + * @return commonParams Parameters to continue sponsored execution to transfer funds to final recipient at correct destination + */ + function _executeFlow(EVMFlowParams memory params) internal returns (CommonFlowParams memory commonParams) { + // Decode the compressed action data + CompressedCall[] memory compressedCalls = abi.decode(params.actionData, (CompressedCall[])); + + // Snapshot balances + uint256 initialAmountSnapshot = IERC20(params.initialToken).balanceOf(address(this)); + uint256 finalAmountSnapshot = IERC20(params.commonParams.finalToken).balanceOf(address(this)); + + // Transfer tokens to MulticallHandler + IERC20(params.initialToken).safeTransfer(multicallHandler, params.commonParams.amountInEVM); + + // Build instructions for MulticallHandler + bytes memory instructions = _buildMulticallInstructions( + compressedCalls, + params.commonParams.finalToken, + address(this) // Send leftover tokens back to this contract + ); + + // Execute via MulticallHandler + MulticallHandler(payable(multicallHandler)).handleV3AcrossMessage( + params.initialToken, + params.commonParams.amountInEVM, + address(this), + instructions + ); + + uint256 finalAmount; + // This means the swap (if one was intended) didn't happen (action failed), so we use the initial token as the final token. + if (initialAmountSnapshot == IERC20(params.initialToken).balanceOf(address(this))) { + params.commonParams.finalToken = params.initialToken; + finalAmount = params.commonParams.amountInEVM; + } else { + uint256 finalBalance = IERC20(params.commonParams.finalToken).balanceOf(address(this)); + if (finalBalance >= finalAmountSnapshot) { + // This means the swap did happen, so we check the balance of the output token and send it. + finalAmount = finalBalance - finalAmountSnapshot; + } else { + // If we somehow lost final tokens, just set the finalAmount to 0. + finalAmount = 0; + } + } + + params.commonParams.extraFeesIncurred = _calcExtraFeesFinal( + params.commonParams.amountInEVM, + params.commonParams.extraFeesIncurred, + finalAmount + ); + params.commonParams.amountInEVM = finalAmount; + + emit ArbitraryActionsExecuted( + params.commonParams.quoteNonce, + params.initialToken, + params.commonParams.amountInEVM, + params.commonParams.finalToken, + finalAmount + ); + + return params.commonParams; + } + + /** + * @notice Builds MulticallHandler Instructions from compressed calls + * @dev Decompresses calls by adding value: 0, and adds drainLeftoverTokens call at the end + */ + function _buildMulticallInstructions( + CompressedCall[] memory compressedCalls, + address finalToken, + address fallbackRecipient + ) internal view returns (bytes memory) { + uint256 callCount = compressedCalls.length; + + // Create Call[] array with value: 0 for each call, plus one for drainLeftoverTokens + MulticallHandler.Call[] memory calls = new MulticallHandler.Call[](callCount + 1); + + // Decompress: add value: 0 to each call + for (uint256 i = 0; i < callCount; ++i) { + calls[i] = MulticallHandler.Call({ + target: compressedCalls[i].target, + callData: compressedCalls[i].callData, + value: 0 + }); + } + + // Add final call to drain leftover tokens back to this contract + calls[callCount] = MulticallHandler.Call({ + target: multicallHandler, + callData: abi.encodeCall(MulticallHandler.drainLeftoverTokens, (finalToken, payable(fallbackRecipient))), + value: 0 + }); + + // Build Instructions struct + MulticallHandler.Instructions memory instructions = MulticallHandler.Instructions({ + calls: calls, + fallbackRecipient: fallbackRecipient + }); + + return abi.encode(instructions); + } + + /// @notice Calculates proportional fees to sponsor in finalToken, given the fees to sponsor in initial token and initial amount + function _calcExtraFeesFinal( + uint256 amount, + uint256 extraFeesToSponsorTokenIn, + uint256 finalAmount + ) internal pure returns (uint256 extraFeesToSponsorFinalToken) { + // Total amount to sponsor is the extra fees to sponsor, ceiling division. + uint256 bpsToSponsor; + { + uint256 totalAmount = amount + extraFeesToSponsorTokenIn; + bpsToSponsor = ((extraFeesToSponsorTokenIn * BPS_PRECISION_SCALAR) + totalAmount - 1) / totalAmount; + } + + // Apply the bps to sponsor to the final amount to get the amount to sponsor, ceiling division. + uint256 bpsToSponsorAdjusted = BPS_PRECISION_SCALAR - bpsToSponsor; + extraFeesToSponsorFinalToken = + (((finalAmount * BPS_PRECISION_SCALAR) + bpsToSponsorAdjusted - 1) / bpsToSponsorAdjusted) - + finalAmount; + } + + /// @notice Allow contract to receive native tokens for arbitrary action execution + receive() external payable virtual {} +} diff --git a/contracts/periphery/mintburn/AuthorizedFundedFlow.sol b/contracts/periphery/mintburn/AuthorizedFundedFlow.sol new file mode 100644 index 000000000..1fdfb7a59 --- /dev/null +++ b/contracts/periphery/mintburn/AuthorizedFundedFlow.sol @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.23; + +/// @notice Library shared between handler contracts and modules to communicate from handler to module what context is a +/// specific function being called in +abstract contract AuthorizedFundedFlow { + // keccak256(abi.encode(uint256(keccak256("erc7201:AuthorizedFundedFlow.bool")) - 1)) & ~bytes32(uint256(0xff)); + bytes32 public constant AUTHORIZED_FUNDED_FLOW_SLOT = + 0xc56a3250645180a53cd9e196b2ee0a634a4f54e2edf59ea457f2083917e4d100; + + error FundedFlowNotAuthorized(); + + modifier authorizeFundedFlow() { + bytes32 slot = AUTHORIZED_FUNDED_FLOW_SLOT; + assembly { + sstore(slot, 1) + } + _; + assembly { + sstore(slot, 0) + } + } + + modifier onlyAuthorizedFlow() { + bytes32 slot = AUTHORIZED_FUNDED_FLOW_SLOT; + bool authorized; + assembly { + authorized := sload(slot) + } + if (!authorized) { + revert FundedFlowNotAuthorized(); + } + _; + } +} diff --git a/contracts/periphery/mintburn/BaseModuleHandler.sol b/contracts/periphery/mintburn/BaseModuleHandler.sol new file mode 100644 index 000000000..cba037ffd --- /dev/null +++ b/contracts/periphery/mintburn/BaseModuleHandler.sol @@ -0,0 +1,48 @@ +//SPDX-License-Identifier: Unlicense +pragma solidity ^0.8.0; + +import { AuthorizedFundedFlow } from "./AuthorizedFundedFlow.sol"; +import { HyperCoreFlowExecutor } from "./HyperCoreFlowExecutor.sol"; +import { HyperCoreFlowRoles } from "./HyperCoreFlowRoles.sol"; + +// Note: v5 is necessary since v4 does not use ERC-7201. +import { AccessControlUpgradeable } from "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol"; +import { ReentrancyGuard } from "@openzeppelin/contracts-v4/security/ReentrancyGuard.sol"; + +/** + * @notice Base contract for module handlers that use delegatecall to interact with HyperCoreFlowExecutor + * @dev Uses AccessControlUpgradeable to ensure storage compatibility with HyperCoreFlowExecutor when using delegatecall + */ +abstract contract BaseModuleHandler is + AccessControlUpgradeable, + ReentrancyGuard, + AuthorizedFundedFlow, + HyperCoreFlowRoles +{ + /// @notice Address of the underlying hypercore module + address public immutable hyperCoreModule; + + constructor(address _donationBox, address _baseToken, bytes32 _roleAdmin) { + hyperCoreModule = address(new HyperCoreFlowExecutor(_donationBox, _baseToken)); + + _setRoleAdmin(PERMISSIONED_BOT_ROLE, _roleAdmin); + _setRoleAdmin(FUNDS_SWEEPER_ROLE, _roleAdmin); + } + + /// @notice Fallback function to proxy all calls to the HyperCore module via delegatecall + /// @dev Permissioning is enforced by the delegated function's own modifiers (e.g. onlyPermissionedBot) + fallback(bytes calldata data) external nonReentrant returns (bytes memory) { + return _delegateToHyperCore(data); + } + + /// @notice Internal delegatecall helper + function _delegateToHyperCore(bytes memory data) internal returns (bytes memory) { + (bool success, bytes memory ret) = hyperCoreModule.delegatecall(data); + if (!success) { + assembly { + revert(add(ret, 32), mload(ret)) + } + } + return ret; + } +} diff --git a/contracts/periphery/mintburn/Constants.sol b/contracts/periphery/mintburn/Constants.sol new file mode 100644 index 000000000..2fd79521e --- /dev/null +++ b/contracts/periphery/mintburn/Constants.sol @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.0; + +// Basis points decimals (4 decimals = 10000 for 100%) +uint256 constant BPS_DECIMALS = 4; + +// Basis points scalar (10000 = 100%) +uint256 constant BPS_SCALAR = 10 ** BPS_DECIMALS; diff --git a/contracts/periphery/mintburn/HyperCoreFlowExecutor.sol b/contracts/periphery/mintburn/HyperCoreFlowExecutor.sol new file mode 100644 index 000000000..3e71df8ab --- /dev/null +++ b/contracts/periphery/mintburn/HyperCoreFlowExecutor.sol @@ -0,0 +1,1155 @@ +//SPDX-License-Identifier: Unlicense +pragma solidity ^0.8.0; + +import { AuthorizedFundedFlow } from "./AuthorizedFundedFlow.sol"; +import { HyperCoreFlowRoles } from "./HyperCoreFlowRoles.sol"; +import { DonationBox } from "../../chain-adapters/DonationBox.sol"; +import { HyperCoreLib } from "../../libraries/HyperCoreLib.sol"; +import { CoreTokenInfo } from "./Structs.sol"; +import { FinalTokenInfo } from "./Structs.sol"; +import { SwapHandler } from "./SwapHandler.sol"; +import { BPS_SCALAR, BPS_DECIMALS } from "./Constants.sol"; +import { CommonFlowParams } from "./Structs.sol"; + +// Note: v5 is necessary since v4 does not use ERC-7201. +import { AccessControlUpgradeable } from "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol"; +import { IERC20 } from "@openzeppelin/contracts-v4/token/ERC20/extensions/IERC20Metadata.sol"; +import { SafeERC20 } from "@openzeppelin/contracts-v4/token/ERC20/utils/SafeERC20.sol"; + +/** + * @title HyperCoreFlowExecutor + * @notice Contract handling HyperCore interactions for transfer-to-core or swap-with-core actions after stablecoin bridge transactions + * @dev This contract is designed to work with stablecoins. baseToken and every finalToken should all be stablecoins. + * + * @dev This contract is intended to be used exclusively via delegatecall from handler contracts. + * Direct calls to this contract will produce incorrect results because functions rely on the + * caller's context, including address(this) for calculations and storage layout from the + * delegating contract. + * + * @custom:security-contact bugs@across.to + */ +contract HyperCoreFlowExecutor is AccessControlUpgradeable, AuthorizedFundedFlow, HyperCoreFlowRoles { + using SafeERC20 for IERC20; + + // Common decimals scalars + uint256 public constant PPM_DECIMALS = 6; + uint256 public constant PPM_SCALAR = 10 ** PPM_DECIMALS; + uint64 public constant ONEX1e8 = 10 ** 8; + + /// @notice The donation box contract. + DonationBox public immutable donationBox; + + /// @notice All operations performed in this contract are relative to this baseToken + address public immutable baseToken; + + /// @notice A struct used for storing state of a swap flow that has been initialized, but not yet finished + struct SwapFlowState { + address finalRecipient; + address finalToken; + uint64 minAmountToSend; // for sponsored: one to one, non-sponsored: one to one minus slippage + uint64 maxAmountToSend; // for sponsored: one to one (from total bridged amt), for non-sponsored: one to one, less bridging fees incurred + bool isSponsored; + bool finalized; + } + + /// @custom:storage-location erc7201:HyperCoreFlowExecutor.main + struct MainStorage { + /// @notice A mapping of token addresses to their core token info. + mapping(address => CoreTokenInfo) coreTokenInfos; + /// @notice A mapping of token address to additional relevan info for final tokens, like Hyperliquid market params + mapping(address => FinalTokenInfo) finalTokenInfos; + /// @notice The block number of the last funds pull action per final token: either as a part of finalizing pending swaps, + /// or an admin funds pull + mapping(address finalToken => uint256 lastPullFundsBlock) lastPullFundsBlock; + /// @notice A mapping containing the pending state between initializing the swap flow and finalizing it + mapping(bytes32 quoteNonce => SwapFlowState swap) swaps; + /// @notice The cumulative amount of funds sponsored for each final token. + mapping(address => uint256) cumulativeSponsoredAmount; + /// @notice The cumulative amount of activation fees sponsored for each final token. + mapping(address => uint256) cumulativeSponsoredActivationFee; + } + + // keccak256(abi.encode(uint256(keccak256("erc7201:HyperCoreFlowExecutor.main")) - 1)) & ~bytes32(uint256(0xff)); + bytes32 private constant MAIN_STORAGE_LOCATION = 0x6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b00; + + function _getMainStorage() private pure returns (MainStorage storage $) { + assembly { + $.slot := MAIN_STORAGE_LOCATION + } + } + + /************************************** + * EVENTS * + **************************************/ + + /** + * @notice Emitted when the donation box is insufficient funds. + * @param quoteNonce Unique identifier for this quote/transaction + * @param token The token address that was requested + * @param amount The amount requested from the donation box + * @param balance The actual balance available in the donation box + */ + event DonationBoxInsufficientFunds(bytes32 indexed quoteNonce, address token, uint256 amount, uint256 balance); + + /** + * @notice Emitted whenever the account is not activated in the non-sponsored flow. We fall back to HyperEVM flow in that case + * @param quoteNonce Unique identifier for this quote/transaction + * @param user The address of the user whose account is not activated + */ + event AccountNotActivated(bytes32 indexed quoteNonce, address user); + + /** + * @notice Emitted when a simple transfer to core is executed. + * @param quoteNonce Unique identifier for this quote/transaction + * @param finalRecipient The address receiving the funds on HyperCore + * @param finalToken The token address being transferred + * @param evmAmountIn The amount received on HyperEVM (in finalToken) + * @param bridgingFeesIncurred The bridging fees incurred (in finalToken) + * @param evmAmountSponsored The amount sponsored from the donation box (in finalToken) + */ + event SimpleTransferFlowCompleted( + bytes32 indexed quoteNonce, + address indexed finalRecipient, + address indexed finalToken, + // All amounts are in finalToken + uint256 evmAmountIn, + uint256 bridgingFeesIncurred, + uint256 evmAmountSponsored + ); + + /** + * @notice Emitted upon successful completion of fallback HyperEVM flow + * @param quoteNonce Unique identifier for this quote/transaction + * @param finalRecipient The address receiving the funds on HyperEVM + * @param finalToken The token address being transferred + * @param evmAmountIn The amount received on HyperEVM (in finalToken) + * @param bridgingFeesIncurred The bridging fees incurred (in finalToken) + * @param evmAmountSponsored The amount sponsored from the donation box (in finalToken) + */ + event FallbackHyperEVMFlowCompleted( + bytes32 indexed quoteNonce, + address indexed finalRecipient, + address indexed finalToken, + // All amounts are in finalToken + uint256 evmAmountIn, + uint256 bridgingFeesIncurred, + uint256 evmAmountSponsored + ); + + /** + * @notice Emitted when a swap flow is initialized + * @param quoteNonce Unique identifier for this quote/transaction + * @param finalRecipient The address that will receive the swapped funds on HyperCore + * @param finalToken The token address to swap to + * @param evmAmountIn The amount received on HyperEVM (in baseToken) + * @param bridgingFeesIncurred The bridging fees incurred (in baseToken) + * @param coreAmountIn The amount sent to HyperCore (in finalToken) + * @param minAmountToSend Minimum amount to send to user after swap (in finalToken) + * @param maxAmountToSend Maximum amount to send to user after swap (in finalToken) + */ + event SwapFlowInitialized( + bytes32 indexed quoteNonce, + address indexed finalRecipient, + address indexed finalToken, + // In baseToken + uint256 evmAmountIn, + uint256 bridgingFeesIncurred, + // In finalToken + uint256 coreAmountIn, + uint64 minAmountToSend, + uint64 maxAmountToSend + ); + + /** + * @notice Emitted when a swap flow is finalized + * @param quoteNonce Unique identifier for this quote/transaction + * @param finalRecipient The address that received the swapped funds on HyperCore + * @param finalToken The token address that was swapped to + * @param totalSent Total amount sent to the final recipient on HyperCore (in finalToken) + * @param evmAmountSponsored The amount sponsored from the donation box (in EVM finalToken) + */ + event SwapFlowFinalized( + bytes32 indexed quoteNonce, + address indexed finalRecipient, + address indexed finalToken, + // In finalToken + uint64 totalSent, + // In EVM finalToken + uint256 evmAmountSponsored + ); + + /** + * @notice Emitted upon cancelling a Limit order + * @param token The token address for which the limit order was placed + * @param cloid Client order ID of the cancelled limit order + */ + event CancelledLimitOrder(address indexed token, uint128 indexed cloid); + + /** + * @notice Emitted upon submitting a Limit order + * @param token The token address for which the limit order is placed + * @param priceX1e8 The limit order price (scaled by 1e8) + * @param sizeX1e8 The limit order size (scaled by 1e8) + * @param cloid Client order ID of the submitted limit order + */ + event SubmittedLimitOrder(address indexed token, uint64 priceX1e8, uint64 sizeX1e8, uint128 indexed cloid); + + /** + * @notice Emitted when we have to fall back from the swap flow because it's too expensive (either to sponsor or the slippage is too big) + * @param quoteNonce Unique identifier for this quote/transaction + * @param finalToken The token address that was intended to be swapped to + * @param estBpsSlippage Estimated slippage in basis points + * @param maxAllowableBpsSlippage Maximum allowable slippage in basis points + */ + event SwapFlowTooExpensive( + bytes32 indexed quoteNonce, + address indexed finalToken, + uint256 estBpsSlippage, + uint256 maxAllowableBpsSlippage + ); + + /** + * @notice Emitted when we can't bridge some token from HyperEVM to HyperCore + * @param quoteNonce Unique identifier for this quote/transaction + * @param token The token address that is unsafe to bridge + * @param amount The amount that was attempted to be bridged + */ + event UnsafeToBridge(bytes32 indexed quoteNonce, address indexed token, uint64 amount); + + /** + * @notice Emitted whenever donationBox funds are used for activating a user account + * @param quoteNonce Unique identifier for this quote/transaction + * @param finalRecipient The address of the user whose account is being activated + * @param fundingToken The token used to fund the account activation + * @param evmAmountSponsored The amount sponsored for activation (in EVM token) + */ + event SponsoredAccountActivation( + bytes32 indexed quoteNonce, + address indexed finalRecipient, + address indexed fundingToken, + uint256 evmAmountSponsored + ); + + /** + * @notice Emitted whenever a new CoreTokenInfo is configured + * @param token The token address being configured + * @param coreIndex The index of the token on HyperCore + * @param canBeUsedForAccountActivation Whether this token can be used to pay for account activation + * @param accountActivationFeeCore The account activation fee amount (in Core token units) + * @param bridgeSafetyBufferCore The safety buffer for bridging (in Core token units) + */ + event SetCoreTokenInfo( + address indexed token, + uint32 coreIndex, + bool canBeUsedForAccountActivation, + uint64 accountActivationFeeCore, + uint64 bridgeSafetyBufferCore + ); + + /// @notice Emitted whenever a new FinalTokenInfo is configured + event SetFinalTokenInfo( + address indexed token, + uint32 spotIndex, + bool isBuy, + uint32 feePpm, + address indexed swapHandler, + uint32 suggestedFeeDiscountBps + ); + + /** + * @notice Emitted when we do an ad-hoc send of sponsorship funds to one of the Swap Handlers + * @param token The token address being sent to the swap handler + * @param evmAmountSponsored The amount sponsored from the donation box (in EVM token) + */ + event SentSponsorshipFundsToSwapHandler(address indexed token, uint256 evmAmountSponsored); + + /************************************** + * ERRORS * + **************************************/ + + /// @notice Thrown when an attempt to finalize a non-existing swap is made + error SwapDoesNotExist(); + + /// @notice Thrown when an attemp to finalize an already finalized swap is made + error SwapAlreadyFinalized(); + + /// @notice Thrown when trying to finalize a quoteNonce, calling a finalizeSwapFlows with an incorrect token + error WrongSwapFinalizationToken(bytes32 quoteNonce); + + /// @notice Emitted when we're inside the sponsored flow and a user doesn't have a HyperCore account activated. The + /// bot should activate user's account first by calling `activateUserAccount` + error AccountNotActivatedError(address user); + + /// @notice Thrown when we can't bridge some token from HyperEVM to HyperCore + error UnsafeToBridgeError(address token, uint64 amount); + + /************************************** + * MODIFIERS * + **************************************/ + + modifier onlyExistingCoreToken(address evmTokenAddress) { + _getExistingCoreTokenInfo(evmTokenAddress); + _; + } + + /// @notice Reverts if the token is not configured + function _getExistingCoreTokenInfo( + address evmTokenAddress + ) internal view returns (CoreTokenInfo memory coreTokenInfo) { + coreTokenInfo = _getMainStorage().coreTokenInfos[evmTokenAddress]; + require( + coreTokenInfo.tokenInfo.evmContract != address(0) && coreTokenInfo.tokenInfo.weiDecimals != 0, + "CoreTokenInfo not set" + ); + } + + /// @notice Reverts if the token is not configured + function _getExistingFinalTokenInfo( + address evmTokenAddress + ) internal view returns (FinalTokenInfo memory finalTokenInfo) { + finalTokenInfo = _getMainStorage().finalTokenInfos[evmTokenAddress]; + require(address(finalTokenInfo.swapHandler) != address(0), "FinalTokenInfo not set"); + } + + /** + * + * @param _donationBox Sponsorship funds live here + * @param _baseToken Main token used with this Forwarder + */ + constructor(address _donationBox, address _baseToken) { + // Set immutable variables only + donationBox = DonationBox(_donationBox); + baseToken = _baseToken; + } + + /**************************************** + * VIEW FUNCTIONS * + **************************************/ + + /** + * @notice Returns the core token info for a given token address. + * @param token The token address. + * @return The core token info for the given token address. + */ + function coreTokenInfos(address token) external view returns (CoreTokenInfo memory) { + return _getMainStorage().coreTokenInfos[token]; + } + + /** + * @notice Returns the final token info for a given token address. + * @param token The token address. + * @return The final token info for the given token address. + */ + function finalTokenInfos(address token) external view returns (FinalTokenInfo memory) { + return _getMainStorage().finalTokenInfos[token]; + } + + /** + * @notice Returns the block number of the last time funds were pulled from the donation box. + * @param token The token address. + * @return The block number of the last time funds were pulled from the donation box for the given token address. + */ + function lastPullFundsBlock(address token) external view returns (uint256) { + return _getMainStorage().lastPullFundsBlock[token]; + } + + /** + * @notice Returns the swap info for a given quote nonce. + * @param quoteNonce The quote nonce. + * @return The swap info for the given quote nonce. + */ + function swaps(bytes32 quoteNonce) external view returns (SwapFlowState memory) { + return _getMainStorage().swaps[quoteNonce]; + } + + /** + * @notice Returns the cumulative sponsored amount for a given token address. + * @param token The token address. + * @return The cumulative sponsored amount for the given token address. + */ + function cumulativeSponsoredAmount(address token) external view returns (uint256) { + return _getMainStorage().cumulativeSponsoredAmount[token]; + } + + /** + * @notice Returns the cumulative sponsored activation fee for a given token address. + * @param token The token address. + * @return The cumulative sponsored activation fee for the given token address. + */ + function cumulativeSponsoredActivationFee(address token) external view returns (uint256) { + return _getMainStorage().cumulativeSponsoredActivationFee[token]; + } + + /************************************** + * CONFIGURATION FUNCTIONS * + **************************************/ + + /** + * @notice Set or update information for the token to use it in this contract + * @dev To be able to use the token in the swap flow, FinalTokenInfo has to be set as well + * @dev Setting core token info to incorrect values can lead to loss of funds. Should NEVER be unset while the + * finalTokenParams are not unset + * @param token The token address being configured + * @param coreIndex The index of the token on HyperCore + * @param canBeUsedForAccountActivation Whether this token can be used to pay for account activation + * @param accountActivationFeeCore The account activation fee amount (in Core token units) + * @param bridgeSafetyBufferCore The safety buffer for bridging (in Core token units) + */ + function setCoreTokenInfo( + address token, + uint32 coreIndex, + bool canBeUsedForAccountActivation, + uint64 accountActivationFeeCore, + uint64 bridgeSafetyBufferCore + ) external onlyRole(DEFAULT_ADMIN_ROLE) { + _setCoreTokenInfo( + token, + coreIndex, + canBeUsedForAccountActivation, + accountActivationFeeCore, + bridgeSafetyBufferCore + ); + } + + /** + * @notice Sets the parameters for a final token. + * @dev This function deploys a new SwapHandler contract if one is not already set. If the final token + * can't be used for account activation, the handler will be left unactivated and would need to be activated by the caller. + * @param finalToken The address of the final token. + * @param spotIndex The index of the asset in the Hyperliquid market. + * @param isBuy Whether the final token is a buy or a sell. + * @param feePpm The fee in parts per million. + * @param suggestedDiscountBps The suggested slippage in basis points. + */ + function setFinalTokenInfo( + address finalToken, + uint32 spotIndex, + bool isBuy, + uint32 feePpm, + uint32 suggestedDiscountBps + ) external onlyExistingCoreToken(finalToken) onlyRole(DEFAULT_ADMIN_ROLE) { + MainStorage storage $ = _getMainStorage(); + SwapHandler swapHandler = $.finalTokenInfos[finalToken].swapHandler; + if (address(swapHandler) == address(0)) { + bytes32 salt = _swapHandlerSalt(finalToken); + swapHandler = new SwapHandler{ salt: salt }(); + } + + $.finalTokenInfos[finalToken] = FinalTokenInfo({ + spotIndex: spotIndex, + isBuy: isBuy, + feePpm: feePpm, + swapHandler: swapHandler, + suggestedDiscountBps: suggestedDiscountBps + }); + + // We don't allow SwapHandler accounts to be uninitiated. That could lead to loss of funds. They instead should + // be pre-funded using `predictSwapHandler` to predict their address + require(HyperCoreLib.coreUserExists(address(swapHandler)), "SwapHandler @ core doesn't exist"); + + emit SetFinalTokenInfo(finalToken, spotIndex, isBuy, feePpm, address(swapHandler), suggestedDiscountBps); + } + + /** + * @notice Predicts the deterministic address of a SwapHandler for a given finalToken using CREATE2 + * @param finalToken The token address for which to predict the SwapHandler address + * @return The predicted address of the SwapHandler contract + */ + function predictSwapHandler(address finalToken) public view returns (address) { + bytes32 salt = _swapHandlerSalt(finalToken); + bytes32 initCodeHash = keccak256(type(SwapHandler).creationCode); + return address(uint160(uint256(keccak256(abi.encodePacked(bytes1(0xff), address(this), salt, initCodeHash))))); + } + + /// @notice Returns the salt to use when creating a SwapHandler via CREATE2 + function _swapHandlerSalt(address finalToken) internal view returns (bytes32) { + return keccak256(abi.encodePacked(address(this), finalToken)); + } + + /************************************** + * FLOW FUNCTIONS * + **************************************/ + + /** + * @notice External entrypoint to execute flow when called via delegatecall from a handler. Works with params + * checked by a handler. Params authorization by a handler is enforced via `onlyAuthorizedFlow` modifier + */ + function executeFlow(CommonFlowParams memory params, uint256 maxUserSlippageBps) external onlyAuthorizedFlow { + if (params.finalToken == baseToken) { + _executeSimpleTransferFlow(params); + } else { + _initiateSwapFlow(params, maxUserSlippageBps); + } + } + + /// @notice External entrypoint to execute simple transfer flow (see `executeFlow` comment for details) + function executeSimpleTransferFlow(CommonFlowParams memory params) external onlyAuthorizedFlow { + _executeSimpleTransferFlow(params); + } + + /// @notice External entrypoint to execute fallback evm flow (see `executeFlow` comment for details) + function fallbackHyperEVMFlow(CommonFlowParams memory params) external onlyAuthorizedFlow { + _fallbackHyperEVMFlow(params); + } + + /// @notice Execute a simple transfer flow in which we transfer `finalToken` to the user on HyperCore after receiving + /// an amount of finalToken from the user on HyperEVM + function _executeSimpleTransferFlow(CommonFlowParams memory params) internal { + address finalToken = params.finalToken; + MainStorage storage $ = _getMainStorage(); + CoreTokenInfo memory coreTokenInfo = $.coreTokenInfos[finalToken]; + + // Check account activation + if (!HyperCoreLib.coreUserExists(params.finalRecipient)) { + if (params.maxBpsToSponsor > 0) { + revert AccountNotActivatedError(params.finalRecipient); + } else { + emit AccountNotActivated(params.quoteNonce, params.finalRecipient); + _fallbackHyperEVMFlow(params); + return; + } + } + + // Calculate sponsorship amount in scope + uint256 amountToSponsor; + { + uint256 maxEvmAmountToSponsor = ((params.amountInEVM + params.extraFeesIncurred) * params.maxBpsToSponsor) / + BPS_SCALAR; + amountToSponsor = params.extraFeesIncurred; + if (amountToSponsor > maxEvmAmountToSponsor) { + amountToSponsor = maxEvmAmountToSponsor; + } + + if (amountToSponsor > 0) { + if (!_availableInDonationBox(params.quoteNonce, finalToken, amountToSponsor)) { + // If the full amount is not available in the donation box, use the balance of the token in the donation box + amountToSponsor = IERC20(finalToken).balanceOf(address(donationBox)); + } + } + } + + // Calculate quoted amounts and check safety + uint256 quotedEvmAmount; + uint64 quotedCoreAmount; + { + uint256 finalAmount = params.amountInEVM + amountToSponsor; + (quotedEvmAmount, quotedCoreAmount) = HyperCoreLib.maximumEVMSendAmountToAmounts( + finalAmount, + coreTokenInfo.tokenInfo.evmExtraWeiDecimals + ); + // If there are no funds left on the destination side of the bridge, the funds will be lost in the + // bridge. We check send safety via `isCoreAmountSafeToBridge` + if ( + !HyperCoreLib.isCoreAmountSafeToBridge( + coreTokenInfo.coreIndex, + quotedCoreAmount, + coreTokenInfo.bridgeSafetyBufferCore + ) + ) { + // If the amount is not safe to bridge because the bridge doesn't have enough liquidity, + // fall back to sending user funds on HyperEVM. + _fallbackHyperEVMFlow(params); + emit UnsafeToBridge(params.quoteNonce, finalToken, quotedCoreAmount); + return; + } + } + + if (amountToSponsor > 0) { + // This will succeed because we checked the balance earlier + donationBox.withdraw(IERC20(finalToken), amountToSponsor); + } + + $.cumulativeSponsoredAmount[finalToken] += amountToSponsor; + + // There is a very slim change that someone is sending > buffer amount in the same EVM block and the balance of + // the bridge is not enough to cover our transfer, so the funds are lost. + HyperCoreLib.transferERC20EVMToCore( + finalToken, + coreTokenInfo.coreIndex, + params.finalRecipient, + quotedEvmAmount, + coreTokenInfo.tokenInfo.evmExtraWeiDecimals + ); + + emit SimpleTransferFlowCompleted( + params.quoteNonce, + params.finalRecipient, + finalToken, + params.amountInEVM, + params.extraFeesIncurred, + amountToSponsor + ); + } + + /** + * @notice Initiates the swap flow. Sends the funds received on EVM side over to a SwapHandler corresponding to a + * finalToken. This is the first leg of the swap flow. Next, the bot should submit a limit order through a `submitLimitOrderFromBot` + * function, and then settle the flow via a `finalizeSwapFlows` function + * @dev Only works for stable -> stable swap flows (or equivalent token flows. Price between tokens is supposed to be approximately one to one) + * @param maxUserSlippageBps Describes a configured user setting. Slippage here is wrt the one to one exchange + */ + function _initiateSwapFlow(CommonFlowParams memory params, uint256 maxUserSlippageBps) internal { + address initialToken = baseToken; + + // Check account activation + if (!HyperCoreLib.coreUserExists(params.finalRecipient)) { + if (params.maxBpsToSponsor > 0) { + revert AccountNotActivatedError(params.finalRecipient); + } else { + emit AccountNotActivated(params.quoteNonce, params.finalRecipient); + params.finalToken = initialToken; + _fallbackHyperEVMFlow(params); + return; + } + } + + MainStorage storage $ = _getMainStorage(); + CoreTokenInfo memory initialCoreTokenInfo = $.coreTokenInfos[initialToken]; + CoreTokenInfo memory finalCoreTokenInfo = $.coreTokenInfos[params.finalToken]; + FinalTokenInfo memory finalTokenInfo = _getExistingFinalTokenInfo(params.finalToken); + + // Calculate limit order amounts and check if feasible + uint64 minAllowableAmountToForwardCore; + uint64 maxAllowableAmountToForwardCore; + // Estimated slippage in ppm, as compared to a one-to-one totalBridgedAmount -> finalAmount conversion + uint256 estSlippagePpm; + { + // In finalToken + (minAllowableAmountToForwardCore, maxAllowableAmountToForwardCore) = _calcAllowableAmtsSwapFlow( + params.amountInEVM, + params.extraFeesIncurred, + initialCoreTokenInfo, + finalCoreTokenInfo, + params.maxBpsToSponsor > 0, + maxUserSlippageBps + ); + + uint64 approxExecutionPriceX1e8 = _getApproxRealizedPrice( + finalTokenInfo, + finalCoreTokenInfo, + initialCoreTokenInfo + ); + uint256 maxAllowableBpsDeviation = params.maxBpsToSponsor > 0 ? params.maxBpsToSponsor : maxUserSlippageBps; + if (finalTokenInfo.isBuy) { + if (approxExecutionPriceX1e8 < ONEX1e8) { + estSlippagePpm = 0; + } else { + // ceil + estSlippagePpm = ((approxExecutionPriceX1e8 - ONEX1e8) * PPM_SCALAR + (ONEX1e8 - 1)) / ONEX1e8; + } + } else { + if (approxExecutionPriceX1e8 > ONEX1e8) { + estSlippagePpm = 0; + } else { + // ceil + estSlippagePpm = ((ONEX1e8 - approxExecutionPriceX1e8) * PPM_SCALAR + (ONEX1e8 - 1)) / ONEX1e8; + } + } + // Add `extraFeesIncurred` to "slippage from one to one" + estSlippagePpm += + (params.extraFeesIncurred * PPM_SCALAR + (params.amountInEVM + params.extraFeesIncurred) - 1) / + (params.amountInEVM + params.extraFeesIncurred); + + if (estSlippagePpm > maxAllowableBpsDeviation * 10 ** (PPM_DECIMALS - BPS_DECIMALS)) { + emit SwapFlowTooExpensive( + params.quoteNonce, + params.finalToken, + (estSlippagePpm + 10 ** (PPM_DECIMALS - BPS_DECIMALS) - 1) / 10 ** (PPM_DECIMALS - BPS_DECIMALS), + maxAllowableBpsDeviation + ); + params.finalToken = initialToken; + _executeSimpleTransferFlow(params); + return; + } + } + + (uint256 tokensToSendEvm, uint64 coreAmountIn) = HyperCoreLib.maximumEVMSendAmountToAmounts( + params.amountInEVM, + initialCoreTokenInfo.tokenInfo.evmExtraWeiDecimals + ); + + // Check that we can safely bridge to HCore (for the trade amount actually needed) + bool isSafeToBridgeMainToken = HyperCoreLib.isCoreAmountSafeToBridge( + initialCoreTokenInfo.coreIndex, + coreAmountIn, + initialCoreTokenInfo.bridgeSafetyBufferCore + ); + + if (!isSafeToBridgeMainToken) { + emit UnsafeToBridge(params.quoteNonce, initialToken, coreAmountIn); + params.finalToken = initialToken; + _fallbackHyperEVMFlow(params); + return; + } + + // Finalize swap flow setup by updating state and funding SwapHandler + // State changes + $.swaps[params.quoteNonce] = SwapFlowState({ + finalRecipient: params.finalRecipient, + finalToken: params.finalToken, + minAmountToSend: minAllowableAmountToForwardCore, + maxAmountToSend: maxAllowableAmountToForwardCore, + isSponsored: params.maxBpsToSponsor > 0, + finalized: false + }); + + emit SwapFlowInitialized( + params.quoteNonce, + params.finalRecipient, + params.finalToken, + params.amountInEVM, + params.extraFeesIncurred, + coreAmountIn, + minAllowableAmountToForwardCore, + maxAllowableAmountToForwardCore + ); + + // Send amount received from user to a corresponding SwapHandler + SwapHandler swapHandler = finalTokenInfo.swapHandler; + IERC20(initialToken).safeTransfer(address(swapHandler), tokensToSendEvm); + swapHandler.transferFundsToSelfOnCore( + initialToken, + initialCoreTokenInfo.coreIndex, + tokensToSendEvm, + initialCoreTokenInfo.tokenInfo.evmExtraWeiDecimals + ); + } + + /** + * @notice Finalizes multiple swap flows associated with a final token, subject to the L1 Hyperliquid balance + * @dev Caller is responsible for providing correct limitOrderOutput amounts per assosicated swap flow. The caller + * has to estimate how much final tokens it received on core based on the input of the corresponding quote nonce + * swap flow + * @param finalToken The token address for the swaps being finalized + * @param quoteNonces Array of quote nonces identifying the swap flows to finalize + * @param limitOrderOuts Array of limit order output amounts corresponding to each quote nonce + * @return finalized The number of swap flows that were successfully finalized + */ + function finalizeSwapFlows( + address finalToken, + bytes32[] calldata quoteNonces, + uint64[] calldata limitOrderOuts + ) external onlyRole(PERMISSIONED_BOT_ROLE) returns (uint256 finalized) { + MainStorage storage $ = _getMainStorage(); + require(quoteNonces.length == limitOrderOuts.length, "length"); + require($.lastPullFundsBlock[finalToken] < block.number, "too soon"); + + CoreTokenInfo memory finalCoreTokenInfo = _getExistingCoreTokenInfo(finalToken); + FinalTokenInfo memory finalTokenInfo = _getExistingFinalTokenInfo(finalToken); + + uint64 availableBalance = HyperCoreLib.spotBalance( + address(finalTokenInfo.swapHandler), + finalCoreTokenInfo.coreIndex + ); + + uint64[] memory totalToSendAmounts = new uint64[](quoteNonces.length); + uint64[] memory additionalToSendAmounts = new uint64[](quoteNonces.length); + uint64 totalAdditionalToSend = 0; + + for (; finalized < quoteNonces.length; ++finalized) { + ( + bool success, + uint64 totalToSend, + uint64 additionalToSend, + uint64 remainingBalance + ) = _prepareSwapFinalization( + quoteNonces[finalized], + limitOrderOuts[finalized], + finalToken, + availableBalance + ); + if (!success) break; + + totalToSendAmounts[finalized] = totalToSend; + additionalToSendAmounts[finalized] = additionalToSend; + totalAdditionalToSend += additionalToSend; + availableBalance = remainingBalance; + } + + if (finalized == 0) return 0; + + $.lastPullFundsBlock[finalToken] = block.number; + + if (totalAdditionalToSend > 0) { + (uint256 totalAdditionalToSendEVM, uint64 totalAdditionalReceivedCore) = HyperCoreLib + .minimumCoreReceiveAmountToAmounts( + totalAdditionalToSend, + finalCoreTokenInfo.tokenInfo.evmExtraWeiDecimals + ); + + if ( + !HyperCoreLib.isCoreAmountSafeToBridge( + finalCoreTokenInfo.coreIndex, + totalAdditionalReceivedCore, + finalCoreTokenInfo.bridgeSafetyBufferCore + ) + ) { + // We expect this situation to be so rare and / or intermittend that we're willing to rely on admin to sweep the funds if this leads to + // swaps being impossible to finalize + revert UnsafeToBridgeError(finalToken, totalAdditionalToSend); + } + + $.cumulativeSponsoredAmount[finalToken] += totalAdditionalToSendEVM; + + // ! Notice: as per HyperEVM <> HyperCore rules, this amount will land on HyperCore *before* all of the core > core sends get executed + // Get additional amount to send from donation box, and send it to self on core + donationBox.withdraw(IERC20(finalToken), totalAdditionalToSendEVM); + IERC20(finalToken).safeTransfer(address(finalTokenInfo.swapHandler), totalAdditionalToSendEVM); + finalTokenInfo.swapHandler.transferFundsToSelfOnCore( + finalToken, + finalCoreTokenInfo.coreIndex, + totalAdditionalToSendEVM, + finalCoreTokenInfo.tokenInfo.evmExtraWeiDecimals + ); + } + + for (uint256 i = 0; i < finalized; ++i) { + SwapFlowState storage swap = $.swaps[quoteNonces[i]]; + (uint256 additionalToSendEVM, ) = HyperCoreLib.minimumCoreReceiveAmountToAmounts( + additionalToSendAmounts[i], + finalCoreTokenInfo.tokenInfo.evmExtraWeiDecimals + ); + + finalTokenInfo.swapHandler.transferFundsToUserOnCore( + finalCoreTokenInfo.coreIndex, + swap.finalRecipient, + totalToSendAmounts[i] + ); + emit SwapFlowFinalized( + quoteNonces[i], + swap.finalRecipient, + finalToken, + totalToSendAmounts[i], + additionalToSendEVM + ); + } + } + + /// @notice Finalizes a single swap flow state, calculating amounts but not sending tokens yet + function _prepareSwapFinalization( + bytes32 quoteNonce, + uint64 limitOrderOut, + address expectedToken, + uint64 availableBalance + ) internal returns (bool success, uint64 totalToSend, uint64 additionalToSend, uint64 balanceRemaining) { + SwapFlowState storage swap = _getMainStorage().swaps[quoteNonce]; + if (swap.finalRecipient == address(0)) revert SwapDoesNotExist(); + if (swap.finalized) revert SwapAlreadyFinalized(); + if (swap.finalToken != expectedToken) revert WrongSwapFinalizationToken(quoteNonce); + + (totalToSend, additionalToSend) = _calcSwapFlowSendAmounts( + limitOrderOut, + swap.minAmountToSend, + swap.maxAmountToSend, + swap.isSponsored + ); + + // `additionalToSend` will land on HCore before this core > core send will need to be executed + balanceRemaining = availableBalance + additionalToSend; + if (totalToSend > balanceRemaining) { + return (false, 0, 0, availableBalance); + } + + swap.finalized = true; + success = true; + balanceRemaining -= totalToSend; + } + + /// @notice Forwards `amount` plus potential sponsorship funds (for bridging fee) to user on HyperEVM + function _fallbackHyperEVMFlow(CommonFlowParams memory params) internal { + uint256 maxEvmAmountToSponsor = ((params.amountInEVM + params.extraFeesIncurred) * params.maxBpsToSponsor) / + BPS_SCALAR; + uint256 sponsorshipFundsToForward = params.extraFeesIncurred > maxEvmAmountToSponsor + ? maxEvmAmountToSponsor + : params.extraFeesIncurred; + + if (!_availableInDonationBox(params.quoteNonce, params.finalToken, sponsorshipFundsToForward)) { + sponsorshipFundsToForward = 0; + } + if (sponsorshipFundsToForward > 0) { + donationBox.withdraw(IERC20(params.finalToken), sponsorshipFundsToForward); + } + uint256 totalAmountToForward = params.amountInEVM + sponsorshipFundsToForward; + IERC20(params.finalToken).safeTransfer(params.finalRecipient, totalAmountToForward); + _getMainStorage().cumulativeSponsoredAmount[params.finalToken] += sponsorshipFundsToForward; + emit FallbackHyperEVMFlowCompleted( + params.quoteNonce, + params.finalRecipient, + params.finalToken, + params.amountInEVM, + params.extraFeesIncurred, + sponsorshipFundsToForward + ); + } + + /** + * @notice Activates a user account on Core by funding the account activation fee. + * @param quoteNonce The nonce of the quote that is used to identify the user. + * @param finalRecipient The address of the recipient of the funds. + * @param fundingToken The address of the token that is used to fund the account activation fee. + */ + function activateUserAccount( + bytes32 quoteNonce, + address finalRecipient, + address fundingToken + ) external onlyRole(PERMISSIONED_BOT_ROLE) { + CoreTokenInfo memory coreTokenInfo = _getExistingCoreTokenInfo(fundingToken); + bool coreUserExists = HyperCoreLib.coreUserExists(finalRecipient); + require(!coreUserExists, "Can't fund account activation for existing user"); + require(coreTokenInfo.canBeUsedForAccountActivation, "Token can't be used for this"); + + // +1 wei for a spot send + uint64 totalBalanceRequiredToActivate = coreTokenInfo.accountActivationFeeCore + 1; + (uint256 evmAmountToSend, ) = HyperCoreLib.minimumCoreReceiveAmountToAmounts( + totalBalanceRequiredToActivate, + coreTokenInfo.tokenInfo.evmExtraWeiDecimals + ); + + bool safeToBridge = HyperCoreLib.isCoreAmountSafeToBridge( + coreTokenInfo.coreIndex, + totalBalanceRequiredToActivate, + coreTokenInfo.bridgeSafetyBufferCore + ); + require(safeToBridge, "Not safe to bridge"); + _getMainStorage().cumulativeSponsoredActivationFee[fundingToken] += evmAmountToSend; + + // donationBox @ evm -> Handler @ evm + donationBox.withdraw(IERC20(fundingToken), evmAmountToSend); + // Handler @ evm -> Handler @ core + HyperCoreLib.transferERC20EVMToSelfOnCore( + fundingToken, + coreTokenInfo.coreIndex, + evmAmountToSend, + coreTokenInfo.tokenInfo.evmExtraWeiDecimals + ); + // The total balance withdrawn from Handler @ Core for this operation is activationFee + amountSent, so we set + // amountSent to 1 wei to only activate the account + // Handler @ core -> finalRecipient @ core + HyperCoreLib.transferERC20CoreToCore(coreTokenInfo.coreIndex, finalRecipient, 1); + + emit SponsoredAccountActivation(quoteNonce, finalRecipient, fundingToken, evmAmountToSend); + } + + /** + * @notice Cancells a pending limit order by `cloid` with an intention to submit a new limit order in its place. To + * be used for stale limit orders to speed up executing user transactions + * @param finalToken The token address for which the limit order was placed + * @param cloid Client order ID of the limit order to cancel + */ + function cancelLimitOrderByCloid(address finalToken, uint128 cloid) external onlyRole(PERMISSIONED_BOT_ROLE) { + FinalTokenInfo memory finalTokenInfo = _getExistingFinalTokenInfo(finalToken); + finalTokenInfo.swapHandler.cancelOrderByCloid(finalTokenInfo.spotIndex, cloid); + + emit CancelledLimitOrder(finalToken, cloid); + } + + function submitLimitOrderFromBot( + address finalToken, + uint64 priceX1e8, + uint64 sizeX1e8, + uint128 cloid + ) external onlyRole(PERMISSIONED_BOT_ROLE) { + FinalTokenInfo memory finalTokenInfo = _getExistingFinalTokenInfo(finalToken); + finalTokenInfo.swapHandler.submitSpotLimitOrder(finalTokenInfo, priceX1e8, sizeX1e8, cloid); + + emit SubmittedLimitOrder(finalToken, priceX1e8, sizeX1e8, cloid); + } + + function _setCoreTokenInfo( + address token, + uint32 coreIndex, + bool canBeUsedForAccountActivation, + uint64 accountActivationFeeCore, + uint64 bridgeSafetyBufferCore + ) internal { + HyperCoreLib.TokenInfo memory tokenInfo = HyperCoreLib.tokenInfo(coreIndex); + + (uint256 accountActivationFeeEVM, ) = HyperCoreLib.minimumCoreReceiveAmountToAmounts( + accountActivationFeeCore, + tokenInfo.evmExtraWeiDecimals + ); + + _getMainStorage().coreTokenInfos[token] = CoreTokenInfo({ + tokenInfo: tokenInfo, + coreIndex: coreIndex, + canBeUsedForAccountActivation: canBeUsedForAccountActivation, + accountActivationFeeEVM: accountActivationFeeEVM, + accountActivationFeeCore: accountActivationFeeCore, + bridgeSafetyBufferCore: bridgeSafetyBufferCore + }); + + emit SetCoreTokenInfo( + token, + coreIndex, + canBeUsedForAccountActivation, + accountActivationFeeCore, + bridgeSafetyBufferCore + ); + } + + /** + * @notice Used for ad-hoc sends of sponsorship funds to associated SwapHandler @ HyperCore + * @param token The final token for which we want to fund the SwapHandler + * @param amount The amount of tokens to send to the SwapHandler + */ + function sendSponsorshipFundsToSwapHandler(address token, uint256 amount) external onlyRole(PERMISSIONED_BOT_ROLE) { + CoreTokenInfo memory coreTokenInfo = _getExistingCoreTokenInfo(token); + FinalTokenInfo memory finalTokenInfo = _getExistingFinalTokenInfo(token); + (uint256 amountEVMToSend, uint64 amountCoreToReceive) = HyperCoreLib.maximumEVMSendAmountToAmounts( + amount, + coreTokenInfo.tokenInfo.evmExtraWeiDecimals + ); + if ( + !HyperCoreLib.isCoreAmountSafeToBridge( + coreTokenInfo.coreIndex, + amountCoreToReceive, + coreTokenInfo.bridgeSafetyBufferCore + ) + ) { + revert UnsafeToBridgeError(token, amountCoreToReceive); + } + + _getMainStorage().cumulativeSponsoredAmount[token] += amountEVMToSend; + + emit SentSponsorshipFundsToSwapHandler(token, amountEVMToSend); + + donationBox.withdraw(IERC20(token), amountEVMToSend); + IERC20(token).safeTransfer(address(finalTokenInfo.swapHandler), amountEVMToSend); + finalTokenInfo.swapHandler.transferFundsToSelfOnCore( + token, + coreTokenInfo.coreIndex, + amountEVMToSend, + coreTokenInfo.tokenInfo.evmExtraWeiDecimals + ); + } + + /// @notice Checks if `amount` of `token` is available to withdraw from donationBox + function _availableInDonationBox( + bytes32 quoteNonce, + address token, + uint256 amount + ) internal returns (bool available) { + uint256 balance = IERC20(token).balanceOf(address(donationBox)); + available = balance >= amount; + if (!available) { + emit DonationBoxInsufficientFunds(quoteNonce, token, amount, balance); + } + } + + function _calcAllowableAmtsSwapFlow( + uint256 amount, + uint256 extraFeesIncurred, + CoreTokenInfo memory initialCoreTokenInfo, + CoreTokenInfo memory finalCoreTokenInfo, + bool isSponsoredFlow, + uint256 maxUserSlippageBps + ) internal pure returns (uint64 minAllowableAmountToForwardCore, uint64 maxAllowableAmountToForwardCore) { + (, uint64 feelessAmountCoreInitialToken) = HyperCoreLib.maximumEVMSendAmountToAmounts( + amount + extraFeesIncurred, + initialCoreTokenInfo.tokenInfo.evmExtraWeiDecimals + ); + uint64 feelessAmountCoreFinalToken = HyperCoreLib.convertCoreDecimalsSimple( + feelessAmountCoreInitialToken, + initialCoreTokenInfo.tokenInfo.weiDecimals, + finalCoreTokenInfo.tokenInfo.weiDecimals + ); + if (isSponsoredFlow) { + minAllowableAmountToForwardCore = feelessAmountCoreFinalToken; + maxAllowableAmountToForwardCore = feelessAmountCoreFinalToken; + } else { + minAllowableAmountToForwardCore = uint64( + (feelessAmountCoreFinalToken * (BPS_SCALAR - maxUserSlippageBps)) / BPS_SCALAR + ); + maxAllowableAmountToForwardCore = feelessAmountCoreFinalToken; + } + } + + /** + * @return totalToSend What we will forward to user on HCore + * @return additionalToSend What we will send from donationBox right now + */ + function _calcSwapFlowSendAmounts( + uint64 limitOrderOut, + uint64 minAmountToSend, + uint64 maxAmountToSend, + bool isSponsored + ) internal pure returns (uint64 totalToSend, uint64 additionalToSend) { + if (isSponsored) { + // `minAmountToSend` is equal to `maxAmountToSend` for the sponsored flow + totalToSend = minAmountToSend; + additionalToSend = totalToSend > limitOrderOut ? totalToSend - limitOrderOut : 0; + } else { + additionalToSend = limitOrderOut < minAmountToSend ? minAmountToSend - limitOrderOut : 0; + uint64 proposedToSend = limitOrderOut + additionalToSend; + totalToSend = proposedToSend > maxAmountToSend ? maxAmountToSend : proposedToSend; + } + } + + /// @notice Reads the current spot price from HyperLiquid and applies a configured suggested discount for faster execution + /// @dev Includes HyperLiquid fees + function _getApproxRealizedPrice( + FinalTokenInfo memory finalTokenInfo, + CoreTokenInfo memory finalCoreTokenInfo, + CoreTokenInfo memory initialCoreTokenInfo + ) internal view returns (uint64 limitPriceX1e8) { + uint256 spotPxRaw = HyperCoreLib.spotPx(finalTokenInfo.spotIndex); + // Convert to 10 ** 8 precision (https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/hyperevm/interacting-with-hypercore) + // `szDecimals` of the base aseet for spot market + uint8 additionalPowersOf10 = finalTokenInfo.isBuy + ? finalCoreTokenInfo.tokenInfo.szDecimals + : initialCoreTokenInfo.tokenInfo.szDecimals; + uint256 spotX1e8 = spotPxRaw * (10 ** additionalPowersOf10); + + // Buy above spot, sell below spot + uint256 adjPpm = finalTokenInfo.isBuy + ? (PPM_SCALAR + finalTokenInfo.suggestedDiscountBps * 10 ** 2 + finalTokenInfo.feePpm) + : (PPM_SCALAR - finalTokenInfo.suggestedDiscountBps * 10 ** 2 - finalTokenInfo.feePpm); + limitPriceX1e8 = uint64((uint256(spotX1e8) * adjPpm) / PPM_SCALAR); + } + + /************************************** + * SWEEP FUNCTIONS * + **************************************/ + + function sweepNative(uint256 amount) external onlyRole(FUNDS_SWEEPER_ROLE) { + (bool success, ) = msg.sender.call{ value: amount }(""); + require(success, "Failed to send native funds"); + } + + function sweepErc20(address token, uint256 amount) external onlyRole(FUNDS_SWEEPER_ROLE) { + IERC20(token).safeTransfer(msg.sender, amount); + } + + function sweepErc20FromDonationBox(address token, uint256 amount) external onlyRole(FUNDS_SWEEPER_ROLE) { + donationBox.withdraw(IERC20(token), amount); + IERC20(token).safeTransfer(msg.sender, amount); + } + + function sweepERC20FromSwapHandler(address token, uint256 amount) external onlyRole(FUNDS_SWEEPER_ROLE) { + SwapHandler swapHandler = _getExistingFinalTokenInfo(token).swapHandler; + swapHandler.sweepErc20(token, amount); + IERC20(token).safeTransfer(msg.sender, amount); + } + + function sweepOnCore(address token, uint64 amount) external onlyRole(FUNDS_SWEEPER_ROLE) { + HyperCoreLib.transferERC20CoreToCore(_getMainStorage().coreTokenInfos[token].coreIndex, msg.sender, amount); + } + + function sweepOnCoreFromSwapHandler( + address finalToken, + uint64 finalTokenAmount, + uint64 baseTokenAmount + ) external onlyRole(FUNDS_SWEEPER_ROLE) { + MainStorage storage $ = _getMainStorage(); + require($.lastPullFundsBlock[finalToken] < block.number, "Can't pull funds twice in the same block"); + $.lastPullFundsBlock[finalToken] = block.number; + + SwapHandler swapHandler = $.finalTokenInfos[finalToken].swapHandler; + if (finalTokenAmount > 0) { + swapHandler.transferFundsToUserOnCore($.coreTokenInfos[finalToken].coreIndex, msg.sender, finalTokenAmount); + } + if (baseTokenAmount > 0) { + swapHandler.transferFundsToUserOnCore($.coreTokenInfos[baseToken].coreIndex, msg.sender, baseTokenAmount); + } + } +} diff --git a/contracts/periphery/mintburn/HyperCoreFlowRoles.sol b/contracts/periphery/mintburn/HyperCoreFlowRoles.sol new file mode 100644 index 000000000..5f30e15df --- /dev/null +++ b/contracts/periphery/mintburn/HyperCoreFlowRoles.sol @@ -0,0 +1,7 @@ +//SPDX-License-Identifier: Unlicense +pragma solidity ^0.8.0; + +abstract contract HyperCoreFlowRoles { + bytes32 public constant PERMISSIONED_BOT_ROLE = keccak256("PERMISSIONED_BOT_ROLE"); + bytes32 public constant FUNDS_SWEEPER_ROLE = keccak256("FUNDS_SWEEPER_ROLE"); +} diff --git a/contracts/periphery/mintburn/Structs.sol b/contracts/periphery/mintburn/Structs.sol new file mode 100644 index 000000000..6b7e6ef47 --- /dev/null +++ b/contracts/periphery/mintburn/Structs.sol @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.0; + +import { HyperCoreLib } from "../../libraries/HyperCoreLib.sol"; +import { SwapHandler } from "./SwapHandler.sol"; + +// Info about the token on HyperCore. +struct CoreTokenInfo { + // The token info on HyperCore. + HyperCoreLib.TokenInfo tokenInfo; + // The HyperCore index id of the token. + uint64 coreIndex; + // Whether the token can be used for account activation fee. + bool canBeUsedForAccountActivation; + // The account activation fee for the token. + uint256 accountActivationFeeEVM; + // The account activation fee for the token on Core. + uint64 accountActivationFeeCore; + // Bridge buffer to use when checking safety of bridging evm -> core. In core units + uint64 bridgeSafetyBufferCore; +} + +struct FinalTokenInfo { + // The index of the market where we're going to swap baseToken -> finalToken + uint32 spotIndex; + // To go baseToken -> finalToken, do we have to enqueue a buy or a sell? + bool isBuy; + // The fee Hyperliquid charges for Limit orders in the market; in parts per million, e.g. 1.4 bps = 140 ppm + uint32 feePpm; + // When enqueuing a limit order, use this to set a price "a bit worse than market" for faster execution + uint32 suggestedDiscountBps; + // Contract where the accounting for all baseToken -> finalToken accounting happens. One pre finalToken + SwapHandler swapHandler; +} + +/// @notice Common parameters shared across flow execution functions +struct CommonFlowParams { + uint256 amountInEVM; + bytes32 quoteNonce; + address finalRecipient; + address finalToken; + uint256 maxBpsToSponsor; + uint256 extraFeesIncurred; +} + +/// @notice Parameters for executing flows with arbitrary EVM actions +struct EVMFlowParams { + CommonFlowParams commonParams; + address initialToken; + bytes actionData; + bool transferToCore; +} diff --git a/contracts/periphery/mintburn/SwapHandler.sol b/contracts/periphery/mintburn/SwapHandler.sol new file mode 100644 index 000000000..92da0e134 --- /dev/null +++ b/contracts/periphery/mintburn/SwapHandler.sol @@ -0,0 +1,74 @@ +//SPDX-License-Identifier: Unlicense +pragma solidity ^0.8.0; +import { IERC20 } from "@openzeppelin/contracts-v4/token/ERC20/IERC20.sol"; +import { SafeERC20 } from "@openzeppelin/contracts-v4/token/ERC20/utils/SafeERC20.sol"; +import { HyperCoreLib } from "../../libraries/HyperCoreLib.sol"; +import { FinalTokenInfo } from "./Structs.sol"; + +contract SwapHandler { + // See https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#asset + uint32 private constant SPOT_MARKET_INDEX_OFFSET = 10_000; + + address public immutable parentHandler; + using SafeERC20 for IERC20; + + constructor() { + parentHandler = msg.sender; + } + + modifier onlyParentHandler() { + require(msg.sender == parentHandler, "Not parent handler"); + _; + } + + function activateCoreAccount( + address erc20EVMAddress, + uint64 erc20CoreIndex, + uint256 amountEVM, + int8 decimalDiff + ) external onlyParentHandler { + HyperCoreLib.transferERC20EVMToSelfOnCore(erc20EVMAddress, erc20CoreIndex, amountEVM, decimalDiff); + } + + function transferFundsToSelfOnCore( + address erc20EVMAddress, + uint64 erc20CoreIndex, + uint256 amountEVM, + int8 decimalDiff + ) external onlyParentHandler { + HyperCoreLib.transferERC20EVMToSelfOnCore(erc20EVMAddress, erc20CoreIndex, amountEVM, decimalDiff); + } + + function transferFundsToUserOnCore( + uint64 erc20CoreIndex, + address to, + uint64 amountCore + ) external onlyParentHandler { + HyperCoreLib.transferERC20CoreToCore(erc20CoreIndex, to, amountCore); + } + + function submitSpotLimitOrder( + FinalTokenInfo memory finalTokenInfo, + uint64 limitPriceX1e8, + uint64 sizeX1e8, + uint128 cloid + ) external onlyParentHandler { + HyperCoreLib.submitLimitOrder( + finalTokenInfo.spotIndex + SPOT_MARKET_INDEX_OFFSET, + finalTokenInfo.isBuy, + limitPriceX1e8, + sizeX1e8, + false, + HyperCoreLib.Tif.GTC, + cloid + ); + } + + function cancelOrderByCloid(uint32 spotIndex, uint128 cloid) external onlyParentHandler { + HyperCoreLib.cancelOrderByCloid(spotIndex + SPOT_MARKET_INDEX_OFFSET, cloid); + } + + function sweepErc20(address token, uint256 amount) external onlyParentHandler { + IERC20(token).safeTransfer(msg.sender, amount); + } +} diff --git a/contracts/periphery/mintburn/sponsored-cctp/SponsoredCCTPDstPeriphery.sol b/contracts/periphery/mintburn/sponsored-cctp/SponsoredCCTPDstPeriphery.sol new file mode 100644 index 000000000..cbd982709 --- /dev/null +++ b/contracts/periphery/mintburn/sponsored-cctp/SponsoredCCTPDstPeriphery.sol @@ -0,0 +1,263 @@ +//SPDX-License-Identifier: Unlicense +pragma solidity ^0.8.0; + +import { BaseModuleHandler } from "../BaseModuleHandler.sol"; +import { IMessageTransmitterV2 } from "../../../external/interfaces/CCTPInterfaces.sol"; +import { SponsoredCCTPQuoteLib } from "../../../libraries/SponsoredCCTPQuoteLib.sol"; +import { SponsoredCCTPInterface } from "../../../interfaces/SponsoredCCTPInterface.sol"; +import { Bytes32ToAddress } from "../../../libraries/AddressConverters.sol"; +import { HyperCoreFlowExecutor } from "../HyperCoreFlowExecutor.sol"; +import { ArbitraryEVMFlowExecutor } from "../ArbitraryEVMFlowExecutor.sol"; +import { CommonFlowParams, EVMFlowParams } from "../Structs.sol"; + +import { IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; +import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; + +/** + * @title SponsoredCCTPDstPeriphery + * @notice Destination chain periphery contract that supports sponsored/non-sponsored CCTP deposits. + * @dev This contract is used to receive tokens via CCTP and execute the flow accordingly. + * @dev IMPORTANT. `BaseModuleHandler` should always be the first contract in inheritance chain. Read + `BaseModuleHandler` contract code to learn more. + */ +contract SponsoredCCTPDstPeriphery is BaseModuleHandler, SponsoredCCTPInterface, ArbitraryEVMFlowExecutor { + using SafeERC20 for IERC20Metadata; + using Bytes32ToAddress for bytes32; + + /// @notice The CCTP message transmitter contract. + IMessageTransmitterV2 public immutable cctpMessageTransmitter; + + /// @notice Base token associated with this handler. The one we receive from the CCTP bridge + address public immutable baseToken; + + /// @custom:storage-location erc7201:SponsoredCCTPDstPeriphery.main + struct MainStorage { + /// @notice The public key of the signer that was used to sign the quotes. + address signer; + /// @notice Allow a buffer for quote deadline validation. CCTP transfer might have taken a while to finalize + uint256 quoteDeadlineBuffer; + /// @notice A mapping of used nonces to prevent replay attacks. + mapping(bytes32 => bool) usedNonces; + } + + // keccak256(abi.encode(uint256(keccak256("erc7201:SponsoredCCTPDstPeriphery.main")) - 1)) & ~bytes32(uint256(0xff)); + bytes32 private constant MAIN_STORAGE_LOCATION = 0xb788edf5b6d001c4df53cb371352fd225afa05a1712075d5f89a08d6b6f79f00; + + function _getMainStorage() private pure returns (MainStorage storage $) { + assembly { + $.slot := MAIN_STORAGE_LOCATION + } + } + + /** + * @notice Constructor for the SponsoredCCTPDstPeriphery contract. + * @param _cctpMessageTransmitter The address of the CCTP message transmitter contract. + * @param _signer The address of the signer that was used to sign the quotes. + * @param _donationBox The address of the donation box contract. This is used to store funds that are used for sponsored flows. + * @param _baseToken The address of the base token which would be the USDC on HyperEVM. + * @param _multicallHandler The address of the multicall handler contract. + */ + constructor( + address _cctpMessageTransmitter, + address _signer, + address _donationBox, + address _baseToken, + address _multicallHandler + ) BaseModuleHandler(_donationBox, _baseToken, DEFAULT_ADMIN_ROLE) ArbitraryEVMFlowExecutor(_multicallHandler) { + baseToken = _baseToken; + + cctpMessageTransmitter = IMessageTransmitterV2(_cctpMessageTransmitter); + + MainStorage storage $ = _getMainStorage(); + $.signer = _signer; + $.quoteDeadlineBuffer = 30 minutes; + + _grantRole(DEFAULT_ADMIN_ROLE, msg.sender); + } + + /** + * @notice Returns the signer address that is used to validate the signatures of the quotes. + * @return The signer address. + */ + function signer() external view returns (address) { + return _getMainStorage().signer; + } + + /** + * @notice Returns the quote deadline buffer. + * @return The quote deadline buffer. + */ + function quoteDeadlineBuffer() external view returns (uint256) { + return _getMainStorage().quoteDeadlineBuffer; + } + + /** + * @notice Returns true if the nonce has been used, false otherwise. + * @param nonce The nonce to check. + * @return True if the nonce has been used, false otherwise. + */ + function usedNonces(bytes32 nonce) external view returns (bool) { + return _getMainStorage().usedNonces[nonce]; + } + + /** + * @notice Sets the signer address that is used to validate the signatures of the quotes. + * @param _signer The new signer address. + */ + function setSigner(address _signer) external nonReentrant onlyRole(DEFAULT_ADMIN_ROLE) { + _getMainStorage().signer = _signer; + } + + /** + * @notice Sets the quote deadline buffer. This is used to prevent the quote from being used after it has expired. + * @param _quoteDeadlineBuffer The new quote deadline buffer. + */ + function setQuoteDeadlineBuffer(uint256 _quoteDeadlineBuffer) external nonReentrant onlyRole(DEFAULT_ADMIN_ROLE) { + _getMainStorage().quoteDeadlineBuffer = _quoteDeadlineBuffer; + } + + /** + * @notice Emmergency function that can be used to recover funds in cases where it is not possible to go + * through the normal flow (e.g. HyperEVM <> HyperCore USDC bridge is blacklisted). Receives the message from + * CCTP and then sends it to final recipient + * @param message The message that is received from CCTP. + * @param attestation The attestation that is received from CCTP. + */ + function emergencyReceiveMessage( + bytes memory message, + bytes memory attestation + ) external nonReentrant onlyRole(PERMISSIONED_BOT_ROLE) { + bool success = cctpMessageTransmitter.receiveMessage(message, attestation); + if (!success) { + return; + } + + // Use try-catch to handle potential abi.decode reverts gracefully + try this.validateMessage(message) returns (bool isValid) { + if (!isValid) { + return; + } + } catch { + // Malformed message that causes abi.decode to revert then early return + return; + } + (SponsoredCCTPInterface.SponsoredCCTPQuote memory quote, uint256 feeExecuted) = SponsoredCCTPQuoteLib + .getSponsoredCCTPQuoteData(message); + + _getMainStorage().usedNonces[quote.nonce] = true; + + IERC20Metadata(baseToken).safeTransfer(quote.finalRecipient.toAddress(), quote.amount - feeExecuted); + + emit EmergencyReceiveMessage( + quote.nonce, + quote.finalRecipient.toAddress(), + baseToken, + quote.amount - feeExecuted + ); + } + + /** + * @notice Receives a message from CCTP and executes the flow accordingly. This function first calls the + * CCTP message transmitter to receive the funds before validating the quote and executing the flow. + * @param message The message that is received from CCTP. + * @param attestation The attestation that is received from CCTP. + * @param signature The signature of the quote. + */ + function receiveMessage( + bytes memory message, + bytes memory attestation, + bytes memory signature + ) external nonReentrant authorizeFundedFlow { + bool success = cctpMessageTransmitter.receiveMessage(message, attestation); + if (!success) { + revert CCTPMessageTransmitterFailed(); + } + + // If the hook data is invalid or the mint recipient is not this contract we cannot process the message + // and therefore we exit. In this case the funds will be kept in this contract. + // Use try-catch to handle potential abi.decode reverts gracefully + try this.validateMessage(message) returns (bool isValid) { + if (!isValid) { + return; + } + } catch { + // Malformed message that causes abi.decode to revert then early return + return; + } + // Extract the quote and the fee that was executed from the message. + (SponsoredCCTPInterface.SponsoredCCTPQuote memory quote, uint256 feeExecuted) = SponsoredCCTPQuoteLib + .getSponsoredCCTPQuoteData(message); + + // Validate the quote and the signature. + bool isQuoteValid = _isQuoteValid(quote, signature); + if (isQuoteValid) { + _getMainStorage().usedNonces[quote.nonce] = true; + } + + uint256 amountAfterFees = quote.amount - feeExecuted; + + CommonFlowParams memory commonParams = CommonFlowParams({ + amountInEVM: amountAfterFees, + quoteNonce: quote.nonce, + finalRecipient: quote.finalRecipient.toAddress(), + // If the quote is invalid we don't want to swap, so we use the base token as the final token + finalToken: isQuoteValid ? quote.finalToken.toAddress() : baseToken, + // If the quote is invalid we don't sponsor the flow or the extra fees + maxBpsToSponsor: isQuoteValid ? quote.maxBpsToSponsor : 0, + extraFeesIncurred: feeExecuted + }); + + // Route to appropriate execution based on executionMode + if ( + isQuoteValid && + (quote.executionMode == uint8(ExecutionMode.ArbitraryActionsToCore) || + quote.executionMode == uint8(ExecutionMode.ArbitraryActionsToEVM)) + ) { + // Execute flow with arbitrary evm actions + _executeWithEVMFlow( + EVMFlowParams({ + commonParams: commonParams, + initialToken: baseToken, + actionData: quote.actionData, + transferToCore: quote.executionMode == uint8(ExecutionMode.ArbitraryActionsToCore) + }) + ); + } else { + // Execute standard HyperCore flow (default) via delegatecall + _delegateToHyperCore( + abi.encodeCall(HyperCoreFlowExecutor.executeFlow, (commonParams, quote.maxUserSlippageBps)) + ); + } + } + + /** + * @notice External wrapper for validateMessage to enable try-catch for safe abi.decode handling + * @param message The CCTP message to validate + * @return True if the message is valid, false otherwise + */ + function validateMessage(bytes memory message) external view returns (bool) { + return SponsoredCCTPQuoteLib.validateMessage(message); + } + + function _isQuoteValid( + SponsoredCCTPInterface.SponsoredCCTPQuote memory quote, + bytes memory signature + ) internal view returns (bool) { + MainStorage storage $ = _getMainStorage(); + return + SponsoredCCTPQuoteLib.validateSignature($.signer, quote, signature) && + !$.usedNonces[quote.nonce] && + quote.deadline + $.quoteDeadlineBuffer >= block.timestamp; + } + + function _executeWithEVMFlow(EVMFlowParams memory params) internal { + params.commonParams = ArbitraryEVMFlowExecutor._executeFlow(params); + + // Route to appropriate destination based on transferToCore flag + _delegateToHyperCore( + params.transferToCore + ? abi.encodeCall(HyperCoreFlowExecutor.executeSimpleTransferFlow, (params.commonParams)) + : abi.encodeCall(HyperCoreFlowExecutor.fallbackHyperEVMFlow, (params.commonParams)) + ); + } +} diff --git a/contracts/periphery/mintburn/sponsored-cctp/SponsoredCCTPSrcPeriphery.sol b/contracts/periphery/mintburn/sponsored-cctp/SponsoredCCTPSrcPeriphery.sol new file mode 100644 index 000000000..0931e0ef1 --- /dev/null +++ b/contracts/periphery/mintburn/sponsored-cctp/SponsoredCCTPSrcPeriphery.sol @@ -0,0 +1,130 @@ +//SPDX-License-Identifier: Unlicense +pragma solidity ^0.8.0; + +import { Ownable } from "@openzeppelin/contracts-v4/access/Ownable.sol"; +import { IERC20 } from "@openzeppelin/contracts-v4/token/ERC20/IERC20.sol"; +import { SafeERC20 } from "@openzeppelin/contracts-v4/token/ERC20/utils/SafeERC20.sol"; +import { ITokenMessengerV2 } from "../../../external/interfaces/CCTPInterfaces.sol"; + +import { SponsoredCCTPQuoteLib } from "../../../libraries/SponsoredCCTPQuoteLib.sol"; +import { SponsoredCCTPInterface } from "../../../interfaces/SponsoredCCTPInterface.sol"; + +/** + * @title SponsoredCCTPSrcPeriphery + * @notice Source chain periphery contract that supports sponsored/non-sponsored CCTP deposits. + * @dev This contract is used to deposit tokens for burn via CCTP. + */ +contract SponsoredCCTPSrcPeriphery is SponsoredCCTPInterface, Ownable { + using SafeERC20 for IERC20; + + /// @notice The CCTP token messenger contract. + ITokenMessengerV2 public immutable cctpTokenMessenger; + + /// @notice The source domain ID for the chain that this contract is deployed on. + uint32 public immutable sourceDomain; + + /// @custom:storage-location erc7201:SponsoredCCTPSrcPeriphery.main + struct MainStorage { + /// @notice The signer address that is used to validate the signatures of the quotes. + address signer; + /// @notice A mapping of used nonces to prevent replay attacks. + mapping(bytes32 => bool) usedNonces; + } + + // keccak256(abi.encode(uint256(keccak256("erc7201:SponsoredCCTPSrcPeriphery.main")) - 1)) & ~bytes32(uint256(0xff)); + bytes32 private constant MAIN_STORAGE_LOCATION = 0xf0a1b42b86a218bb35dbc2254545839ce4b1bf1d3780b5099e3e0abfc7a5b200; + + function _getMainStorage() private pure returns (MainStorage storage $) { + assembly { + $.slot := MAIN_STORAGE_LOCATION + } + } + + /** + * @notice Constructor for the SponsoredCCTPSrcPeriphery contract. + * @param _cctpTokenMessenger The address of the CCTP token messenger contract. + * @param _sourceDomain The source domain ID for the chain that this contract is deployed on. + * @param _signer The signer address that is used to validate the signatures of the quotes. + */ + constructor(address _cctpTokenMessenger, uint32 _sourceDomain, address _signer) { + cctpTokenMessenger = ITokenMessengerV2(_cctpTokenMessenger); + sourceDomain = _sourceDomain; + _getMainStorage().signer = _signer; + } + + /** + * @notice Returns the signer address that is used to validate the signatures of the quotes. + * @return The signer address. + */ + function signer() external view returns (address) { + return _getMainStorage().signer; + } + + /** + * @notice Returns true if the nonce has been used, false otherwise. + * @param nonce The nonce to check. + * @return True if the nonce has been used, false otherwise. + */ + function usedNonces(bytes32 nonce) external view returns (bool) { + return _getMainStorage().usedNonces[nonce]; + } + + /** + * @notice Deposits tokens for burn via CCTP. + * @param quote The quote that contains the data for the deposit. + * @param signature The signature of the quote. + */ + function depositForBurn(SponsoredCCTPInterface.SponsoredCCTPQuote memory quote, bytes memory signature) external { + MainStorage storage $ = _getMainStorage(); + if (!SponsoredCCTPQuoteLib.validateSignature($.signer, quote, signature)) revert InvalidSignature(); + if ($.usedNonces[quote.nonce]) revert InvalidNonce(); + if (quote.deadline < block.timestamp) revert InvalidDeadline(); + if (quote.sourceDomain != sourceDomain) revert InvalidSourceDomain(); + + ( + uint256 amount, + uint32 destinationDomain, + bytes32 mintRecipient, + address burnToken, + bytes32 destinationCaller, + uint256 maxFee, + uint32 minFinalityThreshold, + bytes memory hookData + ) = SponsoredCCTPQuoteLib.getDepositForBurnData(quote); + + $.usedNonces[quote.nonce] = true; + + IERC20(burnToken).safeTransferFrom(msg.sender, address(this), amount); + IERC20(burnToken).forceApprove(address(cctpTokenMessenger), amount); + + cctpTokenMessenger.depositForBurnWithHook( + amount, + destinationDomain, + mintRecipient, + burnToken, + destinationCaller, + maxFee, + minFinalityThreshold, + hookData + ); + + emit SponsoredDepositForBurn( + quote.nonce, + msg.sender, + quote.finalRecipient, + quote.deadline, + quote.maxBpsToSponsor, + quote.maxUserSlippageBps, + quote.finalToken, + signature + ); + } + + /** + * @notice Sets the signer address that is used to validate the signatures of the quotes. + * @param _signer The new signer address. + */ + function setSigner(address _signer) external onlyOwner { + _getMainStorage().signer = _signer; + } +} diff --git a/contracts/periphery/mintburn/sponsored-oft/ComposeMsgCodec.sol b/contracts/periphery/mintburn/sponsored-oft/ComposeMsgCodec.sol new file mode 100644 index 000000000..bb24ef646 --- /dev/null +++ b/contracts/periphery/mintburn/sponsored-oft/ComposeMsgCodec.sol @@ -0,0 +1,84 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.23; +import { BytesLib } from "../../../external/libraries/BytesLib.sol"; + +/// @notice Codec for params passed in OFT `composeMsg`. +library ComposeMsgCodec { + uint256 internal constant NONCE_OFFSET = 0; + uint256 internal constant DEADLINE_OFFSET = 32; + uint256 internal constant MAX_BPS_TO_SPONSOR_OFFSET = 64; + uint256 internal constant MAX_USER_SLIPPAGE_BPS_OFFSET = 96; + uint256 internal constant FINAL_RECIPIENT_OFFSET = 128; + uint256 internal constant FINAL_TOKEN_OFFSET = 160; + uint256 internal constant EXECUTION_MODE_OFFSET = 192; + // Minimum length with empty actionData: 7 regular params (32 bytes each) and 1 dynamic byte array (minumum 64 bytes) + uint256 internal constant MIN_COMPOSE_MSG_BYTE_LENGTH = 288; + + function _encode( + bytes32 nonce, + uint256 deadline, + uint256 maxBpsToSponsor, + uint256 maxUserSlippageBps, + bytes32 finalRecipient, + bytes32 finalToken, + uint8 executionMode, + bytes memory actionData + ) internal pure returns (bytes memory) { + return + abi.encode( + nonce, + deadline, + maxBpsToSponsor, + maxUserSlippageBps, + finalRecipient, + finalToken, + executionMode, + actionData + ); + } + + function _getNonce(bytes memory data) internal pure returns (bytes32 v) { + return BytesLib.toBytes32(data, NONCE_OFFSET); + } + + function _getDeadline(bytes memory data) internal pure returns (uint256 v) { + return BytesLib.toUint256(data, DEADLINE_OFFSET); + } + + function _getMaxBpsToSponsor(bytes memory data) internal pure returns (uint256 v) { + return BytesLib.toUint256(data, MAX_BPS_TO_SPONSOR_OFFSET); + } + + function _getMaxUserSlippageBps(bytes memory data) internal pure returns (uint256 v) { + return BytesLib.toUint256(data, MAX_USER_SLIPPAGE_BPS_OFFSET); + } + + function _getFinalRecipient(bytes memory data) internal pure returns (bytes32 v) { + return BytesLib.toBytes32(data, FINAL_RECIPIENT_OFFSET); + } + + function _getFinalToken(bytes memory data) internal pure returns (bytes32 v) { + return BytesLib.toBytes32(data, FINAL_TOKEN_OFFSET); + } + + function _getExecutionMode(bytes memory data) internal pure returns (uint8 v) { + (, , , , , , uint8 executionMode, ) = abi.decode( + data, + (bytes32, uint256, uint256, uint256, bytes32, bytes32, uint8, bytes) + ); + return executionMode; + } + + function _getActionData(bytes memory data) internal pure returns (bytes memory v) { + (, , , , , , , bytes memory actionData) = abi.decode( + data, + (bytes32, uint256, uint256, uint256, bytes32, bytes32, uint8, bytes) + ); + return actionData; + } + + function _isValidComposeMsgBytelength(bytes memory data) internal pure returns (bool valid) { + // Message must be at least the minimum length (can be longer due to variable actionData) + valid = data.length >= MIN_COMPOSE_MSG_BYTE_LENGTH; + } +} diff --git a/contracts/periphery/mintburn/sponsored-oft/DstOFTHandler.sol b/contracts/periphery/mintburn/sponsored-oft/DstOFTHandler.sol new file mode 100644 index 000000000..add659848 --- /dev/null +++ b/contracts/periphery/mintburn/sponsored-oft/DstOFTHandler.sol @@ -0,0 +1,240 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.23; + +import { BaseModuleHandler } from "../BaseModuleHandler.sol"; +import { ILayerZeroComposer } from "../../../external/interfaces/ILayerZeroComposer.sol"; +import { OFTComposeMsgCodec } from "../../../external/libraries/OFTComposeMsgCodec.sol"; +import { ComposeMsgCodec } from "./ComposeMsgCodec.sol"; +import { ExecutionMode } from "./Structs.sol"; +import { AddressToBytes32, Bytes32ToAddress } from "../../../libraries/AddressConverters.sol"; +import { IOFT, IOAppCore } from "../../../interfaces/IOFT.sol"; +import { HyperCoreFlowExecutor } from "../HyperCoreFlowExecutor.sol"; +import { ArbitraryEVMFlowExecutor } from "../ArbitraryEVMFlowExecutor.sol"; +import { CommonFlowParams, EVMFlowParams } from "../Structs.sol"; + +import { IERC20 } from "@openzeppelin/contracts-v4/token/ERC20/IERC20.sol"; +import { SafeERC20 } from "@openzeppelin/contracts-v4/token/ERC20/utils/SafeERC20.sol"; + +/** + * @notice Handler that receives funds from LZ system, checks authorizations(both against LZ system and src chain + sender), and forwards authorized params to the `_executeFlow` function + * @dev IMPORTANT. `BaseModuleHandler` should always be the first contract in inheritance chain. Read + `BaseModuleHandler` contract code to learn more. + */ +contract DstOFTHandler is BaseModuleHandler, ILayerZeroComposer, ArbitraryEVMFlowExecutor { + using ComposeMsgCodec for bytes; + using Bytes32ToAddress for bytes32; + using AddressToBytes32 for address; + using SafeERC20 for IERC20; + + /// @notice We expect bridge amount that comes through to this Handler to be 1:1 with the src send amount, and we + /// require our src handler to ensure that it is. We don't sponsor extra bridge fees in this handler + uint256 public constant EXTRA_FEES_TO_SPONSOR = 0; + + address public immutable OFT_ENDPOINT_ADDRESS; + address public immutable IOFT_ADDRESS; + + /// @notice Base token associated with this handler. The one we receive from the OFT bridge + address public immutable baseToken; + + /// @custom:storage-location erc7201:DstOFTHandler.main + struct MainStorage { + /// @notice A mapping used to validate an incoming message against a list of authorized src periphery contracts. In + /// bytes32 to support non-EVM src chains + mapping(uint64 eid => bytes32 authorizedSrcPeriphery) authorizedSrcPeripheryContracts; + /// @notice A mapping used for nonce uniqueness checks. Our src periphery and LZ should have prevented this already, + /// but I guess better safe than sorry + mapping(bytes32 quoteNonce => bool used) usedNonces; + } + + // keccak256(abi.encode(uint256(keccak256("erc7201:DstOFTHandler.main")) - 1)) & ~bytes32(uint256(0xff)); + bytes32 private constant MAIN_STORAGE_LOCATION = 0xe61a4c968926ec08fb0c5bf5be95077bf8b3ddd75ead66c94187ce8d5509de00; + + function _getMainStorage() private pure returns (MainStorage storage $) { + assembly { + $.slot := MAIN_STORAGE_LOCATION + } + } + + /** + * @notice Emitted when a new authorized src periphery is configured + * @param srcEid The source chain endpoint ID + * @param srcPeriphery The authorized source periphery contract address (as bytes32) + */ + event SetAuthorizedPeriphery(uint32 srcEid, bytes32 srcPeriphery); + + /// @notice Thrown when trying to call lzCompose from a source periphery that's not been configured in `authorizedSrcPeripheryContracts` + error AuthorizedPeripheryNotSet(uint32 _srcEid); + /// @notice Thrown when source chain recipient is not authorized periphery contract + error UnauthorizedSrcPeriphery(uint32 _srcEid); + /// @notice Thrown when the supplied token does not match the supplied ioft messenger + error TokenIOFTMismatch(); + /// @notice Thrown when the supplied ioft address does not match the supplied endpoint address + error IOFTEndpointMismatch(); + /// @notice Thrown if Quote nonce was already used + error NonceAlreadyUsed(); + /// @notice Thrown if supplied OApp is not configured ioft + error InvalidOApp(); + /// @notice Thrown if called by an unauthorized endpoint + error UnauthorizedEndpoint(); + /// @notice Thrown when supplied _composeMsg format is unexpected + error InvalidComposeMsgFormat(); + + constructor( + address _oftEndpoint, + address _ioft, + address _donationBox, + address _baseToken, + address _multicallHandler + ) BaseModuleHandler(_donationBox, _baseToken, DEFAULT_ADMIN_ROLE) ArbitraryEVMFlowExecutor(_multicallHandler) { + baseToken = _baseToken; + + if (_baseToken != IOFT(_ioft).token()) { + revert TokenIOFTMismatch(); + } + + OFT_ENDPOINT_ADDRESS = _oftEndpoint; + IOFT_ADDRESS = _ioft; + if (address(IOAppCore(IOFT_ADDRESS).endpoint()) != address(OFT_ENDPOINT_ADDRESS)) { + revert IOFTEndpointMismatch(); + } + + _grantRole(DEFAULT_ADMIN_ROLE, msg.sender); + } + + /** + * @notice Returns the authorized src periphery contract for a given source chain endpoint ID. + * @param srcEid The source chain endpoint ID + * @return The authorized src periphery contract address (as bytes32) + */ + function authorizedSrcPeripheryContracts(uint64 srcEid) external view returns (bytes32) { + return _getMainStorage().authorizedSrcPeripheryContracts[srcEid]; + } + + /** + * @notice Returns true if the nonce has been used, false otherwise. + * @param nonce The nonce to check. + * @return True if the nonce has been used, false otherwise. + */ + function usedNonces(bytes32 nonce) external view returns (bool) { + return _getMainStorage().usedNonces[nonce]; + } + + function setAuthorizedPeriphery( + uint32 srcEid, + bytes32 srcPeriphery + ) external nonReentrant onlyRole(DEFAULT_ADMIN_ROLE) { + _getMainStorage().authorizedSrcPeripheryContracts[srcEid] = srcPeriphery; + emit SetAuthorizedPeriphery(srcEid, srcPeriphery); + } + + /** + * @notice Handles incoming composed messages from LayerZero. + * @dev Ensures the message comes from the correct OApp and is sent through the authorized endpoint. + * + * @param _oApp The address of the OApp that is sending the composed message. + * @param _message The composed message payload containing transfer and execution details + */ + function lzCompose( + address _oApp, + bytes32 /* _guid */, + bytes calldata _message, + address /* _executor */, + bytes calldata /* _extraData */ + ) external payable override nonReentrant authorizeFundedFlow { + _requireAuthorizedMessage(_oApp, _message); + + // Decode the actual `composeMsg` payload to extract the recipient address + bytes memory composeMsg = OFTComposeMsgCodec.composeMsg(_message); + + // This check is a safety mechanism against blackholing funds. The funds were sent by the authorized periphery + // contract, but if the length is unexpected, we require funds be rescued, this is not a situation we aim to + // recover from in `lzCompose` call + if (!composeMsg._isValidComposeMsgBytelength()) { + revert InvalidComposeMsgFormat(); + } + + bytes32 quoteNonce = composeMsg._getNonce(); + MainStorage storage $ = _getMainStorage(); + if ($.usedNonces[quoteNonce]) { + revert NonceAlreadyUsed(); + } + $.usedNonces[quoteNonce] = true; + + uint256 amountLD = OFTComposeMsgCodec.amountLD(_message); + uint256 maxBpsToSponsor = composeMsg._getMaxBpsToSponsor(); + uint256 maxUserSlippageBps = composeMsg._getMaxUserSlippageBps(); + address finalRecipient = composeMsg._getFinalRecipient().toAddress(); + address finalToken = composeMsg._getFinalToken().toAddress(); + uint8 executionMode = composeMsg._getExecutionMode(); + bytes memory actionData = composeMsg._getActionData(); + + CommonFlowParams memory commonParams = CommonFlowParams({ + amountInEVM: amountLD, + quoteNonce: quoteNonce, + finalRecipient: finalRecipient, + finalToken: finalToken, + maxBpsToSponsor: maxBpsToSponsor, + extraFeesIncurred: EXTRA_FEES_TO_SPONSOR + }); + + // Route to appropriate execution based on executionMode + if ( + executionMode == uint8(ExecutionMode.ArbitraryActionsToCore) || + executionMode == uint8(ExecutionMode.ArbitraryActionsToEVM) + ) { + // Execute flow with arbitrary evm actions + _executeWithEVMFlow( + EVMFlowParams({ + commonParams: commonParams, + initialToken: baseToken, + actionData: actionData, + transferToCore: executionMode == uint8(ExecutionMode.ArbitraryActionsToCore) + }) + ); + } else { + // Execute standard HyperCore flow (default) via delegatecall + _delegateToHyperCore(abi.encodeCall(HyperCoreFlowExecutor.executeFlow, (commonParams, maxUserSlippageBps))); + } + } + + function _executeWithEVMFlow(EVMFlowParams memory params) internal { + params.commonParams = ArbitraryEVMFlowExecutor._executeFlow(params); + + // Route to appropriate destination based on transferToCore flag + _delegateToHyperCore( + params.transferToCore + ? abi.encodeCall(HyperCoreFlowExecutor.executeSimpleTransferFlow, (params.commonParams)) + : abi.encodeCall(HyperCoreFlowExecutor.fallbackHyperEVMFlow, (params.commonParams)) + ); + } + + /// @notice Checks that message was authorized by LayerZero's identity system and that it came from authorized src periphery + function _requireAuthorizedMessage(address _oApp, bytes calldata _message) internal view { + if (_oApp != IOFT_ADDRESS) { + revert InvalidOApp(); + } + if (msg.sender != OFT_ENDPOINT_ADDRESS) { + revert UnauthorizedEndpoint(); + } + _requireAuthorizedPeriphery(_message); + } + + /// @dev Checks that _message came from the authorized src periphery contract stored in `authorizedSrcPeripheryContracts` + function _requireAuthorizedPeriphery(bytes calldata _message) internal view { + uint32 _srcEid = OFTComposeMsgCodec.srcEid(_message); + bytes32 authorizedPeriphery = _getMainStorage().authorizedSrcPeripheryContracts[_srcEid]; + if (authorizedPeriphery == bytes32(0)) { + revert AuthorizedPeripheryNotSet(_srcEid); + } + + // Decode original sender + bytes32 _composeFromBytes32 = OFTComposeMsgCodec.composeFrom(_message); + + // We don't allow arbitrary src chain callers. If such a caller does send a message to this handler, the funds + // will remain in this contract and will have to be rescued by an admin rescue function + if (authorizedPeriphery != _composeFromBytes32) { + revert UnauthorizedSrcPeriphery(_srcEid); + } + } +} diff --git a/contracts/periphery/mintburn/sponsored-oft/QuoteSignLib.sol b/contracts/periphery/mintburn/sponsored-oft/QuoteSignLib.sol new file mode 100644 index 000000000..135e8e4a6 --- /dev/null +++ b/contracts/periphery/mintburn/sponsored-oft/QuoteSignLib.sol @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.23; + +import { ECDSA } from "@openzeppelin/contracts-v4/utils/cryptography/ECDSA.sol"; +import { SignedQuoteParams } from "./Structs.sol"; + +/// @notice Lib to check the signature for `SignedQuoteParams`. +/// The signature is checked against a keccak hash of abi-encoded fields of `SignedQuoteParams` +library QuoteSignLib { + using ECDSA for bytes32; + + /// @notice Compute the keccak of all `SignedQuoteParams` fields + function hash(SignedQuoteParams calldata p) internal pure returns (bytes32) { + return + keccak256( + abi.encode( + p.srcEid, + p.dstEid, + p.destinationHandler, + p.amountLD, + p.nonce, + p.deadline, + p.maxBpsToSponsor, + p.finalRecipient, + p.finalToken, + p.lzReceiveGasLimit, + p.lzComposeGasLimit, + p.executionMode, + keccak256(p.actionData) // Hash the actionData to keep signature size reasonable + ) + ); + } + + /// @notice Recover the signer for the given params and signature. + function recoverSigner(SignedQuoteParams calldata p, bytes calldata signature) internal pure returns (address) { + bytes32 digest = hash(p); + return digest.recover(signature); + } + + /// @notice Verify that `expectedSigner` signed `p` with `signature`. + function isSignatureValid( + address expectedSigner, + SignedQuoteParams calldata p, + bytes calldata signature + ) internal pure returns (bool) { + return recoverSigner(p, signature) == expectedSigner; + } +} diff --git a/contracts/periphery/mintburn/sponsored-oft/SponsoredOFTSrcPeriphery.sol b/contracts/periphery/mintburn/sponsored-oft/SponsoredOFTSrcPeriphery.sol new file mode 100644 index 000000000..4177bdb6f --- /dev/null +++ b/contracts/periphery/mintburn/sponsored-oft/SponsoredOFTSrcPeriphery.sol @@ -0,0 +1,216 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.23; + +import { Quote } from "./Structs.sol"; +import { QuoteSignLib } from "./QuoteSignLib.sol"; +import { ComposeMsgCodec } from "./ComposeMsgCodec.sol"; + +import { IOFT, IOAppCore, SendParam, MessagingFee } from "../../../interfaces/IOFT.sol"; +import { AddressToBytes32 } from "../../../libraries/AddressConverters.sol"; +import { MinimalLZOptions } from "../../../external/libraries/MinimalLZOptions.sol"; + +import { Ownable } from "@openzeppelin/contracts-v4/access/Ownable.sol"; +import { IERC20 } from "@openzeppelin/contracts-v4/token/ERC20/IERC20.sol"; +import { SafeERC20 } from "@openzeppelin/contracts-v4/token/ERC20/utils/SafeERC20.sol"; + +/// @notice Source chain periphery contract for users to interact with to start a sponsored or a non-sponsored flow +/// that allows custom Accross-supported flows on destination chain. Uses LayzerZero's OFT as an underlying bridge +contract SponsoredOFTSrcPeriphery is Ownable { + using AddressToBytes32 for address; + using MinimalLZOptions for bytes; + using SafeERC20 for IERC20; + + bytes public constant EMPTY_OFT_COMMAND = new bytes(0); + + /// @notice Token that's being sent by an OFT bridge + address public immutable TOKEN; + /// @notice OFT contract to interact with to initiate the bridge + address public immutable OFT_MESSENGER; + + /// @notice Source endpoint id + uint32 public immutable SRC_EID; + + /// @custom:storage-location erc7201:SponsoredOFTSrcPeriphery.main + struct MainStorage { + /// @notice Signer public key to check the signed quote against + address signer; + /// @notice A mapping to enforce only a single usage per quote + mapping(bytes32 => bool) quoteNonces; + } + + // keccak256(abi.encode(uint256(keccak256("erc7201:SponsoredOFTSrcPeriphery.main")) - 1)) & ~bytes32(uint256(0xff)); + bytes32 private constant MAIN_STORAGE_LOCATION = 0xbbe623e022cc184bd276c9a778810da1531bdd4c0bac9d86069eb499aa2eb500; + + function _getMainStorage() private pure returns (MainStorage storage $) { + assembly { + $.slot := MAIN_STORAGE_LOCATION + } + } + + /** + * @notice Event with auxiliary information. To be used in concert with OftSent event to get relevant quote details + * @param quoteNonce Unique identifier for this quote/transaction + * @param originSender The address initiating the transfer on the source chain + * @param finalRecipient The final recipient address on the destination chain (as bytes32) + * @param destinationHandler The handler contract address on the destination chain (as bytes32) + * @param quoteDeadline The timestamp by which the quote expires + * @param maxBpsToSponsor Maximum basis points that can be sponsored + * @param maxUserSlippageBps Maximum user slippage in basis points + * @param finalToken The final token address on the destination chain (as bytes32) + * @param sig The signature authorizing this transfer + */ + event SponsoredOFTSend( + bytes32 indexed quoteNonce, + address indexed originSender, + bytes32 indexed finalRecipient, + bytes32 destinationHandler, + uint256 quoteDeadline, + uint256 maxBpsToSponsor, + uint256 maxUserSlippageBps, + bytes32 finalToken, + bytes sig + ); + + /// @notice Thrown when the source eid of the ioft messenger does not match the src eid supplied + error IncorrectSrcEid(); + /// @notice Thrown when the supplied token does not match the supplied ioft messenger + error TokenIOFTMismatch(); + /// @notice Thrown when the signer for quote does not match `signer` + error IncorrectSignature(); + /// @notice Thrown if Quote has expired + error QuoteExpired(); + /// @notice Thrown if Quote nonce was already used + error NonceAlreadyUsed(); + /// @notice Thrown when provided msg.value is not sufficient to cover OFT bridging fee + error InsufficientNativeFee(); + + constructor(address _token, address _oftMessenger, uint32 _srcEid, address _signer) { + TOKEN = _token; + OFT_MESSENGER = _oftMessenger; + SRC_EID = _srcEid; + if (IOAppCore(_oftMessenger).endpoint().eid() != _srcEid) { + revert IncorrectSrcEid(); + } + if (IOFT(_oftMessenger).token() != _token) { + revert TokenIOFTMismatch(); + } + _getMainStorage().signer = _signer; + } + + /** + * @notice Returns the signer address that is used to validate the signatures of the quotes. + * @return The signer address. + */ + function signer() external view returns (address) { + return _getMainStorage().signer; + } + + /** + * @notice Returns true if the nonce has been used, false otherwise. + * @param nonce The nonce to check. + * @return True if the nonce has been used, false otherwise. + */ + function usedNonces(bytes32 nonce) external view returns (bool) { + return _getMainStorage().quoteNonces[nonce]; + } + + /** + * @notice Main entrypoint function to start the user flow + * @param quote The quote struct containing all transfer parameters + * @param signature The signature authorizing the quote + */ + function deposit(Quote calldata quote, bytes calldata signature) external payable { + // Step 1: validate quote and mark quote nonce used + _validateQuote(quote, signature); + _getMainStorage().quoteNonces[quote.signedParams.nonce] = true; + + // Step 2: build oft send params from quote + (SendParam memory sendParam, MessagingFee memory fee, address refundAddress) = _buildOftTransfer(quote); + + if (fee.nativeFee > msg.value) { + revert InsufficientNativeFee(); + } + // OFT doesn't refund the unused native fee portion. Instead, it expects precise fee.nativeFee to be transferred + // as msg.value, so we refund the user ourselves + uint256 nativeFeeRefund = msg.value - fee.nativeFee; + if (nativeFeeRefund > 0) { + // Adapted from "@openzeppelin/contracts/utils/Address.sol"; + (bool success, ) = payable(refundAddress).call{ value: nativeFeeRefund }(""); + require(success, "Unable to send value, recipient may have reverted"); + } + + // Step 3: pull tokens from user and apporove OFT messenger + IERC20(TOKEN).safeTransferFrom(msg.sender, address(this), quote.signedParams.amountLD); + IERC20(TOKEN).forceApprove(address(OFT_MESSENGER), quote.signedParams.amountLD); + + // Step 4: send oft transfer and emit event with auxiliary data + IOFT(OFT_MESSENGER).send{ value: fee.nativeFee }(sendParam, fee, refundAddress); + emit SponsoredOFTSend( + quote.signedParams.nonce, + msg.sender, + quote.signedParams.finalRecipient, + quote.signedParams.destinationHandler, + quote.signedParams.deadline, + quote.signedParams.maxBpsToSponsor, + quote.unsignedParams.maxUserSlippageBps, + quote.signedParams.finalToken, + signature + ); + } + + function _buildOftTransfer( + Quote calldata quote + ) internal view returns (SendParam memory, MessagingFee memory, address) { + bytes memory composeMsg = ComposeMsgCodec._encode( + quote.signedParams.nonce, + quote.signedParams.deadline, + quote.signedParams.maxBpsToSponsor, + quote.unsignedParams.maxUserSlippageBps, + quote.signedParams.finalRecipient, + quote.signedParams.finalToken, + quote.signedParams.executionMode, + quote.signedParams.actionData + ); + + bytes memory extraOptions = MinimalLZOptions + .newOptions() + .addExecutorLzReceiveOption(uint128(quote.signedParams.lzReceiveGasLimit), uint128(0)) + .addExecutorLzComposeOption(uint16(0), uint128(quote.signedParams.lzComposeGasLimit), uint128(0)); + + SendParam memory sendParam = SendParam( + quote.signedParams.dstEid, + quote.signedParams.destinationHandler, + // Only support OFT sends that don't take fees in sent token. Set `minAmountLD = amountLD` to enforce this + quote.signedParams.amountLD, + quote.signedParams.amountLD, + extraOptions, + composeMsg, + // Only support empty OFT commands + EMPTY_OFT_COMMAND + ); + + MessagingFee memory fee = IOFT(OFT_MESSENGER).quoteSend(sendParam, false); + + return (sendParam, fee, quote.unsignedParams.refundRecipient); + } + + function _validateQuote(Quote calldata quote, bytes calldata signature) internal view { + MainStorage storage $ = _getMainStorage(); + if (!QuoteSignLib.isSignatureValid($.signer, quote.signedParams, signature)) { + revert IncorrectSignature(); + } + if (quote.signedParams.deadline < block.timestamp) { + revert QuoteExpired(); + } + if (quote.signedParams.srcEid != SRC_EID) { + revert IncorrectSrcEid(); + } + if ($.quoteNonces[quote.signedParams.nonce]) { + revert NonceAlreadyUsed(); + } + } + + function setSigner(address _newSigner) external onlyOwner { + _getMainStorage().signer = _newSigner; + } +} diff --git a/contracts/periphery/mintburn/sponsored-oft/Structs.sol b/contracts/periphery/mintburn/sponsored-oft/Structs.sol new file mode 100644 index 000000000..6085ec4c8 --- /dev/null +++ b/contracts/periphery/mintburn/sponsored-oft/Structs.sol @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.23; + +/// @notice Execution modes for the sponsored OFT flow +enum ExecutionMode { + // Send to core and perform swap (if needed) there. + DirectToCore, + // Execute arbitrary actions (like a swap) on HyperEVM, then transfer to HyperCore + ArbitraryActionsToCore, + // Execute arbitrary actions on HyperEVM only (no HyperCore transfer) + ArbitraryActionsToEVM +} + +/// @notice A structure with all the relevant information about a particular sponsored bridging flow order +struct Quote { + SignedQuoteParams signedParams; + UnsignedQuoteParams unsignedParams; +} + +/// @notice Signed params of the sponsored bridging flow quote +struct SignedQuoteParams { + uint32 srcEid; // Source endpoint ID in OFT system. + // Params passed into OFT.send() + uint32 dstEid; // Destination endpoint ID in OFT system. + bytes32 destinationHandler; // `to`. Recipient address. Address of our Composer contract + uint256 amountLD; // Amount to send in local decimals. + // Signed params that go into `composeMsg` + bytes32 nonce; // quote nonce + uint256 deadline; // quote deadline + uint256 maxBpsToSponsor; // max bps (of sent amount) to sponsor for 1:1 + bytes32 finalRecipient; // user address on destination + bytes32 finalToken; // final token user will receive (might be different from OFT token we're sending) + // Signed gas limits for destination-side LZ execution + uint256 lzReceiveGasLimit; // gas limit for `lzReceive` call on destination side + uint256 lzComposeGasLimit; // gas limit for `lzCompose` call on destination side + // Execution mode and action data + uint8 executionMode; // ExecutionMode: DirectToCore, ArbitraryActionsToCore, or ArbitraryActionsToEVM + bytes actionData; // Encoded action data for arbitrary execution. Empty for DirectToCore mode. +} + +/// @notice Unsigned params of the sponsored bridging flow quote: user is free to choose these +struct UnsignedQuoteParams { + address refundRecipient; // recipient of extra msg.value passed into the OFT send on src chain + uint256 maxUserSlippageBps; // slippage tolerance for the swap on the destination +} diff --git a/contracts/test/MockEndpoint.sol b/contracts/test/MockEndpoint.sol new file mode 100644 index 000000000..4ee7870d4 --- /dev/null +++ b/contracts/test/MockEndpoint.sol @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.0; + +import { IEndpoint } from "../interfaces/IOFT.sol"; + +contract MockEndpoint is IEndpoint { + uint32 internal _eid; + + constructor(uint32 eid_) { + _eid = eid_; + } + + function eid() external view override returns (uint32) { + return _eid; + } +} diff --git a/contracts/test/MockOFTMessenger.sol b/contracts/test/MockOFTMessenger.sol index 79b2c40ae..8b732eb82 100644 --- a/contracts/test/MockOFTMessenger.sol +++ b/contracts/test/MockOFTMessenger.sol @@ -8,7 +8,7 @@ import "../interfaces/IOFT.sol"; * @dev This contract is intended to be inherited by other chain-specific adapters and spoke pools. * @custom:security-contact bugs@across.to */ -contract MockOFTMessenger is IOFT { +contract MockOFTMessenger is IOFT, IOAppCore { address public token; uint256 public nativeFee; uint256 public lzFee; @@ -16,12 +16,33 @@ contract MockOFTMessenger is IOFT { uint256 public amountReceivedLDToReturn; bool public useCustomReceipt; + // IOAppCore endpoint for tests that require endpoint().eid() + IEndpoint public endpoint_; + + // Captured call data for assertions in tests + SendParam public lastSendParam; + MessagingFee public lastFee; + address public lastRefundAddress; + uint256 public lastMsgValue; + uint256 public sendCallCount; + constructor(address _token) { token = _token; } + // Test helper to set the endpoint used by IOAppCore + function setEndpoint(address endpointAddr) external { + endpoint_ = IEndpoint(endpointAddr); + } + + // IOAppCore + function endpoint() external view returns (IEndpoint iEndpoint) { + return endpoint_; + } + + // IOFT function quoteSend( - SendParam calldata, /*_sendParam*/ + SendParam calldata /*_sendParam*/, bool /*_payInLzToken*/ ) external view returns (MessagingFee memory) { return MessagingFee(nativeFee, lzFee); @@ -29,9 +50,14 @@ contract MockOFTMessenger is IOFT { function send( SendParam calldata _sendParam, - MessagingFee calldata, /*_fee*/ - address /*_refundAddress*/ + MessagingFee calldata _fee, + address _refundAddress ) external payable returns (MessagingReceipt memory, OFTReceipt memory) { + lastSendParam = _sendParam; + lastFee = _fee; + lastRefundAddress = _refundAddress; + lastMsgValue = msg.value; + sendCallCount++; if (useCustomReceipt) { return ( MessagingReceipt(0, 0, MessagingFee(0, 0)), diff --git a/contracts/test/interfaces/IHyperCoreFlowExecutor.sol b/contracts/test/interfaces/IHyperCoreFlowExecutor.sol new file mode 100644 index 000000000..c4bd58a9d --- /dev/null +++ b/contracts/test/interfaces/IHyperCoreFlowExecutor.sol @@ -0,0 +1,189 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.0; + +import { CommonFlowParams } from "../../periphery/mintburn/Structs.sol"; +import { DonationBox } from "../../chain-adapters/DonationBox.sol"; + +/** + * @title IHyperCoreFlowExecutor + * @notice Interface for HyperCoreFlowExecutor contract handling HyperCore interactions for transfer-to-core or swap-with-core actions after stablecoin bridge transactions + * @custom:security-contact bugs@across.to + */ +interface IHyperCoreFlowExecutor { + /************************************** + * PUBLIC VARIABLE GETTERS * + **************************************/ + + /// @notice Common decimals scalars - parts per million decimals + /// @return PPM_DECIMALS Parts per million decimals (6) + function PPM_DECIMALS() external returns (uint256); + + /// @notice Parts per million scalar (10^6) + /// @return PPM_SCALAR Parts per million scalar + function PPM_SCALAR() external returns (uint256); + + /// @notice Decimals to use for Price calculations in limit order-related calculation functions + /// @return PX_D Price decimals (8) + function PX_D() external returns (uint8); + + /// @notice One in 1e8 format (10^8) + /// @return ONEX1e8 One in 1e8 format + function ONEX1e8() external returns (uint64); + + /// @notice The donation box contract. + /// @return donationBox Address of the donation box contract + function donationBox() external returns (DonationBox); + + /// @notice All operations performed in this contract are relative to this baseToken + /// @return baseToken Address of the base token + function baseToken() external returns (address); + + /************************************** + * FLOW FUNCTIONS * + **************************************/ + + /** + * @notice External entrypoint to execute flow when called via delegatecall from a handler. Works with params + * checked by a handler. Params authorization by a handler is enforced via `onlyAuthorizedFlow` modifier + * @param params Common flow parameters + * @param maxUserSlippageBps Maximum user slippage in basis points + */ + function executeFlow(CommonFlowParams memory params, uint256 maxUserSlippageBps) external; + + /// @notice External entrypoint to execute simple transfer flow (see `executeFlow` comment for details) + /// @param params Common flow parameters + function executeSimpleTransferFlow(CommonFlowParams memory params) external; + + /// @notice External entrypoint to execute fallback evm flow (see `executeFlow` comment for details) + /// @param params Common flow parameters + function fallbackHyperEVMFlow(CommonFlowParams memory params) external; + + /** + * @notice Finalizes multiple swap flows associated with a final token, subject to the L1 Hyperliquid balance + * @dev Caller is responsible for providing correct limitOrderOutput amounts per assosicated swap flow. The caller + * has to estimate how much final tokens it received on core based on the input of the corresponding quote nonce + * swap flow + * @param finalToken The final token address + * @param quoteNonces Array of quote nonces to finalize + * @param limitOrderOuts Array of limit order outputs corresponding to each quote nonce + * @return finalized Number of swaps successfully finalized + */ + function finalizeSwapFlows( + address finalToken, + bytes32[] calldata quoteNonces, + uint64[] calldata limitOrderOuts + ) external returns (uint256 finalized); + + /** + * @notice Activates a user account on Core by funding the account activation fee. + * @param quoteNonce The nonce of the quote that is used to identify the user. + * @param finalRecipient The address of the recipient of the funds. + * @param fundingToken The address of the token that is used to fund the account activation fee. + */ + function activateUserAccount(bytes32 quoteNonce, address finalRecipient, address fundingToken) external; + + /** + * @notice Cancells a pending limit order by `cloid` with an intention to submit a new limit order in its place. To + * be used for stale limit orders to speed up executing user transactions + * @param finalToken The final token address + * @param cloid The client order ID to cancel + */ + function cancelLimitOrderByCloid(address finalToken, uint128 cloid) external; + + /** + * @notice Submits a limit order from the bot + * @param finalToken The final token address + * @param priceX1e8 Price in 1e8 format + * @param sizeX1e8 Size in 1e8 format + * @param cloid The client order ID + */ + function submitLimitOrderFromBot(address finalToken, uint64 priceX1e8, uint64 sizeX1e8, uint128 cloid) external; + + /** + * @notice Set or update information for the token to use it in this contract + * @dev To be able to use the token in the swap flow, FinalTokenInfo has to be set as well + * @dev Setting core token info to incorrect values can lead to loss of funds. Should NEVER be unset while the + * finalTokenParams are not unset + * @param token The token address + * @param coreIndex The HyperCore index of the token + * @param canBeUsedForAccountActivation Whether the token can be used for account activation + * @param accountActivationFeeCore The account activation fee in core units + * @param bridgeSafetyBufferCore The bridge safety buffer in core units + */ + function setCoreTokenInfo( + address token, + uint32 coreIndex, + bool canBeUsedForAccountActivation, + uint64 accountActivationFeeCore, + uint64 bridgeSafetyBufferCore + ) external; + + /** + * @notice Sets the parameters for a final token. + * @dev This function deploys a new SwapHandler contract if one is not already set. If the final token + * can't be used for account activation, the handler will be left unactivated and would need to be activated by the caller. + * @param finalToken The address of the final token. + * @param assetIndex The index of the asset in the Hyperliquid market. + * @param isBuy Whether the final token is a buy or a sell. + * @param feePpm The fee in parts per million. + * @param suggestedDiscountBps The suggested slippage in basis points. + * @param accountActivationFeeToken A token to pay account activation fee in. Only used if adding a new final token + */ + function setFinalTokenInfo( + address finalToken, + uint32 assetIndex, + bool isBuy, + uint32 feePpm, + uint32 suggestedDiscountBps, + address accountActivationFeeToken + ) external; + + /** + * @notice Predicts the deterministic address of a SwapHandler for a given finalToken using CREATE2 + * @param finalToken The final token address + * @return The predicted address of the SwapHandler + */ + function predictSwapHandler(address finalToken) external view returns (address); + + /** + * @notice Used for ad-hoc sends of sponsorship funds to associated SwapHandler @ HyperCore + * @param token The final token for which we want to fund the SwapHandler + * @param amount The amount to send + */ + function sendSponsorshipFundsToSwapHandler(address token, uint256 amount) external; + + /** + * @notice Sweeps ERC20 tokens from the contract + * @param token The token address to sweep + * @param amount The amount to sweep + */ + function sweepErc20(address token, uint256 amount) external; + + /** + * @notice Sweeps ERC20 tokens from the donation box + * @param token The token address to sweep + * @param amount The amount to sweep + */ + function sweepErc20FromDonationBox(address token, uint256 amount) external; + + /** + * @notice Sweeps ERC20 tokens from a SwapHandler + * @param token The token address to sweep + * @param amount The amount to sweep + */ + function sweepERC20FromSwapHandler(address token, uint256 amount) external; + + /** + * @notice Sweeps tokens from Core + * @param token The token address + * @param amount The amount to sweep in core units + */ + function sweepOnCore(address token, uint64 amount) external; + + /** + * @notice Sweeps tokens from Core from a SwapHandler + * @param token The token address + * @param amount The amount to sweep in core units + */ + function sweepOnCoreFromSwapHandler(address token, uint64 amount) external; +} diff --git a/deploy/consts.ts b/deploy/consts.ts index 5608f59b2..d906cdd73 100644 --- a/deploy/consts.ts +++ b/deploy/consts.ts @@ -194,6 +194,7 @@ export const L2_ADDRESS_MAP: { [key: number]: { [contractName: string]: string } [CHAIN_IDs.ARBITRUM]: { l2GatewayRouter: "0x5288c571Fd7aD117beA99bF60FE0846C4E84F933", cctpV2TokenMessenger: "0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d", + cctpV2MessageTransmitter: "0x81D40F21F12A8F0E3252Bccb954D722d4c464B64", uniswapV3SwapRouter: "0xE592427A0AEce92De3Edee1F18E0157C05861564", "1inchV6Router": "0x111111125421cA6dc452d289314280a0f8842A65", permit2: "0x000000000022D473030F116dDEE9F6B43aC78BA3", @@ -205,6 +206,8 @@ export const L2_ADDRESS_MAP: { [key: number]: { [contractName: string]: string } [CHAIN_IDs.HYPEREVM]: { helios: "0xc19b7ef43a6ebd393446f401d1ecfac01b181ac0", cctpV2TokenMessenger: "0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d", + oftEndpoint: "0x3A73033C0b1407574C76BdBAc67f126f6b4a9AA9", + cctpV2MessageTransmitter: "0x81D40F21F12A8F0E3252Bccb954D722d4c464B64", permit2: "0x000000000022D473030F116dDEE9F6B43aC78BA3", }, [CHAIN_IDs.MONAD]: { @@ -219,6 +222,7 @@ export const L2_ADDRESS_MAP: { [key: number]: { [contractName: string]: string } [CHAIN_IDs.POLYGON]: { fxChild: "0x8397259c983751DAf40400790063935a11afa28a", cctpV2TokenMessenger: "0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d", + cctpV2MessageTransmitter: "0x81D40F21F12A8F0E3252Bccb954D722d4c464B64", uniswapV3SwapRouter: "0xE592427A0AEce92De3Edee1F18E0157C05861564", "1inchV6Router": "0x111111125421cA6dc452d289314280a0f8842A65", permit2: "0x000000000022D473030F116dDEE9F6B43aC78BA3", @@ -236,6 +240,7 @@ export const L2_ADDRESS_MAP: { [key: number]: { [contractName: string]: string } }, [CHAIN_IDs.OPTIMISM]: { cctpV2TokenMessenger: "0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d", + cctpV2MessageTransmitter: "0x81D40F21F12A8F0E3252Bccb954D722d4c464B64", uniswapV3SwapRouter: "0xE592427A0AEce92De3Edee1F18E0157C05861564", "1inchV6Router": "0x111111125421cA6dc452d289314280a0f8842A65", permit2: "0x000000000022D473030F116dDEE9F6B43aC78BA3", @@ -247,6 +252,7 @@ export const L2_ADDRESS_MAP: { [key: number]: { [contractName: string]: string } }, [CHAIN_IDs.BASE]: { cctpV2TokenMessenger: "0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d", + cctpV2MessageTransmitter: "0x81D40F21F12A8F0E3252Bccb954D722d4c464B64", uniswapV3SwapRouter: "0x2626664c2603336E57B271c5C0b26F421741e481", "1inchV6Router": "0x111111125421cA6dc452d289314280a0f8842A65", permit2: "0x000000000022D473030F116dDEE9F6B43aC78BA3", @@ -274,6 +280,7 @@ export const L2_ADDRESS_MAP: { [key: number]: { [contractName: string]: string } [CHAIN_IDs.LINEA]: { lineaMessageService: "0x508Ca82Df566dCD1B0DE8296e70a96332cD644ec", cctpV2TokenMessenger: "0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d", + cctpV2MessageTransmitter: "0x81D40F21F12A8F0E3252Bccb954D722d4c464B64", lineaTokenBridge: "0x353012dc4a9A6cF55c941bADC267f82004A8ceB9", permit2: "0x31c2F6fcFf4F8759b3Bd5Bf0e1084A055615c768", // PancakeSwap Permit2 }, @@ -295,6 +302,7 @@ export const L2_ADDRESS_MAP: { [key: number]: { [contractName: string]: string } }, [CHAIN_IDs.UNICHAIN]: { cctpV2TokenMessenger: "0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d", + cctpV2MessageTransmitter: "0x81D40F21F12A8F0E3252Bccb954D722d4c464B64", permit2: "0x000000000022D473030F116dDEE9F6B43aC78BA3", }, [CHAIN_IDs.UNICHAIN_SEPOLIA]: { diff --git a/foundry.toml b/foundry.toml index c02c0959c..cd0c2b3f7 100644 --- a/foundry.toml +++ b/foundry.toml @@ -8,29 +8,12 @@ out = "out" test = "test/evm/foundry" libs = ["node_modules", "lib"] cache_path = "cache-foundry" -remappings = [ - "@across-protocol/=node_modules/@across-protocol/", - "@ensdomains/=node_modules/@ensdomains/", - "@eth-optimism/=node_modules/@eth-optimism/", - "@gnosis.pm/=node_modules/@gnosis.pm/", - "@maticnetwork/=node_modules/@maticnetwork/", - "@matterlabs/=node_modules/@matterlabs/", - "@openzeppelin/=node_modules/@openzeppelin/", - "@scroll-tech/=node_modules/@scroll-tech/", - "@uniswap/=node_modules/@uniswap/", - "arb-bridge-eth/=node_modules/arb-bridge-eth/", - "arb-bridge-peripherals/=node_modules/arb-bridge-peripherals/", - "arbos-precompiles/=node_modules/arbos-precompiles/", - "base64-sol/=node_modules/base64-sol/", - "eth-gas-reporter/=node_modules/eth-gas-reporter/", - "hardhat-deploy/=node_modules/hardhat-deploy/", - "hardhat/=node_modules/hardhat/", -] via_ir = true optimizer_runs = 800 solc_version = "0.8.30" revert_strings = "strip" -fs_permissions = [{ access = "read", path = "./"}] +fs_permissions = [{ access = "read-write", path = "./" }] +use_literal_content = true solc = "0.8.30" evm_version = "prague" @@ -49,7 +32,6 @@ arbitrum = "${NODE_URL_42161}" base = "${NODE_URL_8453}" bsc = "${NODE_URL_56}" ethereum = "${NODE_URL_1}" -hyperevm = "${NODE_URL_999}" ink = "${NODE_URL_57073}" lens = "${NODE_URL_232}" linea = "${NODE_URL_59144}" @@ -65,6 +47,11 @@ soneium = "${NODE_URL_1868}" unichain = "${NODE_URL_130}" world-chain = "${NODE_URL_480}" zksync = "${NODE_URL_324}" +hyperevm = "${NODE_URL_999}" + +# testnet +arbitrum_sepolia = "${NODE_URL_421614}" +hyperevm_testnet = "${NODE_URL_998}" [etherscan] diff --git a/generated/constants.json b/generated/constants.json index 8404baea4..f331c9459 100644 --- a/generated/constants.json +++ b/generated/constants.json @@ -590,6 +590,7 @@ "L2_ADDRESS_MAP": { "10": { "cctpV2TokenMessenger": "0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d", + "cctpV2MessageTransmitter": "0x81D40F21F12A8F0E3252Bccb954D722d4c464B64", "uniswapV3SwapRouter": "0xE592427A0AEce92De3Edee1F18E0157C05861564", "1inchV6Router": "0x111111125421cA6dc452d289314280a0f8842A65", "permit2": "0x000000000022D473030F116dDEE9F6B43aC78BA3" @@ -600,11 +601,13 @@ }, "130": { "cctpV2TokenMessenger": "0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d", + "cctpV2MessageTransmitter": "0x81D40F21F12A8F0E3252Bccb954D722d4c464B64", "permit2": "0x000000000022D473030F116dDEE9F6B43aC78BA3" }, "137": { "fxChild": "0x8397259c983751DAf40400790063935a11afa28a", "cctpV2TokenMessenger": "0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d", + "cctpV2MessageTransmitter": "0x81D40F21F12A8F0E3252Bccb954D722d4c464B64", "uniswapV3SwapRouter": "0xE592427A0AEce92De3Edee1F18E0157C05861564", "1inchV6Router": "0x111111125421cA6dc452d289314280a0f8842A65", "permit2": "0x000000000022D473030F116dDEE9F6B43aC78BA3" @@ -639,6 +642,8 @@ "999": { "helios": "0xc19b7ef43a6ebd393446f401d1ecfac01b181ac0", "cctpV2TokenMessenger": "0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d", + "oftEndpoint": "0x3A73033C0b1407574C76BdBAc67f126f6b4a9AA9", + "cctpV2MessageTransmitter": "0x81D40F21F12A8F0E3252Bccb954D722d4c464B64", "permit2": "0x000000000022D473030F116dDEE9F6B43aC78BA3" }, "1135": { @@ -657,6 +662,7 @@ }, "8453": { "cctpV2TokenMessenger": "0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d", + "cctpV2MessageTransmitter": "0x81D40F21F12A8F0E3252Bccb954D722d4c464B64", "uniswapV3SwapRouter": "0x2626664c2603336E57B271c5C0b26F421741e481", "1inchV6Router": "0x111111125421cA6dc452d289314280a0f8842A65", "permit2": "0x000000000022D473030F116dDEE9F6B43aC78BA3" @@ -678,6 +684,7 @@ "42161": { "l2GatewayRouter": "0x5288c571Fd7aD117beA99bF60FE0846C4E84F933", "cctpV2TokenMessenger": "0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d", + "cctpV2MessageTransmitter": "0x81D40F21F12A8F0E3252Bccb954D722d4c464B64", "uniswapV3SwapRouter": "0xE592427A0AEce92De3Edee1F18E0157C05861564", "1inchV6Router": "0x111111125421cA6dc452d289314280a0f8842A65", "permit2": "0x000000000022D473030F116dDEE9F6B43aC78BA3" @@ -688,6 +695,7 @@ "59144": { "lineaMessageService": "0x508Ca82Df566dCD1B0DE8296e70a96332cD644ec", "cctpV2TokenMessenger": "0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d", + "cctpV2MessageTransmitter": "0x81D40F21F12A8F0E3252Bccb954D722d4c464B64", "lineaTokenBridge": "0x353012dc4a9A6cF55c941bADC267f82004A8ceB9", "permit2": "0x31c2F6fcFf4F8759b3Bd5Bf0e1084A055615c768" }, diff --git a/hardhat.config.ts b/hardhat.config.ts index c16202c5b..3bc4a907c 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -1,3 +1,25 @@ +const { subtask } = require("hardhat/config"); +const { TASK_COMPILE_SOLIDITY_GET_SOURCE_PATHS } = require("hardhat/builtin-tasks/task-names"); + +subtask(TASK_COMPILE_SOLIDITY_GET_SOURCE_PATHS).setAction(async (_: any, __: any, runSuper: any) => { + const paths = await runSuper(); + + // Filter out files that cause problems when using "paris" hardfork (currently used to compile everything when IS_TEST=true) + // Reference: https://github.com/NomicFoundation/hardhat/issues/2306#issuecomment-1039452928 + if (process.env.IS_TEST === "true" || process.env.CI === "true") { + return paths.filter((p: any) => { + return ( + !p.includes("contracts/periphery/mintburn") && + !p.includes("contracts/external/libraries/BytesLib.sol") && + !p.includes("contracts/libraries/SponsoredCCTPQuoteLib.sol") && + !p.includes("contracts/external/libraries/MinimalLZOptions.sol") + ); + }); + } + + return paths; +}); + import * as dotenv from "dotenv"; dotenv.config(); import { HardhatUserConfig } from "hardhat/config"; @@ -110,6 +132,7 @@ const config: HardhatUserConfig = { "contracts/Cher_SpokePool.sol": LARGE_CONTRACT_COMPILER_SETTINGS, "contracts/Blast_SpokePool.sol": LARGEST_CONTRACT_COMPILER_SETTINGS, "contracts/Tatara_SpokePool.sol": LARGE_CONTRACT_COMPILER_SETTINGS, + "contracts/periphery/mintburn/HyperCoreFlowExecutor.sol": LARGE_CONTRACT_COMPILER_SETTINGS, }, }, zksolc: { @@ -125,6 +148,7 @@ const config: HardhatUserConfig = { "SpokePoolVerifier", "ZkSync_SpokePool", "Lens_SpokePool", + "AcrossEventEmitter", ], }, }, @@ -158,6 +182,7 @@ const config: HardhatUserConfig = { gasMultiplier: 4.0, }, hyperevm: getDefaultHardhatConfig(CHAIN_IDs.HYPEREVM), + "hyperevm-testnet": getDefaultHardhatConfig(CHAIN_IDs.HYPEREVM_TESTNET, true), monad: getDefaultHardhatConfig(CHAIN_IDs.MONAD), "polygon-amoy": getDefaultHardhatConfig(CHAIN_IDs.POLYGON_AMOY), base: getDefaultHardhatConfig(CHAIN_IDs.BASE), diff --git a/package.json b/package.json index 6e8200890..7b184ddc6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@across-protocol/contracts", - "version": "4.1.16", + "version": "4.1.16-beta.0", "author": "UMA Team", "license": "AGPL-3.0-only", "repository": { @@ -25,8 +25,8 @@ "lint-js": "yarn prettier --list-different **/*.js **/*.ts", "lint-rust": "cargo +nightly fmt --all -- --check && cargo clippy", "lint-fix": "yarn prettier --write **/*.js **/*.ts ./programs/**/*.rs ./contracts**/*.sol && cargo +nightly fmt --all && cargo clippy", - "clean-fast": "for dir in node_modules cache cache-zk artifacts artifacts-zk dist typechain out zkout; do mv \"${dir}\" \"_${dir}\"; rm -rf \"_${dir}\" &; done", - "clean": "rm -rf node_modules cache cache-zk artifacts artifacts-zk dist typechain out zkout", + "clean-fast": "for dir in node_modules cache cache-zk artifacts artifacts-zk dist typechain cache-foundry out zkout; do mv \"${dir}\" \"_${dir}\"; rm -rf \"_${dir}\" &; done", + "clean": "rm -rf node_modules cache cache-zk artifacts artifacts-zk dist typechain cache-foundry out zkout", "generate-svm-artifacts": "bash ./scripts/svm/buildHelpers/buildIdl.sh && sh ./scripts/svm/buildHelpers/generateSvmAssets.sh && yarn generate-svm-clients", "generate-svm-clients": "yarn ts-node ./scripts/svm/buildHelpers/generateSvmClients.ts && yarn ts-node ./scripts/svm/buildHelpers/renameClientsImports.ts", "build-evm": "hardhat compile", @@ -118,6 +118,7 @@ "hardhat": "^2.14.0", "hardhat-deploy": "^0.11.12", "hardhat-gas-reporter": "^1.0.8", + "hardhat-preprocessor": "^0.1.5", "husky": "^4.2.3", "mocha": "^9.0.3", "multiformats": "9.9.0", @@ -140,8 +141,9 @@ } }, "publishConfig": { - "registry": "https://registry.npmjs.com/", - "access": "public" + "registry": "https://registry.npmjs.org/", + "access": "public", + "provenance": false }, "overrides": { "secp256k1@3.7.1": "3.8.1", diff --git a/remappings.txt b/remappings.txt new file mode 100644 index 000000000..37f0a3482 --- /dev/null +++ b/remappings.txt @@ -0,0 +1,18 @@ +@across-protocol/=node_modules/@across-protocol/ +@ensdomains/=node_modules/@ensdomains/ +@eth-optimism/=node_modules/@eth-optimism/ +@gnosis.pm/=node_modules/@gnosis.pm/ +@maticnetwork/=node_modules/@maticnetwork/ +@matterlabs/=node_modules/@matterlabs/ +@openzeppelin/=node_modules/@openzeppelin/ +@scroll-tech/=node_modules/@scroll-tech/ +@uniswap/=node_modules/@uniswap/ +arb-bridge-eth/=node_modules/arb-bridge-eth/ +arb-bridge-peripherals/=node_modules/arb-bridge-peripherals/ +arbos-precompiles/=node_modules/arbos-precompiles/ +base64-sol/=node_modules/base64-sol/ +eth-gas-reporter/=node_modules/eth-gas-reporter/ +hardhat-deploy/=node_modules/hardhat-deploy/ +hardhat/=node_modules/hardhat/ +@uma/=node_modules/@uma/ +forge-std/=lib/forge-std/src/ diff --git a/script/DeployHyperliquidDepositHandler.s.sol b/script/DeployHyperliquidDepositHandler.s.sol new file mode 100644 index 000000000..2c0afa490 --- /dev/null +++ b/script/DeployHyperliquidDepositHandler.s.sol @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.0; + +import { Script } from "forge-std/Script.sol"; +import { Test } from "forge-std/Test.sol"; +import { console } from "forge-std/console.sol"; +import { HyperliquidDepositHandler } from "../contracts/handlers/HyperliquidDepositHandler.sol"; +import { HyperCoreLib } from "../contracts/libraries/HyperCoreLib.sol"; +import { IERC20 } from "@openzeppelin/contracts-v4/token/ERC20/IERC20.sol"; + +// How to run: +// forge script script/DeployHyperliquidDepositHandler.s.sol:DeployHyperliquidDepositHandler --rpc-url hyperevm -vvvv + +contract DeployHyperliquidDepositHandler is Script, Test { + function run() external { + string memory deployerMnemonic = vm.envString("MNEMONIC"); + uint256 deployerPrivateKey = vm.deriveKey(deployerMnemonic, 0); + // Set the initial signer to the deployer's address. + address signer = vm.addr(deployerPrivateKey); + + address spokePool = 0x35E63eA3eb0fb7A3bc543C71FB66412e1F6B0E04; + + // Get the current chain ID + uint256 chainId = block.chainid; + + // Set up USDH as a supported token for this handler. + IERC20 usdh = IERC20(0x111111a1a0667d36bD57c0A9f569b98057111111); + uint64 usdhTokenIndex = 360; + uint256 usdhActivationFee = 1000000; // 1 USDH + int8 usdhDecimalDiff = -2; // USDH has 2 extra decimals on Core compared to EVM. + + vm.startBroadcast(deployerPrivateKey); + + HyperliquidDepositHandler hyperliquidDepositHandler = new HyperliquidDepositHandler(signer, spokePool); + + // Activate Handler account so it can write to CoreWriter by sending 1 core wei. + HyperCoreLib.transferERC20CoreToCore(usdhTokenIndex, address(hyperliquidDepositHandler), 1); + hyperliquidDepositHandler.addSupportedToken(address(usdh), usdhTokenIndex, 1000000, usdhDecimalDiff); + + // Log the deployed addresses + console.log("Chain ID:", chainId); + console.log("HyperliquidDepositHandler deployed to:", address(hyperliquidDepositHandler)); + console.log("Signer required to sign payloads for handleV3AcrossMessage:", signer); + console.log("USDH token index:", usdhTokenIndex); + console.log("USDH activation fee:", usdhActivationFee); + console.log("USDH decimal diff:", usdhDecimalDiff); + + vm.stopBroadcast(); + } +} diff --git a/script/mintburn/LimitOrderCalcCli.s.sol b/script/mintburn/LimitOrderCalcCli.s.sol new file mode 100644 index 000000000..cca991d5d --- /dev/null +++ b/script/mintburn/LimitOrderCalcCli.s.sol @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.0; + +import "forge-std/Script.sol"; +import "forge-std/console2.sol"; +import "./LimitOrderCalcUtils.sol"; + +/* +@notice Thin CLI wrapper exposing public functions for calling internal library helpers from the command line. +Usage example (Selling USDT0 into USDC): + +forge script script/mintburn/LimitOrderCalcCli.s.sol:LimitOrderCalcCli \ + --sig "calcLOAmounts(uint64,uint64,bool,uint64,uint8,uint8,uint8,uint8)" \ + 100000000 99990000 false 80 8 2 8 8 +*/ +contract LimitOrderCalcCli is Script { + // Suggested price with discount/premium + function calcSuggestedPrice( + uint64 spotX1e8, + bool isBuy, + uint32 suggestedDiscountBps + ) external view returns (uint64 limitPriceX1e8) { + return LimitOrderCalcUtils._getSuggestedPriceX1e8(spotX1e8, isBuy, suggestedDiscountBps); + } + + // Remaining LO budget given size/price/fees + function calcRemainingLOBudget( + uint64 pxX1e8, + uint64 szX1e8, + bool isBuy, + uint64 feePpm, + uint8 weiDecimalsTokenHave, + uint8 weiDecimalsTokenWant + ) external pure returns (uint64 budget) { + return + LimitOrderCalcUtils._calcRemainingLOBudget( + pxX1e8, + szX1e8, + isBuy, + feePpm, + weiDecimalsTokenHave, + weiDecimalsTokenWant + ); + } + + // Calculate limit order size given budget and desired price + function calcLOAmounts( + uint64 coreBudget, + uint64 pxX1e8, + bool isBuy, + uint64 feePpm, + uint8 tokenHaveWeiD, + uint8 tokenHaveSzD, + uint8 tokenWantWeiD, + uint8 tokenWantSzD + ) external pure returns (uint64 szX1e8, uint64 coreToSend, uint64 guaranteedCoreOut) { + return + LimitOrderCalcUtils._calcLOAmounts( + coreBudget, + pxX1e8, + isBuy, + feePpm, + tokenHaveWeiD, + tokenHaveSzD, + tokenWantWeiD, + tokenWantSzD + ); + } +} diff --git a/script/mintburn/LimitOrderCalcUtils.sol b/script/mintburn/LimitOrderCalcUtils.sol new file mode 100644 index 000000000..f26f1628e --- /dev/null +++ b/script/mintburn/LimitOrderCalcUtils.sol @@ -0,0 +1,154 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.0; + +library LimitOrderCalcUtils { + uint256 constant PPM_SCALAR = 10 ** 6; + uint256 constant BPS_SCALAR = 10 ** 4; + uint8 constant PX_D = 8; + + /// @notice Reads the current spot price from HyperLiquid and applies a configured suggested discount for faster execution + function _getSuggestedPriceX1e8( + uint64 spotX1e8, + bool isBuy, + uint32 suggestedDiscountBps + ) internal view returns (uint64 limitPriceX1e8) { + // Buy above spot, sell below spot + uint256 adjBps = isBuy ? (BPS_SCALAR + suggestedDiscountBps) : (BPS_SCALAR - suggestedDiscountBps); + limitPriceX1e8 = uint64((uint256(spotX1e8) * adjBps) / BPS_SCALAR); + } + + /************************************** + * LIMIT ORDER CALCULATION UTILS * + **************************************/ + + /// @notice Given the size and price of a limit order, returns the remaining `budget` that Limit order expects to spend + function _calcRemainingLOBudget( + uint64 pxX1e8, + uint64 szX1e8, + bool isBuy, + uint64 feePpm, + uint8 weiDecimalsTokenHave, + uint8 weiDecimalsTokenWant + ) internal pure returns (uint64 budget) { + uint8 quoteWeiD = isBuy ? weiDecimalsTokenHave : weiDecimalsTokenWant; + uint8 baseWeiD = isBuy ? weiDecimalsTokenWant : weiDecimalsTokenHave; + + if (isBuy) { + // We have quoteTokens. Estimate how many quoteTokens we are GUARANTEED to have had to enqueue the LO in the first place (proportional) + // qTR is quote tokens real. qTD quote token decimals. + // szX1e8 * pxX1e8 / 10 ** 8 = qTX1e8Net + // qTR * 10 ** 8 * (10 ** 6 - feePpm) / (10 ** 6 * 10 ** qTD) = qTX1e8Net + // qTR = szX1e8 * pxX1e8 * 10 ** 6 * 10 ** qTD / (10 ** 8 * 10 ** 8 * (10 ** 6 - feePpm)) + budget = uint64( + (uint256(szX1e8) * uint256(pxX1e8) * PPM_SCALAR * 10 ** (quoteWeiD)) / + (10 ** 16 * (PPM_SCALAR - feePpm)) + ); + } else { + // We have baseTokens. Convert `szX1e8` to base token budget. A simple decimals conversion here + budget = uint64((szX1e8 * 10 ** (baseWeiD)) / 10 ** 8); + } + } + + /** + * @notice The purpose of this function is best described by its return params. Given a budget and a price, determines + * size to set, tokens to send, and min amount received. + * @return szX1e8 size value to supply when sending a limit order to HyperCore + * @return coreToSend the number of tokens to send for this trade to suceed; <= coreBudget + * @return guaranteedCoreOut the ABSOLUTE MINIMUM that we're guaranteed to receive when the limit order fully settles + */ + function _calcLOAmounts( + uint64 coreBudget, + uint64 pxX1e8, + bool isBuy, + uint64 feePpm, + uint8 tokenHaveWeiD, + uint8 tokenHaveSzD, + uint8 tokenWantWeiD, + uint8 tokenWantSzD + ) internal pure returns (uint64 szX1e8, uint64 coreToSend, uint64 guaranteedCoreOut) { + if (isBuy) { + return + _calcLOAmountsBuy(coreBudget, pxX1e8, tokenHaveWeiD, tokenHaveSzD, tokenWantWeiD, tokenWantSzD, feePpm); + } else { + return + _calcLOAmountsSell( + coreBudget, + pxX1e8, + tokenWantWeiD, + tokenWantSzD, + tokenHaveWeiD, + tokenHaveSzD, + feePpm + ); + } + } + + /** + * @notice Given the quote budget and the price, this function calculates the size of the buy limit order to set + * as well as the minimum amount of out token to expect. This calculation is based on the HIP-1 spot trading formula. + * Source: https://hyperliquid.gitbook.io/hyperliquid-docs/hyperliquid-improvement-proposals-hips/hip-1-native-token-standard#spot-trading + * @param quoteBudget The budget of the quote in base token. + * @param pxX1e8 The price of the quote token in base token. + * @param quoteD The decimals of the quote token. + * @param quoteSz The size decimals of the quote token. + * @param baseD The decimals of the base token. + * @param baseSz The size decimals of the base token. + * @param feePpm The fee in ppm that is applied to the quote. + * @return szX1e8 The size of the limit order to set. + * @return tokensToSendCore The number of tokens to send for this trade to suceed. + * @return minAmountOutCore The minimum amount of out token to expect. + */ + function _calcLOAmountsBuy( + uint64 quoteBudget, + uint64 pxX1e8, + uint8 quoteD, + uint8 quoteSz, + uint8 baseD, + uint8 baseSz, + uint64 feePpm + ) internal pure returns (uint64 szX1e8, uint64 tokensToSendCore, uint64 minAmountOutCore) { + uint256 px = (pxX1e8 * 10 ** (PX_D + quoteSz)) / 10 ** (8 + baseSz); + // quoteD >= quoteSz always + uint256 sz = (quoteBudget * (PPM_SCALAR - feePpm) * 10 ** PX_D) / (PPM_SCALAR * px * 10 ** (quoteD - quoteSz)); + // baseD >= baseSz always + uint64 outBaseNet = uint64(sz * 10 ** (baseD - baseSz)); + szX1e8 = uint64((uint256(outBaseNet) * 10 ** 8) / 10 ** baseD); + tokensToSendCore = quoteBudget; + minAmountOutCore = outBaseNet; + } + + /** + * @notice Given the quote budget and the price, this function calculates the size of the sell limit order to set + * as well as the minimum amount of out token to expect. This calculation is based on the HIP-1 spot trading formula. + * Source: https://hyperliquid.gitbook.io/hyperliquid-docs/hyperliquid-improvement-proposals-hips/hip-1-native-token-standard#spot-trading + * @param baseBudget The budget of the quote in base token. + * @param pxX1e8 The price of the quote token in base token. + * @param quoteD The decimals of the quote token. + * @param quoteSz The size decimals of the quote token. + * @param baseD The decimals of the base token. + * @param baseSz The size decimals of the base token. + * @param feePpm The fee in ppm that is applied to the quote. + * @return szX1e8 The size of the limit order to set. + * @return tokensToSendCore The number of tokens to send for this trade to suceed. + * @return minAmountOutCore The minimum amount of out token to expect. + */ + function _calcLOAmountsSell( + uint64 baseBudget, + uint64 pxX1e8, + uint8 quoteD, + uint8 quoteSz, + uint8 baseD, + uint8 baseSz, + uint64 feePpm + ) internal pure returns (uint64 szX1e8, uint64 tokensToSendCore, uint64 minAmountOutCore) { + uint64 sz = uint64(baseBudget / 10 ** (baseD - baseSz)); + uint256 px = (pxX1e8 * 10 ** (PX_D + quoteSz)) / 10 ** (8 + baseSz); + + // quoteD >= quoteSz always + uint64 outQuoteGross = uint64((px * sz * 10 ** (quoteD - quoteSz)) / 10 ** PX_D); + uint64 outQuoteNet = uint64((outQuoteGross * (PPM_SCALAR - feePpm)) / PPM_SCALAR); + szX1e8 = uint64((sz * 10 ** 8) / 10 ** baseSz); + tokensToSendCore = baseBudget; + minAmountOutCore = outQuoteNet; + } +} diff --git a/script/mintburn/ReadHCoreTokenInfoUtil.s.sol b/script/mintburn/ReadHCoreTokenInfoUtil.s.sol new file mode 100644 index 000000000..6c480bf2f --- /dev/null +++ b/script/mintburn/ReadHCoreTokenInfoUtil.s.sol @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.0; + +import { Script } from "forge-std/Script.sol"; +import { console } from "forge-std/console.sol"; +import { Constants } from "../utils/Constants.sol"; + +contract ReadHCoreTokenInfoUtil is Script { + string internal constant HCORE_JSON_PATH = "./script/mintburn/hypercore-tokens.json"; + + struct TokenJson { + uint256 index; + address evmAddress; + bool canBeUsedForAccountActivation; + uint256 accountActivationFeeCore; + uint256 bridgeSafetyBufferCore; + } + + function readToken(string memory tokenName) public view returns (TokenJson memory info) { + string memory json = vm.readFile(HCORE_JSON_PATH); + string memory base = string.concat(".", tokenName); + + info.index = vm.parseJsonUint(json, string.concat(base, ".index")); + + // evmAddress can be null in JSON; parseJsonAddress would revert. Try/catch and leave zero if unset. + try this._parseAddress(json, string.concat(base, ".evmAddress")) returns (address a) { + info.evmAddress = a; + } catch { + info.evmAddress = address(0); + } + + // Required fields for CoreTokenInfo + info.canBeUsedForAccountActivation = vm.parseJsonBool( + json, + string.concat(base, ".canBeUsedForAccountActivation") + ); + info.accountActivationFeeCore = vm.parseJsonUint(json, string.concat(base, ".accountActivationFeeCore")); + info.bridgeSafetyBufferCore = vm.parseJsonUint(json, string.concat(base, ".bridgeSafetyBufferCore")); + } + + function resolveEvmAddress(TokenJson memory info, uint256 /* chainId */) public pure returns (address evm) { + require(info.evmAddress != address(0), "evmAddress required in JSON"); + return info.evmAddress; + } + + // Wrapper to make parseJsonAddress usable in try/catch (external visibility) + function _parseAddress(string memory json, string memory key) external pure returns (address) { + return vm.parseJsonAddress(json, key); + } +} diff --git a/script/mintburn/cctp/113DeploySponsoredCCTPSrcPeriphery.sol b/script/mintburn/cctp/113DeploySponsoredCCTPSrcPeriphery.sol new file mode 100644 index 000000000..87b500933 --- /dev/null +++ b/script/mintburn/cctp/113DeploySponsoredCCTPSrcPeriphery.sol @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.0; + +import { console } from "forge-std/console.sol"; +import { DeploymentUtils } from "../../utils/DeploymentUtils.sol"; + +import { SponsoredCCTPSrcPeriphery } from "../../../contracts/periphery/mintburn/sponsored-cctp/SponsoredCCTPSrcPeriphery.sol"; + +// Deploy: forge script script/mintburn/cctp/113DeploySponsoredCCTPSrcPeriphery.sol:DeploySponsoredCCTPSrcPeriphery --rpc-url -vvvv +contract DeploySponsoredCCTPSrcPeriphery is DeploymentUtils { + function run() external { + console.log("Deploying SponsoredCCTPSrcPeriphery..."); + console.log("Chain ID:", block.chainid); + + string memory deployerMnemonic = vm.envString("MNEMONIC"); + uint256 deployerPrivateKey = vm.deriveKey(deployerMnemonic, 0); + address deployer = vm.addr(deployerPrivateKey); + + _loadConfig("./script/mintburn/cctp/config.toml", true); + + address cctpTokenMessenger = config.get("cctpTokenMessenger").toAddress(); + uint32 sourceDomain = config.get("cctpDomainId").toUint32(); + + vm.startBroadcast(deployerPrivateKey); + + // TODO: use create2 for final deployment + SponsoredCCTPSrcPeriphery sponsoredCCTPSrcPeriphery = new SponsoredCCTPSrcPeriphery( + cctpTokenMessenger, + sourceDomain, + deployer + ); + + console.log("SponsoredCCTPSrcPeriphery deployed to:", address(sponsoredCCTPSrcPeriphery)); + } +} diff --git a/script/mintburn/cctp/114DeploySponsoredCCTPDstPeriphery.sol b/script/mintburn/cctp/114DeploySponsoredCCTPDstPeriphery.sol new file mode 100644 index 000000000..0faccfa92 --- /dev/null +++ b/script/mintburn/cctp/114DeploySponsoredCCTPDstPeriphery.sol @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.0; + +import { console } from "forge-std/console.sol"; + +import { DeploymentUtils } from "../../utils/DeploymentUtils.sol"; +import { DonationBox } from "../../../contracts/chain-adapters/DonationBox.sol"; +import { SponsoredCCTPDstPeriphery } from "../../../contracts/periphery/mintburn/sponsored-cctp/SponsoredCCTPDstPeriphery.sol"; + +// Deploy: forge script script/mintburn/cctp/114DeploySponsoredCCTPDstPeriphery.sol:DeploySponsoredCCTPDstPeriphery --rpc-url -vvvv +contract DeploySponsoredCCTPDstPeriphery is DeploymentUtils { + function run() external { + console.log("Deploying SponsoredCCTPDstPeriphery..."); + console.log("Chain ID:", block.chainid); + + string memory deployerMnemonic = vm.envString("MNEMONIC"); + uint256 deployerPrivateKey = vm.deriveKey(deployerMnemonic, 0); + address deployer = vm.addr(deployerPrivateKey); + + _loadConfig("./script/mintburn/cctp/config.toml", true); + + address cctpMessageTransmitter = config.get("cctpMessageTransmitter").toAddress(); + + vm.startBroadcast(deployerPrivateKey); + + DonationBox donationBox = new DonationBox(); + console.log("DonationBox deployed to:", address(donationBox)); + + // USDC on HyperEVM + address baseToken = config.get("baseToken").toAddress(); + address multicallHandler = config.get("multicallHandler").toAddress(); + + // TODO: use create2 for final deployment + SponsoredCCTPDstPeriphery sponsoredCCTPDstPeriphery = new SponsoredCCTPDstPeriphery( + cctpMessageTransmitter, + deployer, + address(donationBox), + baseToken, + multicallHandler + ); + + console.log("SponsoredCCTPDstPeriphery deployed to:", address(sponsoredCCTPDstPeriphery)); + + donationBox.transferOwnership(address(sponsoredCCTPDstPeriphery)); + + console.log("DonationBox ownership transferred to:", address(sponsoredCCTPDstPeriphery)); + + vm.stopBroadcast(); + } +} diff --git a/script/mintburn/cctp/config.toml b/script/mintburn/cctp/config.toml new file mode 100644 index 000000000..c1ffd4da8 --- /dev/null +++ b/script/mintburn/cctp/config.toml @@ -0,0 +1,70 @@ +[base] +endpoint_url = "${NODE_URL_8453}" + +[base.address] +cctpTokenMessenger = "0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d" +sponsoredCCTPSrcPeriphery = "0xA7A8d1efC1EE3E69999D370380949092251a5c20" +usdc = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913" + +[base.uint] +cctpDomainId = 6 + +[arbitrum] +endpoint_url = "${NODE_URL_42161}" + +[arbitrum.address] +cctpTokenMessenger = "0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d" +sponsoredCCTPSrcPeriphery = "0xce1FFE01eBB4f8521C12e74363A396ee3d337E1B" +usdc = "0xaf88d065e77c8cC2239327C5EDb3A432268e5831" + +[arbitrum.uint] +cctpDomainId = 3 + +# Arbitrum Sepolia +[arbitrum-sepolia] +endpoint_url = "${NODE_URL_421614}" + +[arbitrum-sepolia.address] +cctpTokenMessenger = "0x8FE6B999Dc680CcFDD5Bf7EB0974218be2542DAA" + +[arbitrum-sepolia.uint] +cctpDomainId = 3 + + +# HyperEVM Mainnet +[999] +endpoint_url = "${NODE_URL_999}" + +[999.address] +cctpMessageTransmitter = "0x81D40F21F12A8F0E3252Bccb954D722d4c464B64" +# USDC +baseToken = "0xb88339CB7199b77E23DB6E890353E22632Ba630f" +multicallHandler = "0x5E7840E06fAcCb6d1c3b5F5E0d1d3d07F2829bba" + +[999.uint] +cctpDomainId = 19 +coreIndex = 0 +accountActivationFeeCore = 100000000 +bridgeSafetyBufferCore = 100000000 + +[999.bool] +canBeUsedForAccountActivation = true + +# HyperEVM testnet +[998] +endpoint_url = "${NODE_URL_998}" + +[998.address] +cctpTokenMessenger = "0x8FE6B999Dc680CcFDD5Bf7EB0974218be2542DAA" +cctpMessageTransmitter = "0xE737e5cEBEEBa77EFE34D4aa090756590b1CE275" +# USDC +baseToken = "0x2B3370eE501B4a559b57D449569354196457D8Ab" + +[998.uint] +cctpDomainId = 19 +coreIndex = 0 +accountActivationFeeCore = 100000000 +bridgeSafetyBufferCore = 100000000 + +[998.bool] +canBeUsedForAccountActivation = true \ No newline at end of file diff --git a/script/mintburn/cctp/createSponsoredDeposit.sol b/script/mintburn/cctp/createSponsoredDeposit.sol new file mode 100644 index 000000000..73c9052aa --- /dev/null +++ b/script/mintburn/cctp/createSponsoredDeposit.sol @@ -0,0 +1,161 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.0; + +import { console } from "forge-std/console.sol"; +import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import { DeploymentUtils } from "../../utils/DeploymentUtils.sol"; +import { SponsoredCCTPSrcPeriphery } from "../../../contracts/periphery/mintburn/sponsored-cctp/SponsoredCCTPSrcPeriphery.sol"; +import { ArbitraryEVMFlowExecutor } from "../../../contracts/periphery/mintburn/ArbitraryEVMFlowExecutor.sol"; +import { SponsoredCCTPInterface } from "../../../contracts/interfaces/SponsoredCCTPInterface.sol"; +import { AddressToBytes32 } from "../../../contracts/libraries/AddressConverters.sol"; + +interface IHyperSwapRouter { + struct ExactInputSingleParams { + address tokenIn; + address tokenOut; + uint24 fee; + address recipient; + uint256 amountIn; + uint256 amountOutMinimum; + uint160 sqrtPriceLimitX96; + } + + function exactInputSingle(ExactInputSingleParams calldata params) external payable returns (uint256 amountOut); + + function multicall(uint256 deadline, bytes[] calldata data) external payable returns (bytes[] memory results); +} + +// Run: forge script script/mintburn/cctp/createSponsoredDeposit.sol:CreateSponsoredDeposit --rpc-url -vvvv +contract CreateSponsoredDeposit is DeploymentUtils { + using AddressToBytes32 for address; + + function run() external { + console.log("Creating sponsored deposit..."); + console.log("Chain ID:", block.chainid); + + string memory deployerMnemonic = vm.envString("MNEMONIC"); + uint256 deployerPrivateKey = vm.deriveKey(deployerMnemonic, 0); + address deployer = vm.addr(deployerPrivateKey); + + _loadConfig("./script/mintburn/cctp/config.toml", true); + + address contractAddress = config.get("sponsoredCCTPSrcPeriphery").toAddress(); + SponsoredCCTPSrcPeriphery sponsoredCCTPSrcPeriphery = SponsoredCCTPSrcPeriphery(contractAddress); + + // Verify that the signer matches the contract's signer + require(sponsoredCCTPSrcPeriphery.signer() == deployer, "quote signer mismatch"); + + ArbitraryEVMFlowExecutor.CompressedCall[] + memory compressedCalls = new ArbitraryEVMFlowExecutor.CompressedCall[](2); + compressedCalls[0] = ArbitraryEVMFlowExecutor.CompressedCall({ + target: address(0xb88339CB7199b77E23DB6E890353E22632Ba630f), + callData: abi.encodeWithSelector( + IERC20.approve.selector, + // HyperSwap Router + address(0x6D99e7f6747AF2cDbB5164b6DD50e40D4fDe1e77), + 99990 + ) + }); + bytes[] memory hyperSwapRouterCalls = new bytes[](1); + hyperSwapRouterCalls[0] = abi.encodeWithSelector( + IHyperSwapRouter.exactInputSingle.selector, + IHyperSwapRouter.ExactInputSingleParams({ + tokenIn: address(0xb88339CB7199b77E23DB6E890353E22632Ba630f), + tokenOut: address(0xB8CE59FC3717ada4C02eaDF9682A9e934F625ebb), + fee: 100, + recipient: address(0x369232198fBBe6b42921A79B2D3ea4430d378c00), + amountIn: 99990, + amountOutMinimum: 0, + sqrtPriceLimitX96: 0 + }) + ); + compressedCalls[1] = ArbitraryEVMFlowExecutor.CompressedCall({ + target: address(0x6D99e7f6747AF2cDbB5164b6DD50e40D4fDe1e77), + callData: abi.encodeWithSelector( + IHyperSwapRouter.multicall.selector, + block.timestamp + 60 * 20, + hyperSwapRouterCalls + ) + }); + bytes memory actionData = abi.encode(compressedCalls); + bytes memory actionDataEmpty = abi.encode(new ArbitraryEVMFlowExecutor.CompressedCall[](0)); + bytes memory emptyActionData = ""; + + // Create the SponsoredCCTPQuote + SponsoredCCTPInterface.SponsoredCCTPQuote memory quote = SponsoredCCTPInterface.SponsoredCCTPQuote({ + sourceDomain: config.get("cctpDomainId").toUint32(), // Arbitrum CCTP domain + destinationDomain: 19, // HyperEVM CCTP domain + mintRecipient: address(0xb63c02e60C05F05975653edC83F876C334E07C6d).toBytes32(), // Destination handler contract + amount: 10000, // 100 USDC (6 decimals) + burnToken: config.get("usdc").toAddress().toBytes32(), // USDC on Arbitrum + destinationCaller: address(0xb63c02e60C05F05975653edC83F876C334E07C6d).toBytes32(), // Destination handler contract + maxFee: 1, // 0 max fee + minFinalityThreshold: 1000, // Minimum finality threshold + nonce: keccak256(abi.encodePacked(block.timestamp, deployer, vm.getNonce(deployer))), // Generate nonce + deadline: block.timestamp + 10800, // 3 hours from now + maxBpsToSponsor: 400, // 4% max sponsorship (400 basis points) + maxUserSlippageBps: 0, // 4% max user slippage (400 basis points) + finalRecipient: address(0x9A8f92a830A5cB89a3816e3D267CB7791c16b04D).toBytes32(), // Final recipient + finalToken: address(0xb88339CB7199b77E23DB6E890353E22632Ba630f).toBytes32(), // USDC on HyperEVM + executionMode: uint8(SponsoredCCTPInterface.ExecutionMode.DirectToCore), // DirectToCore mode + actionData: emptyActionData // Empty for DirectToCore mode + }); + + console.log("SponsoredCCTPQuote created:"); + console.log(" sourceDomain:", quote.sourceDomain); + console.log(" destinationDomain:", quote.destinationDomain); + console.log(" amount:", quote.amount); + console.log(" nonce:"); + console.logBytes32(quote.nonce); + console.log(" deadline:", quote.deadline); + console.logBytes(quote.actionData); + + // Create signature hash (same logic as TypeScript) + bytes32 hash1 = keccak256( + abi.encode( + quote.sourceDomain, + quote.destinationDomain, + quote.mintRecipient, + quote.amount, + quote.burnToken, + quote.destinationCaller, + quote.maxFee, + quote.minFinalityThreshold + ) + ); + + bytes32 hash2 = keccak256( + abi.encode( + quote.nonce, + quote.deadline, + quote.maxBpsToSponsor, + quote.maxUserSlippageBps, + quote.finalRecipient, + quote.finalToken, + quote.executionMode, + keccak256(quote.actionData) + ) + ); + + bytes32 typedDataHash = keccak256(abi.encode(hash1, hash2)); + console.log("Signature Hash:"); + console.logBytes32(typedDataHash); + + // Sign the hash using the deployer's private key + (uint8 v, bytes32 r, bytes32 s) = vm.sign(deployerPrivateKey, typedDataHash); + bytes memory signature = abi.encodePacked(r, s, v); + + console.log("Signature created"); + console.log("Signer:", deployer); + + // Call depositForBurn + vm.startBroadcast(deployerPrivateKey); + + console.log("Calling depositForBurn..."); + sponsoredCCTPSrcPeriphery.depositForBurn(quote, signature); + + console.log("Transaction completed successfully!"); + + vm.stopBroadcast(); + } +} diff --git a/script/mintburn/cctp/setUpTokens.sol b/script/mintburn/cctp/setUpTokens.sol new file mode 100644 index 000000000..696785304 --- /dev/null +++ b/script/mintburn/cctp/setUpTokens.sol @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.0; + +import { console } from "forge-std/console.sol"; +import { Script } from "forge-std/Script.sol"; +import { Config } from "forge-std/Config.sol"; + +import { SponsoredCCTPDstPeriphery } from "../../../contracts/periphery/mintburn/sponsored-cctp/SponsoredCCTPDstPeriphery.sol"; +import { HyperCoreFlowExecutor } from "../../../contracts/periphery/mintburn/HyperCoreFlowExecutor.sol"; + +contract setUpTokens is Script, Config { + function run() external { + console.log("Setting up tokens..."); + + string memory deployerMnemonic = vm.envString("MNEMONIC"); + uint256 deployerPrivateKey = vm.deriveKey(deployerMnemonic, 0); + address deployer = vm.addr(deployerPrivateKey); + + _loadConfig("./script/mintburn/cctp/config.toml", true); + + address baseToken = config.get("baseToken").toAddress(); + uint32 coreIndex = config.get("coreIndex").toUint32(); + bool canBeUsedForAccountActivation = config.get("canBeUsedForAccountActivation").toBool(); + uint64 accountActivationFeeCore = config.get("accountActivationFeeCore").toUint64(); + uint64 bridgeSafetyBufferCore = config.get("bridgeSafetyBufferCore").toUint64(); + + // DonationBox@0x6f1Cd5f317a7228269EaB2b496313862de712CCb + // SponsoredCCTPDstPeriphery@0x06C61D54958a0772Ee8aF41789466d39FfeaeB13 + // HyperCoreFlowExecutor@0x82C8aB69e358F354eCb7Ff35239Cd326DeFf2072 + HyperCoreFlowExecutor dstPeriphery = HyperCoreFlowExecutor(payable(0xF962E0e485A5B9f8aDa9a438cEecc35c0020B6e7)); + + vm.startBroadcast(deployerPrivateKey); + console.log( + "Checking if sender has DEFAULT_ADMIN_ROLE:", + dstPeriphery.hasRole(dstPeriphery.DEFAULT_ADMIN_ROLE(), deployer) + ); + // dstPeriphery.setCoreTokenInfo( + // baseToken, + // coreIndex, + // canBeUsedForAccountActivation, + // accountActivationFeeCore, + // bridgeSafetyBufferCore + // ); + + console.log("Core token info set for:", baseToken); + console.log("Core index:", coreIndex); + console.log("Can be used for account activation:", canBeUsedForAccountActivation); + console.log("Account activation fee core:", accountActivationFeeCore); + console.log("Bridge safety buffer core:", bridgeSafetyBufferCore); + + vm.stopBroadcast(); + } +} diff --git a/script/mintburn/hypercore-tokens.json b/script/mintburn/hypercore-tokens.json new file mode 100644 index 000000000..5832fc3d9 --- /dev/null +++ b/script/mintburn/hypercore-tokens.json @@ -0,0 +1,38 @@ +{ + "usdc": { + "name": "USDC", + "index": 0, + "tokenId": "0x6d1e7cde53ba9467b783cb7c530ce054", + "szDecimals": 8, + "weiDecimals": 8, + "isCanonical": true, + "evmAddress": null, + "canBeUsedForAccountActivation": true, + "accountActivationFeeCore": 100000000, + "bridgeSafetyBufferCore": 100000000000000000 + }, + "usdt0": { + "name": "USDT0", + "index": 268, + "tokenId": "0x25faedc3f054130dbb4e4203aca63567", + "szDecimals": 2, + "weiDecimals": 8, + "isCanonical": false, + "evmAddress": "0xb8ce59fc3717ada4c02eadf9682a9e934f625ebb", + "canBeUsedForAccountActivation": true, + "accountActivationFeeCore": 100000000, + "bridgeSafetyBufferCore": 100000000000000000 + }, + "usdh": { + "name": "USDH", + "index": 360, + "tokenId": "0x54e00a5988577cb0b0c9ab0cb6ef7f4b", + "szDecimals": 2, + "weiDecimals": 8, + "isCanonical": false, + "evmAddress": "0x111111a1a0667d36bd57c0a9f569b98057111111", + "canBeUsedForAccountActivation": true, + "accountActivationFeeCore": 100000000, + "bridgeSafetyBufferCore": 100000000000000000 + } +} diff --git a/script/mintburn/oft/CreateSponsoredDeposit.s.sol b/script/mintburn/oft/CreateSponsoredDeposit.s.sol new file mode 100644 index 000000000..73c848e96 --- /dev/null +++ b/script/mintburn/oft/CreateSponsoredDeposit.s.sol @@ -0,0 +1,289 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.0; + +import { Script } from "forge-std/Script.sol"; +import { Config } from "forge-std/Config.sol"; +import { Vm } from "forge-std/Vm.sol"; +import { SponsoredOFTSrcPeriphery } from "../../../contracts/periphery/mintburn/sponsored-oft/SponsoredOFTSrcPeriphery.sol"; +import { Quote, SignedQuoteParams, UnsignedQuoteParams } from "../../../contracts/periphery/mintburn/sponsored-oft/Structs.sol"; +import { AddressToBytes32 } from "../../../contracts/libraries/AddressConverters.sol"; +import { ComposeMsgCodec } from "../../../contracts/periphery/mintburn/sponsored-oft/ComposeMsgCodec.sol"; +import { MinimalLZOptions } from "../../../contracts/external/libraries/MinimalLZOptions.sol"; +import { IOFT, SendParam, MessagingFee, IOAppCore } from "../../../contracts/interfaces/IOFT.sol"; +import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; + +/// @notice Used in place of // import { QuoteSignLib } from "../contracts/periphery/mintburn/sponsored-oft/QuoteSignLib.sol"; +/// just for the hashing function that works with a memory funciton argument +library DebugQuoteSignLib { + /// @notice Compute the keccak of all `SignedQuoteParams` fields. Accept memory arg + function hashMemory(SignedQuoteParams memory p) internal pure returns (bytes32) { + return + keccak256( + abi.encode( + p.srcEid, + p.dstEid, + p.destinationHandler, + p.amountLD, + p.nonce, + p.deadline, + p.maxBpsToSponsor, + p.finalRecipient, + p.finalToken, + p.lzReceiveGasLimit, + p.lzComposeGasLimit, + p.executionMode, + keccak256(p.actionData) // Hash the actionData to keep signature size reasonable + ) + ); + } + + /// @notice Sign the quote using Foundry's Vm cheatcode and return concatenated bytes signature (r,s,v). + function signMemory(Vm vm, uint256 pk, SignedQuoteParams memory p) internal pure returns (bytes memory) { + bytes32 digest = hashMemory(p); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(pk, digest); + return abi.encodePacked(r, s, v); + } +} + +/* +Examples: + +- Simple transfer (no swap), sponsored (e.g. 1%): + forge script script/mintburn/oft/CreateSponsoredDeposit.s.sol:CreateSponsoredDeposit \ + --sig "run(string,uint256,uint256)" usdt0 1000000 100 --rpc-url arbitrum -vvvv + +- Simple transfer (no swap), non-sponsored: + forge script script/mintburn/oft/CreateSponsoredDeposit.s.sol:CreateSponsoredDeposit \ + --sig "run(string,uint256,uint256)" usdt0 1000000 0 --rpc-url arbitrum -vvvv + +- Simple transfer (no swap) with explicit recipient, sponsored: + forge script script/mintburn/oft/CreateSponsoredDeposit.s.sol:CreateSponsoredDeposit \ + --sig "run(string,uint256,address,uint256)" usdt0 1000000 0xRecipient 100 --rpc-url arbitrum -vvvv + +- Simple transfer (no swap) with explicit recipient, non-sponsored: + forge script script/mintburn/oft/CreateSponsoredDeposit.s.sol:CreateSponsoredDeposit \ + --sig "run(string,uint256,address,uint256)" usdt0 1000000 0xRecipient 0 --rpc-url arbitrum -vvvv + +- Swap flow (finalToken specified), sponsored (e.g. 1%): + forge script script/mintburn/oft/CreateSponsoredDeposit.s.sol:CreateSponsoredDeposit \ + --sig "run(string,uint256,uint256,address)" usdt0 1000000 100 0xFinalToken --rpc-url arbitrum -vvvv + +- Swap flow (finalToken specified), non-sponsored: + forge script script/mintburn/oft/CreateSponsoredDeposit.s.sol:CreateSponsoredDeposit \ + --sig "run(string,uint256,uint256,address)" usdt0 1000000 0 0xFinalToken --rpc-url arbitrum -vvvv + +- Swap flow with explicit recipient, sponsored: + forge script script/mintburn/oft/CreateSponsoredDeposit.s.sol:CreateSponsoredDeposit \ + --sig "run(string,uint256,address,uint256,address)" usdt0 1000000 0xRecipient 100 0xFinalToken --rpc-url arbitrum -vvvv +*/ +contract CreateSponsoredDeposit is Script, Config { + using AddressToBytes32 for address; + using SafeERC20 for IERC20; + using MinimalLZOptions for bytes; + + struct DepositEnv { + address srcPeriphery; + address token; + address destinationHandler; + address dstToken; + uint32 srcEid; + uint32 dstEid; + } + + function run() external pure { + revert("see header for supported run signatures"); + } + + function run(string memory) external pure { + revert("see header for supported run signatures"); + } + + /// @notice Simple transfer entrypoint: finalToken defaults to the input token from config, recipient defaults to signer. + function run(string memory tokenName, uint256 amountLD, uint256 maxBpsToSponsor) external { + require(bytes(tokenName).length != 0, "token key required"); + string memory configPath = string(abi.encodePacked("./script/mintburn/oft/", tokenName, ".toml")); + _loadConfigAndForks(configPath, false); + DepositEnv memory env = _resolveEnv(); + + string memory mnemonic = vm.envString("MNEMONIC"); + uint256 pk = vm.deriveKey(mnemonic, 0); + address deployer = vm.addr(pk); + + address recipient = deployer; + address finalToken = env.dstToken; // default to destination token: simple transfer + _execute(env, pk, deployer, amountLD, recipient, maxBpsToSponsor, finalToken); + } + + /// @notice Simple transfer entrypoint with explicit recipient (finalToken defaults to the input token). + function run(string memory tokenName, uint256 amountLD, address finalRecipient, uint256 maxBpsToSponsor) external { + require(bytes(tokenName).length != 0, "token key required"); + string memory configPath = string(abi.encodePacked("./script/mintburn/oft/", tokenName, ".toml")); + _loadConfigAndForks(configPath, false); + DepositEnv memory env = _resolveEnv(); + + string memory mnemonic = vm.envString("MNEMONIC"); + uint256 pk = vm.deriveKey(mnemonic, 0); + address deployer = vm.addr(pk); + + address recipient = finalRecipient == address(0) ? deployer : finalRecipient; + address finalToken = env.dstToken; // simple transfer uses destination token + _execute(env, pk, deployer, amountLD, recipient, maxBpsToSponsor, finalToken); + } + + /// @notice Run with default finalRecipient = signer, and custom sponsorship and finalToken (swap) configuration. + function run(string memory tokenName, uint256 amountLD, uint256 maxBpsToSponsor, address finalToken) external { + require(bytes(tokenName).length != 0, "token key required"); + string memory configPath = string(abi.encodePacked("./script/mintburn/oft/", tokenName, ".toml")); + _loadConfigAndForks(configPath, false); + DepositEnv memory env = _resolveEnv(); + + string memory mnemonic = vm.envString("MNEMONIC"); + uint256 pk = vm.deriveKey(mnemonic, 0); + address deployer = vm.addr(pk); + + address recipient = deployer; // default to signer address + _execute(env, pk, deployer, amountLD, recipient, maxBpsToSponsor, finalToken); + } + + /// @notice Run with an explicit finalRecipient, and custom sponsorship and finalToken (swap) configuration. + function run( + string memory tokenName, + uint256 amountLD, + address finalRecipient, + uint256 maxBpsToSponsor, + address finalToken + ) external { + require(bytes(tokenName).length != 0, "token key required"); + string memory configPath = string(abi.encodePacked("./script/mintburn/oft/", tokenName, ".toml")); + _loadConfigAndForks(configPath, false); + DepositEnv memory env = _resolveEnv(); + + string memory mnemonic = vm.envString("MNEMONIC"); + uint256 pk = vm.deriveKey(mnemonic, 0); + address deployer = vm.addr(pk); + + address recipient = finalRecipient == address(0) ? deployer : finalRecipient; + _execute(env, pk, deployer, amountLD, recipient, maxBpsToSponsor, finalToken); + } + + function _execute( + DepositEnv memory env, + uint256 deployerPrivateKey, + address deployer, + uint256 amountLD, + address finalRecipient, + uint256 maxBpsToSponsor, + address finalToken + ) private { + SponsoredOFTSrcPeriphery srcPeripheryContract = SponsoredOFTSrcPeriphery(env.srcPeriphery); + require(srcPeripheryContract.signer() == deployer, "quote signer mismatch"); + + bytes32 nonce = bytes32(uint256(block.timestamp)); + uint256 deadline = block.timestamp + 1 hours; + uint256 maxUserSlippageBps = 200; // 2% + uint256 lzReceiveGasLimit = 200_000; + uint256 lzComposeGasLimit = 300_000; + address refundRecipient = deployer; + + SignedQuoteParams memory signedParams = SignedQuoteParams({ + srcEid: env.srcEid, + dstEid: env.dstEid, + destinationHandler: env.destinationHandler.toBytes32(), + amountLD: amountLD, + nonce: nonce, + deadline: deadline, + maxBpsToSponsor: maxBpsToSponsor, + finalRecipient: finalRecipient.toBytes32(), + finalToken: finalToken.toBytes32(), + lzReceiveGasLimit: lzReceiveGasLimit, + lzComposeGasLimit: lzComposeGasLimit, + executionMode: 0, + actionData: "" + }); + + UnsignedQuoteParams memory unsignedParams = UnsignedQuoteParams({ + refundRecipient: refundRecipient, + maxUserSlippageBps: maxUserSlippageBps + }); + + Quote memory quote = Quote({ signedParams: signedParams, unsignedParams: unsignedParams }); + + bytes memory signature = DebugQuoteSignLib.signMemory(vm, deployerPrivateKey, signedParams); + + MessagingFee memory fee = _quoteMessagingFee(srcPeripheryContract, quote); + + vm.startBroadcast(deployerPrivateKey); + IERC20(env.token).forceApprove(address(srcPeripheryContract), amountLD); + srcPeripheryContract.deposit{ value: fee.nativeFee }(quote, signature); + vm.stopBroadcast(); + } + + function _resolveEnv() internal returns (DepositEnv memory env) { + uint256 srcChainId = block.chainid; + uint256 srcForkId = forkOf[srcChainId]; + require(srcForkId != 0, "src chain not in config"); + vm.selectFork(srcForkId); + + env.srcPeriphery = config.get("src_periphery").toAddress(); + env.token = config.get("token").toAddress(); + require(env.srcPeriphery != address(0) && env.token != address(0), "missing src config"); + + // Resolve srcEid from the messenger endpoint + address srcMessenger = SponsoredOFTSrcPeriphery(env.srcPeriphery).OFT_MESSENGER(); + env.srcEid = IOAppCore(srcMessenger).endpoint().eid(); + + // Always use HyperEVM as destination for local testing + uint256 dstChainId = 999; + uint256 dstForkId = forkOf[dstChainId]; + require(dstForkId != 0, "dst chain not in config"); + vm.selectFork(dstForkId); + + env.destinationHandler = config.get("dst_handler").toAddress(); + env.dstToken = config.get("token").toAddress(); + address dstMessenger = config.get("oft_messenger").toAddress(); + require( + env.destinationHandler != address(0) && env.dstToken != address(0) && dstMessenger != address(0), + "missing dst config" + ); + env.dstEid = IOAppCore(dstMessenger).endpoint().eid(); + + // Switch back to source fork for execution + vm.selectFork(srcForkId); + } + + function _quoteMessagingFee( + SponsoredOFTSrcPeriphery srcPeripheryContract, + Quote memory quote + ) internal view returns (MessagingFee memory) { + address oftMessenger = srcPeripheryContract.OFT_MESSENGER(); + + bytes memory composeMsg = ComposeMsgCodec._encode( + quote.signedParams.nonce, + quote.signedParams.deadline, + quote.signedParams.maxBpsToSponsor, + quote.unsignedParams.maxUserSlippageBps, + quote.signedParams.finalRecipient, + quote.signedParams.finalToken, + quote.signedParams.executionMode, + quote.signedParams.actionData + ); + + bytes memory extraOptions = MinimalLZOptions + .newOptions() + .addExecutorLzReceiveOption(uint128(quote.signedParams.lzReceiveGasLimit), uint128(0)) + .addExecutorLzComposeOption(uint16(0), uint128(quote.signedParams.lzComposeGasLimit), uint128(0)); + + SendParam memory sendParam = SendParam({ + dstEid: quote.signedParams.dstEid, + to: quote.signedParams.destinationHandler, + amountLD: quote.signedParams.amountLD, + minAmountLD: quote.signedParams.amountLD, + extraOptions: extraOptions, + composeMsg: composeMsg, + oftCmd: srcPeripheryContract.EMPTY_OFT_COMMAND() + }); + + return IOFT(oftMessenger).quoteSend(sendParam, false); + } +} diff --git a/script/mintburn/oft/DeployDstHandler.s.sol b/script/mintburn/oft/DeployDstHandler.s.sol new file mode 100644 index 000000000..7c1cdd38a --- /dev/null +++ b/script/mintburn/oft/DeployDstHandler.s.sol @@ -0,0 +1,83 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.0; + +import { Script } from "forge-std/Script.sol"; +import { Test } from "forge-std/Test.sol"; +import { console } from "forge-std/console.sol"; + +import { DonationBox } from "../../../contracts/chain-adapters/DonationBox.sol"; +import { DeploymentUtils } from "../../utils/DeploymentUtils.sol"; +import { DstOFTHandler } from "../../../contracts/periphery/mintburn/sponsored-oft/DstOFTHandler.sol"; +import { DstHandlerConfigLib } from "./DstHandlerConfigLib.s.sol"; +import { IOAppCore } from "../../../contracts/interfaces/IOFT.sol"; +import { PermissionedMulticallHandler } from "../../../contracts/handlers/PermissionedMulticallHandler.sol"; + +/* +forge script script/mintburn/oft/DeployDstHandler.s.sol:DeployDstOFTHandler \ + --sig "run(string)" usdt0 \ + --rpc-url hyperevm -vvvv --broadcast --verify + */ +contract DeployDstOFTHandler is Script, Test, DeploymentUtils, DstHandlerConfigLib { + function run(string memory tokenName) external { + console.log("Deploying DstOFTHandler..."); + console.log("Chain ID:", block.chainid); + + _loadTokenConfig(tokenName); + + // Ensure we deploy on the configured destination fork so subsequent configuration + // operates on the same fork where the contract exists. + uint256 dstForkId = forkOf[block.chainid]; + vm.selectFork(dstForkId); + + string memory deployerMnemonic = vm.envString("MNEMONIC"); + uint256 deployerPrivateKey = vm.deriveKey(deployerMnemonic, 0); + address deployer = vm.addr(deployerPrivateKey); + + address ioft = config.get("oft_messenger").toAddress(); + address baseToken = config.get("token").toAddress(); + // address multicallHandler = _getOptionalAddress("multicall_handler"); + address oftEndpoint = address(IOAppCore(ioft).endpoint()); + require(oftEndpoint != address(0) && ioft != address(0) && baseToken != address(0), "config missing"); + + vm.startBroadcast(deployerPrivateKey); + + DonationBox donationBox = new DonationBox(); + // if (multicallHandler == address(0)) { + PermissionedMulticallHandler multicallHandler = new PermissionedMulticallHandler(deployer); + // } + DstOFTHandler dstOFTHandler = new DstOFTHandler( + oftEndpoint, + ioft, + address(donationBox), + baseToken, + address(multicallHandler) + ); + donationBox.transferOwnership(address(dstOFTHandler)); + // TODO: this won't work for multisig-owned `multicallHandler`s + multicallHandler.grantRole(multicallHandler.WHITELISTED_CALLER_ROLE(), address(dstOFTHandler)); + + console.log("DstOFTHandler deployed to:", address(dstOFTHandler)); + + vm.stopBroadcast(); + + // Persist the deployment address under this chain in TOML + config.set("dst_handler", address(dstOFTHandler)); + + // TODO: right, Foundry can't work with precompiles at all :( + // _configureCoreTokenInfo(tokenName, address(dstOFTHandler)); + _configureAuthorizedPeripheries(address(dstOFTHandler), deployerPrivateKey); + } + + /// @notice Returns a default zero address if not present + function _getOptionalAddress(string memory key) internal returns (address addr) { + try this.getAddress(key) returns (address value) { + addr = value; + } catch { + console.log("Optional not found: ", key); + } + } + + function getAddress(string memory key) external returns (address) { + return config.get(key).toAddress(); + } +} diff --git a/script/mintburn/oft/DeploySrcPeriphery.s.sol b/script/mintburn/oft/DeploySrcPeriphery.s.sol new file mode 100644 index 000000000..0d91eb084 --- /dev/null +++ b/script/mintburn/oft/DeploySrcPeriphery.s.sol @@ -0,0 +1,157 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.0; + +import { Script } from "forge-std/Script.sol"; +import { Test } from "forge-std/Test.sol"; +import { console } from "forge-std/console.sol"; + +import { DeploymentUtils } from "./../../utils/DeploymentUtils.sol"; +import { SponsoredOFTSrcPeriphery } from "../../../contracts/periphery/mintburn/sponsored-oft/SponsoredOFTSrcPeriphery.sol"; +import { IOAppCore } from "../../../contracts/interfaces/IOFT.sol"; + +/* +Example usage commands: + +# Deploy using token key (loads ./script/mintburn/oft/.toml) +forge script script/mintburn/oft/DeploySrcPeriphery.s.sol:DepoySrcOFTPeriphery \ + --sig "run(string,address)" usdt0 0xSigner \ + --rpc-url arbitrum --broadcast --verify -vvvv + +# Deploy using token key with explicit final owner +forge script script/mintburn/oft/DeploySrcPeriphery.s.sol:DepoySrcOFTPeriphery \ + --sig "run(string,address,address)" usdt0 0xSigner 0xFinalOwner \ + --rpc-url arbitrum --broadcast --verify -vvvv + +Note that both of these will update: +- the config + deployments file: ./script/mintburn/oft/.toml +- the `broadcast/` folder with the latest_run params (used by the precommit hooks to populate some generated artifacts file) + +*/ + +contract DepoySrcOFTPeriphery is Script, Test, DeploymentUtils { + enum OwnershipInstruction { + KeepDeployer, + Transfer, + Renounce + } + + struct OwnershipConfig { + bool useDefaultOwner; + address finalOwner; + } + + /// Deploy by token name key. Builds path: ./script/mintburn/oft/.toml + /// Final owner assumed to be the deployer. + function run(string memory tokenName, address signer) external { + require(bytes(tokenName).length != 0, "token key required"); + string memory configPath = string(abi.encodePacked("./script/mintburn/oft/", tokenName, ".toml")); + OwnershipConfig memory ownershipConfig = OwnershipConfig({ useDefaultOwner: true, finalOwner: address(0) }); + _deployFromConfig(configPath, signer, ownershipConfig); + } + + /// Deploy by token name key with explicit final owner. + function run(string memory tokenName, address signer, address finalOwner) external { + require(bytes(tokenName).length != 0, "token key required"); + string memory configPath = string(abi.encodePacked("./script/mintburn/oft/", tokenName, ".toml")); + OwnershipConfig memory ownershipConfig = OwnershipConfig({ useDefaultOwner: false, finalOwner: finalOwner }); + _deployFromConfig(configPath, signer, ownershipConfig); + } + + function _deploy( + address token, + address oftMessenger, + address signer, + OwnershipConfig memory ownershipConfig + ) internal returns (SponsoredOFTSrcPeriphery srcOftPeriphery) { + console.log("Deploying SponsoredOFTSrcPeriphery..."); + console.log("Chain ID:", block.chainid); + + string memory deployerMnemonic = vm.envString("MNEMONIC"); + uint256 deployerPrivateKey = vm.deriveKey(deployerMnemonic, 0); + address deployer = vm.addr(deployerPrivateKey); + + require(token != address(0), "Token address cannot be zero"); + require(oftMessenger != address(0), "OFT messenger cannot be zero"); + require(signer != address(0), "Signer cannot be zero"); + + uint32 srcEid = IOAppCore(oftMessenger).endpoint().eid(); + + (OwnershipInstruction ownershipInstruction, address resolvedFinalOwner) = _resolveOwnership( + deployer, + ownershipConfig + ); + + console.log("Token:", token); + console.log("OFT messenger:", oftMessenger); + console.log("Source EID:", uint256(srcEid)); + console.log("Signer:", signer); + console.log("Deployer:", deployer); + + if (ownershipInstruction == OwnershipInstruction.Transfer) { + console.log("Final owner (post-transfer):", resolvedFinalOwner); + } else if (ownershipInstruction == OwnershipInstruction.Renounce) { + console.log("Final owner (post-transfer): "); + } else { + console.log("Final owner (post-transfer): deployer"); + } + + vm.startBroadcast(deployerPrivateKey); + + srcOftPeriphery = new SponsoredOFTSrcPeriphery(token, oftMessenger, srcEid, signer); + + console.log("SponsoredOFTSrcPeriphery deployed to:", address(srcOftPeriphery)); + + if (ownershipInstruction == OwnershipInstruction.Transfer) { + srcOftPeriphery.transferOwnership(resolvedFinalOwner); + console.log("Ownership transferred to:", resolvedFinalOwner); + } else if (ownershipInstruction == OwnershipInstruction.Renounce) { + srcOftPeriphery.renounceOwnership(); + console.log("Ownership renounced to address(0)"); + } else { + console.log("Ownership retained by deployer"); + } + + vm.stopBroadcast(); + return srcOftPeriphery; + } + + function _deployFromConfig( + string memory configPath, + address signer, + OwnershipConfig memory ownershipConfig + ) internal { + // Load config and enable write-back + _loadConfig(configPath, true); + + address token = config.get("token").toAddress(); + address oftMessenger = config.get("oft_messenger").toAddress(); + + require(token != address(0), "token not set"); + require(oftMessenger != address(0), "oft_messenger not set"); + require(signer != address(0), "signer not set"); + + SponsoredOFTSrcPeriphery srcOftPeriphery = _deploy(token, oftMessenger, signer, ownershipConfig); + + // Persist the deployment address under this chain in TOML + config.set("src_periphery", address(srcOftPeriphery)); + } + + function _resolveOwnership( + address deployer, + OwnershipConfig memory config + ) internal pure returns (OwnershipInstruction instruction, address finalOwner) { + if (config.useDefaultOwner) { + return (OwnershipInstruction.KeepDeployer, deployer); + } + + if (config.finalOwner == address(0)) { + return (OwnershipInstruction.Renounce, address(0)); + } + + if (config.finalOwner == deployer) { + return (OwnershipInstruction.KeepDeployer, deployer); + } + + return (OwnershipInstruction.Transfer, config.finalOwner); + } +} diff --git a/script/mintburn/oft/DstHandlerConfigLib.s.sol b/script/mintburn/oft/DstHandlerConfigLib.s.sol new file mode 100644 index 000000000..932d6fdf4 --- /dev/null +++ b/script/mintburn/oft/DstHandlerConfigLib.s.sol @@ -0,0 +1,98 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.0; + +import { Config } from "forge-std/Config.sol"; +import { console } from "forge-std/console.sol"; +import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; + +import { ReadHCoreTokenInfoUtil } from "../../mintburn/ReadHCoreTokenInfoUtil.s.sol"; +import { DstOFTHandler } from "../../../contracts/periphery/mintburn/sponsored-oft/DstOFTHandler.sol"; +import { HyperCoreFlowExecutor } from "../../../contracts/periphery/mintburn/HyperCoreFlowExecutor.sol"; +import { IOAppCore, IEndpoint } from "../../../contracts/interfaces/IOFT.sol"; +import { AddressToBytes32 } from "../../../contracts/libraries/AddressConverters.sol"; + +/// @notice Shared helper for configuring `DstOFTHandler` instances using TOML and JSON metadata. +abstract contract DstHandlerConfigLib is Config { + using AddressToBytes32 for address; + + function _loadTokenConfig(string memory tokenKey) internal { + require(bytes(tokenKey).length != 0, "token key required"); + string memory configPath = string(abi.encodePacked("./script/mintburn/oft/", tokenKey, ".toml")); + _loadConfigAndForks(configPath, true); + } + + function _configureCoreTokenInfo(string memory tokenName, address dstHandlerAddress) internal { + require(dstHandlerAddress != address(0), "dst handler not set"); + + ReadHCoreTokenInfoUtil reader = new ReadHCoreTokenInfoUtil(); + ReadHCoreTokenInfoUtil.TokenJson memory info = reader.readToken(tokenName); + address tokenAddr = reader.resolveEvmAddress(info, block.chainid); + require(tokenAddr != address(0), "token addr missing"); + + console.log("Configuring CoreTokenInfo for", tokenName); + console.log("Dst handler:", dstHandlerAddress); + console.log("Token:", tokenAddr); + + HyperCoreFlowExecutor(dstHandlerAddress).setCoreTokenInfo( + tokenAddr, + uint32(info.index), + info.canBeUsedForAccountActivation, + uint64(info.accountActivationFeeCore), + uint64(info.bridgeSafetyBufferCore) + ); + } + + function _configureAuthorizedPeripheries(address dstHandlerAddress, uint256 configurerPrivateKey) internal { + require(dstHandlerAddress != address(0), "dst handler not set"); + + uint256[] memory chainIdList = config.getChainIds(); + + uint256 dstChainId = block.chainid; + uint256 dstForkId = forkOf[dstChainId]; + require(dstForkId != 0, "dst chain not in config"); + // Ensure active fork is the destination before instantiating the typed contract + vm.selectFork(dstForkId); + DstOFTHandler handler = DstOFTHandler(payable(dstHandlerAddress)); + + for (uint256 i = 0; i < chainIdList.length; i++) { + uint256 srcChainId = chainIdList[i]; + if (srcChainId == dstChainId) { + continue; + } + address srcPeriphery = config.get(srcChainId, "src_periphery").toAddress(); + address oftMessenger = config.get(srcChainId, "oft_messenger").toAddress(); + if (srcPeriphery == address(0) || oftMessenger == address(0)) { + console.log( + "Skipping authorizing periphery for chain", + srcChainId, + "srcPeriphery or oftMessenger not set" + ); + continue; + } + + uint256 srcForkId = forkOf[srcChainId]; + require(srcForkId != 0, "src chain not in config"); + + // Switch to calling src chain contracts to get srcEid + vm.selectFork(srcForkId); + + uint32 srcEid; + try IOAppCore(oftMessenger).endpoint() returns (IEndpoint ep) { + srcEid = ep.eid(); + } catch { + continue; + } + + // Switch to calling dst chain contracts to read dst chain state + update periphery if needed + vm.selectFork(dstForkId); + + bytes32 expected = srcPeriphery.toBytes32(); + if (handler.authorizedSrcPeripheryContracts(uint64(srcEid)) != expected) { + console.log("Authorizing periphery", srcPeriphery, "for srcEid", uint256(srcEid)); + vm.startBroadcast(configurerPrivateKey); + handler.setAuthorizedPeriphery(srcEid, expected); + vm.stopBroadcast(); + } + } + } +} diff --git a/script/mintburn/oft/README.md b/script/mintburn/oft/README.md new file mode 100644 index 000000000..dcebcecad --- /dev/null +++ b/script/mintburn/oft/README.md @@ -0,0 +1,118 @@ +### Deployment process. It's IMPORTANT to perform all steps, otherwise funds WILL get lost + +0. **Deploy all wanted Src periphery contracts** + +See `./DeploySrcPeriphery.s.sol` for commands + +1. **Run deployment script** + +``` +forge script script/mintburn/oft/DeployDstHandler.s.sol:DeployDstOFTHandler \ + --sig "run(string)" usdt0 \ + --rpc-url hyperevm -vvvv --broadcast --verify +``` + +Make sure all the src peripheries are correctly configured (currently addrs for this config are taken from usdt0.toml) + +2. **Configure baseToken** + Example with cast for USDT0: + +``` +cast send $DEPLOYED_DST_OFT "setCoreTokenInfo(address,uint32,bool,uint64,uint64)" 0xB8CE59FC3717ada4C02eaDF9682A9e934F625ebb 268 true 100000000 100000000000000 --rpc-url hyperevm --account dev +``` + +2.(a) Configure additional tokens, like USDC: + +CoreTokenInfo: + +``` +cast send $DEPLOYED_DST_OFT "setCoreTokenInfo(address,uint32,bool,uint64,uint64)" 0xb88339CB7199b77E23DB6E890353E22632Ba630f 0 true 100000000 100000000000000 --rpc-url hyperevm --account dev +``` + +(before adding FinalTokenInfo): + +predict SwapHandler address: + +cast parse-bytes32-address $(cast call $DEPLOYED_DST_OFT "predictSwapHandler(address)" 0xb88339CB7199b77E23DB6E890353E22632Ba630f --rpc-url hyperevm) + +Activate HyperLiquid account for that (see step 3 for an example). + +And FinalTokenInfo: + +``` +cast send $DEPLOYED_DST_OFT "setFinalTokenInfo(address,uint32,bool,uint32,uint32,address)" 0xb88339CB7199b77E23DB6E890353E22632Ba630f 166 false 140 2 0xB8CE59FC3717ada4C02eaDF9682A9e934F625ebb --rpc-url hyperevm --account dev +``` + +3. **Activate $DEPLOYED_DST_OFT account on HyperCore** + +Go to Hyperliquid UI and set 1 wei of USDC to it. Check activation status with: + +``` +curl -s https://api.hyperliquid.xyz/info \ + -H 'content-type: application/json' \ + -d "{\"type\":\"userRole\",\"user\":\"$DEPLOYED_DST_OFT\"}" +``` + +You want to see `{"role":"user"}%` as a response. That means that the account was activated. Otherwise, would say `{"role":"missing"}%` + +4. Transfer ownership to multisig / trusted account / renounce ownership of DstOftHandler. TODO: could be enabled in the script if we could set core token info from there ... Foundry doesn't let us interact with precompiles from scripts (can't skip sim) + +5. Test that the baseToken + core accounts are functioning correctly by calling the `CreateSponsoredDeposit` script and waiting for tokens to show up on the Hyperliquid balance of the dev wallet. + +``` +forge script script/mintburn/oft/CreateSponsoredDeposit.s.sol:CreateSponsoredDeposit \ + --sig "run(string,uint256,uint256)" usdt0 1000000 100 --rpc-url arbitrum -vvvv --broadcast +``` + +### Misc + +Grant `PERMISSIONED_BOT_ROLE`: + +``` +cast send $DEPLOYED_DST_OFT "grantRole(bytes32,address)" 0x5300fde95a5e446527bf6aa7c91bd6661bef5398afc77061d9bc87efb80b7ef6 $ROLE_RECIPIENT --rpc-url hyperevm --account dev +``` + +Open orders for an `ADDR`: + +``` +ADDR=0xe199ce8af95937e3a0c61d437b0cbf2481c81c8c +curl -s https://api.hyperliquid.xyz/info \ + -H 'content-type: application/json' \ + -d "{\"type\":\"openOrders\",\"user\":\"$ADDR\"}" +``` + +Spot balances for an addr: + +``` +curl -s https://api.hyperliquid.xyz/info \ + -H 'content-type: application/json' \ + -d "{\"type\":\"spotClearinghouseState\",\"user\":\"$ADDR\"}" +``` + +Activate user account: + +``` +cast send $DEPLOYED_DST_OFT "activateUserAccount(bytes32,address,address)" 0x00000000000000000000000000000000000000000000000000000000690D6445 0x67C6Cf5288E5D8Bc474126949B3A50Cfe5512AF9 0xb8ce59fc3717ada4c02eadf9682a9e934f625ebb --rpc-url hyperevm --account dev +``` + +Calculations + limit order submission: + +Calc size based on coreIn + price + +``` +forge script script/mintburn/LimitOrderCalcCli.s.sol:LimitOrderCalcCli \ + --sig "calcLOAmounts(uint64,uint64,bool,uint64,uint8,uint8,uint8,uint8)" \ + 1033000000 99970000 false 140 8 2 8 8 +``` + +Submit Limit order: + +``` +cast send $DEPLOYED_DST_OFT "submitLimitOrderFromBot(address,uint64,uint64,uint128)" 0xb88339CB7199b77E23DB6E890353E22632Ba630f 99990000 100000000 1 --rpc-url hyperevm --account dev +``` + +Finalize a swap flow: + +``` +cast send $DEPLOYED_DST_OFT "finalizeSwapFlows(address,bytes32[],uint64[])" 0xb88339CB7199b77E23DB6E890353E22632Ba630f "[0x00000000000000000000000000000000000000000000000000000000690D3DBE]" "[122576511]" --rpc-url hyperevm --account dev +``` diff --git a/script/mintburn/oft/UpdateAuthorizedPeripheries.s.sol b/script/mintburn/oft/UpdateAuthorizedPeripheries.s.sol new file mode 100644 index 000000000..ff406ce67 --- /dev/null +++ b/script/mintburn/oft/UpdateAuthorizedPeripheries.s.sol @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.0; + +import { Script } from "forge-std/Script.sol"; +import { console } from "forge-std/console.sol"; + +import { DstHandlerConfigLib } from "./DstHandlerConfigLib.s.sol"; + +/* +forge script script/mintburn/oft/UpdateAuthorizedPeripheries.s.sol:UpdateAuthorizedPeripheries \ + --sig "run(string)" usdt0 \ + --rpc-url hyperevm --broadcast -vvvv + */ +contract UpdateAuthorizedPeripheries is Script, DstHandlerConfigLib { + function run() external pure { + revert("Missing args. Use run(string tokenKey)"); + } + + function run(string memory tokenKey) external { + require(bytes(tokenKey).length != 0, "args"); + _run(tokenKey); + } + + function _run(string memory tokenKey) internal { + _loadTokenConfig(tokenKey); + + // Resolve deployer + string memory deployerMnemonic = vm.envString("MNEMONIC"); + uint256 deployerPrivateKey = vm.deriveKey(deployerMnemonic, 0); + address deployer = vm.addr(deployerPrivateKey); + + address dstHandlerAddress = config.get("dst_handler").toAddress(); + require(dstHandlerAddress != address(0), "dst_handler not set"); + + console.log("Updating authorized peripheries on DstOFTHandler..."); + console.log("Dst chain:", block.chainid); + console.log("Deployer:", deployer); + console.log("Dst handler:", dstHandlerAddress); + + _configureAuthorizedPeripheries(dstHandlerAddress, deployerPrivateKey); + } +} diff --git a/script/mintburn/oft/usdt0.toml b/script/mintburn/oft/usdt0.toml new file mode 100644 index 000000000..e04d31b1a --- /dev/null +++ b/script/mintburn/oft/usdt0.toml @@ -0,0 +1,20 @@ +[1] +endpoint_url = "${NODE_URL_1}" + +[1.address] +token = "0xdAC17F958D2ee523a2206206994597C13D831ec7" +oft_messenger = "0x6C96dE32CEa08842dcc4058c14d3aaAD7Fa41dee" +src_periphery = "0x4607BceaF7b22cb0c46882FFc9fAB3c6efe66e5a" + +[arbitrum.address] +token = "0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9" +oft_messenger = "0x14E4A1B13bf7F943c8ff7C51fb60FA964A298D92" +src_periphery = "0x2ac5Ee3796E027dA274fbDe84c82173a65868940" + +[999] +endpoint_url = "${NODE_URL_999}" + +[999.address] +token = "0xB8CE59FC3717ada4C02eaDF9682A9e934F625ebb" +oft_messenger = "0x904861a24F30EC96ea7CFC3bE9EA4B476d237e98" +dst_handler = "0x40153DdFAd90C49dbE3F5c9F96f2a5B25ec67461" diff --git a/script/utils/Constants.sol b/script/utils/Constants.sol index 1d4b3551b..1b1d65de4 100644 --- a/script/utils/Constants.sol +++ b/script/utils/Constants.sol @@ -61,26 +61,6 @@ contract Constants is Script { address zkUsdcSharedBridge_324; } - // L2 Address Map - struct L2Addresses { - address l2GatewayRouter; - address fxChild; - address cctpTokenMessenger; - address cctpMessageTransmitter; - address uniswapV3SwapRouter; - address helios; - address zkErc20Bridge; - address zkUSDCBridge; - address lineaMessageService; - address cctpV2TokenMessenger; - address lineaTokenBridge; - address scrollERC20GatewayRouter; - address scrollGasPriceOracle; - address scrollMessenger; - address l2Weth; - address polygonZkEvmBridge; - } - // OP Stack Address Map struct OpStackAddresses { address L1CrossDomainMessenger; @@ -222,20 +202,20 @@ contract Constants is Script { } // Circle domain IDs mapping - function getCircleDomainId(uint256 chainId) public view returns (uint256) { - int256 cctpDomain = _getCctpDomain(chainId); + function getCircleDomainId(uint256 chainId) public view returns (uint32) { + int32 cctpDomain = _getCctpDomain(chainId); if (cctpDomain == -1) { revert("Circle domain ID not found"); } - return uint256(cctpDomain); + return uint32(cctpDomain); } function hasCctpDomain(uint256 chainId) public view returns (bool) { return _getCctpDomain(chainId) != -1; } - function _getCctpDomain(uint256 chainId) internal view returns (int256) { - return vm.parseJsonInt(file, string.concat(".PUBLIC_NETWORKS.", vm.toString(chainId), ".cctpDomain")); + function _getCctpDomain(uint256 chainId) internal view returns (int32) { + return int32(vm.parseJsonInt(file, string.concat(".PUBLIC_NETWORKS.", vm.toString(chainId), ".cctpDomain"))); } function getOftEid(uint256 chainId) public view returns (uint256) { diff --git a/script/utils/DeploymentUtils.sol b/script/utils/DeploymentUtils.sol index 845420608..d66365375 100644 --- a/script/utils/DeploymentUtils.sol +++ b/script/utils/DeploymentUtils.sol @@ -4,6 +4,7 @@ pragma solidity ^0.8.0; import { Script } from "forge-std/Script.sol"; import { Test } from "forge-std/Test.sol"; import { console } from "forge-std/console.sol"; +import { Config } from "forge-std/Config.sol"; import { Upgrades, Core, UnsafeUpgrades } from "@openzeppelin/foundry-upgrades/src/LegacyUpgrades.sol"; import { Options } from "@openzeppelin/foundry-upgrades/src/Options.sol"; import { ERC1967Proxy } from "@openzeppelin/contracts-v4/proxy/ERC1967/ERC1967Proxy.sol"; @@ -15,7 +16,7 @@ import { DeployedAddresses } from "./DeployedAddresses.sol"; * @notice Foundry smart contract script that provides deployment utilities for Across Protocol contracts * @dev This contract implements the equivalent functionality of utils.hre.ts for Foundry scripts */ -contract DeploymentUtils is Script, Test, Constants, DeployedAddresses { +contract DeploymentUtils is Script, Test, Constants, DeployedAddresses, Config { // Struct to hold deployment information struct DeploymentInfo { address hubPool; diff --git a/scripts/hyperliquidDepositHandler.ts b/scripts/hyperliquidDepositHandler.ts new file mode 100644 index 000000000..16d7e9838 --- /dev/null +++ b/scripts/hyperliquidDepositHandler.ts @@ -0,0 +1,176 @@ +// @notice Utility script to interact with HyperliquidDepositHandler contract. +// @dev Run with `yarn hardhat run ./scripts/hyperliquidDepositHandler.ts --network hyperevm` + +import { getNodeUrl } from "../utils"; +import { Contract, ethers } from "../utils/utils"; +import { hre } from "../utils/utils.hre"; + +async function main() { + const chainId = parseInt(await hre.getChainId()); + const nodeUrl = getNodeUrl(chainId); + const wallet = ethers.Wallet.fromMnemonic((hre.network.config.accounts as any).mnemonic); + console.log(`Connected to node ${nodeUrl} for chain ${chainId}`); + const signer = wallet.connect(new ethers.providers.JsonRpcProvider(nodeUrl)); + + // Deposit USDH to spot: + const amountToDeposit = ethers.utils.parseUnits("1", 6); + const depositHandler = new Contract( + "0x861E127036B28D32f3777B4676F6bbb9e007d195", + [ + { + inputs: [ + { + internalType: "address", + name: "token", + type: "address", + }, + { + internalType: "uint256", + name: "amount", + type: "uint256", + }, + { + internalType: "address", + name: "user", + type: "address", + }, + ], + name: "depositToHypercore", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + + { + inputs: [ + { internalType: "address", name: "token", type: "address" }, + { internalType: "uint256", name: "evmAmount", type: "uint256" }, + { internalType: "address", name: "user", type: "address" }, + ], + name: "sweepERC20ToUser", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { internalType: "address", name: "token", type: "address" }, + { internalType: "uint256", name: "amount", type: "uint256" }, + { internalType: "address", name: "user", type: "address" }, + ], + name: "sweepDonationBoxFundsToUser", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { internalType: "address", name: "token", type: "address" }, + { internalType: "uint64", name: "coreAmount", type: "uint64" }, + { internalType: "address", name: "user", type: "address" }, + ], + name: "sweepCoreFundsToUser", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [], + name: "donationBox", + outputs: [{ internalType: "contract DonationBox", name: "", type: "address" }], + stateMutability: "view", + type: "function", + }, + ], + signer + ); + const usdh = new Contract( + "0x111111a1a0667d36bD57c0A9f569b98057111111", + [ + { + inputs: [ + { + internalType: "address", + name: "guy", + type: "address", + }, + { + internalType: "uint256", + name: "wad", + type: "uint256", + }, + ], + name: "approve", + outputs: [ + { + internalType: "bool", + name: "", + type: "bool", + }, + ], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { internalType: "address", name: "to", type: "address" }, + { internalType: "uint256", name: "value", type: "uint256" }, + ], + name: "transfer", + outputs: [{ internalType: "bool", name: "", type: "bool" }], + stateMutability: "nonpayable", + type: "function", + }, + ], + signer + ); + + // Approve (1000 x amountToDeposit) USDH to be spent by the deposit handler. + const approvalTxn = await usdh.approve(depositHandler.address, amountToDeposit.mul(1000)); + const receipt = await approvalTxn.wait(); + console.log(`approval:`, receipt); + + // Fund donation box with 1 activation fee + 1 wei. + const donationBox = await depositHandler.donationBox(); + const transferTxn = await usdh.transfer(donationBox, amountToDeposit.add(1)); + const transferReceipt = await transferTxn.wait(); + console.log(`donationBox funding:`, transferReceipt); + + // // Sweep 1 USDH from the deposit handler on HyperEVM to a user's address. + // const txn = await depositHandler.sweepERC20ToUser( + // usdh.address, + // "1000001", + // "0x9A8f92a830A5cB89a3816e3D267CB7791c16b04D", + // ); + // console.log(`sweepERC20ToUser`, await txn.wait()); + + // // Sweep 1 USDH from the deposit handler on HyperCore to a user's address. + // const txn = await depositHandler.sweepCoreFundsToUser( + // usdh.address, + // "100000001", // USDH has 8 decimals on Core. + // "0x9A8f92a830A5cB89a3816e3D267CB7791c16b04D", + // ); + // console.log(`sweepCoreFundsToUser`, await txn.wait()); + + // // Sweep 1 USDH from the deposit handler's donation box on HyperEVM to a user's address. + // const txn = await depositHandler.sweepDonationBoxFundsToUser( + // usdh.address, + // "1000000", + // "0x9A8f92a830A5cB89a3816e3D267CB7791c16b04D", + // ); + // console.log(`sweepDonationBoxFundsToUser`, await txn.wait()); + + // Deposit 1 USDH from the user's address on HyperEVM to the user's account on HyperCore. + // If user account needs to be activated, the deposit handler will pull 1 USDH + 1 wei from its donation box and + // activate the user's account. + const txn = await depositHandler.depositToHypercore(usdh.address, amountToDeposit, signer.address); + console.log(`depositToHypercore`, await txn.wait()); +} + +main().then( + () => process.exit(0), + (error) => { + console.log(error); + process.exit(1); + } +); diff --git a/tasks/verifyBytecode.ts b/tasks/verifyBytecode.ts index 9e6ca230d..71bea5ead 100644 --- a/tasks/verifyBytecode.ts +++ b/tasks/verifyBytecode.ts @@ -2,99 +2,324 @@ import { task } from "hardhat/config"; import type { HardhatRuntimeEnvironment } from "hardhat/types"; import "hardhat-deploy"; import "@nomiclabs/hardhat-ethers"; +import fs from "fs"; +import path from "path"; +import { execSync } from "child_process"; + +type VerifyBytecodeArgs = { + contract?: string; + txHash?: string; + libraries?: string; + broadcast?: string; +}; + +/** + * Best-effort parser for `foundry.toml` that extracts the default profile's `out` directory. + * Falls back to `/out` if anything goes wrong. + */ +function getFoundryOutDir(): string { + const root = process.cwd(); + const configPath = path.join(root, "foundry.toml"); + + if (!fs.existsSync(configPath)) { + return path.join(root, "out"); + } + + const contents = fs.readFileSync(configPath, "utf8"); + const lines = contents.split(/\r?\n/); + + let inDefaultProfile = false; + for (const rawLine of lines) { + const line = rawLine.trim(); + + if (line.startsWith("[") && line.endsWith("]")) { + // Enter or exit `[profile.default]` section. + inDefaultProfile = line === "[profile.default]"; + continue; + } + + if (!inDefaultProfile) continue; + + const match = line.match(/^out\s*=\s*"(.*)"\s*$/); + if (match) { + const configuredOut = match[1].trim(); + if (configuredOut.length > 0) { + return path.isAbsolute(configuredOut) ? configuredOut : path.join(root, configuredOut); + } + } + } + + // Default Foundry output directory. + return path.join(root, "out"); +} + +function normalizeFoundryBytecode(raw: any, key: "bytecode" | "deployedBytecode"): [string, any] { + const value = raw[key]; + if (!value) { + return ["0x", {}]; + } + + if (typeof value === "string") { + const linksKey = key === "bytecode" ? "linkReferences" : "deployedLinkReferences"; + const links = raw[linksKey] ?? {}; + return [value, links]; + } + + if (typeof value === "object") { + return [value.object ?? "0x", value.linkReferences ?? {}]; + } + + return ["0x", {}]; +} /** - * Verify that the deployment init code (creation bytecode + encoded constructor args) - * matches the locally reconstructed init code from artifacts and recorded args. - * - * Compares keccak256(initCodeOnChain) vs keccak256(initCodeLocal). - * - * Sample usage: - * yarn hardhat verify-bytecode --contract Arbitrum_Adapter --network mainnet - * yarn hardhat verify-bytecode --contract Arbitrum_Adapter --tx-hash 0x... --network mainnet - * yarn hardhat verify-bytecode --contract X --tx-hash 0x... --libraries "MyLib=0x...,OtherLib=0x..." --network mainnet + * Load a Foundry artifact (`out/...json`) and adapt it into a Hardhat-style artifact + * that can be consumed by `ethers.getContractFactoryFromArtifact`. + */ +function loadFoundryArtifact(contractName: string): any { + const outDir = getFoundryOutDir(); + const candidates = [ + path.join(outDir, `${contractName}.sol`, `${contractName}.json`), + path.join(outDir, `${contractName}.json`), + ]; + + const artifactPath = candidates.find((p) => fs.existsSync(p)); + if (!artifactPath) { + throw new Error( + `Could not find Foundry artifact for contract "${contractName}". Tried:\n` + + candidates.map((p) => ` - ${p}`).join("\n") + ); + } + + const rawJson = fs.readFileSync(artifactPath, "utf8"); + const raw: any = JSON.parse(rawJson); + + const abi = raw.abi ?? []; + const [bytecode, linkReferences] = normalizeFoundryBytecode(raw, "bytecode"); + const [deployedBytecode, deployedLinkReferences] = normalizeFoundryBytecode(raw, "deployedBytecode"); + + return { + _format: "hh-foundry-compat-0", + contractName, + sourceName: raw.sourceName ?? raw.source_name ?? path.basename(artifactPath), + abi, + bytecode, + deployedBytecode, + linkReferences, + deployedLinkReferences, + }; +} + +function ensureForgeBuildArtifacts() { + try { + // This keeps Foundry's `out/` artifacts up to date when verifying Foundry deployments. + console.log("Running `forge build` to refresh Foundry artifacts..."); + execSync("forge build", { stdio: "inherit" }); + } catch (error: any) { + throw new Error(`forge build failed: ${error?.message ?? String(error)}`); + } +} + +/** +Verify that the deployment init code (creation bytecode + encoded constructor args) +matches the locally reconstructed init code from artifacts and recorded args. + +Compares keccak256(initCodeOnChain) vs keccak256(initCodeLocal). + +Sample usage: +yarn hardhat verify-bytecode --contract Arbitrum_Adapter --network mainnet +yarn hardhat verify-bytecode --contract Arbitrum_Adapter --tx-hash 0x... --network mainnet +yarn hardhat verify-bytecode --contract X --tx-hash 0x... --libraries "MyLib=0x...,OtherLib=0x..." --network mainnet + +For Foundry deployments that used `forge script --broadcast`, you can instead +point this task at the Foundry broadcast JSON: + +yarn hardhat verify-bytecode \ + --contract DstOFTHandler \ + --broadcast broadcast/DeployDstHandler.s.sol/999/run-latest.json \ + --network hyperevm */ task("verify-bytecode", "Verify deploy transaction input against local artifacts") .addOptionalParam("contract", "Contract name; falls back to env CONTRACT") // @dev For proxies, we don't save transactionHash in deployments/. You have to provide it manually via --tx-hash 0x... by checking e.g. block explorer first .addOptionalParam("txHash", "Deployment transaction hash (defaults to deployments JSON)") .addOptionalParam("libraries", "Libraries to link. JSON string or 'Name=0x..,Other=0x..'") - .setAction( - async (args: { contract?: string; txHash?: string; libraries?: string }, hre: HardhatRuntimeEnvironment) => { - const { deployments, ethers, artifacts, network } = hre; + .addOptionalParam( + "broadcast", + "Path to Foundry broadcast JSON (e.g. broadcast/DeployFoo.s.sol/1/run-latest.json). " + + "If set, constructor args and default txHash are taken from this file instead of hardhat-deploy deployments." + ) + .setAction(async (args: VerifyBytecodeArgs, hre: HardhatRuntimeEnvironment) => { + const { deployments, ethers, artifacts, network } = hre; - // make sure we're using latest local contract artifacts for verification + const useFoundryArtifacts = Boolean(args.broadcast); + + // For Hardhat deployments, make sure we're using latest local Hardhat artifacts. + if (!useFoundryArtifacts) { await hre.run("compile"); + } else { + // For Foundry deployments, refresh Foundry's `out/` artifacts instead. + ensureForgeBuildArtifacts(); + } - const contractName = args.contract || process.env.CONTRACT; - if (!contractName) throw new Error("Please provide --contract or set CONTRACT env var"); + const contractName = args.contract || process.env.CONTRACT; + if (!contractName) throw new Error("Please provide --contract or set CONTRACT env var"); - const deployment = await deployments.get(contractName); - const deployedAddress: string = deployment.address; - const constructorArgs: any[] = deployment.args || []; - - const parseLibraries = (s?: string): Record => { - if (!s) return {}; - const out: Record = {}; - const trimmed = s.trim(); - if (trimmed.startsWith("{") && trimmed.endsWith("}")) { - const parsed = JSON.parse(trimmed); - for (const [k, v] of Object.entries(parsed)) out[k] = String(v); - return out; - } - for (const part of trimmed.split(/[\,\n]/)) { - const [k, v] = part.split("=").map((x) => x.trim()); - if (k && v) out[k] = v; - } - return out; + /** + * Resolve constructor args, deployed address and default tx hash either from: + * - hardhat-deploy deployments (default), or + * - Foundry broadcast JSON (when --broadcast is provided). + */ + let deployedAddress: string | undefined; + let constructorArgs: any[] = []; + let defaultTxHash: string | undefined; + + if (args.broadcast) { + const resolvedPath = path.isAbsolute(args.broadcast) ? args.broadcast : path.join(process.cwd(), args.broadcast); + + if (!fs.existsSync(resolvedPath)) { + throw new Error(`Broadcast file not found at path ${resolvedPath}`); + } + + // Narrow JSON structure to only what we need. + type BroadcastTx = { + hash?: string; + transactionType?: string; + contractName?: string; + contractAddress?: string; + arguments?: any[]; + transaction?: { + input?: string; + }; }; + type BroadcastJson = { + transactions?: BroadcastTx[]; + }; + + const raw = fs.readFileSync(resolvedPath, "utf8"); + const parsed: BroadcastJson = JSON.parse(raw); + const txs = parsed.transactions || []; - // Read local compilation artifact - const artifact = await artifacts.readArtifact(contractName); - console.log("Reading compilation artifact for", artifact.sourceName); - - /** - * TODO - * the `libraries` bit is untested. Could be wrong. Could remove this part if we don't have contracts with dynamic libraries - * artifact.linkReferences might help solve this better. Also, deployments.libraries. Implement only if required later. - */ - const libraries: Record = parseLibraries(args.libraries); - const factory = await ethers.getContractFactoryFromArtifact( - artifact, - Object.keys(libraries).length ? { libraries } : {} + const createTxsForContract = txs.filter( + (tx) => tx.transactionType === "CREATE" && tx.contractName === contractName ); - // Note: `factory.getDeployTransaction` populates the transaction with whatever data we WOULD put in it if we were deploying it right now - const populatedDeployTransaction = factory.getDeployTransaction(...constructorArgs); - const expectedInit: string = ethers.utils.hexlify(populatedDeployTransaction.data!).toLowerCase(); - if (!expectedInit || expectedInit === "0x") { - throw new Error("Failed to reconstruct deployment init code from local artifacts"); + if (!createTxsForContract.length) { + throw new Error(`No CREATE transaction for contract "${contractName}" found in broadcast file ${resolvedPath}`); + } + + let selected: BroadcastTx; + if (args.txHash) { + const match = createTxsForContract.find( + (tx) => tx.hash && tx.hash.toLowerCase() === args.txHash!.toLowerCase() + ); + if (!match) { + throw new Error( + `No CREATE transaction with hash ${args.txHash} for contract "${contractName}" in ${resolvedPath}` + ); + } + selected = match; + } else if (createTxsForContract.length === 1) { + selected = createTxsForContract[0]; + } else { + const hashes = createTxsForContract + .map((tx) => tx.hash) + .filter(Boolean) + .join(", "); + throw new Error( + `Multiple CREATE transactions for contract "${contractName}" found in ${resolvedPath}. ` + + `Please re-run with --tx-hash set to one of: ${hashes}` + ); + } + + if (!selected.hash) { + throw new Error(`Selected broadcast transaction for "${contractName}" is missing a tx hash`); } - // Get on-chain creation input - const txHash = args.txHash ?? deployment.transactionHash; - if (!txHash) { - throw new Error("Could not find deployment tx hash. Pass --tx-hash when running script."); + deployedAddress = selected.contractAddress; + constructorArgs = selected.arguments || []; + defaultTxHash = selected.hash; + } else { + const deployment = await deployments.get(contractName); + deployedAddress = deployment.address; + constructorArgs = deployment.args || []; + defaultTxHash = deployment.transactionHash; + } + + const parseLibraries = (s?: string): Record => { + if (!s) return {}; + const out: Record = {}; + const trimmed = s.trim(); + if (trimmed.startsWith("{") && trimmed.endsWith("}")) { + const parsed = JSON.parse(trimmed); + for (const [k, v] of Object.entries(parsed)) out[k] = String(v); + return out; } - const tx = await ethers.provider.getTransaction(txHash); - if (!tx) throw new Error(`Transaction not found for hash ${txHash}`); - if (tx.to && tx.to != "") { - throw new Error(`Transaction ${txHash} is not a direct contract creation (tx.to=${tx.to})`); + for (const part of trimmed.split(/[\,\n]/)) { + const [k, v] = part.split("=").map((x) => x.trim()); + if (k && v) out[k] = v; } + return out; + }; - const expectedHash = ethers.utils.keccak256(expectedInit); - const onchainHash = ethers.utils.keccak256(tx.data.toLowerCase()); + // Read local compilation artifact (Hardhat or Foundry) for reconstructing init code. + const artifact = useFoundryArtifacts + ? loadFoundryArtifact(contractName) + : await artifacts.readArtifact(contractName); + console.log( + "Reading compilation artifact for", + (artifact as any).sourceName ?? (useFoundryArtifacts ? "" : "") + ); - console.log("\n=============== Deploy Tx Verification ==============="); - console.log(`Contract : ${contractName}`); - console.log(`Network : ${network.name}`); + /** + * TODO + * the `libraries` bit is untested. Could be wrong. Could remove this part if we don't have contracts with dynamic libraries + * artifact.linkReferences might help solve this better. Also, deployments.libraries. Implement only if required later. + */ + const libraries: Record = parseLibraries(args.libraries); + const factory = await ethers.getContractFactoryFromArtifact( + artifact, + Object.keys(libraries).length ? { libraries } : {} + ); + + // Note: `factory.getDeployTransaction` populates the transaction with whatever data we WOULD put in it if we were deploying it right now + const populatedDeployTransaction = factory.getDeployTransaction(...constructorArgs); + const expectedInit: string = ethers.utils.hexlify(populatedDeployTransaction.data!).toLowerCase(); + if (!expectedInit || expectedInit === "0x") { + throw new Error("Failed to reconstruct deployment init code from local artifacts"); + } + + // Get on-chain creation input + const txHash = args.txHash ?? defaultTxHash; + if (!txHash) { + throw new Error( + "Could not find deployment tx hash. Pass --tx-hash when running script, " + + "or ensure deployments / broadcast metadata includes it." + ); + } + const tx = await ethers.provider.getTransaction(txHash); + if (!tx) throw new Error(`Transaction not found for hash ${txHash}`); + if (tx.to && tx.to != "") { + throw new Error(`Transaction ${txHash} is not a direct contract creation (tx.to=${tx.to})`); + } + + const expectedHash = ethers.utils.keccak256(expectedInit); + const onchainHash = ethers.utils.keccak256(tx.data.toLowerCase()); + + console.log("\n=============== Deploy Tx Verification ==============="); + console.log(`Contract : ${contractName}`); + console.log(`Network : ${network.name}`); + if (deployedAddress) { console.log(`Deployed address : ${deployedAddress}`); - if (txHash) console.log(`Tx hash : ${txHash}`); - console.log("-------------------------------------------------------"); - console.log(`On-chain init hash : ${onchainHash}`); - console.log(`Local init hash : ${expectedHash}`); - console.log("-------------------------------------------------------"); - console.log(onchainHash === expectedHash ? "✅ MATCH" : "❌ MISMATCH – init code differs"); - console.log("=======================================================\n"); } - ); + if (args.broadcast) { + console.log(`Broadcast file : ${args.broadcast}`); + } + if (txHash) console.log(`Tx hash : ${txHash}`); + console.log("-------------------------------------------------------"); + console.log(`On-chain init hash : ${onchainHash}`); + console.log(`Local init hash : ${expectedHash}`); + console.log("-------------------------------------------------------"); + console.log(onchainHash === expectedHash ? "✅ MATCH" : "❌ MISMATCH – init code differs"); + console.log("=======================================================\n"); + }); diff --git a/test/evm/foundry/local/HyperCoreFlowExecutor.t.sol b/test/evm/foundry/local/HyperCoreFlowExecutor.t.sol new file mode 100644 index 000000000..2cf61d9c2 --- /dev/null +++ b/test/evm/foundry/local/HyperCoreFlowExecutor.t.sol @@ -0,0 +1,168 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.0; + +import { Test } from "forge-std/Test.sol"; +import { IERC20 } from "@openzeppelin/contracts-v4/token/ERC20/IERC20.sol"; + +import { BaseSimulatorTest } from "./external/hyper-evm-lib/test/BaseSimulatorTest.sol"; +import { PrecompileLib } from "./external/hyper-evm-lib/src/PrecompileLib.sol"; +import { HyperCoreLib } from "../../../../contracts/libraries/HyperCoreLib.sol"; + +import { DonationBox } from "../../../../contracts/chain-adapters/DonationBox.sol"; +import { MockERC20 } from "../../../../contracts/test/MockERC20.sol"; +import { IHyperCoreFlowExecutor } from "../../../../contracts/test/interfaces/IHyperCoreFlowExecutor.sol"; +import { CommonFlowParams } from "../../../../contracts/periphery/mintburn/Structs.sol"; +import { HyperCoreFlowExecutor } from "../../../../contracts/periphery/mintburn/HyperCoreFlowExecutor.sol"; +import { BaseModuleHandler } from "../../../../contracts/periphery/mintburn/BaseModuleHandler.sol"; + +contract TestHyperCoreHandler is BaseModuleHandler { + constructor(address donationBox, address baseToken) BaseModuleHandler(donationBox, baseToken, DEFAULT_ADMIN_ROLE) { + _grantRole(DEFAULT_ADMIN_ROLE, msg.sender); + _grantRole(PERMISSIONED_BOT_ROLE, msg.sender); + _grantRole(FUNDS_SWEEPER_ROLE, msg.sender); + } + + function callExecuteSimpleTransferFlow(CommonFlowParams memory params) external authorizeFundedFlow { + _delegateToHyperCore(abi.encodeWithSelector(HyperCoreFlowExecutor.executeSimpleTransferFlow.selector, params)); + } + + function callFallbackHyperEVMFlow(CommonFlowParams memory params) external authorizeFundedFlow { + _delegateToHyperCore(abi.encodeWithSelector(HyperCoreFlowExecutor.fallbackHyperEVMFlow.selector, params)); + } + + function callSetCoreTokenInfo( + address token, + uint32 coreIndex, + bool canBeUsedForAccountActivation, + uint64 accountActivationFeeCore, + uint64 bridgeSafetyBufferCore + ) external { + _delegateToHyperCore( + abi.encodeWithSelector( + HyperCoreFlowExecutor.setCoreTokenInfo.selector, + token, + coreIndex, + canBeUsedForAccountActivation, + accountActivationFeeCore, + bridgeSafetyBufferCore + ) + ); + } +} + +contract HyperCoreFlowExecutorTest is BaseSimulatorTest { + using PrecompileLib for address; + + TestHyperCoreHandler internal handler; + DonationBox internal donationBox; + MockERC20 internal token; + + address internal finalRecipient; + bytes32 internal constant QUOTE_NONCE = keccak256("quote-1"); + uint32 internal constant CORE_INDEX = 0; // USDC index per BaseSimulatorTest + + function setUp() public override { + super.setUp(); + + finalRecipient = makeAddr("finalRecipient"); + + token = MockERC20(0xb88339CB7199b77E23DB6E890353E22632Ba630f); + donationBox = new DonationBox(); + handler = new TestHyperCoreHandler(address(donationBox), address(token)); + + // Make the handler the owner of DonationBox so it can withdraw during sponsorship + vm.prank(donationBox.owner()); + donationBox.transferOwnership(address(handler)); + + // Set token info in the module via delegatecall + handler.callSetCoreTokenInfo(address(token), CORE_INDEX, true, 1e6, 1e6); + + // Ensure recipient has an active HyperCore account for sponsored simple transfer path + hyperCore.forceAccountActivation(finalRecipient); + } + + function _defaultParams( + uint256 amountInEVM, + uint256 maxBpsToSponsor, + uint256 extraFeesIncurred + ) internal view returns (CommonFlowParams memory) { + return + CommonFlowParams({ + amountInEVM: amountInEVM, + quoteNonce: QUOTE_NONCE, + finalRecipient: finalRecipient, + finalToken: address(token), + maxBpsToSponsor: maxBpsToSponsor, + extraFeesIncurred: extraFeesIncurred + }); + } + + function testExecuteSimpleTransferFlow_Sponsored_EmitsAndUsesDonation() public { + uint256 amountIn = 1_000e6; + uint256 extraFees = 50e6; // 5% of amount + uint256 maxBps = 500; // 5% + + // Ensure bridge has enough liquidity so safety check passes + address bridgeAddr = HyperCoreLib.toAssetBridgeAddress(CORE_INDEX); + hyperCore.forceSpot(bridgeAddr, CORE_INDEX, uint64(10_000_000e8)); // large buffer + + // Handler must hold user funds that arrived via bridge + deal(address(token), address(handler), amountIn, true); + // Donation box must hold enough to sponsor the extra fees + deal(address(token), address(donationBox), extraFees, true); + + CommonFlowParams memory params = _defaultParams(amountIn, maxBps, extraFees); + + // Expect the SimpleTransferFlowCompleted event with sponsorship equal to extraFees (capped by bps) + vm.expectEmit(address(handler)); + emit HyperCoreFlowExecutor.SimpleTransferFlowCompleted( + // delegated event emitted by handler context + params.quoteNonce, + params.finalRecipient, + params.finalToken, + params.amountInEVM, + params.extraFeesIncurred, + extraFees + ); + handler.callExecuteSimpleTransferFlow(params); + } + + function testExecuteSimpleTransferFlow_Unsponsored_NotActivated_FallsBackToEVM() public { + // Make recipient not activated + address unactivated = makeAddr("unactivated"); + + uint256 amountIn = 2_000e6; + uint256 extraFees = 25e6; // ignored as maxBpsToSponsor=0 + uint256 maxBps = 0; + + // Handler holds bridged funds to forward on EVM fallback + deal(address(token), address(handler), amountIn, true); + + CommonFlowParams memory params = CommonFlowParams({ + amountInEVM: amountIn, + quoteNonce: keccak256("quote-2"), + finalRecipient: unactivated, + finalToken: address(token), + maxBpsToSponsor: maxBps, + extraFeesIncurred: extraFees + }); + + uint256 balBefore = IERC20(address(token)).balanceOf(unactivated); + + // Expect FallbackHyperEVMFlowCompleted with zero sponsorship + vm.expectEmit(address(handler)); + emit HyperCoreFlowExecutor.FallbackHyperEVMFlowCompleted( + params.quoteNonce, + params.finalRecipient, + params.finalToken, + params.amountInEVM, + params.extraFeesIncurred, + 0 + ); + handler.callFallbackHyperEVMFlow(params); + + // Recipient received amountIn on EVM + uint256 balAfter = IERC20(address(token)).balanceOf(unactivated); + assertEq(balAfter - balBefore, amountIn, "fallback EVM transfer incorrect"); + } +} diff --git a/test/evm/foundry/local/HyperCoreMockHelper.sol b/test/evm/foundry/local/HyperCoreMockHelper.sol new file mode 100644 index 000000000..afbec6eb0 --- /dev/null +++ b/test/evm/foundry/local/HyperCoreMockHelper.sol @@ -0,0 +1,160 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.0; + +import { Test } from "forge-std/Test.sol"; +import { HyperCoreLib } from "../../../../contracts/libraries/HyperCoreLib.sol"; + +/** + * @title HyperCoreMockHelper + * @notice Helper contract for setting up HyperCore precompile mocks in tests + * @dev Inherit from this contract in your test contracts to easily mock HyperCore precompiles + */ +abstract contract HyperCoreMockHelper is Test { + // HyperCore precompile addresses + address internal constant CORE_USER_EXISTS_PRECOMPILE = address(0x0000000000000000000000000000000000000810); + address internal constant TOKEN_INFO_PRECOMPILE = address(0x000000000000000000000000000000000000080C); + address internal constant SPOT_BALANCE_PRECOMPILE = address(0x0000000000000000000000000000000000000801); + address internal constant CORE_WRITER_PRECOMPILE = address(0x3333333333333333333333333333333333333333); + + /** + * @notice Mock the CoreUserExists precompile + * @param exists Whether the core user exists + */ + function mockCoreUserExists(bool exists) internal { + vm.mockCall( + CORE_USER_EXISTS_PRECOMPILE, + bytes(""), // Match any calldata + abi.encode(exists) + ); + } + + /** + * @notice Mock the TokenInfo precompile with custom token information + * @param tokenInfo The token info struct to return from the mock + */ + function mockTokenInfo(HyperCoreLib.TokenInfo memory tokenInfo) internal { + vm.mockCall( + TOKEN_INFO_PRECOMPILE, + bytes(""), // Match any calldata + abi.encode(tokenInfo) + ); + } + + /** + * @notice Mock the TokenInfo precompile with default values for a token + * @param evmContract The EVM contract address for the token + * @param name The token name + * @param decimals The token decimals + */ + function mockTokenInfoDefault(address evmContract, string memory name, uint8 decimals) internal { + HyperCoreLib.TokenInfo memory tokenInfo = HyperCoreLib.TokenInfo({ + name: name, + spots: new uint64[](0), + deployerTradingFeeShare: 0, + deployer: address(0), + evmContract: evmContract, + szDecimals: decimals, + weiDecimals: decimals, + evmExtraWeiDecimals: 0 + }); + mockTokenInfo(tokenInfo); + } + + /** + * @notice Mock the SpotBalance precompile + * @param spotBalance The spot balance struct to return from the mock + */ + function mockSpotBalance(HyperCoreLib.SpotBalance memory spotBalance) internal { + vm.mockCall( + SPOT_BALANCE_PRECOMPILE, + bytes(""), // Match any calldata + abi.encode(spotBalance) + ); + } + + /** + * @notice Mock the SpotBalance precompile with default values + * @param total The total balance + * @param hold The held balance + * @param entryNtl The entry notional value + */ + function mockSpotBalanceDefault(uint64 total, uint64 hold, uint64 entryNtl) internal { + HyperCoreLib.SpotBalance memory spotBalance = HyperCoreLib.SpotBalance({ + total: total, + hold: hold, + entryNtl: entryNtl + }); + mockSpotBalance(spotBalance); + } + + /** + * @notice Mock the CoreWriter precompile + * @param success Whether the core writer operation should succeed + */ + function mockCoreWriter(bool success) internal { + vm.mockCall( + CORE_WRITER_PRECOMPILE, + bytes(""), // Match any calldata + abi.encode(success) + ); + } + + /** + * @notice Setup all HyperCore precompile mocks with default values + * @param tokenAddress The EVM token contract address + * @param tokenName The token name + * @param tokenDecimals The token decimals + * @dev This is a convenience function that mocks all precompiles with sensible defaults + */ + function setupDefaultHyperCoreMocks(address tokenAddress, string memory tokenName, uint8 tokenDecimals) internal { + // 1. Mock CoreUserExists precompile - user exists + mockCoreUserExists(true); + + // 2. Mock TokenInfo precompile with default values + mockTokenInfoDefault(tokenAddress, tokenName, tokenDecimals); + + // 3. Mock SpotBalance precompile with default balance + mockSpotBalanceDefault(10e8, 0, 0); + + // 4. Mock CoreWriter precompile - operations succeed + mockCoreWriter(true); + } + + /** + * @notice Setup all HyperCore precompile mocks with default values for multiple tokens + * @param tokenAddresses Array of EVM token contract addresses + * @param tokenNames Array of token names + * @param tokenDecimals Array of token decimals + * @dev All arrays must be the same length + */ + function setupDefaultHyperCoreMocksMultiToken( + address[] memory tokenAddresses, + string[] memory tokenNames, + uint8[] memory tokenDecimals + ) internal { + require( + tokenAddresses.length == tokenNames.length && tokenNames.length == tokenDecimals.length, + "Array length mismatch" + ); + + // Mock core user exists and core writer once + mockCoreUserExists(true); + mockCoreWriter(true); + mockSpotBalanceDefault(10e8, 0, 0); + + // Mock token info for each token + // Note: This mocks with "any calldata", so all tokens will return the last mocked value + // For more specific mocking per token, use mockTokenInfo() with specific calldata + for (uint256 i = 0; i < tokenAddresses.length; i++) { + mockTokenInfoDefault(tokenAddresses[i], tokenNames[i], tokenDecimals[i]); + } + } + + /** + * @notice Clear all HyperCore precompile mocks + * @dev Useful when you need to change mock behavior mid-test + */ + function clearHyperCoreMocks() internal { + vm.clearMockedCalls(); + } +} diff --git a/test/evm/foundry/local/OP_Adapter.t.sol b/test/evm/foundry/local/OP_Adapter.t.sol new file mode 100644 index 000000000..e9a5aa8fd --- /dev/null +++ b/test/evm/foundry/local/OP_Adapter.t.sol @@ -0,0 +1,95 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.0; + +import { Test } from "forge-std/Test.sol"; + +import { ERC20, IERC20 } from "@openzeppelin/contracts-v4/token/ERC20/ERC20.sol"; +import { IL1StandardBridge } from "@eth-optimism/contracts/L1/messaging/IL1StandardBridge.sol"; +import { IOpUSDCBridgeAdapter } from "../../../../contracts/external/interfaces/IOpUSDCBridgeAdapter.sol"; +import { ITokenMessenger } from "../../../../contracts/external/interfaces/CCTPInterfaces.sol"; + +import { OP_Adapter } from "../../../../contracts/chain-adapters/OP_Adapter.sol"; +import { WETH9Interface } from "../../../../contracts/external/interfaces/WETH9Interface.sol"; +import { WETH9 } from "../../../../contracts/external/WETH9.sol"; + +contract OP_AdapterTest is Test { + ERC20 l1Usdc; + WETH9 l1Weth; + + IL1StandardBridge standardBridge; + IOpUSDCBridgeAdapter opUSDCBridge; + ITokenMessenger cctpMessenger; + + uint32 constant RECIPIENT_CIRCLE_DOMAIN_ID = 1; + + function setUp() public { + l1Usdc = new ERC20("l1Usdc", "l1Usdc"); + l1Weth = new WETH9(); + + standardBridge = IL1StandardBridge(makeAddr("standardBridge")); + opUSDCBridge = IOpUSDCBridgeAdapter(makeAddr("opUSDCBridge")); + cctpMessenger = ITokenMessenger(makeAddr("cctpMessenger")); + } + + function testUSDCNotSet() public { + new OP_Adapter( + WETH9Interface(address(l1Weth)), + IERC20(address(0)), + address(0), + standardBridge, + IOpUSDCBridgeAdapter(address(0)), + ITokenMessenger(address(0)), + RECIPIENT_CIRCLE_DOMAIN_ID + ); + } + + function testL1UsdcBridgeSet() public { + new OP_Adapter( + WETH9Interface(address(l1Weth)), + IERC20(address(l1Usdc)), + address(0), + standardBridge, + opUSDCBridge, + ITokenMessenger(address(0)), + RECIPIENT_CIRCLE_DOMAIN_ID + ); + } + + function testCctpMessengerSet() public { + new OP_Adapter( + WETH9Interface(address(l1Weth)), + IERC20(address(0)), + address(0), + standardBridge, + IOpUSDCBridgeAdapter(address(0)), + cctpMessenger, + RECIPIENT_CIRCLE_DOMAIN_ID + ); + } + + function testNeitherSet() public { + vm.expectRevert(OP_Adapter.InvalidBridgeConfig.selector); + new OP_Adapter( + WETH9Interface(address(l1Weth)), + IERC20(address(l1Usdc)), + address(0), + standardBridge, + IOpUSDCBridgeAdapter(address(0)), + ITokenMessenger(address(0)), + RECIPIENT_CIRCLE_DOMAIN_ID + ); + } + + function testBothSet() public { + vm.expectRevert(OP_Adapter.InvalidBridgeConfig.selector); + new OP_Adapter( + WETH9Interface(address(l1Weth)), + IERC20(address(l1Usdc)), + address(0), + standardBridge, + opUSDCBridge, + cctpMessenger, + RECIPIENT_CIRCLE_DOMAIN_ID + ); + } +} diff --git a/test/evm/foundry/local/SponsoredOFTSrcPeriphery.t.sol b/test/evm/foundry/local/SponsoredOFTSrcPeriphery.t.sol new file mode 100644 index 000000000..39c2c0465 --- /dev/null +++ b/test/evm/foundry/local/SponsoredOFTSrcPeriphery.t.sol @@ -0,0 +1,250 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.23; + +import { Test } from "forge-std/Test.sol"; + +import { SponsoredOFTSrcPeriphery } from "../../../../contracts/periphery/mintburn/sponsored-oft/SponsoredOFTSrcPeriphery.sol"; +import { Quote, SignedQuoteParams, UnsignedQuoteParams } from "../../../../contracts/periphery/mintburn/sponsored-oft/Structs.sol"; +import { AddressToBytes32 } from "../../../../contracts/libraries/AddressConverters.sol"; + +import { MockERC20 } from "../../../../contracts/test/MockERC20.sol"; +import { MockOFTMessenger } from "../../../../contracts/test/MockOFTMessenger.sol"; +import { MockEndpoint } from "../../../../contracts/test/MockEndpoint.sol"; + +import { IERC20 } from "@openzeppelin/contracts-v4/token/ERC20/IERC20.sol"; +import { DebugQuoteSignLib } from "../../../../script/mintburn/oft/CreateSponsoredDeposit.s.sol"; + +contract SponsoredOFTSrcPeripheryTest is Test { + using AddressToBytes32 for address; + + uint32 internal constant SRC_EID = 101; + + address internal owner; + address internal user; + uint256 internal signerPk; + address internal signer; + address internal refundRecipient; + + MockERC20 internal token; + MockEndpoint internal endpoint; + MockOFTMessenger internal oft; + SponsoredOFTSrcPeriphery internal periphery; + + uint256 internal constant USER_INITIAL_BAL = 1_000_000 ether; + uint256 internal constant SEND_AMOUNT = 1_000 ether; + uint256 internal constant QUOTED_NATIVE_FEE = 0.01 ether; + + function setUp() public { + owner = address(this); + user = vm.addr(111); + signerPk = 0xA11CE; + signer = vm.addr(signerPk); + refundRecipient = vm.addr(222); + + token = new MockERC20(); + endpoint = new MockEndpoint(SRC_EID); + oft = new MockOFTMessenger(address(token)); + oft.setEndpoint(address(endpoint)); + oft.setFeesToReturn(QUOTED_NATIVE_FEE, 0); + + periphery = new SponsoredOFTSrcPeriphery(address(token), address(oft), SRC_EID, signer); + + // Fund user with tokens and ETH + deal(address(token), user, USER_INITIAL_BAL, true); + vm.deal(user, 100 ether); + + // Pre-approve the periphery to pull SEND_AMOUNT + vm.startPrank(user); + IERC20(address(token)).approve(address(periphery), type(uint256).max); + vm.stopPrank(); + } + + // Helpers + function createDefaultQuote( + bytes32 nonce, + uint256 deadline, + address destHandlerAddr, + address finalRecipientAddr, + address finalTokenAddr + ) internal view returns (Quote memory q) { + SignedQuoteParams memory sp = SignedQuoteParams({ + srcEid: SRC_EID, + dstEid: uint32(201), + destinationHandler: destHandlerAddr.toBytes32(), + amountLD: SEND_AMOUNT, + nonce: nonce, + deadline: deadline, + maxBpsToSponsor: 500, // 5% + finalRecipient: finalRecipientAddr.toBytes32(), + finalToken: finalTokenAddr.toBytes32(), + lzReceiveGasLimit: 500_000, + lzComposeGasLimit: 500_000, + executionMode: uint8(0), // DirectToCore + actionData: "" + }); + + UnsignedQuoteParams memory up = UnsignedQuoteParams({ + refundRecipient: refundRecipient, + maxUserSlippageBps: 300 // 3% + }); + + q = Quote({ signedParams: sp, unsignedParams: up }); + } + + function signQuote(uint256 pk, Quote memory q) internal view returns (bytes memory sig) { + sig = DebugQuoteSignLib.signMemory(vm, pk, q.signedParams); + } + + function testDepositHappyPath() public { + bytes32 nonce = keccak256("q-1"); + uint256 deadline = block.timestamp + 1 days; + address destHandler = address(0x1234); + address finalRecipientAddr = address(0xBEEF); + address finalTokenAddr = address(0xCAFE); + + Quote memory quote = createDefaultQuote(nonce, deadline, destHandler, finalRecipientAddr, finalTokenAddr); + bytes memory signature = signQuote(signerPk, quote); + + uint256 extra = 0.123 ether; + uint256 refundRecipientBalBefore = refundRecipient.balance; + + vm.prank(user); + vm.expectEmit(address(periphery)); + emit SponsoredOFTSrcPeriphery.SponsoredOFTSend( + nonce, + user, + finalRecipientAddr.toBytes32(), + destHandler.toBytes32(), + deadline, + 500, + 300, + finalTokenAddr.toBytes32(), + signature + ); + periphery.deposit{ value: QUOTED_NATIVE_FEE + extra }(quote, signature); + + // Refund only the extra portion + assertEq(refundRecipient.balance - refundRecipientBalBefore, extra, "unexpected refund amount"); + assertEq(address(periphery).balance, 0, "periphery should not retain ETH"); + + // OFT was called with precise native fee as msg.value + assertEq(oft.lastMsgValue(), QUOTED_NATIVE_FEE, "incorrect msg.value to OFT"); + assertEq(oft.sendCallCount(), 1, "send not called exactly once"); + + // Validate send params + ( + uint32 spDstEid, + bytes32 spTo, + uint256 spAmountLD, + uint256 spMinAmountLD, + bytes memory spExtraOptions, + bytes memory spComposeMsg, + bytes memory spOftCmd + ) = oft.lastSendParam(); + spExtraOptions; // silence - structure validated implicitly by OFT quote/send success + assertEq(spDstEid, quote.signedParams.dstEid, "dstEid mismatch"); + assertEq(spTo, quote.signedParams.destinationHandler, "destination handler mismatch"); + assertEq(spAmountLD, SEND_AMOUNT, "amountLD mismatch"); + assertEq(spMinAmountLD, SEND_AMOUNT, "minAmountLD should equal amountLD (no fee-in-token)"); + assertEq(spOftCmd.length, 0, "oftCmd must be empty"); + + // Validate composeMsg encoding (layout from ComposeMsgCodec._encode) + ( + bytes32 gotNonce, + uint256 gotDeadline, + uint256 gotMaxBpsToSponsor, + uint256 gotMaxUserSlippageBps, + bytes32 gotFinalRecipient, + bytes32 gotFinalToken, + uint8 gotExecutionMode, + bytes memory gotActionData + ) = abi.decode(spComposeMsg, (bytes32, uint256, uint256, uint256, bytes32, bytes32, uint8, bytes)); + + assertEq(gotNonce, nonce, "nonce mismatch"); + assertEq(gotDeadline, deadline, "deadline mismatch"); + assertEq(gotMaxBpsToSponsor, 500, "maxBpsToSponsor mismatch"); + assertEq(gotMaxUserSlippageBps, 300, "maxUserSlippageBps mismatch"); + assertEq(gotFinalRecipient, finalRecipientAddr.toBytes32(), "finalRecipient mismatch"); + assertEq(gotFinalToken, finalTokenAddr.toBytes32(), "finalToken mismatch"); + assertEq(gotExecutionMode, 0, "executionMode mismatch"); + assertEq(keccak256(gotActionData), keccak256(""), "actionData mismatch"); + + // ERC20 was pulled and approved + assertEq(IERC20(address(token)).balanceOf(user), USER_INITIAL_BAL - SEND_AMOUNT, "user balance mismatch"); + assertEq(IERC20(address(token)).balanceOf(address(periphery)), SEND_AMOUNT, "periphery balance mismatch"); + assertEq(IERC20(address(token)).allowance(address(periphery), address(oft)), SEND_AMOUNT, "allowance mismatch"); + + // Nonce is marked used + // assertTrue(periphery.getMainStorage().usedNonces[nonce], "nonce should be marked used"); + } + + function testDepositRevertsOnInsufficientNativeFee() public { + bytes32 nonce = keccak256("q-2"); + uint256 deadline = block.timestamp + 1 days; + Quote memory quote = createDefaultQuote(nonce, deadline, address(0x1234), address(0xBEEF), address(0xCAFE)); + bytes memory signature = signQuote(signerPk, quote); + + vm.prank(user); + vm.expectRevert(SponsoredOFTSrcPeriphery.InsufficientNativeFee.selector); + periphery.deposit{ value: QUOTED_NATIVE_FEE - 1 }(quote, signature); + } + + function testDepositRevertsOnInvalidSignature() public { + bytes32 nonce = keccak256("q-3"); + uint256 deadline = block.timestamp + 1 days; + Quote memory quote = createDefaultQuote(nonce, deadline, address(0x9999), address(0x8888), address(0x7777)); + bytes memory signature = signQuote(signerPk, quote); + + // Corrupt the signature (flip 1 bit) + signature[0] = bytes1(uint8(signature[0]) ^ 0x01); + + vm.prank(user); + // ECDSA may revert with its own error for malformed signatures; accept any revert here + vm.expectRevert(); + periphery.deposit{ value: QUOTED_NATIVE_FEE }(quote, signature); + } + + function testDepositRevertsOnIncorrectSigner() public { + // Produce a well-formed signature from a non-authorized key + uint256 wrongPk = 0xB0B; + address wrongSigner = vm.addr(wrongPk); + vm.assume(wrongSigner != signer); + + bytes32 nonce = keccak256("q-3b"); + uint256 deadline = block.timestamp + 1 days; + + Quote memory quote = createDefaultQuote(nonce, deadline, address(0x1111), address(0x2222), address(0x3333)); + // Sign with wrong private key + bytes memory signature = signQuote(wrongPk, quote); + + vm.prank(user); + vm.expectRevert(SponsoredOFTSrcPeriphery.IncorrectSignature.selector); + periphery.deposit{ value: QUOTED_NATIVE_FEE }(quote, signature); + } + + function testDepositRevertsOnExpiredQuote() public { + bytes32 nonce = keccak256("q-4"); + uint256 pastDeadline = block.timestamp - 1; + Quote memory quote = createDefaultQuote(nonce, pastDeadline, address(0xA1), address(0xB2), address(0xC3)); + bytes memory signature = signQuote(signerPk, quote); + + vm.prank(user); + vm.expectRevert(SponsoredOFTSrcPeriphery.QuoteExpired.selector); + periphery.deposit{ value: QUOTED_NATIVE_FEE }(quote, signature); + } + + function testDepositRevertsOnNonceReuse() public { + bytes32 nonce = keccak256("q-5"); + uint256 deadline = block.timestamp + 1 days; + Quote memory quote = createDefaultQuote(nonce, deadline, address(0xD1), address(0xD2), address(0xD3)); + bytes memory signature = signQuote(signerPk, quote); + + vm.prank(user); + periphery.deposit{ value: QUOTED_NATIVE_FEE }(quote, signature); + + // Try again with the same quote/nonce + vm.prank(user); + vm.expectRevert(SponsoredOFTSrcPeriphery.NonceAlreadyUsed.selector); + periphery.deposit{ value: QUOTED_NATIVE_FEE }(quote, signature); + } +} diff --git a/test/evm/foundry/local/SponsorredCCTPDstPeriphery.t.sol b/test/evm/foundry/local/SponsorredCCTPDstPeriphery.t.sol new file mode 100644 index 000000000..34042f60a --- /dev/null +++ b/test/evm/foundry/local/SponsorredCCTPDstPeriphery.t.sol @@ -0,0 +1,650 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.0; + +import { Test, console } from "forge-std/Test.sol"; +import { SponsoredCCTPDstPeriphery } from "../../../../contracts/periphery/mintburn/sponsored-cctp/SponsoredCCTPDstPeriphery.sol"; +import { IHyperCoreFlowExecutor } from "../../../../contracts/test/interfaces/IHyperCoreFlowExecutor.sol"; +import { SponsoredCCTPInterface } from "../../../../contracts/interfaces/SponsoredCCTPInterface.sol"; +import { IMessageTransmitterV2 } from "../../../../contracts/external/interfaces/CCTPInterfaces.sol"; +import { AddressToBytes32, Bytes32ToAddress } from "../../../../contracts/libraries/AddressConverters.sol"; +import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import { HyperCoreMockHelper } from "./HyperCoreMockHelper.sol"; +import { BaseSimulatorTest } from "./external/hyper-evm-lib/test/BaseSimulatorTest.sol"; +import { PrecompileLib } from "./external/hyper-evm-lib/src/PrecompileLib.sol"; +import { CoreWriterLib } from "./external/hyper-evm-lib/src/CoreWriterLib.sol"; +import { CoreSimulatorLib } from "./external/hyper-evm-lib/test/simulation/CoreSimulatorLib.sol"; + +contract MockMessageTransmitter is IMessageTransmitterV2 { + bool internal shouldSucceed = true; + + function setShouldSucceed(bool _shouldSucceed) external { + shouldSucceed = _shouldSucceed; + } + + function receiveMessage(bytes calldata, bytes calldata) external view override returns (bool) { + return shouldSucceed; + } +} + +contract MockDonationBox { + function withdraw(IERC20 token, uint256 amount) external { + token.transfer(msg.sender, amount); + } + + function mockTransfer(address token, uint256 amount) external { + IERC20(token).transfer(msg.sender, amount); + } +} + +contract MockUSDC is Test { + string public name = "USD Coin"; + string public symbol = "USDC"; + uint8 public decimals = 6; + mapping(address => uint256) public balanceOf; + mapping(address => mapping(address => uint256)) public allowance; + + function mint(address to, uint256 amount) external { + balanceOf[to] += amount; + } + + function transfer(address to, uint256 amount) external returns (bool) { + balanceOf[msg.sender] -= amount; + balanceOf[to] += amount; + return true; + } + + function transferFrom(address from, address to, uint256 amount) external returns (bool) { + allowance[from][msg.sender] -= amount; + balanceOf[from] -= amount; + balanceOf[to] += amount; + return true; + } + + function approve(address spender, uint256 amount) external returns (bool) { + allowance[msg.sender][spender] = amount; + return true; + } +} + +contract SponsoredCCTPDstPeripheryTest is BaseSimulatorTest { + using AddressToBytes32 for address; + using Bytes32ToAddress for bytes32; + + SponsoredCCTPDstPeriphery public periphery; + MockMessageTransmitter public messageTransmitter; + MockDonationBox public donationBox; + MockUSDC public usdc; + + address public signer; + uint256 public signerPrivateKey; + address public admin; + // address public user; + address public finalRecipient; + address public multicallHandler; + + uint32 constant SOURCE_DOMAIN = 0; + uint32 constant DESTINATION_DOMAIN = 1; + uint32 constant CORE_INDEX = 0; + uint32 constant MIN_FINALITY_THRESHOLD = 100; + + uint256 constant DEFAULT_AMOUNT = 1000e6; // 1000 USDC + uint256 constant DEFAULT_MAX_FEE = 10e6; // 10 USDC + uint256 constant FEE_EXECUTED = 5e6; // 5 USDC + + function setUp() public override { + super.setUp(); + + admin = makeAddr("admin"); + // user = makeAddr("user"); + finalRecipient = makeAddr("finalRecipient"); + multicallHandler = makeAddr("multicallHandler"); + + // Create signer + signerPrivateKey = 0x1234; + signer = vm.addr(signerPrivateKey); + + // Deploy mock contracts + messageTransmitter = new MockMessageTransmitter(); + donationBox = new MockDonationBox(); + usdc = MockUSDC(0xb88339CB7199b77E23DB6E890353E22632Ba630f); + + // Setup HyperCore precompile mocks using the helper + hyperCore.forceAccountActivation(finalRecipient); + + // Deploy periphery + vm.startPrank(admin); + periphery = new SponsoredCCTPDstPeriphery( + address(messageTransmitter), + signer, + address(donationBox), + address(usdc), + multicallHandler + ); + + IHyperCoreFlowExecutor(address(periphery)).setCoreTokenInfo(address(usdc), CORE_INDEX, true, 1e6, 1e6); + vm.stopPrank(); + + // Deal USDC to periphery for testing + deal(address(usdc), address(periphery), 10000e6); + } + + /// @dev Helper function to create a valid CCTP message + function createCCTPMessage( + SponsoredCCTPInterface.SponsoredCCTPQuote memory quote, + uint256 feeExecuted + ) internal view returns (bytes memory) { + // (bytes32, uint256, uint256, uint256, bytes32, bytes32, uint8, bytes) + // BurnMessage body + bytes memory hookData = abi.encode( + quote.nonce, + quote.deadline, + quote.maxBpsToSponsor, + quote.maxUserSlippageBps, + quote.finalRecipient, + quote.finalToken, + quote.executionMode, + quote.actionData + ); + + bytes memory messageBody = abi.encodePacked( + uint32(2), // version + quote.burnToken, // burnToken + quote.mintRecipient, // mintRecipient + uint256(quote.amount), // amount (32 bytes) + bytes32(0), // padding (32 bytes - for alignment) + quote.maxFee, // maxFee (32 bytes) + feeExecuted, // feeExecuted (32 bytes) + uint256(0), // hookDataRecipient (32 bytes - not used) + hookData // hookData + ); + + // CCTP Message format as per MessageV2 + bytes memory message = abi.encodePacked( + uint32(2), // version + quote.sourceDomain, // sourceDomain + quote.destinationDomain, // destinationDomain + bytes32(uint256(1)), // nonce (CCTP nonce) + quote.burnToken, // sender (token messenger on source) + bytes32(uint256(uint160(address(messageTransmitter)))), // recipient (token messenger on dest) + quote.destinationCaller, // destinationCaller + quote.minFinalityThreshold, // minFinalityThreshold + uint32(0), // finalityThresholdExecuted + messageBody // messageBody + ); + + return message; + } + + /// @dev Helper function to sign a quote + function signQuote( + SponsoredCCTPInterface.SponsoredCCTPQuote memory quote, + uint256 privateKey + ) internal pure returns (bytes memory) { + bytes32 hash1 = keccak256( + abi.encode( + quote.sourceDomain, + quote.destinationDomain, + quote.mintRecipient, + quote.amount, + quote.burnToken, + quote.destinationCaller, + quote.maxFee, + quote.minFinalityThreshold + ) + ); + + bytes32 hash2 = keccak256( + abi.encode( + quote.nonce, + quote.deadline, + quote.maxBpsToSponsor, + quote.maxUserSlippageBps, + quote.finalRecipient, + quote.finalToken, + quote.executionMode, + keccak256(quote.actionData) + ) + ); + + bytes32 typedDataHash = keccak256(abi.encode(hash1, hash2)); + + (uint8 v, bytes32 r, bytes32 s) = vm.sign(privateKey, typedDataHash); + return abi.encodePacked(r, s, v); + } + + /// @dev Helper function to create a default valid quote + function createDefaultQuote() internal view returns (SponsoredCCTPInterface.SponsoredCCTPQuote memory) { + return + SponsoredCCTPInterface.SponsoredCCTPQuote({ + sourceDomain: SOURCE_DOMAIN, + destinationDomain: DESTINATION_DOMAIN, + mintRecipient: bytes32(uint256(uint160(address(periphery)))), + amount: DEFAULT_AMOUNT, + burnToken: address(usdc).toBytes32(), + destinationCaller: bytes32(0), + maxFee: DEFAULT_MAX_FEE, + minFinalityThreshold: MIN_FINALITY_THRESHOLD, + nonce: keccak256("test-nonce-1"), + deadline: block.timestamp + 1 hours, + maxBpsToSponsor: 100, // 1% + maxUserSlippageBps: 50, // 0.5% + finalRecipient: finalRecipient.toBytes32(), + finalToken: address(usdc).toBytes32(), + executionMode: uint8(SponsoredCCTPInterface.ExecutionMode.DirectToCore), + actionData: bytes("") + }); + } + + /*////////////////////////////////////////////////////////////// + BASIC MESSAGE VALIDATION TESTS + //////////////////////////////////////////////////////////////*/ + + function test_ReceiveMessage_ValidQuote_Success() public { + SponsoredCCTPInterface.SponsoredCCTPQuote memory quote = createDefaultQuote(); + bytes memory signature = signQuote(quote, signerPrivateKey); + bytes memory message = createCCTPMessage(quote, FEE_EXECUTED); + bytes memory attestation = bytes("mock-attestation"); + + // Note: setUp already mocks HyperCore precompiles + // The actual event emitted is SimpleTransferFlowCompleted from HyperCoreFlowExecutor + periphery.receiveMessage(message, attestation, signature); + + // Verify nonce is marked as used + assertTrue(periphery.usedNonces(quote.nonce)); + } + + function test_ReceiveMessage_InvalidSignature_FallsBackToUnsponsoredFlow() public { + SponsoredCCTPInterface.SponsoredCCTPQuote memory quote = createDefaultQuote(); + + // Sign with wrong private key + uint256 wrongPrivateKey = 0x5678; + bytes memory wrongSignature = signQuote(quote, wrongPrivateKey); + + bytes memory message = createCCTPMessage(quote, FEE_EXECUTED); + bytes memory attestation = bytes("mock-attestation"); + + // Should not revert, but should process as unsponsored + periphery.receiveMessage(message, attestation, wrongSignature); + + // Nonce should NOT be marked as used since signature was invalid + assertFalse(periphery.usedNonces(quote.nonce)); + } + + function test_ReceiveMessage_ExpiredDeadline_FallsBackToUnsponsoredFlow() public { + vm.warp(block.timestamp + 2 hours); + SponsoredCCTPInterface.SponsoredCCTPQuote memory quote = createDefaultQuote(); + quote.deadline = block.timestamp - 1 hours; // Expired deadline + + bytes memory signature = signQuote(quote, signerPrivateKey); + bytes memory message = createCCTPMessage(quote, FEE_EXECUTED); + bytes memory attestation = bytes("mock-attestation"); + + // Should not revert, but should process as unsponsored + periphery.receiveMessage(message, attestation, signature); + + // Nonce should NOT be marked as used + assertFalse(periphery.usedNonces(quote.nonce)); + } + + function test_ReceiveMessage_DeadlineWithinBuffer_Success() public { + vm.warp(block.timestamp + 1 hours); + SponsoredCCTPInterface.SponsoredCCTPQuote memory quote = createDefaultQuote(); + + // Set deadline to 15 minutes ago (within 30 minute buffer) + quote.deadline = block.timestamp - 15 minutes; + + bytes memory signature = signQuote(quote, signerPrivateKey); + bytes memory message = createCCTPMessage(quote, FEE_EXECUTED); + bytes memory attestation = bytes("mock-attestation"); + + periphery.receiveMessage(message, attestation, signature); + + // Should be processed as valid since within buffer + assertTrue(periphery.usedNonces(quote.nonce)); + } + + function test_ReceiveMessage_DeadlineOutsideBuffer_FallsBack() public { + vm.warp(block.timestamp + 1 hours); + SponsoredCCTPInterface.SponsoredCCTPQuote memory quote = createDefaultQuote(); + + // Set deadline to 31 minutes ago (outside 30 minute buffer) + quote.deadline = block.timestamp - 31 minutes; + + bytes memory signature = signQuote(quote, signerPrivateKey); + bytes memory message = createCCTPMessage(quote, FEE_EXECUTED); + bytes memory attestation = bytes("mock-attestation"); + + periphery.receiveMessage(message, attestation, signature); + + // Should NOT be processed as valid + assertFalse(periphery.usedNonces(quote.nonce)); + } + + function test_ReceiveMessage_ReplayAttack_SecondAttemptNotSponsored() public { + SponsoredCCTPInterface.SponsoredCCTPQuote memory quote = createDefaultQuote(); + bytes memory signature = signQuote(quote, signerPrivateKey); + bytes memory message = createCCTPMessage(quote, FEE_EXECUTED); + bytes memory attestation = bytes("mock-attestation"); + + // First call - should succeed + periphery.receiveMessage(message, attestation, signature); + assertTrue(periphery.usedNonces(quote.nonce)); + + // Second call with same nonce - should process as unsponsored + // (CCTP prevents actual replay, but this tests nonce checking) + quote.deadline = block.timestamp + 2 hours; // Update deadline + bytes memory newSignature = signQuote(quote, signerPrivateKey); + bytes memory newMessage = createCCTPMessage(quote, FEE_EXECUTED); + + // This would fail at CCTP level in practice, but for testing our logic: + messageTransmitter.setShouldSucceed(true); + periphery.receiveMessage(newMessage, attestation, newSignature); + + // Nonce already used, so not considered valid + assertTrue(periphery.usedNonces(quote.nonce)); + } + + /*////////////////////////////////////////////////////////////// + MESSAGE DECODING TESTS + //////////////////////////////////////////////////////////////*/ + + function test_MessageDecoding_InvalidMintRecipient_KeepsFundsInContract() public { + SponsoredCCTPInterface.SponsoredCCTPQuote memory quote = createDefaultQuote(); + + // Set mint recipient to wrong address + quote.mintRecipient = bytes32(uint256(uint160(address(0x1234)))); + + bytes memory signature = signQuote(quote, signerPrivateKey); + bytes memory message = createCCTPMessage(quote, FEE_EXECUTED); + bytes memory attestation = bytes("mock-attestation"); + + uint256 balanceBefore = usdc.balanceOf(address(periphery)); + + // Should not revert, but message validation fails so funds stay in contract + periphery.receiveMessage(message, attestation, signature); + + uint256 balanceAfter = usdc.balanceOf(address(periphery)); + assertEq(balanceAfter, balanceBefore); + } + + function test_MessageDecoding_InvalidFinalRecipient_FailsValidation() public { + SponsoredCCTPInterface.SponsoredCCTPQuote memory quote = createDefaultQuote(); + + // Set invalid final recipient (has upper bits set) + quote.finalRecipient = bytes32(uint256(1) << 200); + + bytes memory signature = signQuote(quote, signerPrivateKey); + bytes memory message = createCCTPMessage(quote, FEE_EXECUTED); + bytes memory attestation = bytes("mock-attestation"); + + // Should not revert, but should fail validation + periphery.receiveMessage(message, attestation, signature); + + // Nonce should NOT be used since message validation failed + assertFalse(periphery.usedNonces(quote.nonce)); + } + + function test_MessageDecoding_InvalidFinalToken_FailsValidation() public { + SponsoredCCTPInterface.SponsoredCCTPQuote memory quote = createDefaultQuote(); + + // Set invalid final token (has upper bits set) + quote.finalToken = bytes32(uint256(1) << 200); + + bytes memory signature = signQuote(quote, signerPrivateKey); + bytes memory message = createCCTPMessage(quote, FEE_EXECUTED); + bytes memory attestation = bytes("mock-attestation"); + + // Should not revert, but should fail validation + periphery.receiveMessage(message, attestation, signature); + + // Nonce should NOT be used + assertFalse(periphery.usedNonces(quote.nonce)); + } + + function test_MessageDecoding_ExtractsCorrectQuoteData() public { + SponsoredCCTPInterface.SponsoredCCTPQuote memory quote = createDefaultQuote(); + quote.maxBpsToSponsor = 250; // 2.5% + quote.maxUserSlippageBps = 100; // 1% + + bytes memory signature = signQuote(quote, signerPrivateKey); + bytes memory message = createCCTPMessage(quote, FEE_EXECUTED); + bytes memory attestation = bytes("mock-attestation"); + + // Verify the message is processed successfully + periphery.receiveMessage(message, attestation, signature); + + // Verify nonce is marked as used, confirming successful processing + assertTrue(periphery.usedNonces(quote.nonce)); + } + + /*////////////////////////////////////////////////////////////// + EXECUTION MODE TESTS + //////////////////////////////////////////////////////////////*/ + + function test_ReceiveMessage_DirectToCore_Success() public { + SponsoredCCTPInterface.SponsoredCCTPQuote memory quote = createDefaultQuote(); + quote.executionMode = uint8(SponsoredCCTPInterface.ExecutionMode.DirectToCore); + + bytes memory signature = signQuote(quote, signerPrivateKey); + bytes memory message = createCCTPMessage(quote, FEE_EXECUTED); + bytes memory attestation = bytes("mock-attestation"); + + periphery.receiveMessage(message, attestation, signature); + + assertTrue(periphery.usedNonces(quote.nonce)); + } + + struct CompressedCall { + address target; + bytes callData; + } + + /*////////////////////////////////////////////////////////////// + ADMIN FUNCTION TESTS + //////////////////////////////////////////////////////////////*/ + + function test_SetSigner_OnlyAdmin() public { + address newSigner = makeAddr("newSigner"); + + vm.prank(admin); + periphery.setSigner(newSigner); + + assertEq(periphery.signer(), newSigner); + } + + function test_SetSigner_NotAdmin_Reverts() public { + address newSigner = makeAddr("newSigner"); + + vm.prank(user); + vm.expectRevert(); + periphery.setSigner(newSigner); + } + + function test_SetQuoteDeadlineBuffer_OnlyAdmin() public { + uint256 newBuffer = 1 hours; + + vm.prank(admin); + periphery.setQuoteDeadlineBuffer(newBuffer); + + assertEq(periphery.quoteDeadlineBuffer(), newBuffer); + } + + function test_SetQuoteDeadlineBuffer_NotAdmin_Reverts() public { + uint256 newBuffer = 1 hours; + + vm.prank(user); + vm.expectRevert(); + periphery.setQuoteDeadlineBuffer(newBuffer); + } + + function test_SetQuoteDeadlineBuffer_AffectsValidation() public { + // Set buffer to 0 + vm.prank(admin); + periphery.setQuoteDeadlineBuffer(0); + + SponsoredCCTPInterface.SponsoredCCTPQuote memory quote = createDefaultQuote(); + quote.deadline = block.timestamp - 1; // 1 second ago + + bytes memory signature = signQuote(quote, signerPrivateKey); + bytes memory message = createCCTPMessage(quote, FEE_EXECUTED); + bytes memory attestation = bytes("mock-attestation"); + + periphery.receiveMessage(message, attestation, signature); + + // Should NOT be processed as valid (no buffer) + assertFalse(periphery.usedNonces(quote.nonce)); + } + + /*////////////////////////////////////////////////////////////// + SIGNATURE VALIDATION TESTS + //////////////////////////////////////////////////////////////*/ + + function test_SignatureValidation_DifferentSigner_Fails() public { + // Change signer + address newSigner = makeAddr("newSigner"); + vm.prank(admin); + periphery.setSigner(newSigner); + + // Create quote signed with old signer + SponsoredCCTPInterface.SponsoredCCTPQuote memory quote = createDefaultQuote(); + bytes memory signature = signQuote(quote, signerPrivateKey); + bytes memory message = createCCTPMessage(quote, FEE_EXECUTED); + bytes memory attestation = bytes("mock-attestation"); + + periphery.receiveMessage(message, attestation, signature); + + // Should not be valid with different signer + assertFalse(periphery.usedNonces(quote.nonce)); + } + + function test_SignatureValidation_ModifiedAmount_Fails() public { + SponsoredCCTPInterface.SponsoredCCTPQuote memory quote = createDefaultQuote(); + quote.amount = 1000e6; + + // Sign quote + bytes memory signature = signQuote(quote, signerPrivateKey); + + // Modify amount in message + quote.amount = 2000e6; + bytes memory message = createCCTPMessage(quote, FEE_EXECUTED); + bytes memory attestation = bytes("mock-attestation"); + + periphery.receiveMessage(message, attestation, signature); + + // Should fail validation + assertFalse(periphery.usedNonces(quote.nonce)); + } + + function test_SignatureValidation_ModifiedRecipient_Fails() public { + SponsoredCCTPInterface.SponsoredCCTPQuote memory quote = createDefaultQuote(); + bytes memory signature = signQuote(quote, signerPrivateKey); + + // Modify final recipient + quote.finalRecipient = makeAddr("attacker").toBytes32(); + bytes memory message = createCCTPMessage(quote, FEE_EXECUTED); + bytes memory attestation = bytes("mock-attestation"); + + periphery.receiveMessage(message, attestation, signature); + + // Should fail validation + assertFalse(periphery.usedNonces(quote.nonce)); + } + + function test_SignatureValidation_ModifiedActionData_Fails() public { + SponsoredCCTPInterface.SponsoredCCTPQuote memory quote = createDefaultQuote(); + quote.executionMode = uint8(SponsoredCCTPInterface.ExecutionMode.ArbitraryActionsToCore); + + // Original action data + address[] memory targets = new address[](1); + targets[0] = address(usdc); + bytes[] memory callDatas = new bytes[](1); + callDatas[0] = abi.encodeWithSignature("transfer(address,uint256)", finalRecipient, 100e6); + quote.actionData = abi.encode(targets, callDatas); + + // Sign original quote + bytes memory signature = signQuote(quote, signerPrivateKey); + + // Modify action data + callDatas[0] = abi.encodeWithSignature("transfer(address,uint256)", makeAddr("attacker"), 100e6); + quote.actionData = abi.encode(targets, callDatas); + + bytes memory message = createCCTPMessage(quote, FEE_EXECUTED); + bytes memory attestation = bytes("mock-attestation"); + + periphery.receiveMessage(message, attestation, signature); + + // Should fail validation (actionData is hashed in signature) + assertFalse(periphery.usedNonces(quote.nonce)); + } + + /*////////////////////////////////////////////////////////////// + EDGE CASE TESTS + //////////////////////////////////////////////////////////////*/ + + function test_ReceiveMessage_ZeroAmount_HandledGracefully() public { + SponsoredCCTPInterface.SponsoredCCTPQuote memory quote = createDefaultQuote(); + quote.amount = 0; + + bytes memory signature = signQuote(quote, signerPrivateKey); + bytes memory message = createCCTPMessage(quote, 0); + bytes memory attestation = bytes("mock-attestation"); + + // Should not revert + periphery.receiveMessage(message, attestation, signature); + + assertTrue(periphery.usedNonces(quote.nonce)); + } + + function test_ReceiveMessage_EmptyActionData_DirectToCore() public { + SponsoredCCTPInterface.SponsoredCCTPQuote memory quote = createDefaultQuote(); + quote.executionMode = uint8(SponsoredCCTPInterface.ExecutionMode.DirectToCore); + quote.actionData = bytes(""); + + bytes memory signature = signQuote(quote, signerPrivateKey); + bytes memory message = createCCTPMessage(quote, FEE_EXECUTED); + bytes memory attestation = bytes("mock-attestation"); + + periphery.receiveMessage(message, attestation, signature); + + assertTrue(periphery.usedNonces(quote.nonce)); + } + + function test_ReceiveMessage_MultipleQuotesInSequence() public { + // Test multiple different quotes in sequence + for (uint256 i = 0; i < 5; i++) { + SponsoredCCTPInterface.SponsoredCCTPQuote memory quote = createDefaultQuote(); + quote.nonce = keccak256(abi.encodePacked("test-nonce", i)); + + bytes memory signature = signQuote(quote, signerPrivateKey); + bytes memory message = createCCTPMessage(quote, FEE_EXECUTED); + bytes memory attestation = bytes("mock-attestation"); + + periphery.receiveMessage(message, attestation, signature); + + assertTrue(periphery.usedNonces(quote.nonce)); + } + } + + function test_View_UsedNonces() public { + bytes32 testNonce = keccak256("test-view-nonce"); + assertFalse(periphery.usedNonces(testNonce)); + + SponsoredCCTPInterface.SponsoredCCTPQuote memory quote = createDefaultQuote(); + quote.nonce = testNonce; + + bytes memory signature = signQuote(quote, signerPrivateKey); + bytes memory message = createCCTPMessage(quote, FEE_EXECUTED); + bytes memory attestation = bytes("mock-attestation"); + + periphery.receiveMessage(message, attestation, signature); + + assertTrue(periphery.usedNonces(testNonce)); + } + + function test_View_ContractReferences() public { + assertEq(address(periphery.cctpMessageTransmitter()), address(messageTransmitter)); + assertEq(periphery.signer(), signer); + assertEq(periphery.quoteDeadlineBuffer(), 30 minutes); + assertEq(address(IHyperCoreFlowExecutor(address(periphery)).donationBox()), address(donationBox)); + } +} diff --git a/test/evm/foundry/local/external/hyper-evm-lib/src/CoreWriterLib.sol b/test/evm/foundry/local/external/hyper-evm-lib/src/CoreWriterLib.sol new file mode 100644 index 000000000..7a4d7a254 --- /dev/null +++ b/test/evm/foundry/local/external/hyper-evm-lib/src/CoreWriterLib.sol @@ -0,0 +1,202 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; + +import { PrecompileLib } from "./PrecompileLib.sol"; +import { HLConstants } from "./common/HLConstants.sol"; +import { HLConversions } from "./common/HLConversions.sol"; + +import { ICoreWriter } from "./interfaces/ICoreWriter.sol"; + +/** + * @title CoreWriterLib v1.0 + * @author Obsidian (https://x.com/ObsidianAudits) + * @notice A library for interacting with HyperEVM's CoreWriter + * + * @dev Additional functionality for: + * - Bridging assets between EVM and HyperCore + * - Converting decimal representations between EVM and HyperCore amounts + * - Security checks before sending actions to CoreWriter + */ +library CoreWriterLib { + using SafeERC20 for IERC20; + + ICoreWriter constant coreWriter = ICoreWriter(0x3333333333333333333333333333333333333333); + + error CoreWriterLib__StillLockedUntilTimestamp(uint64 lockedUntilTimestamp); + error CoreWriterLib__CannotSelfTransfer(); + error CoreWriterLib__HypeTransferFailed(); + error CoreWriterLib__CoreAmountTooLarge(uint256 amount); + error CoreWriterLib__EvmAmountTooSmall(uint256 amount); + + /*////////////////////////////////////////////////////////////// + EVM <---> Core Bridging + //////////////////////////////////////////////////////////////*/ + + function bridgeToCore(address tokenAddress, uint256 evmAmount) internal { + uint64 tokenIndex = PrecompileLib.getTokenIndex(tokenAddress); + bridgeToCore(tokenIndex, evmAmount); + } + + function bridgeToCore(uint64 token, uint256 evmAmount) internal { + // Check if amount would be 0 after conversion to prevent token loss + uint64 coreAmount = HLConversions.evmToWei(token, evmAmount); + if (coreAmount == 0) revert CoreWriterLib__EvmAmountTooSmall(evmAmount); + address systemAddress = getSystemAddress(token); + if (isHype(token)) { + (bool success, ) = systemAddress.call{ value: evmAmount }(""); + require(success, "HYPE transfer failed"); + } else { + PrecompileLib.TokenInfo memory info = PrecompileLib.tokenInfo(uint32(token)); + address tokenAddress = info.evmContract; + IERC20(tokenAddress).safeTransfer(systemAddress, evmAmount); + } + } + + function bridgeToEvm(address tokenAddress, uint256 evmAmount) internal { + uint64 tokenIndex = PrecompileLib.getTokenIndex(tokenAddress); + bridgeToEvm(tokenIndex, evmAmount, true); + } + + // NOTE: For bridging non-HYPE tokens, the contract must hold some HYPE on core (enough to cover the transfer gas), otherwise spotSend will fail + function bridgeToEvm(uint64 token, uint256 amount, bool isEvmAmount) internal { + address systemAddress = getSystemAddress(token); + + uint64 coreAmount; + if (isEvmAmount) { + coreAmount = HLConversions.evmToWei(token, amount); + if (coreAmount == 0) revert CoreWriterLib__EvmAmountTooSmall(amount); + } else { + if (amount > type(uint64).max) revert CoreWriterLib__CoreAmountTooLarge(amount); + coreAmount = uint64(amount); + } + + spotSend(systemAddress, token, coreAmount); + } + + function spotSend(address to, uint64 token, uint64 amountWei) internal { + // Self-transfers will always fail, so reverting here + require(to != address(this), "Cannot self-transfer"); + + coreWriter.sendRawAction( + abi.encodePacked(uint8(1), HLConstants.SPOT_SEND_ACTION, abi.encode(to, token, amountWei)) + ); + } + + /*////////////////////////////////////////////////////////////// + Bridging Utils + //////////////////////////////////////////////////////////////*/ + + function getSystemAddress(uint64 index) internal view returns (address) { + if (index == HLConstants.hypeTokenIndex()) { + return HLConstants.HYPE_SYSTEM_ADDRESS; + } + return address(HLConstants.BASE_SYSTEM_ADDRESS + index); + } + + function isHype(uint64 index) internal view returns (bool) { + return index == HLConstants.hypeTokenIndex(); + } + + /*////////////////////////////////////////////////////////////// + Staking + //////////////////////////////////////////////////////////////*/ + function delegateToken(address validator, uint64 amountWei, bool undelegate) internal { + coreWriter.sendRawAction( + abi.encodePacked(uint8(1), HLConstants.TOKEN_DELEGATE_ACTION, abi.encode(validator, amountWei, undelegate)) + ); + } + + function depositStake(uint64 amountWei) internal { + coreWriter.sendRawAction(abi.encodePacked(uint8(1), HLConstants.STAKING_DEPOSIT_ACTION, abi.encode(amountWei))); + } + + function withdrawStake(uint64 amountWei) internal { + coreWriter.sendRawAction( + abi.encodePacked(uint8(1), HLConstants.STAKING_WITHDRAW_ACTION, abi.encode(amountWei)) + ); + } + + /*////////////////////////////////////////////////////////////// + Trading + //////////////////////////////////////////////////////////////*/ + + function toMilliseconds(uint64 timestamp) internal pure returns (uint64) { + return timestamp * 1000; + } + + function _canWithdrawFromVault(address vault) internal view returns (bool, uint64) { + PrecompileLib.UserVaultEquity memory vaultEquity = PrecompileLib.userVaultEquity(address(this), vault); + + return ( + toMilliseconds(uint64(block.timestamp)) > vaultEquity.lockedUntilTimestamp, + vaultEquity.lockedUntilTimestamp + ); + } + + function vaultTransfer(address vault, bool isDeposit, uint64 usdAmount) internal { + if (!isDeposit) { + (bool canWithdraw, uint64 lockedUntilTimestamp) = _canWithdrawFromVault(vault); + + if (!canWithdraw) revert CoreWriterLib__StillLockedUntilTimestamp(lockedUntilTimestamp); + } + + coreWriter.sendRawAction( + abi.encodePacked(uint8(1), HLConstants.VAULT_TRANSFER_ACTION, abi.encode(vault, isDeposit, usdAmount)) + ); + } + + function transferUsdClass(uint64 ntl, bool toPerp) internal { + coreWriter.sendRawAction( + abi.encodePacked(uint8(1), HLConstants.USD_CLASS_TRANSFER_ACTION, abi.encode(ntl, toPerp)) + ); + } + + function placeLimitOrder( + uint32 asset, + bool isBuy, + uint64 limitPx, + uint64 sz, + bool reduceOnly, + uint8 encodedTif, + uint128 cloid + ) internal { + coreWriter.sendRawAction( + abi.encodePacked( + uint8(1), + HLConstants.LIMIT_ORDER_ACTION, + abi.encode(asset, isBuy, limitPx, sz, reduceOnly, encodedTif, cloid) + ) + ); + } + + function addApiWallet(address wallet, string memory name) internal { + coreWriter.sendRawAction( + abi.encodePacked(uint8(1), HLConstants.ADD_API_WALLET_ACTION, abi.encode(wallet, name)) + ); + } + + function cancelOrderByOrderId(uint32 asset, uint64 orderId) internal { + coreWriter.sendRawAction( + abi.encodePacked(uint8(1), HLConstants.CANCEL_ORDER_BY_OID_ACTION, abi.encode(asset, orderId)) + ); + } + + function cancelOrderByCloid(uint32 asset, uint128 cloid) internal { + coreWriter.sendRawAction( + abi.encodePacked(uint8(1), HLConstants.CANCEL_ORDER_BY_CLOID_ACTION, abi.encode(asset, cloid)) + ); + } + + function finalizeEvmContract(uint64 token, uint8 encodedVariant, uint64 createNonce) internal { + coreWriter.sendRawAction( + abi.encodePacked( + uint8(1), + HLConstants.FINALIZE_EVM_CONTRACT_ACTION, + abi.encode(token, encodedVariant, createNonce) + ) + ); + } +} diff --git a/test/evm/foundry/local/external/hyper-evm-lib/src/PrecompileLib.sol b/test/evm/foundry/local/external/hyper-evm-lib/src/PrecompileLib.sol new file mode 100644 index 000000000..166df1853 --- /dev/null +++ b/test/evm/foundry/local/external/hyper-evm-lib/src/PrecompileLib.sol @@ -0,0 +1,400 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import { ITokenRegistry } from "./interfaces/ITokenRegistry.sol"; +import { HLConstants } from "./common/HLConstants.sol"; + +/** + * @title PrecompileLib v1.0 + * @author Obsidian (https://x.com/ObsidianAudits) + * @notice A library with helper functions for interacting with HyperEVM's precompiles + */ +library PrecompileLib { + // Onchain record of token indices for each linked evm contract + ITokenRegistry constant REGISTRY = ITokenRegistry(0x0b51d1A9098cf8a72C325003F44C194D41d7A85B); + + /*////////////////////////////////////////////////////////////// + Custom Utility Functions + (Overloads accepting token address instead of index) + //////////////////////////////////////////////////////////////*/ + + /** + * @notice Gets TokenInfo for a given token address by looking up its index and fetching from the precompile. + * @dev Overload of tokenInfo(uint64 token) + */ + function tokenInfo(address tokenAddress) internal view returns (TokenInfo memory) { + uint64 index = getTokenIndex(tokenAddress); + return tokenInfo(index); + } + + /** + * @notice Gets SpotInfo for the token/USDC market using the token address. + * @dev Overload of spotInfo(uint64 tokenIndex) + * Finds the spot market where USDC (index 0) is the quote. + */ + function spotInfo(address tokenAddress) internal view returns (SpotInfo memory) { + uint64 tokenIndex = getTokenIndex(tokenAddress); + uint64 spotIndex = getSpotIndex(tokenIndex); + return spotInfo(spotIndex); + } + + /** + * @notice Gets the spot price for the token/USDC market using the token address. + * @dev Overload of spotPx(uint64 spotIndex) + */ + function spotPx(address tokenAddress) internal view returns (uint64) { + uint64 tokenIndex = getTokenIndex(tokenAddress); + uint64 spotIndex = getSpotIndex(tokenIndex); + return spotPx(spotIndex); + } + + /** + * @notice Gets a user's spot balance for a given token address. + * @dev Overload of spotBalance(address user, uint64 token) + */ + function spotBalance(address user, address tokenAddress) internal view returns (SpotBalance memory) { + uint64 tokenIndex = getTokenIndex(tokenAddress); + return spotBalance(user, tokenIndex); + } + + /** + * @notice Gets the index of a token from its address. Reverts if token is not linked to HyperCore. + */ + function getTokenIndex(address tokenAddress) internal view returns (uint64) { + return REGISTRY.getTokenIndex(tokenAddress); + } + + /** + * @notice Gets the spot market index for the token/USDC pair for a token using its address. + * @dev Overload of getSpotIndex(uint64 tokenIndex) + */ + function getSpotIndex(address tokenAddress) internal view returns (uint64) { + uint64 tokenIndex = getTokenIndex(tokenAddress); + return getSpotIndex(tokenIndex); + } + + /** + * @notice Gets the spot market index for a token. + * @dev If only one spot market exists, returns it. Otherwise, finds the spot market with USDC as the quote token. + */ + function getSpotIndex(uint64 tokenIndex) internal view returns (uint64) { + uint64[] memory spots = tokenInfo(tokenIndex).spots; + + if (spots.length == 1) return spots[0]; + + for (uint256 idx = 0; idx < spots.length; idx++) { + SpotInfo memory spot = spotInfo(spots[idx]); + if (spot.tokens[1] == 0) { + // index 0 = USDC + return spots[idx]; + } + } + revert PrecompileLib__SpotIndexNotFound(); + } + + /*////////////////////////////////////////////////////////////// + Using Alternate Quote Token (non USDC) + //////////////////////////////////////////////////////////////*/ + + /** + * @notice Gets the spot market index for a token/quote pair. + * Iterates all spot markets for the token and matches the quote token index. + * @dev Overload of getSpotIndex(uint64 tokenIndex) + */ + function getSpotIndex(uint64 tokenIndex, uint64 quoteTokenIndex) internal view returns (uint64) { + uint64[] memory spots = tokenInfo(tokenIndex).spots; + + for (uint256 idx = 0; idx < spots.length; idx++) { + SpotInfo memory spot = spotInfo(spots[idx]); + if (spot.tokens[1] == quoteTokenIndex) { + return spots[idx]; + } + } + revert PrecompileLib__SpotIndexNotFound(); + } + + /** + * @notice Gets SpotInfo for a token/quote pair using token addresses. + * Looks up both token and quote indices, then finds the spot market. + * @dev Overload of spotInfo(uint64 spotIndex) + */ + function spotInfo(address token, address quoteToken) internal view returns (SpotInfo memory) { + uint64 tokenIndex = getTokenIndex(token); + uint64 quoteTokenIndex = getTokenIndex(quoteToken); + uint64 spotIndex = getSpotIndex(tokenIndex, quoteTokenIndex); + return spotInfo(spotIndex); + } + + /** + * @notice Gets the spot price for a token/quote pair using token addresses. + * Looks up both token and quote indices, then finds the spot market. + * @dev Overload of spotPx(uint64 spotIndex) + */ + function spotPx(address token, address quoteToken) internal view returns (uint64) { + uint64 tokenIndex = getTokenIndex(token); + uint64 quoteTokenIndex = getTokenIndex(quoteToken); + uint64 spotIndex = getSpotIndex(tokenIndex, quoteTokenIndex); + return spotPx(spotIndex); + } + + /*////////////////////////////////////////////////////////////// + Price decimals normalization + //////////////////////////////////////////////////////////////*/ + + // returns spot price as a fixed-point integer with 8 decimals + function normalizedSpotPx(uint64 spotIndex) internal view returns (uint256) { + SpotInfo memory info = spotInfo(spotIndex); + uint8 baseSzDecimals = tokenInfo(info.tokens[0]).szDecimals; + return spotPx(spotIndex) * 10 ** baseSzDecimals; + } + + // returns mark price as a fixed-point integer with 6 decimals + function normalizedMarkPx(uint32 perpIndex) internal view returns (uint256) { + PerpAssetInfo memory info = perpAssetInfo(perpIndex); + return markPx(perpIndex) * 10 ** info.szDecimals; + } + + // returns perp oracle price as a fixed-point integer with 6 decimals + function normalizedOraclePx(uint32 perpIndex) internal view returns (uint256) { + PerpAssetInfo memory info = perpAssetInfo(perpIndex); + return oraclePx(perpIndex) * 10 ** info.szDecimals; + } + + /*////////////////////////////////////////////////////////////// + Precompile Calls + //////////////////////////////////////////////////////////////*/ + + function position(address user, uint16 perp) internal view returns (Position memory) { + (bool success, bytes memory result) = HLConstants.POSITION_PRECOMPILE_ADDRESS.staticcall( + abi.encode(user, perp) + ); + if (!success) revert PrecompileLib__PositionPrecompileFailed(); + return abi.decode(result, (Position)); + } + + function spotBalance(address user, uint64 token) internal view returns (SpotBalance memory) { + (bool success, bytes memory result) = HLConstants.SPOT_BALANCE_PRECOMPILE_ADDRESS.staticcall( + abi.encode(user, token) + ); + if (!success) revert PrecompileLib__SpotBalancePrecompileFailed(); + return abi.decode(result, (SpotBalance)); + } + + function userVaultEquity(address user, address vault) internal view returns (UserVaultEquity memory) { + (bool success, bytes memory result) = HLConstants.VAULT_EQUITY_PRECOMPILE_ADDRESS.staticcall( + abi.encode(user, vault) + ); + if (!success) revert PrecompileLib__VaultEquityPrecompileFailed(); + return abi.decode(result, (UserVaultEquity)); + } + + function withdrawable(address user) internal view returns (uint64) { + (bool success, bytes memory result) = HLConstants.WITHDRAWABLE_PRECOMPILE_ADDRESS.staticcall(abi.encode(user)); + if (!success) revert PrecompileLib__WithdrawablePrecompileFailed(); + return abi.decode(result, (Withdrawable)).withdrawable; + } + + function delegations(address user) internal view returns (Delegation[] memory) { + (bool success, bytes memory result) = HLConstants.DELEGATIONS_PRECOMPILE_ADDRESS.staticcall(abi.encode(user)); + if (!success) revert PrecompileLib__DelegationsPrecompileFailed(); + return abi.decode(result, (Delegation[])); + } + + function delegatorSummary(address user) internal view returns (DelegatorSummary memory) { + (bool success, bytes memory result) = HLConstants.DELEGATOR_SUMMARY_PRECOMPILE_ADDRESS.staticcall( + abi.encode(user) + ); + if (!success) revert PrecompileLib__DelegatorSummaryPrecompileFailed(); + return abi.decode(result, (DelegatorSummary)); + } + + function markPx(uint32 perpIndex) internal view returns (uint64) { + (bool success, bytes memory result) = HLConstants.MARK_PX_PRECOMPILE_ADDRESS.staticcall(abi.encode(perpIndex)); + if (!success) revert PrecompileLib__MarkPxPrecompileFailed(); + return abi.decode(result, (uint64)); + } + + function oraclePx(uint32 perpIndex) internal view returns (uint64) { + (bool success, bytes memory result) = HLConstants.ORACLE_PX_PRECOMPILE_ADDRESS.staticcall( + abi.encode(perpIndex) + ); + if (!success) revert PrecompileLib__OraclePxPrecompileFailed(); + return abi.decode(result, (uint64)); + } + + function spotPx(uint64 spotIndex) internal view returns (uint64) { + (bool success, bytes memory result) = HLConstants.SPOT_PX_PRECOMPILE_ADDRESS.staticcall(abi.encode(spotIndex)); + if (!success) revert PrecompileLib__SpotPxPrecompileFailed(); + return abi.decode(result, (uint64)); + } + + function perpAssetInfo(uint32 perp) internal view returns (PerpAssetInfo memory) { + (bool success, bytes memory result) = HLConstants.PERP_ASSET_INFO_PRECOMPILE_ADDRESS.staticcall( + abi.encode(perp) + ); + if (!success) revert PrecompileLib__PerpAssetInfoPrecompileFailed(); + return abi.decode(result, (PerpAssetInfo)); + } + + function spotInfo(uint64 spotIndex) internal view returns (SpotInfo memory) { + (bool success, bytes memory result) = HLConstants.SPOT_INFO_PRECOMPILE_ADDRESS.staticcall( + abi.encode(spotIndex) + ); + if (!success) revert PrecompileLib__SpotInfoPrecompileFailed(); + return abi.decode(result, (SpotInfo)); + } + + function tokenInfo(uint64 token) internal view returns (TokenInfo memory) { + (bool success, bytes memory result) = HLConstants.TOKEN_INFO_PRECOMPILE_ADDRESS.staticcall(abi.encode(token)); + if (!success) revert PrecompileLib__TokenInfoPrecompileFailed(); + return abi.decode(result, (TokenInfo)); + } + + function tokenSupply(uint64 token) internal view returns (TokenSupply memory) { + (bool success, bytes memory result) = HLConstants.TOKEN_SUPPLY_PRECOMPILE_ADDRESS.staticcall(abi.encode(token)); + if (!success) revert PrecompileLib__TokenSupplyPrecompileFailed(); + return abi.decode(result, (TokenSupply)); + } + + function l1BlockNumber() internal view returns (uint64) { + (bool success, bytes memory result) = HLConstants.L1_BLOCK_NUMBER_PRECOMPILE_ADDRESS.staticcall(abi.encode()); + if (!success) revert PrecompileLib__L1BlockNumberPrecompileFailed(); + return abi.decode(result, (uint64)); + } + + function bbo(uint64 asset) internal view returns (Bbo memory) { + (bool success, bytes memory result) = HLConstants.BBO_PRECOMPILE_ADDRESS.staticcall(abi.encode(asset)); + if (!success) revert PrecompileLib__BboPrecompileFailed(); + return abi.decode(result, (Bbo)); + } + + function accountMarginSummary( + uint32 perpDexIndex, + address user + ) internal view returns (AccountMarginSummary memory) { + (bool success, bytes memory result) = HLConstants.ACCOUNT_MARGIN_SUMMARY_PRECOMPILE_ADDRESS.staticcall( + abi.encode(perpDexIndex, user) + ); + if (!success) revert PrecompileLib__AccountMarginSummaryPrecompileFailed(); + return abi.decode(result, (AccountMarginSummary)); + } + + function coreUserExists(address user) internal view returns (bool) { + (bool success, bytes memory result) = HLConstants.CORE_USER_EXISTS_PRECOMPILE_ADDRESS.staticcall( + abi.encode(user) + ); + if (!success) revert PrecompileLib__CoreUserExistsPrecompileFailed(); + return abi.decode(result, (CoreUserExists)).exists; + } + + /*////////////////////////////////////////////////////////////// + Structs + //////////////////////////////////////////////////////////////*/ + struct Position { + int64 szi; + uint64 entryNtl; + int64 isolatedRawUsd; + uint32 leverage; + bool isIsolated; + } + + struct SpotBalance { + uint64 total; + uint64 hold; + uint64 entryNtl; + } + + struct UserVaultEquity { + uint64 equity; + uint64 lockedUntilTimestamp; + } + + struct Withdrawable { + uint64 withdrawable; + } + + struct Delegation { + address validator; + uint64 amount; + uint64 lockedUntilTimestamp; + } + + struct DelegatorSummary { + uint64 delegated; + uint64 undelegated; + uint64 totalPendingWithdrawal; + uint64 nPendingWithdrawals; + } + + struct PerpAssetInfo { + string coin; + uint32 marginTableId; + uint8 szDecimals; + uint8 maxLeverage; + bool onlyIsolated; + } + + struct SpotInfo { + string name; + uint64[2] tokens; + } + + struct TokenInfo { + string name; + uint64[] spots; + uint64 deployerTradingFeeShare; + address deployer; + address evmContract; + uint8 szDecimals; + uint8 weiDecimals; + int8 evmExtraWeiDecimals; + } + + struct UserBalance { + address user; + uint64 balance; + } + + struct TokenSupply { + uint64 maxSupply; + uint64 totalSupply; + uint64 circulatingSupply; + uint64 futureEmissions; + UserBalance[] nonCirculatingUserBalances; + } + + struct Bbo { + uint64 bid; + uint64 ask; + } + + struct AccountMarginSummary { + int64 accountValue; + uint64 marginUsed; + uint64 ntlPos; + int64 rawUsd; + } + + struct CoreUserExists { + bool exists; + } + + error PrecompileLib__PositionPrecompileFailed(); + error PrecompileLib__SpotBalancePrecompileFailed(); + error PrecompileLib__VaultEquityPrecompileFailed(); + error PrecompileLib__WithdrawablePrecompileFailed(); + error PrecompileLib__DelegationsPrecompileFailed(); + error PrecompileLib__DelegatorSummaryPrecompileFailed(); + error PrecompileLib__MarkPxPrecompileFailed(); + error PrecompileLib__OraclePxPrecompileFailed(); + error PrecompileLib__SpotPxPrecompileFailed(); + error PrecompileLib__PerpAssetInfoPrecompileFailed(); + error PrecompileLib__SpotInfoPrecompileFailed(); + error PrecompileLib__TokenInfoPrecompileFailed(); + error PrecompileLib__TokenSupplyPrecompileFailed(); + error PrecompileLib__L1BlockNumberPrecompileFailed(); + error PrecompileLib__BboPrecompileFailed(); + error PrecompileLib__AccountMarginSummaryPrecompileFailed(); + error PrecompileLib__CoreUserExistsPrecompileFailed(); + error PrecompileLib__SpotIndexNotFound(); +} diff --git a/test/evm/foundry/local/external/hyper-evm-lib/src/common/HLConstants.sol b/test/evm/foundry/local/external/hyper-evm-lib/src/common/HLConstants.sol new file mode 100644 index 000000000..cbccb2840 --- /dev/null +++ b/test/evm/foundry/local/external/hyper-evm-lib/src/common/HLConstants.sol @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import { ICoreWriter } from "../interfaces/ICoreWriter.sol"; + +library HLConstants { + /*////////////////////////////////////////////////////////////// + Addresses + //////////////////////////////////////////////////////////////*/ + + address constant POSITION_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000000800; + address constant SPOT_BALANCE_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000000801; + address constant VAULT_EQUITY_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000000802; + address constant WITHDRAWABLE_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000000803; + address constant DELEGATIONS_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000000804; + address constant DELEGATOR_SUMMARY_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000000805; + address constant MARK_PX_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000000806; + address constant ORACLE_PX_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000000807; + address constant SPOT_PX_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000000808; + address constant L1_BLOCK_NUMBER_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000000809; + address constant PERP_ASSET_INFO_PRECOMPILE_ADDRESS = 0x000000000000000000000000000000000000080a; + address constant SPOT_INFO_PRECOMPILE_ADDRESS = 0x000000000000000000000000000000000000080b; + address constant TOKEN_INFO_PRECOMPILE_ADDRESS = 0x000000000000000000000000000000000000080C; + address constant TOKEN_SUPPLY_PRECOMPILE_ADDRESS = 0x000000000000000000000000000000000000080D; + address constant BBO_PRECOMPILE_ADDRESS = 0x000000000000000000000000000000000000080e; + address constant ACCOUNT_MARGIN_SUMMARY_PRECOMPILE_ADDRESS = 0x000000000000000000000000000000000000080F; + address constant CORE_USER_EXISTS_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000000810; + + uint160 constant BASE_SYSTEM_ADDRESS = uint160(0x2000000000000000000000000000000000000000); + address constant HYPE_SYSTEM_ADDRESS = 0x2222222222222222222222222222222222222222; + + uint8 constant HYPE_EVM_EXTRA_DECIMALS = 10; + + /*////////////////////////////////////////////////////////////// + HYPE Token Index + //////////////////////////////////////////////////////////////*/ + function hypeTokenIndex() internal view returns (uint64) { + return block.chainid == 998 ? 1105 : 150; + } + + function isHype(uint64 index) internal view returns (bool) { + return index == hypeTokenIndex(); + } + + /*////////////////////////////////////////////////////////////// + CoreWriter Actions + //////////////////////////////////////////////////////////////*/ + + uint24 constant LIMIT_ORDER_ACTION = 1; + uint24 constant VAULT_TRANSFER_ACTION = 2; + + uint24 constant TOKEN_DELEGATE_ACTION = 3; + uint24 constant STAKING_DEPOSIT_ACTION = 4; + uint24 constant STAKING_WITHDRAW_ACTION = 5; + + uint24 constant SPOT_SEND_ACTION = 6; + uint24 constant USD_CLASS_TRANSFER_ACTION = 7; + + uint24 constant FINALIZE_EVM_CONTRACT_ACTION = 8; + uint24 constant ADD_API_WALLET_ACTION = 9; + uint24 constant CANCEL_ORDER_BY_OID_ACTION = 10; + uint24 constant CANCEL_ORDER_BY_CLOID_ACTION = 11; + + /*////////////////////////////////////////////////////////////// + Limit Order Time in Force + //////////////////////////////////////////////////////////////*/ + + uint8 public constant LIMIT_ORDER_TIF_ALO = 1; + uint8 public constant LIMIT_ORDER_TIF_GTC = 2; + uint8 public constant LIMIT_ORDER_TIF_IOC = 3; +} diff --git a/test/evm/foundry/local/external/hyper-evm-lib/src/common/HLConversions.sol b/test/evm/foundry/local/external/hyper-evm-lib/src/common/HLConversions.sol new file mode 100644 index 000000000..b473e5515 --- /dev/null +++ b/test/evm/foundry/local/external/hyper-evm-lib/src/common/HLConversions.sol @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import { HLConstants } from "./HLConstants.sol"; +import { PrecompileLib } from "../PrecompileLib.sol"; +import { SafeCast } from "@openzeppelin/contracts/utils/math/SafeCast.sol"; + +library HLConversions { + error HLConversions__InvalidToken(uint64 token); + + /** + * @dev Converts an EVM amount to a Core (wei) amount, handling both positive and negative extra decimals + * Note: If evmExtraWeiDecimals > 0, and evmAmount < 10**evmExtraWeiDecimals, the result will be 0 + */ + function evmToWei(uint64 token, uint256 evmAmount) internal view returns (uint64) { + PrecompileLib.TokenInfo memory info = PrecompileLib.tokenInfo(uint32(token)); + + if (info.evmContract != address(0)) { + if (info.evmExtraWeiDecimals > 0) { + uint256 amount = evmAmount / (10 ** uint8(info.evmExtraWeiDecimals)); + return SafeCast.toUint64(amount); + } else if (info.evmExtraWeiDecimals < 0) { + uint256 amount = evmAmount * (10 ** uint8(-info.evmExtraWeiDecimals)); + return SafeCast.toUint64(amount); + } + } else if (HLConstants.isHype(token)) { + return SafeCast.toUint64(evmAmount / (10 ** HLConstants.HYPE_EVM_EXTRA_DECIMALS)); + } + + revert HLConversions__InvalidToken(token); + } + + function weiToEvm(uint64 token, uint64 amountWei) internal view returns (uint256) { + PrecompileLib.TokenInfo memory info = PrecompileLib.tokenInfo(uint32(token)); + if (info.evmContract != address(0)) { + if (info.evmExtraWeiDecimals > 0) { + return (uint256(amountWei) * (10 ** uint8(info.evmExtraWeiDecimals))); + } else if (info.evmExtraWeiDecimals < 0) { + return amountWei / (10 ** uint8(-info.evmExtraWeiDecimals)); + } + } else if (HLConstants.isHype(token)) { + return (uint256(amountWei) * (10 ** HLConstants.HYPE_EVM_EXTRA_DECIMALS)); + } + + revert HLConversions__InvalidToken(token); + } + + function szToWei(uint64 token, uint64 sz) internal view returns (uint64) { + PrecompileLib.TokenInfo memory info = PrecompileLib.tokenInfo(uint32(token)); + return sz * uint64(10 ** (info.weiDecimals - info.szDecimals)); + } + + function weiToSz(uint64 token, uint64 amountWei) internal view returns (uint64) { + PrecompileLib.TokenInfo memory info = PrecompileLib.tokenInfo(uint32(token)); + return amountWei / uint64(10 ** (info.weiDecimals - info.szDecimals)); + } + + // for USDC between spot and perp + function weiToPerp(uint64 amountWei) internal pure returns (uint64) { + return amountWei / 10 ** 2; + } + + function perpToWei(uint64 perpAmount) internal pure returns (uint64) { + return perpAmount * 10 ** 2; + } + + function spotToAssetId(uint64 spot) internal pure returns (uint64) { + return spot + 10000; + } + + function assetToSpotId(uint64 asset) internal pure returns (uint64) { + return asset - 10000; + } +} diff --git a/test/evm/foundry/local/external/hyper-evm-lib/src/interfaces/ICoreWriter.sol b/test/evm/foundry/local/external/hyper-evm-lib/src/interfaces/ICoreWriter.sol new file mode 100644 index 000000000..3dfb730cc --- /dev/null +++ b/test/evm/foundry/local/external/hyper-evm-lib/src/interfaces/ICoreWriter.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +interface ICoreWriter { + function sendRawAction(bytes calldata data) external; +} diff --git a/test/evm/foundry/local/external/hyper-evm-lib/src/interfaces/ITokenRegistry.sol b/test/evm/foundry/local/external/hyper-evm-lib/src/interfaces/ITokenRegistry.sol new file mode 100644 index 000000000..e4fee44db --- /dev/null +++ b/test/evm/foundry/local/external/hyper-evm-lib/src/interfaces/ITokenRegistry.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +interface ITokenRegistry { + function getTokenIndex(address evmContract) external view returns (uint32 index); +} diff --git a/test/evm/foundry/local/external/hyper-evm-lib/test/BaseSimulatorTest.sol b/test/evm/foundry/local/external/hyper-evm-lib/test/BaseSimulatorTest.sol new file mode 100644 index 000000000..ad4bd64e7 --- /dev/null +++ b/test/evm/foundry/local/external/hyper-evm-lib/test/BaseSimulatorTest.sol @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import { Test } from "forge-std/Test.sol"; +import { PrecompileLib } from "../src/PrecompileLib.sol"; +import { CoreWriterLib } from "../src/CoreWriterLib.sol"; +import { HLConversions } from "../src/common/HLConversions.sol"; +import { HLConstants } from "../src/common/HLConstants.sol"; +import { HyperCore } from "./simulation/HyperCore.sol"; +import { CoreSimulatorLib } from "./simulation/CoreSimulatorLib.sol"; + +/** + * @title BaseSimulatorTest + * @notice Base test contract that sets up the HyperCore simulation + */ +abstract contract BaseSimulatorTest is Test { + using PrecompileLib for address; + using HLConversions for *; + + HyperCore public hyperCore; + + // Common token addresses + address public constant USDT0 = 0xB8CE59FC3717ada4C02eaDF9682A9e934F625ebb; + address public constant uBTC = 0x9FDBdA0A5e284c32744D2f17Ee5c74B284993463; + address public constant uETH = 0xBe6727B535545C67d5cAa73dEa54865B92CF7907; + address public constant uSOL = 0x068f321Fa8Fb9f0D135f290Ef6a3e2813e1c8A29; + + // Common token indices + uint64 public constant USDC_TOKEN = 0; + uint64 public constant HYPE_TOKEN = 150; + + address user = makeAddr("user"); + + function setUp() public virtual { + string memory hyperliquidRpc = "https://rpc.hyperliquid.xyz/evm"; + vm.createSelectFork(hyperliquidRpc); + + hyperCore = CoreSimulatorLib.init(); + + hyperCore.forceAccountActivation(user); + hyperCore.forceSpot(user, USDC_TOKEN, 1000e8); + hyperCore.forcePerpBalance(user, 1000e6); + } +} diff --git a/test/evm/foundry/local/external/hyper-evm-lib/test/simulation/CoreSimulatorLib.sol b/test/evm/foundry/local/external/hyper-evm-lib/test/simulation/CoreSimulatorLib.sol new file mode 100644 index 000000000..ce380261d --- /dev/null +++ b/test/evm/foundry/local/external/hyper-evm-lib/test/simulation/CoreSimulatorLib.sol @@ -0,0 +1,198 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import { Vm } from "forge-std/Vm.sol"; + +import { HyperCore } from "./HyperCore.sol"; +import { CoreWriterSim } from "./CoreWriterSim.sol"; +import { PrecompileSim } from "./PrecompileSim.sol"; + +import { HLConstants } from "../../src/PrecompileLib.sol"; + +Vm constant vm = Vm(address(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D)); +CoreWriterSim constant coreWriter = CoreWriterSim(0x3333333333333333333333333333333333333333); + +contract HypeSystemContract { + receive() external payable { + coreWriter.nativeTransferCallback{ value: msg.value }(msg.sender, msg.sender, msg.value); + } +} + +/** + * @title CoreSimulatorLib + * @dev A library used to simulate HyperCore functionality in foundry tests + */ +library CoreSimulatorLib { + uint256 constant NUM_PRECOMPILES = 17; + + HyperCore constant hyperCore = HyperCore(payable(0x9999999999999999999999999999999999999999)); + + // ERC20 Transfer event signature + bytes32 constant TRANSFER_EVENT_SIG = keccak256("Transfer(address,address,uint256)"); + + function init() internal returns (HyperCore) { + vm.pauseGasMetering(); + + HyperCore coreImpl = new HyperCore(); + + vm.etch(address(hyperCore), address(coreImpl).code); + vm.etch(address(coreWriter), type(CoreWriterSim).runtimeCode); + + // Initialize precompiles + for (uint160 i = 0; i < NUM_PRECOMPILES; i++) { + address precompileAddress = address(uint160(0x0000000000000000000000000000000000000800) + i); + vm.etch(precompileAddress, type(PrecompileSim).runtimeCode); + vm.allowCheatcodes(precompileAddress); + } + + // System addresses + address hypeSystemAddress = address(0x2222222222222222222222222222222222222222); + vm.etch(hypeSystemAddress, type(HypeSystemContract).runtimeCode); + + // Start recording logs for token transfer tracking + vm.recordLogs(); + + vm.allowCheatcodes(address(hyperCore)); + vm.allowCheatcodes(address(coreWriter)); + + vm.resumeGasMetering(); + + return hyperCore; + } + + function nextBlock() internal { + // Get all recorded logs + Vm.Log[] memory entries = vm.getRecordedLogs(); + + // Process any ERC20 transfers to system addresses (EVM->Core transfers are processed before CoreWriter actions) + for (uint256 i = 0; i < entries.length; i++) { + Vm.Log memory entry = entries[i]; + + // Check if it's a Transfer event + if (entry.topics[0] == TRANSFER_EVENT_SIG) { + address from = address(uint160(uint256(entry.topics[1]))); + address to = address(uint160(uint256(entry.topics[2]))); + uint256 amount = abi.decode(entry.data, (uint256)); + + // Check if destination is a system address + if (isSystemAddress(to)) { + uint64 tokenIndex = getTokenIndexFromSystemAddress(to); + + // Call tokenTransferCallback on HyperCoreWrite + hyperCore.executeTokenTransfer(address(0), tokenIndex, from, amount); + } + } + } + + // Clear recorded logs for next block + vm.recordLogs(); + + // Advance block + vm.roll(block.number + 1); + vm.warp(block.timestamp + 1); + + // liquidate any positions that are liquidatable + hyperCore.liquidatePositions(); + + // Process any pending actions + coreWriter.executeQueuedActions(); + + // Process pending orders + hyperCore.processPendingOrders(); + } + + ////// TESTING CONFIG SETTERS ///////// + + function setRevertOnFailure(bool _revertOnFailure) internal { + coreWriter.setRevertOnFailure(_revertOnFailure); + } + + // cheatcodes // + function forceAccountActivation(address account) internal { + hyperCore.forceAccountActivation(account); + } + + function forceSpot(address account, uint64 token, uint64 _wei) internal { + hyperCore.forceSpot(account, token, _wei); + } + + function forceTokenInfo( + uint64 index, + string memory name, + address evmContract, + uint8 szDecimals, + uint8 weiDecimals, + int8 evmExtraWeiDecimals + ) internal { + hyperCore.forceTokenInfo(index, name, evmContract, szDecimals, weiDecimals, evmExtraWeiDecimals); + } + + function forcePerpBalance(address account, uint64 usd) internal { + hyperCore.forcePerpBalance(account, usd); + } + + function forceStaking(address account, uint64 _wei) internal { + hyperCore.forceStaking(account, _wei); + } + + function forceDelegation(address account, address validator, uint64 amount, uint64 lockedUntilTimestamp) internal { + hyperCore.forceDelegation(account, validator, amount, lockedUntilTimestamp); + } + + function forceVaultEquity(address account, address vault, uint64 usd, uint64 lockedUntilTimestamp) internal { + hyperCore.forceVaultEquity(account, vault, usd, lockedUntilTimestamp); + } + + function setMarkPx(uint32 perp, uint64 markPx) internal { + hyperCore.setMarkPx(perp, markPx); + } + + function setMarkPx(uint32 perp, uint64 priceDiffBps, bool isIncrease) internal { + hyperCore.setMarkPx(perp, priceDiffBps, isIncrease); + } + + function setSpotPx(uint32 spotMarketId, uint64 spotPx) internal { + hyperCore.setSpotPx(spotMarketId, spotPx); + } + + function setSpotPx(uint32 spotMarketId, uint64 priceDiffBps, bool isIncrease) internal { + hyperCore.setSpotPx(spotMarketId, priceDiffBps, isIncrease); + } + + function setVaultMultiplier(address vault, uint64 multiplier) internal { + hyperCore.setVaultMultiplier(vault, multiplier); + } + + ///// VIEW AND PURE ///////// + + function isSystemAddress(address addr) internal view returns (bool) { + // Check if it's the HYPE system address + if (addr == address(0x2222222222222222222222222222222222222222)) { + return true; + } + + // Check if it's a token system address (0x2000...0000 + index) + uint160 baseAddr = uint160(0x2000000000000000000000000000000000000000); + uint160 addrInt = uint160(addr); + + if (addrInt >= baseAddr && addrInt < baseAddr + 10000) { + uint64 tokenIndex = uint64(addrInt - baseAddr); + + return tokenExists(tokenIndex); + } + + return false; + } + + function getTokenIndexFromSystemAddress(address systemAddr) internal pure returns (uint64) { + if (systemAddr == address(0x2222222222222222222222222222222222222222)) { + return 150; // HYPE token index + } + return uint64(uint160(systemAddr) - uint160(0x2000000000000000000000000000000000000000)); + } + + function tokenExists(uint64 token) internal view returns (bool) { + (bool success, ) = HLConstants.TOKEN_INFO_PRECOMPILE_ADDRESS.staticcall(abi.encode(token)); + return success; + } +} diff --git a/test/evm/foundry/local/external/hyper-evm-lib/test/simulation/CoreWriterSim.sol b/test/evm/foundry/local/external/hyper-evm-lib/test/simulation/CoreWriterSim.sol new file mode 100644 index 000000000..2c3e014ef --- /dev/null +++ b/test/evm/foundry/local/external/hyper-evm-lib/test/simulation/CoreWriterSim.sol @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.24; + +import { Heap } from "@openzeppelin/contracts/utils/structs/Heap.sol"; +import { Address } from "@openzeppelin/contracts/utils/Address.sol"; +import { HyperCore, CoreExecution } from "./HyperCore.sol"; + +contract CoreWriterSim { + using Address for address; + using Heap for Heap.Uint256Heap; + + uint128 private _sequence; + + Heap.Uint256Heap private _actionQueue; + + struct Action { + uint256 timestamp; + bytes data; + uint256 value; + } + + mapping(uint256 id => Action) _actions; + + event RawAction(address indexed user, bytes data); + + HyperCore constant _hyperCore = HyperCore(payable(0x9999999999999999999999999999999999999999)); + + /////// testing config + ///////////////////////// + bool revertOnFailure; + + function setRevertOnFailure(bool _revertOnFailure) public { + revertOnFailure = _revertOnFailure; + } + + function enqueueAction(bytes memory data, uint256 value) public { + enqueueAction(block.timestamp, data, value); + } + + function enqueueAction(uint256 timestamp, bytes memory data, uint256 value) public { + uint256 uniqueId = (uint256(timestamp) << 128) | uint256(_sequence++); + + _actions[uniqueId] = Action(timestamp, data, value); + _actionQueue.insert(uniqueId); + } + + function executeQueuedActions() external { + while (_actionQueue.length() > 0) { + Action memory action = _actions[_actionQueue.peek()]; + + // the action queue is a priority queue so the timestamp takes precedence in the + // ordering which means we can safely stop processing if the actions are delayed + if (action.timestamp > block.timestamp) { + break; + } + + (bool success, ) = address(_hyperCore).call{ value: action.value }(action.data); + + if (revertOnFailure && !success) { + revert("CoreWriter action failed: Reverting due to revertOnFailure flag"); + } + + _actionQueue.pop(); + } + + _hyperCore.processStakingWithdrawals(); + } + + function tokenTransferCallback(uint64 token, address from, uint256 value) public { + // there's a special case when transferring to the L1 via the system address which + // is that the balance isn't reflected on the L1 until after the EVM block has finished + // and the subsequent EVM block has been processed, this means that the balance can be + // in limbo for the user + tokenTransferCallback(msg.sender, token, from, value); + } + + function tokenTransferCallback(address sender, uint64 token, address from, uint256 value) public { + enqueueAction(abi.encodeCall(CoreExecution.executeTokenTransfer, (sender, token, from, value)), 0); + } + + function nativeTransferCallback(address sender, address from, uint256 value) public payable { + enqueueAction(abi.encodeCall(CoreExecution.executeNativeTransfer, (sender, from, value)), value); + } + + function sendRawAction(bytes calldata data) external { + uint8 version = uint8(data[0]); + require(version == 1); + + uint24 kind = (uint24(uint8(data[1])) << 16) | (uint24(uint8(data[2])) << 8) | (uint24(uint8(data[3]))); + + bytes memory call = abi.encodeCall(HyperCore.executeRawAction, (msg.sender, kind, data[4:])); + + enqueueAction(block.timestamp, call, 0); + + emit RawAction(msg.sender, data); + } +} diff --git a/test/evm/foundry/local/external/hyper-evm-lib/test/simulation/HyperCore.sol b/test/evm/foundry/local/external/hyper-evm-lib/test/simulation/HyperCore.sol new file mode 100644 index 000000000..bbeaf0b0c --- /dev/null +++ b/test/evm/foundry/local/external/hyper-evm-lib/test/simulation/HyperCore.sol @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.24; + +import { CoreExecution } from "./hyper-core/CoreExecution.sol"; +import { DoubleEndedQueue } from "@openzeppelin/contracts/utils/structs/DoubleEndedQueue.sol"; +import { HLConstants } from "../../src/PrecompileLib.sol"; + +contract HyperCore is CoreExecution { + using DoubleEndedQueue for DoubleEndedQueue.Bytes32Deque; + + function executeRawAction(address sender, uint24 kind, bytes calldata data) public payable { + if (kind == HLConstants.LIMIT_ORDER_ACTION) { + LimitOrderAction memory action = abi.decode(data, (LimitOrderAction)); + + // for perps (check that the ID is not a spot asset ID) + if (action.asset < 1e4 || action.asset >= 1e5) { + executePerpLimitOrder(sender, action); + } else { + executeSpotLimitOrder(sender, action); + } + return; + } + + if (kind == HLConstants.VAULT_TRANSFER_ACTION) { + executeVaultTransfer(sender, abi.decode(data, (VaultTransferAction))); + return; + } + + if (kind == HLConstants.TOKEN_DELEGATE_ACTION) { + executeTokenDelegate(sender, abi.decode(data, (TokenDelegateAction))); + return; + } + + if (kind == HLConstants.STAKING_DEPOSIT_ACTION) { + executeStakingDeposit(sender, abi.decode(data, (StakingDepositAction))); + return; + } + + if (kind == HLConstants.STAKING_WITHDRAW_ACTION) { + executeStakingWithdraw(sender, abi.decode(data, (StakingWithdrawAction))); + return; + } + + if (kind == HLConstants.SPOT_SEND_ACTION) { + executeSpotSend(sender, abi.decode(data, (SpotSendAction))); + return; + } + + if (kind == HLConstants.USD_CLASS_TRANSFER_ACTION) { + executeUsdClassTransfer(sender, abi.decode(data, (UsdClassTransferAction))); + return; + } + } + + /// @dev unstaking takes 7 days and after which it will automatically appear in the users + /// spot balance so we need to check this at the end of each operation to simulate that. + function processStakingWithdrawals() public { + while (_withdrawQueue.length() > 0) { + WithdrawRequest memory request = deserializeWithdrawRequest(_withdrawQueue.front()); + + if (request.lockedUntilTimestamp > block.timestamp) { + break; + } + + _withdrawQueue.popFront(); + + _accounts[request.account].spot[HLConstants.hypeTokenIndex()] += request.amount; + } + } +} diff --git a/test/evm/foundry/local/external/hyper-evm-lib/test/simulation/PrecompileSim.sol b/test/evm/foundry/local/external/hyper-evm-lib/test/simulation/PrecompileSim.sol new file mode 100644 index 000000000..c17e0188e --- /dev/null +++ b/test/evm/foundry/local/external/hyper-evm-lib/test/simulation/PrecompileSim.sol @@ -0,0 +1,100 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.24; + +import { HyperCore } from "./HyperCore.sol"; + +import { Vm } from "forge-std/Vm.sol"; + +/// @dev this contract is deployed for each different precompile address such that the fallback can be executed for each +contract PrecompileSim { + Vm internal constant vm = Vm(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); + HyperCore constant _hyperCore = HyperCore(payable(0x9999999999999999999999999999999999999999)); + + address constant POSITION_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000000800; + address constant SPOT_BALANCE_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000000801; + address constant VAULT_EQUITY_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000000802; + address constant WITHDRAWABLE_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000000803; + address constant DELEGATIONS_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000000804; + address constant DELEGATOR_SUMMARY_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000000805; + address constant MARK_PX_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000000806; + address constant ORACLE_PX_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000000807; + address constant SPOT_PX_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000000808; + address constant L1_BLOCK_NUMBER_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000000809; + address constant PERP_ASSET_INFO_PRECOMPILE_ADDRESS = 0x000000000000000000000000000000000000080a; + address constant SPOT_INFO_PRECOMPILE_ADDRESS = 0x000000000000000000000000000000000000080b; + address constant TOKEN_INFO_PRECOMPILE_ADDRESS = 0x000000000000000000000000000000000000080C; + address constant TOKEN_SUPPLY_PRECOMPILE_ADDRESS = 0x000000000000000000000000000000000000080D; + address constant BBO_PRECOMPILE_ADDRESS = 0x000000000000000000000000000000000000080e; + address constant ACCOUNT_MARGIN_SUMMARY_PRECOMPILE_ADDRESS = 0x000000000000000000000000000000000000080F; + address constant CORE_USER_EXISTS_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000000810; + + receive() external payable {} + + fallback(bytes calldata data) external returns (bytes memory) { + if (address(this) == SPOT_BALANCE_PRECOMPILE_ADDRESS) { + (address user, uint64 token) = abi.decode(data, (address, uint64)); + return abi.encode(_hyperCore.readSpotBalance(user, token)); + } + + if (address(this) == VAULT_EQUITY_PRECOMPILE_ADDRESS) { + (address user, address vault) = abi.decode(data, (address, address)); + return abi.encode(_hyperCore.readUserVaultEquity(user, vault)); + } + + if (address(this) == WITHDRAWABLE_PRECOMPILE_ADDRESS) { + address user = abi.decode(data, (address)); + return abi.encode(_hyperCore.readWithdrawable(user)); + } + + if (address(this) == DELEGATIONS_PRECOMPILE_ADDRESS) { + address user = abi.decode(data, (address)); + return abi.encode(_hyperCore.readDelegations(user)); + } + + if (address(this) == DELEGATOR_SUMMARY_PRECOMPILE_ADDRESS) { + address user = abi.decode(data, (address)); + return abi.encode(_hyperCore.readDelegatorSummary(user)); + } + + if (address(this) == POSITION_PRECOMPILE_ADDRESS) { + (address user, uint16 perp) = abi.decode(data, (address, uint16)); + return abi.encode(_hyperCore.readPosition(user, perp)); + } + + if (address(this) == CORE_USER_EXISTS_PRECOMPILE_ADDRESS) { + address user = abi.decode(data, (address)); + return abi.encode(_hyperCore.coreUserExists(user)); + } + + if (address(this) == MARK_PX_PRECOMPILE_ADDRESS) { + uint32 perp = abi.decode(data, (uint32)); + return abi.encode(_hyperCore.readMarkPx(perp)); + } + + if (address(this) == ACCOUNT_MARGIN_SUMMARY_PRECOMPILE_ADDRESS) { + address user = abi.decode(data, (address)); + return abi.encode(_hyperCore.readAccountMarginSummary(user)); + } + + if (address(this) == TOKEN_INFO_PRECOMPILE_ADDRESS) { + uint64 index = abi.decode(data, (uint64)); + return abi.encode(_hyperCore.readTokenInfo(index)); + } + + return _makeRpcCall(address(this), data); + } + + function _makeRpcCall(address target, bytes memory params) internal returns (bytes memory) { + // Construct the JSON-RPC payload + string memory jsonPayload = string.concat( + '[{"to":"', + vm.toString(target), + '","data":"', + vm.toString(params), + '"},"latest"]' + ); + + // Make the RPC call + return vm.rpc("eth_call", jsonPayload); + } +} diff --git a/test/evm/foundry/local/external/hyper-evm-lib/test/simulation/hyper-core/CoreExecution.sol b/test/evm/foundry/local/external/hyper-evm-lib/test/simulation/hyper-core/CoreExecution.sol new file mode 100644 index 000000000..3ede903af --- /dev/null +++ b/test/evm/foundry/local/external/hyper-evm-lib/test/simulation/hyper-core/CoreExecution.sol @@ -0,0 +1,575 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.24; + +import { SafeCast } from "@openzeppelin/contracts/utils/math/SafeCast.sol"; +import { EnumerableSet } from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; +import { DoubleEndedQueue } from "@openzeppelin/contracts/utils/structs/DoubleEndedQueue.sol"; +import { Heap } from "@openzeppelin/contracts/utils/structs/Heap.sol"; + +import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; + +import { PrecompileLib } from "../../../src/PrecompileLib.sol"; +import { CoreWriterLib } from "../../../src/CoreWriterLib.sol"; +import { HLConversions } from "../../../src/common/HLConversions.sol"; + +import { RealL1Read } from "../../utils/RealL1Read.sol"; +import { CoreView } from "./CoreView.sol"; + +contract CoreExecution is CoreView { + using SafeCast for uint256; + using EnumerableSet for EnumerableSet.AddressSet; + using EnumerableSet for EnumerableSet.Bytes32Set; + using DoubleEndedQueue for DoubleEndedQueue.Bytes32Deque; + using Heap for Heap.Uint256Heap; + using SafeERC20 for IERC20; + using RealL1Read for *; + + using EnumerableSet for EnumerableSet.UintSet; + + EnumerableSet.Bytes32Set private _openPerpPositions; + + // Maps user address to a set of perp indices they have active positions in + mapping(address => EnumerableSet.UintSet) private _userPerpPositions; + + uint16 constant MAX_PERP_INDEX = 256; // Adjust based on expected number of perp markets + uint64 constant MM_BPS = 125; // 1.25% maintenance margin fraction (adjust as needed, e.g., for 40x max leverage) + + function _getKey(address user, uint16 perpIndex) internal pure returns (bytes32) { + return bytes32((uint256(uint160(user)) << 16) | uint256(perpIndex)); + } + + function executeTokenTransfer( + address, + uint64 token, + address from, + uint256 value + ) public payable initAccountWithToken(from, token) { + if (_accounts[from].activated) { + _accounts[from].spot[token] += toWei(value, _tokens[token].evmExtraWeiDecimals); + } else { + _latentSpotBalance[from][token] += toWei(value, _tokens[token].evmExtraWeiDecimals); + } + } + + function executeNativeTransfer( + address, + address from, + uint256 value + ) public payable initAccountWithToken(from, HYPE_TOKEN_INDEX) { + if (_accounts[from].activated) { + _accounts[from].spot[HYPE_TOKEN_INDEX] += (value / 1e10).toUint64(); + } else { + _latentSpotBalance[from][HYPE_TOKEN_INDEX] += (value / 1e10).toUint64(); + } + } + + function executePerpLimitOrder( + address sender, + LimitOrderAction memory action + ) public initAccountWithPerp(sender, uint16(action.asset)) { + uint16 perpIndex = uint16(action.asset); + PrecompileLib.Position memory position = _accounts[sender].positions[perpIndex]; + + bool isolated = position.isIsolated; + + uint256 markPx = PrecompileLib.markPx(perpIndex); + + if (!isolated) { + if (action.isBuy) { + if (markPx <= action.limitPx) { + _executePerpLong(sender, action, markPx); + } + } else { + if (markPx >= action.limitPx) { + _executePerpShort(sender, action, markPx); + } + } + } + } + + function _executePerpLong(address sender, LimitOrderAction memory action, uint256 markPx) internal { + uint16 perpIndex = uint16(action.asset); + int64 szi = _accounts[sender].positions[perpIndex].szi; + uint32 leverage = _accounts[sender].positions[perpIndex].leverage; + + uint64 _markPx = markPx.toUint64(); + + // Add require checks for safety (e.g., leverage > 0, action.sz > 0, etc.) + require(leverage > 0, "Invalid leverage"); + require(action.sz > 0, "Invalid size"); + require(markPx > 0, "Invalid price"); + int64 newSzi = szi + int64(action.sz); + + if (szi >= 0) { + // No PnL realization for same-direction increase + // Update position size (more positive for long) + _accounts[sender].positions[perpIndex].szi += int64(action.sz); + + // Additive update to entryNtl to preserve weighted average + // New entryNtl = old_entryNtl + (action.sz * markPx) + _accounts[sender].positions[perpIndex].entryNtl += uint64(action.sz) * uint64(markPx); + + // Deduct additional margin for the added size only + // To minimize integer truncation: (action.sz * markPx) / leverage + _accounts[sender].perpBalance -= (uint64(action.sz) * uint64(markPx)) / leverage; + + _accounts[sender].margin[perpIndex] += (uint64(action.sz) * uint64(markPx)) / leverage; + } else { + if (newSzi <= 0) { + uint64 avgEntryPrice = _accounts[sender].positions[perpIndex].entryNtl / uint64(-szi); + int64 pnl = int64(action.sz) * (int64(avgEntryPrice) - int64(_markPx)); + + uint64 closedMargin = ((uint64(action.sz) * _accounts[sender].positions[perpIndex].entryNtl) / + uint64(-szi)) / leverage; + _accounts[sender].perpBalance += closedMargin; + + _accounts[sender].perpBalance = pnl > 0 + ? _accounts[sender].perpBalance + uint64(pnl) + : _accounts[sender].perpBalance - uint64(-pnl); + _accounts[sender].margin[perpIndex] -= closedMargin; + + _accounts[sender].positions[perpIndex].szi = newSzi; + _accounts[sender].positions[perpIndex].entryNtl = uint64(-newSzi) * avgEntryPrice; + } else { + uint64 avgEntryPrice = _accounts[sender].positions[perpIndex].entryNtl / uint64(-szi); + int64 pnl = int64(-szi) * (int64(avgEntryPrice) - int64(_markPx)); + _accounts[sender].perpBalance = pnl > 0 + ? _accounts[sender].perpBalance + uint64(pnl) + : _accounts[sender].perpBalance - uint64(-pnl); + uint64 oldMargin = _accounts[sender].positions[perpIndex].entryNtl / leverage; + _accounts[sender].perpBalance += oldMargin; + uint64 newLongSize = uint64(newSzi); + uint64 newMargin = (newLongSize * _markPx) / leverage; + _accounts[sender].perpBalance -= newMargin; + _accounts[sender].margin[perpIndex] += newMargin; + _accounts[sender].positions[perpIndex].szi = newSzi; + _accounts[sender].positions[perpIndex].entryNtl = newLongSize * _markPx; + } + } + + bytes32 key = _getKey(sender, perpIndex); + if (szi == 0 && newSzi != 0) { + _openPerpPositions.add(key); + _userPerpPositions[sender].add(perpIndex); + } else if (szi != 0 && newSzi == 0) { + _openPerpPositions.remove(key); + _userPerpPositions[sender].remove(perpIndex); + } + } + + function _executePerpShort(address sender, LimitOrderAction memory action, uint256 markPx) internal { + uint16 perpIndex = uint16(action.asset); + int64 szi = _accounts[sender].positions[perpIndex].szi; + uint32 leverage = _accounts[sender].positions[perpIndex].leverage; + + uint64 _markPx = markPx.toUint64(); + + // Add require checks for safety (e.g., leverage > 0, action.sz > 0, etc.) + require(leverage > 0, "Invalid leverage"); + require(action.sz > 0, "Invalid size"); + require(markPx > 0, "Invalid price"); + int64 newSzi = szi - int64(action.sz); + + if (szi <= 0) { + // No PnL realization for same-direction increase + // Update position size (more negative for short) + _accounts[sender].positions[perpIndex].szi -= int64(action.sz); + + // Additive update to entryNtl to preserve weighted average + // New entryNtl = old_entryNtl + (action.sz * markPx) + _accounts[sender].positions[perpIndex].entryNtl += uint64(action.sz) * uint64(markPx); + + // Deduct additional margin for the added size only + // To minimize integer truncation: (action.sz * markPx) / leverage + _accounts[sender].perpBalance -= (uint64(action.sz) * uint64(markPx)) / leverage; + + _accounts[sender].margin[perpIndex] += (uint64(action.sz) * uint64(markPx)) / leverage; + } else { + if (newSzi >= 0) { + uint64 avgEntryPrice = _accounts[sender].positions[perpIndex].entryNtl / uint64(szi); + int64 pnl = int64(action.sz) * (int64(_markPx) - int64(avgEntryPrice)); + uint64 closedMargin = ((uint64(action.sz) * _accounts[sender].positions[perpIndex].entryNtl) / + uint64(szi)) / leverage; + _accounts[sender].perpBalance += closedMargin; + + _accounts[sender].perpBalance = pnl > 0 + ? _accounts[sender].perpBalance + uint64(pnl) + : _accounts[sender].perpBalance - uint64(-pnl); + + _accounts[sender].margin[perpIndex] -= closedMargin; + _accounts[sender].positions[perpIndex].szi = newSzi; + _accounts[sender].positions[perpIndex].entryNtl = uint64(newSzi) * avgEntryPrice; + } else { + uint64 avgEntryPrice = _accounts[sender].positions[perpIndex].entryNtl / uint64(szi); + int64 pnl = int64(szi) * (int64(_markPx) - int64(avgEntryPrice)); + _accounts[sender].perpBalance = pnl > 0 + ? _accounts[sender].perpBalance + uint64(pnl) + : _accounts[sender].perpBalance - uint64(-pnl); + uint64 oldMargin = _accounts[sender].positions[perpIndex].entryNtl / leverage; + _accounts[sender].perpBalance += oldMargin; + uint64 newShortSize = uint64(-newSzi); + uint64 newMargin = (newShortSize * _markPx) / leverage; + _accounts[sender].perpBalance -= newMargin; + _accounts[sender].margin[perpIndex] += newMargin; + _accounts[sender].positions[perpIndex].szi = newSzi; + _accounts[sender].positions[perpIndex].entryNtl = newShortSize * _markPx; + } + } + + bytes32 key = _getKey(sender, perpIndex); + if (szi == 0 && newSzi != 0) { + _openPerpPositions.add(key); + _userPerpPositions[sender].add(perpIndex); + } else if (szi != 0 && newSzi == 0) { + _openPerpPositions.remove(key); + _userPerpPositions[sender].remove(perpIndex); + } + + // Optional: Add margin sufficiency check after updates + // e.g., require(_accounts[sender].perpBalance >= someMaintenanceMargin, "Insufficient margin"); + } + + // basic simulation of spot trading, not accounting for orderbook depth, or fees + function executeSpotLimitOrder( + address sender, + LimitOrderAction memory action + ) public initAccountWithSpotMarket(sender, uint32(HLConversions.assetToSpotId(action.asset))) { + PrecompileLib.SpotInfo memory spotInfo = RealL1Read.spotInfo(uint32(HLConversions.assetToSpotId(action.asset))); + + PrecompileLib.TokenInfo memory baseToken = _tokens[spotInfo.tokens[0]]; + + uint64 spotPx = readSpotPx(uint32(HLConversions.assetToSpotId(action.asset))); + + uint64 fromToken; + uint64 toToken; + + if (isActionExecutable(action, spotPx)) { + if (action.isBuy) { + fromToken = spotInfo.tokens[1]; + toToken = spotInfo.tokens[0]; + + uint64 amountIn = action.sz * spotPx; + uint64 amountOut = action.sz * (10 ** (baseToken.weiDecimals - baseToken.szDecimals)).toUint64(); + + if (_accounts[sender].spot[fromToken] >= amountIn) { + _accounts[sender].spot[fromToken] -= amountIn; + _accounts[sender].spot[toToken] += amountOut; + } else { + revert("insufficient balance"); + } + } else { + fromToken = spotInfo.tokens[0]; + toToken = spotInfo.tokens[1]; + + uint64 amountIn = action.sz * (10 ** (baseToken.weiDecimals - baseToken.szDecimals)).toUint64(); + + uint64 amountOut = action.sz * spotPx; + + if (_accounts[sender].spot[fromToken] >= amountIn) { + _accounts[sender].spot[fromToken] -= amountIn; + _accounts[sender].spot[toToken] += amountOut; + } else { + revert("insufficient balance"); + } + } + } else { + _pendingOrders.push(PendingOrder({ sender: sender, action: action })); + } + } + + function executeSpotSend( + address sender, + SpotSendAction memory action + ) + public + whenActivated(sender) + initAccountWithToken(sender, action.token) + initAccountWithToken(action.destination, action.token) + { + if (action._wei > _accounts[sender].spot[action.token]) { + revert("insufficient balance"); + } + + // handle account activation case + if (_accounts[action.destination].activated == false) { + _chargeUSDCFee(sender); + + _accounts[action.destination].activated = true; + + _accounts[sender].spot[action.token] -= action._wei; + _accounts[action.destination].spot[action.token] += _latentSpotBalance[sender][action.token] + action._wei; + + // this will no longer be needed + _latentSpotBalance[sender][action.token] = 0; + + // officially init the destination account + _initializedAccounts[action.destination] = true; + _initializedSpotBalance[action.destination][action.token] = true; + return; + } + + address systemAddress = CoreWriterLib.getSystemAddress(action.token); + + _accounts[sender].spot[action.token] -= action._wei; + + if (action.destination != systemAddress) { + _accounts[action.destination].spot[action.token] += action._wei; + } else { + uint256 transferAmount; + if (action.token == HYPE_TOKEN_INDEX) { + transferAmount = action._wei * 1e10; + deal(systemAddress, systemAddress.balance + transferAmount); + vm.prank(systemAddress); + (bool success, ) = address(sender).call{ value: transferAmount, gas: 30000 }(""); + if (!success) { + revert("transfer failed"); + } + return; + } + + address evmContract = _tokens[action.token].evmContract; + transferAmount = fromWei(action._wei, _tokens[action.token].evmExtraWeiDecimals); + deal(evmContract, systemAddress, IERC20(evmContract).balanceOf(systemAddress) + transferAmount); + vm.prank(systemAddress); + IERC20(evmContract).safeTransfer(action.destination, transferAmount); + } + } + + function _chargeUSDCFee(address sender) internal { + if (_accounts[sender].spot[USDC_TOKEN_INDEX] >= 1e8) { + _accounts[sender].spot[USDC_TOKEN_INDEX] -= 1e8; + } else if (_accounts[sender].perpBalance >= 1e8) { + _accounts[sender].perpBalance -= 1e8; + } else { + revert("insufficient USDC balance for fee"); + } + } + + function executeUsdClassTransfer( + address sender, + UsdClassTransferAction memory action + ) public whenActivated(sender) { + if (action.toPerp) { + if (fromPerp(action.ntl) <= _accounts[sender].spot[USDC_TOKEN_INDEX]) { + _accounts[sender].perpBalance += action.ntl; + _accounts[sender].spot[USDC_TOKEN_INDEX] -= fromPerp(action.ntl); + } + } else { + if (action.ntl <= _accounts[sender].perpBalance) { + _accounts[sender].perpBalance -= action.ntl; + _accounts[sender].spot[USDC_TOKEN_INDEX] += fromPerp(action.ntl); + } + } + } + + function executeVaultTransfer( + address sender, + VaultTransferAction memory action + ) public whenActivated(sender) initAccountWithVault(sender, action.vault) { + // first update their vault equity + _accounts[sender].vaultEquity[action.vault].equity = readUserVaultEquity(sender, action.vault).equity; + + if (action.isDeposit) { + if (action.usd <= _accounts[sender].perpBalance) { + _accounts[sender].vaultEquity[action.vault].equity += action.usd; + _accounts[sender].vaultEquity[action.vault].lockedUntilTimestamp = uint64( + (block.timestamp + 86400) * 1000 + ); + _accounts[sender].perpBalance -= action.usd; + _vaultEquity[action.vault] += action.usd; + } else { + revert("insufficient balance"); + } + } else { + PrecompileLib.UserVaultEquity storage userVaultEquity = _accounts[sender].vaultEquity[action.vault]; + + // a zero amount means withdraw the entire amount + action.usd = action.usd == 0 ? userVaultEquity.equity : action.usd; + + // the vaults have a minimum withdraw of 1 / 100,000,000 + if (action.usd < _vaultEquity[action.vault] / 1e8) { + revert("does not meet minimum withdraw"); + } + + if ( + action.usd <= userVaultEquity.equity && userVaultEquity.lockedUntilTimestamp / 1000 <= block.timestamp + ) { + userVaultEquity.equity -= action.usd; + _accounts[sender].perpBalance += action.usd; + } else { + revert("equity too low, or locked"); + } + } + } + + function executeStakingDeposit(address sender, StakingDepositAction memory action) public whenActivated(sender) { + if (action._wei <= _accounts[sender].spot[HYPE_TOKEN_INDEX]) { + _accounts[sender].spot[HYPE_TOKEN_INDEX] -= action._wei; + _accounts[sender].staking += action._wei; + } + } + + function executeStakingWithdraw(address sender, StakingWithdrawAction memory action) public whenActivated(sender) { + if (action._wei <= _accounts[sender].staking) { + _accounts[sender].staking -= action._wei; + + WithdrawRequest memory withrawRequest = WithdrawRequest({ + account: sender, + amount: action._wei, + lockedUntilTimestamp: uint32(block.timestamp + 7 days) + }); + + _withdrawQueue.pushBack(serializeWithdrawRequest(withrawRequest)); + } + } + + function executeTokenDelegate(address sender, TokenDelegateAction memory action) public whenActivated(sender) { + require(_validators.contains(action.validator)); + + if (action.isUndelegate) { + PrecompileLib.Delegation storage delegation = _accounts[sender].delegations[action.validator]; + if (action._wei <= delegation.amount && block.timestamp * 1000 > delegation.lockedUntilTimestamp) { + _accounts[sender].staking += action._wei; + delegation.amount -= action._wei; + } + } else { + if (action._wei <= _accounts[sender].staking) { + _accounts[sender].staking -= action._wei; + _accounts[sender].delegations[action.validator].amount += action._wei; + _accounts[sender].delegations[action.validator].lockedUntilTimestamp = ((block.timestamp + 84600) * + 1000).toUint64(); + } + } + } + + function setMarkPx(uint32 perp, uint64 priceDiffBps, bool isIncrease) public { + uint64 basePrice = readMarkPx(perp); + if (isIncrease) { + _perpMarkPrice[perp] = (basePrice * (10000 + priceDiffBps)) / 10000; + } else { + _perpMarkPrice[perp] = (basePrice * (10000 - priceDiffBps)) / 10000; + } + } + + function setMarkPx(uint32 perp, uint64 markPx) public { + _perpMarkPrice[perp] = markPx; + } + + function setSpotPx(uint32 spotMarketId, uint64 priceDiffBps, bool isIncrease) public { + uint64 basePrice = readSpotPx(spotMarketId); + if (isIncrease) { + _spotPrice[spotMarketId] = (basePrice * (10000 + priceDiffBps)) / 10000; + } else { + _spotPrice[spotMarketId] = (basePrice * (10000 - priceDiffBps)) / 10000; + } + } + + function setSpotPx(uint32 spotMarketId, uint64 spotPx) public { + _spotPrice[spotMarketId] = spotPx; + } + + function isActionExecutable(LimitOrderAction memory action, uint64 px) internal pure returns (bool) { + bool executable = action.isBuy ? action.limitPx >= px : action.limitPx <= px; + return executable; + } + + function setVaultMultiplier(address vault, uint64 multiplier) public { + _vaultMultiplier[vault] = multiplier; + } + + function processPendingOrders() public { + for (uint256 i = _pendingOrders.length; i > 0; i--) { + PendingOrder memory order = _pendingOrders[i - 1]; + uint32 spotMarketId = order.action.asset - 1e4; + uint64 spotPx = readSpotPx(spotMarketId); + + if (isActionExecutable(order.action, spotPx)) { + executeSpotLimitOrder(order.sender, order.action); + + // Remove executed order by swapping with last and popping + _pendingOrders[i - 1] = _pendingOrders[_pendingOrders.length - 1]; + _pendingOrders.pop(); + } + } + } + + ////////// PERP LIQUIDATIONS //////////////////// + function isLiquidatable(address user) public returns (bool) { + uint64 totalNotional = 0; + int64 totalUPnL = 0; + uint64 totalLocked = 0; + uint64 mmReq = 0; + + uint256 len = _userPerpPositions[user].length(); + + for (uint256 i = len; i > 0; i--) { + uint16 perpIndex = uint16(_userPerpPositions[user].at(i - 1)); + PrecompileLib.Position memory pos = _accounts[user].positions[perpIndex]; + if (pos.szi != 0) { + uint64 markPx = readMarkPx(perpIndex); + int64 szi = pos.szi; + uint64 avgEntry = pos.entryNtl / abs(szi); + int64 uPnL = szi * (int64(markPx) - int64(avgEntry)); + totalUPnL += uPnL; + totalLocked += _accounts[user].margin[perpIndex]; + + uint64 positionNotional = abs(szi) * markPx; + totalNotional += positionNotional; + + // Per-perp maintenance margin requirement based on max leverage + uint32 maxLev = _getMaxLeverage(perpIndex); + uint64 mmBps = 5000 / maxLev; // 5000 / maxLev gives bps for mm_fraction = 0.5 / maxLev + mmReq += (positionNotional * mmBps) / 10000; + } + } + + if (totalNotional == 0) { + return false; + } + + int64 equity = int64(_accounts[user].perpBalance) + int64(totalLocked) + totalUPnL; + + return equity < int64(mmReq); + } + + function abs(int64 value) internal pure returns (uint64) { + return value > 0 ? uint64(value) : uint64(-value); + } + + function _getMaxLeverage(uint16 perpIndex) public view returns (uint32) { + return _maxLeverage[perpIndex]; + } + + // simplified liquidation, nukes all positions and resets the perp balance + // for future: make this more realistic + function _liquidateUser(address user) public { + uint256 len = _userPerpPositions[user].length(); + for (uint256 i = len; i > 0; i--) { + uint16 perpIndex = uint16(_userPerpPositions[user].at(i - 1)); + + bytes32 key = _getKey(user, perpIndex); + _openPerpPositions.remove(key); + _accounts[user].positions[perpIndex].szi = 0; + _accounts[user].positions[perpIndex].entryNtl = 0; + _accounts[user].margin[perpIndex] = 0; + _userPerpPositions[user].remove(perpIndex); + } + + _accounts[user].perpBalance = 0; + } + + function liquidatePositions() public { + uint256 len = _openPerpPositions.length(); + + if (len == 0) return; + + for (uint256 i = len; i > 0; i--) { + bytes32 key = _openPerpPositions.at(i - 1); + address user = address(uint160(uint256(key) >> 16)); + if (isLiquidatable(user)) { + _liquidateUser(user); + } + } + } +} diff --git a/test/evm/foundry/local/external/hyper-evm-lib/test/simulation/hyper-core/CoreState.sol b/test/evm/foundry/local/external/hyper-evm-lib/test/simulation/hyper-core/CoreState.sol new file mode 100644 index 000000000..5cdbcd0b6 --- /dev/null +++ b/test/evm/foundry/local/external/hyper-evm-lib/test/simulation/hyper-core/CoreState.sol @@ -0,0 +1,373 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.24; + +import { SafeCast } from "@openzeppelin/contracts/utils/math/SafeCast.sol"; +import { EnumerableSet } from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; +import { DoubleEndedQueue } from "@openzeppelin/contracts/utils/structs/DoubleEndedQueue.sol"; +import { Heap } from "@openzeppelin/contracts/utils/structs/Heap.sol"; + +import { PrecompileLib } from "../../../src/PrecompileLib.sol"; +import { HLConstants } from "../../../src/CoreWriterLib.sol"; + +import { RealL1Read } from "../../utils/RealL1Read.sol"; +import { StdCheats, Vm } from "forge-std/StdCheats.sol"; + +/// Modified from https://github.com/ambitlabsxyz/hypercore +contract CoreState is StdCheats { + using SafeCast for uint256; + using EnumerableSet for EnumerableSet.AddressSet; + using DoubleEndedQueue for DoubleEndedQueue.Bytes32Deque; + using Heap for Heap.Uint256Heap; + + using RealL1Read for *; + + Vm internal constant vm = Vm(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); + + uint64 public immutable HYPE_TOKEN_INDEX; + uint64 public constant USDC_TOKEN_INDEX = 0; + + constructor() { + HYPE_TOKEN_INDEX = HLConstants.hypeTokenIndex(); + } + + struct WithdrawRequest { + address account; + uint64 amount; + uint32 lockedUntilTimestamp; + } + + struct AccountData { + bool activated; + mapping(uint64 token => uint64 balance) spot; + mapping(address vault => PrecompileLib.UserVaultEquity) vaultEquity; + uint64 staking; + mapping(address validator => PrecompileLib.Delegation) delegations; + uint64 perpBalance; + mapping(uint16 perpIndex => PrecompileLib.Position) positions; + mapping(uint16 perpIndex => uint64 margin) margin; + } + + struct PendingOrder { + address sender; + LimitOrderAction action; + } + + // registered token info + mapping(uint64 token => PrecompileLib.TokenInfo) internal _tokens; + + mapping(address account => AccountData) internal _accounts; + + mapping(address account => bool initialized) internal _initializedAccounts; + mapping(address account => mapping(uint64 token => bool initialized)) internal _initializedSpotBalance; + mapping(address account => mapping(address vault => bool initialized)) internal _initializedVaults; + + mapping(address account => mapping(uint32 perpIndex => bool initialized)) internal _initializedPerpPosition; + mapping(uint16 perpIndex => uint32 maxLeverage) internal _maxLeverage; + + mapping(address account => mapping(uint64 token => uint64 latentBalance)) internal _latentSpotBalance; + + mapping(uint32 perpIndex => uint64 markPrice) internal _perpMarkPrice; + mapping(uint32 spotMarketId => uint64 spotPrice) internal _spotPrice; + + mapping(address vault => uint64) internal _vaultEquity; + + DoubleEndedQueue.Bytes32Deque internal _withdrawQueue; + + PendingOrder[] internal _pendingOrders; + + EnumerableSet.AddressSet internal _validators; + + mapping(address vault => uint64) internal _vaultMultiplier; + + ///////////////////////// + /// STATE INITIALIZERS/// + ///////////////////////// + + modifier initAccountWithToken(address _account, uint64 token) { + if (!_initializedSpotBalance[_account][token]) { + registerTokenInfo(token); + _initializeAccountWithToken(_account, token); + } + _; + } + + modifier initAccountWithSpotMarket(address _account, uint32 spotMarketId) { + uint64 baseToken = PrecompileLib.spotInfo(spotMarketId).tokens[0]; + uint64 quoteToken = PrecompileLib.spotInfo(spotMarketId).tokens[1]; + + if (!_initializedSpotBalance[_account][baseToken]) { + registerTokenInfo(baseToken); + _initializeAccountWithToken(_account, baseToken); + } + + if (!_initializedSpotBalance[_account][quoteToken]) { + registerTokenInfo(quoteToken); + _initializeAccountWithToken(_account, quoteToken); + } + + _; + } + + modifier initAccountWithVault(address _account, address _vault) { + if (!_initializedVaults[_account][_vault]) { + _initializeAccount(_account); + _initializeAccountWithVault(_account, _vault); + } + _; + } + + modifier initAccountWithPerp(address _account, uint16 perp) { + if (_maxLeverage[perp] == 0) { + _maxLeverage[perp] = RealL1Read.position(address(1), perp).leverage; + } + + if (_initializedPerpPosition[_account][perp] == false) { + _initializeAccount(_account); + _initializeAccountWithPerp(_account, perp); + } + _; + } + + modifier initAccount(address _account) { + if (!_initializedAccounts[_account]) { + _initializeAccount(_account); + } + _; + } + + function _initializeAccountWithToken(address _account, uint64 token) internal { + _initializeAccount(_account); + + if (_accounts[_account].activated == false) { + return; + } + + _initializedSpotBalance[_account][token] = true; + _accounts[_account].spot[token] = RealL1Read.spotBalance(_account, token).total; + } + + function _initializeAccountWithVault(address _account, address _vault) internal { + _initializedVaults[_account][_vault] = true; + _accounts[_account].vaultEquity[_vault] = RealL1Read.userVaultEquity(_account, _vault); + } + + function _initializeAccountWithPerp(address _account, uint16 perp) internal { + _initializedPerpPosition[_account][perp] = true; + _accounts[_account].positions[perp] = RealL1Read.position(_account, perp); + } + + function _initializeAccount(address _account) internal { + bool initialized = _initializedAccounts[_account]; + + if (initialized) { + return; + } + + AccountData storage account = _accounts[_account]; + + // check if the acc is created on Core + RealL1Read.CoreUserExists memory coreUserExists = RealL1Read.coreUserExists(_account); + if (!coreUserExists.exists) { + return; + } + + _initializedAccounts[_account] = true; + account.activated = true; + + // setting perp balance + account.perpBalance = RealL1Read.withdrawable(_account).withdrawable; + + // setting staking balance + PrecompileLib.DelegatorSummary memory summary = RealL1Read.delegatorSummary(_account); + account.staking = summary.undelegated; + // note: no way to track the pending withdrawals, and have a way to credit them later + + // set delegations + PrecompileLib.Delegation[] memory delegations = RealL1Read.delegations(_account); + for (uint256 i = 0; i < delegations.length; i++) { + account.delegations[delegations[i].validator] = delegations[i]; + } + } + + modifier whenActivated(address sender) { + if (_accounts[sender].activated == false) { + return; + } + _; + } + + function registerTokenInfo(uint64 index) public { + // if the token is already registered, return early + if (bytes(_tokens[index].name).length > 0) { + return; + } + + PrecompileLib.TokenInfo memory tokenInfo = RealL1Read.tokenInfo(uint32(index)); + + // this means that the precompile call failed + if (tokenInfo.evmContract == RealL1Read.INVALID_ADDRESS) return; + _tokens[index] = tokenInfo; + } + + function forceTokenInfo( + uint64 index, + string memory name, + address evmContract, + uint8 szDecimals, + uint8 weiDecimals, + int8 evmExtraWeiDecimals + ) public { + PrecompileLib.TokenInfo memory tokenInfo = PrecompileLib.TokenInfo({ + name: name, + spots: new uint64[](0), + deployerTradingFeeShare: 0, + deployer: address(0), + evmContract: evmContract, + szDecimals: szDecimals, + weiDecimals: weiDecimals, + evmExtraWeiDecimals: evmExtraWeiDecimals + }); + _tokens[index] = tokenInfo; + } + + function registerValidator(address validator) public { + _validators.add(validator); + } + + /// @dev account creation can be forced when there isnt a reliance on testing that workflow. + function forceAccountActivation(address account) public { + _accounts[account].activated = true; + } + + function forceSpot(address account, uint64 token, uint64 _wei) public payable { + if (_accounts[account].activated == false) { + forceAccountActivation(account); + } + + if (_initializedSpotBalance[account][token] == false) { + registerTokenInfo(token); + _initializeAccountWithToken(account, token); + } + + _accounts[account].spot[token] = _wei; + } + + function forcePerpBalance(address account, uint64 usd) public payable { + if (_accounts[account].activated == false) { + forceAccountActivation(account); + } + if (_initializedAccounts[account] == false) { + _initializeAccount(account); + } + + _accounts[account].perpBalance = usd; + } + + function forceStaking(address account, uint64 _wei) public payable { + forceAccountActivation(account); + _accounts[account].staking = _wei; + } + + function forceDelegation(address account, address validator, uint64 amount, uint64 lockedUntilTimestamp) public { + forceAccountActivation(account); + _accounts[account].delegations[validator] = PrecompileLib.Delegation({ + validator: validator, + amount: amount, + lockedUntilTimestamp: lockedUntilTimestamp + }); + } + + function forceVaultEquity(address account, address vault, uint64 usd, uint64 lockedUntilTimestamp) public payable { + forceAccountActivation(account); + + _vaultEquity[vault] -= _accounts[account].vaultEquity[vault].equity; + _vaultEquity[vault] += usd; + + _accounts[account].vaultEquity[vault].equity = usd; + _accounts[account].vaultEquity[vault].lockedUntilTimestamp = lockedUntilTimestamp > 0 + ? lockedUntilTimestamp + : uint64((block.timestamp + 3600) * 1000); + } + + //////// conversions //////// + + function toWei(uint256 amount, int8 evmExtraWeiDecimals) internal pure returns (uint64) { + uint256 _wei = evmExtraWeiDecimals == 0 + ? amount + : evmExtraWeiDecimals > 0 + ? amount / 10 ** uint8(evmExtraWeiDecimals) + : amount * 10 ** uint8(-evmExtraWeiDecimals); + + return _wei.toUint64(); + } + + function fromWei(uint64 _wei, int8 evmExtraWeiDecimals) internal pure returns (uint256) { + return + evmExtraWeiDecimals == 0 + ? _wei + : evmExtraWeiDecimals > 0 + ? _wei * 10 ** uint8(evmExtraWeiDecimals) + : _wei / 10 ** uint8(-evmExtraWeiDecimals); + } + + function fromPerp(uint64 usd) internal pure returns (uint64) { + return usd * 1e2; + } + + // converting a withdraw request into a bytes32 + function serializeWithdrawRequest(CoreState.WithdrawRequest memory request) internal pure returns (bytes32) { + return + bytes32( + (uint256(uint160(request.account)) << 96) | + (uint256(request.amount) << 32) | + uint40(request.lockedUntilTimestamp) + ); + } + + function deserializeWithdrawRequest(bytes32 data) internal pure returns (CoreState.WithdrawRequest memory request) { + request.account = address(uint160(uint256(data) >> 96)); + request.amount = uint64(uint256(data) >> 32); + request.lockedUntilTimestamp = uint32(uint256(data)); + } + + struct LimitOrderAction { + uint32 asset; + bool isBuy; + uint64 limitPx; + uint64 sz; + bool reduceOnly; + uint8 encodedTif; + uint128 cloid; + } + + struct VaultTransferAction { + address vault; + bool isDeposit; + uint64 usd; + } + + struct TokenDelegateAction { + address validator; + uint64 _wei; + bool isUndelegate; + } + + struct StakingDepositAction { + uint64 _wei; + } + + struct StakingWithdrawAction { + uint64 _wei; + } + + struct SpotSendAction { + address destination; + uint64 token; + uint64 _wei; + } + + struct UsdClassTransferAction { + uint64 ntl; + bool toPerp; + } +} diff --git a/test/evm/foundry/local/external/hyper-evm-lib/test/simulation/hyper-core/CoreView.sol b/test/evm/foundry/local/external/hyper-evm-lib/test/simulation/hyper-core/CoreView.sol new file mode 100644 index 000000000..571948642 --- /dev/null +++ b/test/evm/foundry/local/external/hyper-evm-lib/test/simulation/hyper-core/CoreView.sol @@ -0,0 +1,128 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.24; + +import { EnumerableSet } from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; +import { DoubleEndedQueue } from "@openzeppelin/contracts/utils/structs/DoubleEndedQueue.sol"; +import { SafeCast } from "@openzeppelin/contracts/utils/math/SafeCast.sol"; + +import { PrecompileLib } from "../../../src/PrecompileLib.sol"; +import { RealL1Read } from "../../utils/RealL1Read.sol"; + +import { CoreState } from "./CoreState.sol"; + +/// Modified from https://github.com/ambitlabsxyz/hypercore +contract CoreView is CoreState { + using EnumerableSet for EnumerableSet.AddressSet; + using DoubleEndedQueue for DoubleEndedQueue.Bytes32Deque; + using SafeCast for uint256; + + function tokenExists(uint32 token) public view returns (bool) { + return bytes(_tokens[token].name).length > 0; + } + + function readMarkPx(uint32 perp) public returns (uint64) { + if (_perpMarkPrice[perp] == 0) { + return RealL1Read.markPx(perp); + } + + return _perpMarkPrice[perp]; + } + + function readSpotPx(uint32 spotMarketId) public view returns (uint64) { + if (_spotPrice[spotMarketId] == 0) { + return PrecompileLib.spotPx(spotMarketId); + } + + return _spotPrice[spotMarketId]; + } + + function readSpotBalance(address account, uint64 token) public returns (PrecompileLib.SpotBalance memory) { + if (_initializedSpotBalance[account][token] == false) { + return RealL1Read.spotBalance(account, token); + } + + return PrecompileLib.SpotBalance({ total: _accounts[account].spot[token], entryNtl: 0, hold: 0 }); + } + + // Even if the HyperCore account is not created, the precompile returns 0 (it does not revert) + function readWithdrawable(address account) public returns (PrecompileLib.Withdrawable memory) { + if (_accounts[account].activated == false) { + return RealL1Read.withdrawable(account); + } + + return PrecompileLib.Withdrawable({ withdrawable: _accounts[account].perpBalance }); + } + + function readUserVaultEquity( + address user, + address vault + ) public view returns (PrecompileLib.UserVaultEquity memory) { + PrecompileLib.UserVaultEquity memory equity = _accounts[user].vaultEquity[vault]; + uint64 multiplier = _vaultMultiplier[vault]; + if (multiplier != 0) equity.equity = uint64((uint256(equity.equity) * multiplier) / 1e18); + return equity; + } + + function readDelegation( + address user, + address validator + ) public view returns (PrecompileLib.Delegation memory delegation) { + delegation.validator = validator; + delegation.amount = _accounts[user].delegations[validator].amount; + delegation.lockedUntilTimestamp = _accounts[user].delegations[validator].lockedUntilTimestamp; + } + + function readDelegations(address user) public view returns (PrecompileLib.Delegation[] memory userDelegations) { + address[] memory validators = _validators.values(); + + userDelegations = new PrecompileLib.Delegation[](validators.length); + for (uint256 i; i < userDelegations.length; i++) { + userDelegations[i].validator = validators[i]; + + PrecompileLib.Delegation memory delegation = _accounts[user].delegations[validators[i]]; + userDelegations[i].amount = delegation.amount; + userDelegations[i].lockedUntilTimestamp = delegation.lockedUntilTimestamp; + } + } + + function readDelegatorSummary(address user) public view returns (PrecompileLib.DelegatorSummary memory summary) { + address[] memory validators = _validators.values(); + + for (uint256 i; i < validators.length; i++) { + PrecompileLib.Delegation memory delegation = _accounts[user].delegations[validators[i]]; + summary.delegated += delegation.amount; + } + + summary.undelegated = _accounts[user].staking; + + for (uint256 i; i < _withdrawQueue.length(); i++) { + WithdrawRequest memory request = deserializeWithdrawRequest(_withdrawQueue.at(i)); + if (request.account == user) { + summary.nPendingWithdrawals++; + summary.totalPendingWithdrawal += request.amount; + } + } + } + + function readPosition(address user, uint16 perp) public view returns (PrecompileLib.Position memory) { + return _accounts[user].positions[perp]; + } + + function coreUserExists(address account) public returns (bool) { + if (_accounts[account].activated == false) { + return RealL1Read.coreUserExists(account).exists; + } + + return _accounts[account].activated; + } + + function readAccountMarginSummary(address user) public view returns (PrecompileLib.AccountMarginSummary memory) { + // 1. maintain an enumerable set for the perps that a user is in + // 2. iterate over their positions and calculate position value, add them up (value = abs(sz * markPx)) + return PrecompileLib.accountMarginSummary(0, user); + } + + function readTokenInfo(uint64 index) public view returns (PrecompileLib.TokenInfo memory) { + return _tokens[index]; + } +} diff --git a/test/evm/foundry/local/external/hyper-evm-lib/test/utils/RealL1Read.sol b/test/evm/foundry/local/external/hyper-evm-lib/test/utils/RealL1Read.sol new file mode 100644 index 000000000..84f9b0387 --- /dev/null +++ b/test/evm/foundry/local/external/hyper-evm-lib/test/utils/RealL1Read.sol @@ -0,0 +1,294 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import { Vm } from "forge-std/Vm.sol"; +import { PrecompileLib } from "../../src/PrecompileLib.sol"; +import { console } from "forge-std/console.sol"; + +// Makes RPC calls to get real precompile data (independent of the test environment) +library RealL1Read { + struct Position { + int64 szi; + uint64 entryNtl; + int64 isolatedRawUsd; + uint32 leverage; + bool isIsolated; + } + + struct SpotBalance { + uint64 total; + uint64 hold; + uint64 entryNtl; + } + + struct UserVaultEquity { + uint64 equity; + uint64 lockedUntilTimestamp; + } + + struct Withdrawable { + uint64 withdrawable; + } + + struct Delegation { + address validator; + uint64 amount; + uint64 lockedUntilTimestamp; + } + + struct DelegatorSummary { + uint64 delegated; + uint64 undelegated; + uint64 totalPendingWithdrawal; + uint64 nPendingWithdrawals; + } + + struct PerpAssetInfo { + string coin; + uint32 marginTableId; + uint8 szDecimals; + uint8 maxLeverage; + bool onlyIsolated; + } + + struct SpotInfo { + string name; + uint64[2] tokens; + } + + struct TokenInfo { + string name; + uint64[] spots; + uint64 deployerTradingFeeShare; + address deployer; + address evmContract; + uint8 szDecimals; + uint8 weiDecimals; + int8 evmExtraWeiDecimals; + } + + struct UserBalance { + address user; + uint64 balance; + } + + struct TokenSupply { + uint64 maxSupply; + uint64 totalSupply; + uint64 circulatingSupply; + uint64 futureEmissions; + UserBalance[] nonCirculatingUserBalances; + } + + struct Bbo { + uint64 bid; + uint64 ask; + } + + struct AccountMarginSummary { + int64 accountValue; + uint64 marginUsed; + uint64 ntlPos; + int64 rawUsd; + } + + struct CoreUserExists { + bool exists; + } + + Vm constant vm = Vm(address(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D)); + + address constant POSITION_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000000800; + address constant SPOT_BALANCE_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000000801; + address constant VAULT_EQUITY_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000000802; + address constant WITHDRAWABLE_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000000803; + address constant DELEGATIONS_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000000804; + address constant DELEGATOR_SUMMARY_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000000805; + address constant MARK_PX_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000000806; + address constant ORACLE_PX_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000000807; + address constant SPOT_PX_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000000808; + address constant L1_BLOCK_NUMBER_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000000809; + address constant PERP_ASSET_INFO_PRECOMPILE_ADDRESS = 0x000000000000000000000000000000000000080a; + address constant SPOT_INFO_PRECOMPILE_ADDRESS = 0x000000000000000000000000000000000000080b; + address constant TOKEN_INFO_PRECOMPILE_ADDRESS = 0x000000000000000000000000000000000000080C; + address constant TOKEN_SUPPLY_PRECOMPILE_ADDRESS = 0x000000000000000000000000000000000000080D; + address constant BBO_PRECOMPILE_ADDRESS = 0x000000000000000000000000000000000000080e; + address constant ACCOUNT_MARGIN_SUMMARY_PRECOMPILE_ADDRESS = 0x000000000000000000000000000000000000080F; + address constant CORE_USER_EXISTS_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000000810; + + address constant INVALID_ADDRESS = address(1); + + function _makeRpcCall(address target, bytes memory params) internal returns (bytes memory) { + // Construct the JSON-RPC payload + string memory jsonPayload = string.concat( + '[{"to":"', + vm.toString(target), + '","data":"', + vm.toString(params), + '"},"latest"]' + ); + + bool useArchivedBlockNumber = false; + + if (useArchivedBlockNumber) { + string memory blockNumberHex = string.concat("0x", toHexString(block.number)); + + jsonPayload = string.concat( + '[{"to":"', + vm.toString(target), + '","data":"', + vm.toString(params), + '"},"', + blockNumberHex, + '"]' + ); + } + + // Make the RPC call + try vm.rpc("eth_call", jsonPayload) returns (bytes memory data) { + return data; + } catch { + return ""; + } + } + + function toHexString(uint256 a) internal pure returns (string memory) { + uint256 count = 0; + uint256 b = a; + while (b != 0) { + count++; + b /= 16; + } + bytes memory res = new bytes(count); + for (uint256 i = 0; i < count; ++i) { + b = a % 16; + res[count - i - 1] = toHexDigit(uint8(b)); + a /= 16; + } + return string(res); + } + + function toHexDigit(uint8 d) internal pure returns (bytes1) { + if (0 <= d && d <= 9) { + return bytes1(uint8(bytes1("0")) + d); + } else if (10 <= uint8(d) && uint8(d) <= 15) { + return bytes1(uint8(bytes1("a")) + d - 10); + } + // revert("Invalid hex digit"); + revert(); + } + + function position(address user, uint16 perp) internal returns (PrecompileLib.Position memory) { + bytes memory result = _makeRpcCall(POSITION_PRECOMPILE_ADDRESS, abi.encode(user, perp)); + + if (result.length == 0) { + return PrecompileLib.Position({ szi: 0, entryNtl: 0, isolatedRawUsd: 0, leverage: 0, isIsolated: false }); + } + return abi.decode(result, (PrecompileLib.Position)); + } + + function spotBalance(address user, uint64 token) internal returns (PrecompileLib.SpotBalance memory) { + bytes memory result = _makeRpcCall(SPOT_BALANCE_PRECOMPILE_ADDRESS, abi.encode(user, token)); + if (result.length == 0) { + return PrecompileLib.SpotBalance({ total: 0, hold: 0, entryNtl: 0 }); + } + return abi.decode(result, (PrecompileLib.SpotBalance)); + } + + function userVaultEquity(address user, address vault) internal returns (PrecompileLib.UserVaultEquity memory) { + bytes memory result = _makeRpcCall(VAULT_EQUITY_PRECOMPILE_ADDRESS, abi.encode(user, vault)); + if (result.length == 0) { + return PrecompileLib.UserVaultEquity({ equity: 0, lockedUntilTimestamp: 0 }); + } + return abi.decode(result, (PrecompileLib.UserVaultEquity)); + } + + function withdrawable(address user) internal returns (PrecompileLib.Withdrawable memory) { + bytes memory result = _makeRpcCall(WITHDRAWABLE_PRECOMPILE_ADDRESS, abi.encode(user)); + if (result.length == 0) { + return PrecompileLib.Withdrawable({ withdrawable: 45 }); + } + return abi.decode(result, (PrecompileLib.Withdrawable)); + } + + function delegations(address user) internal returns (PrecompileLib.Delegation[] memory) { + bytes memory result = _makeRpcCall(DELEGATIONS_PRECOMPILE_ADDRESS, abi.encode(user)); + if (result.length == 0) { + return new PrecompileLib.Delegation[](0); + } + return abi.decode(result, (PrecompileLib.Delegation[])); + } + + function delegatorSummary(address user) internal returns (PrecompileLib.DelegatorSummary memory) { + bytes memory result = _makeRpcCall(DELEGATOR_SUMMARY_PRECOMPILE_ADDRESS, abi.encode(user)); + return abi.decode(result, (PrecompileLib.DelegatorSummary)); + } + + function markPx(uint32 index) internal returns (uint64) { + bytes memory result = _makeRpcCall(MARK_PX_PRECOMPILE_ADDRESS, abi.encode(index)); + return abi.decode(result, (uint64)); + } + + function oraclePx(uint32 index) internal returns (uint64) { + bytes memory result = _makeRpcCall(ORACLE_PX_PRECOMPILE_ADDRESS, abi.encode(index)); + return abi.decode(result, (uint64)); + } + + function spotPx(uint32 index) internal returns (uint64) { + bytes memory result = _makeRpcCall(SPOT_PX_PRECOMPILE_ADDRESS, abi.encode(index)); + return abi.decode(result, (uint64)); + } + + function l1BlockNumber() internal returns (uint64) { + bytes memory result = _makeRpcCall(L1_BLOCK_NUMBER_PRECOMPILE_ADDRESS, abi.encode()); + return abi.decode(result, (uint64)); + } + + function perpAssetInfo(uint32 perp) internal returns (PerpAssetInfo memory) { + bytes memory result = _makeRpcCall(PERP_ASSET_INFO_PRECOMPILE_ADDRESS, abi.encode(perp)); + return abi.decode(result, (PerpAssetInfo)); + } + + function spotInfo(uint32 spot) internal returns (PrecompileLib.SpotInfo memory) { + bytes memory result = _makeRpcCall(SPOT_INFO_PRECOMPILE_ADDRESS, abi.encode(spot)); + return abi.decode(result, (PrecompileLib.SpotInfo)); + } + + function tokenInfo(uint32 token) internal returns (PrecompileLib.TokenInfo memory) { + bytes memory result = _makeRpcCall(TOKEN_INFO_PRECOMPILE_ADDRESS, abi.encode(token)); + if (result.length == 0) { + return + PrecompileLib.TokenInfo({ + name: "", + spots: new uint64[](0), + deployerTradingFeeShare: 0, + deployer: INVALID_ADDRESS, + evmContract: INVALID_ADDRESS, + szDecimals: 0, + weiDecimals: 0, + evmExtraWeiDecimals: 0 + }); + } + return abi.decode(result, (PrecompileLib.TokenInfo)); + } + + function tokenSupply(uint32 token) internal returns (TokenSupply memory) { + bytes memory result = _makeRpcCall(TOKEN_SUPPLY_PRECOMPILE_ADDRESS, abi.encode(token)); + return abi.decode(result, (TokenSupply)); + } + + function bbo(uint32 asset) internal returns (Bbo memory) { + bytes memory result = _makeRpcCall(BBO_PRECOMPILE_ADDRESS, abi.encode(asset)); + return abi.decode(result, (Bbo)); + } + + function accountMarginSummary(uint32 perp_dex_index, address user) internal returns (AccountMarginSummary memory) { + bytes memory result = _makeRpcCall(ACCOUNT_MARGIN_SUMMARY_PRECOMPILE_ADDRESS, abi.encode(perp_dex_index, user)); + return abi.decode(result, (AccountMarginSummary)); + } + + function coreUserExists(address user) internal returns (CoreUserExists memory) { + bytes memory result = _makeRpcCall(CORE_USER_EXISTS_PRECOMPILE_ADDRESS, abi.encode(user)); + return abi.decode(result, (CoreUserExists)); + } +} diff --git a/test/evm/hardhat/chain-adapters/Arbitrum_Adapter.ts b/test/evm/hardhat/chain-adapters/Arbitrum_Adapter.ts index 2e58c5d2f..57c31b866 100644 --- a/test/evm/hardhat/chain-adapters/Arbitrum_Adapter.ts +++ b/test/evm/hardhat/chain-adapters/Arbitrum_Adapter.ts @@ -27,8 +27,8 @@ import { MessagingReceiptStructOutput, OFTReceiptStructOutput, SendParamStruct, -} from "../../../../typechain/contracts/interfaces/IOFT"; -import { IOFT__factory } from "../../../../typechain/factories/contracts/interfaces/IOFT__factory"; +} from "../../../../typechain/contracts/interfaces/IOFT.sol/IOFT"; +import { IOFT__factory } from "../../../../typechain/factories/contracts/interfaces/IOFT.sol/IOFT__factory"; import { hubPoolFixture, enableTokensForLP } from "../fixtures/HubPool.Fixture"; import { constructSingleChainTree } from "../MerkleLib.utils"; import { CIRCLE_DOMAIN_IDs } from "../../../../deploy/consts"; diff --git a/test/evm/hardhat/chain-adapters/OP_Adapter.ts b/test/evm/hardhat/chain-adapters/OP_Adapter.ts index c5e2778bd..e3b612ed5 100644 --- a/test/evm/hardhat/chain-adapters/OP_Adapter.ts +++ b/test/evm/hardhat/chain-adapters/OP_Adapter.ts @@ -12,9 +12,11 @@ import { toWei, getContractFactory, seedWallet, + toBN, } from "../../../../utils/utils"; import { hubPoolFixture, enableTokensForLP } from "../fixtures/HubPool.Fixture"; import { constructSingleChainTree } from "../MerkleLib.utils"; +import { ZERO_ADDRESS } from "@uma/common"; let hubPool: Contract, adapter: Contract, weth: Contract, usdc: Contract, mockSpoke: Contract, timer: Contract; let l2Weth: string, l2Usdc: string; @@ -63,7 +65,9 @@ describe("OP Adapter", function () { usdc.address, l1CrossDomainMessenger.address, l1StandardBridge.address, - opUSDCBridge.address + opUSDCBridge.address, + ZERO_ADDRESS, + toBN("322") ); // Seed the HubPool some funds so it can send L1->L2 messages. await hubPool.connect(liquidityProvider).loadEthForL2Calls({ value: toWei("1") }); diff --git a/test/evm/hardhat/chain-adapters/Polygon_Adapter.ts b/test/evm/hardhat/chain-adapters/Polygon_Adapter.ts index b36181d4d..f8f3850a6 100644 --- a/test/evm/hardhat/chain-adapters/Polygon_Adapter.ts +++ b/test/evm/hardhat/chain-adapters/Polygon_Adapter.ts @@ -32,8 +32,8 @@ import { MessagingReceiptStructOutput, OFTReceiptStructOutput, SendParamStruct, -} from "../../../../typechain/contracts/interfaces/IOFT"; -import { IOFT__factory } from "../../../../typechain/factories/contracts/interfaces/IOFT__factory"; +} from "../../../../typechain/contracts/interfaces/IOFT.sol/IOFT"; +import { IOFT__factory } from "../../../../typechain/factories/contracts/interfaces/IOFT.sol/IOFT__factory"; import { CIRCLE_DOMAIN_IDs } from "../../../../deploy/consts"; import { AdapterStore, AdapterStore__factory } from "../../../../typechain"; import { CHAIN_IDs } from "@across-protocol/constants"; diff --git a/test/evm/hardhat/chain-specific-spokepools/Arbitrum_SpokePool.ts b/test/evm/hardhat/chain-specific-spokepools/Arbitrum_SpokePool.ts index 36a74031f..690d3a206 100644 --- a/test/evm/hardhat/chain-specific-spokepools/Arbitrum_SpokePool.ts +++ b/test/evm/hardhat/chain-specific-spokepools/Arbitrum_SpokePool.ts @@ -26,8 +26,8 @@ import { MessagingReceiptStructOutput, OFTReceiptStructOutput, SendParamStruct, -} from "../../../../typechain/contracts/interfaces/IOFT"; -import { IOFT__factory } from "../../../../typechain/factories/contracts/interfaces/IOFT__factory"; +} from "../../../../typechain/contracts/interfaces/IOFT.sol/IOFT"; +import { IOFT__factory } from "../../../../typechain/factories/contracts/interfaces/IOFT.sol/IOFT__factory"; import { CHAIN_IDs } from "@across-protocol/constants"; let hubPool: Contract, arbitrumSpokePool: Contract, dai: Contract, weth: Contract, l2UsdtContract: Contract; diff --git a/test/evm/hardhat/chain-specific-spokepools/Polygon_SpokePool.ts b/test/evm/hardhat/chain-specific-spokepools/Polygon_SpokePool.ts index e74092837..b3989e5c3 100644 --- a/test/evm/hardhat/chain-specific-spokepools/Polygon_SpokePool.ts +++ b/test/evm/hardhat/chain-specific-spokepools/Polygon_SpokePool.ts @@ -25,13 +25,13 @@ import { } from "../../../../utils/utils"; import { getOftEid } from "../../../../utils/utils"; import { CHAIN_IDs } from "@across-protocol/constants"; -import { IOFT__factory } from "../../../../typechain/factories/contracts/interfaces/IOFT__factory"; +import { IOFT__factory } from "../../../../typechain/factories/contracts/interfaces/IOFT.sol/IOFT__factory"; import { MessagingFeeStructOutput, MessagingReceiptStructOutput, OFTReceiptStructOutput, SendParamStruct, -} from "../../../../typechain/contracts/interfaces/IOFT"; +} from "../../../../typechain/contracts/interfaces/IOFT.sol/IOFT"; import { hre } from "../../../../utils/utils.hre"; import { hubPoolFixture } from "../fixtures/HubPool.Fixture"; import { buildRelayerRefundLeaves, buildRelayerRefundTree, constructSingleRelayerRefundTree } from "../MerkleLib.utils"; diff --git a/yarn.lock b/yarn.lock index d911321b1..5b1feb8a7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10358,6 +10358,13 @@ hardhat-gas-reporter@^1.0.4, hardhat-gas-reporter@^1.0.8: eth-gas-reporter "^0.2.24" sha1 "^1.1.1" +hardhat-preprocessor@^0.1.5: + version "0.1.5" + resolved "https://registry.yarnpkg.com/hardhat-preprocessor/-/hardhat-preprocessor-0.1.5.tgz#75b22641fd6a680739c995d03bd5f7868eb72144" + integrity sha512-j8m44mmPxpxAAd0G8fPHRHOas/INZdzptSur0TNJvMEGcFdLDhbHHxBcqZVQ/bmiW42q4gC60AP4CXn9EF018g== + dependencies: + murmur-128 "^0.2.1" + hardhat-typechain@^0.3.5: version "0.3.5" resolved "https://registry.yarnpkg.com/hardhat-typechain/-/hardhat-typechain-0.3.5.tgz#8e50616a9da348b33bd001168c8fda9c66b7b4af"