{"id":18935906,"url":"https://github.com/0xjuancito/multichain-auditor","last_synced_at":"2025-03-31T04:04:05.522Z","repository":{"id":168691914,"uuid":"643011741","full_name":"0xJuancito/multichain-auditor","owner":"0xJuancito","description":"Observations and tips checklist for auditing protocols on multiple chains 🧐","archived":false,"fork":false,"pushed_at":"2024-09-02T16:20:11.000Z","size":198,"stargazers_count":685,"open_issues_count":0,"forks_count":90,"subscribers_count":11,"default_branch":"main","last_synced_at":"2025-03-31T04:03:59.241Z","etag":null,"topics":["arbitrum","auditing","base","bsc","ethereum","moonbeam","optimism","polygon","solidity","zksync"],"latest_commit_sha":null,"homepage":"","language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/0xJuancito.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-05-19T21:41:52.000Z","updated_at":"2025-03-20T18:27:48.000Z","dependencies_parsed_at":"2024-06-16T23:27:16.710Z","dependency_job_id":"640dde67-09f5-41d4-9377-367f6e606723","html_url":"https://github.com/0xJuancito/multichain-auditor","commit_stats":null,"previous_names":["0xjuancito/multichain-auditor"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/0xJuancito%2Fmultichain-auditor","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/0xJuancito%2Fmultichain-auditor/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/0xJuancito%2Fmultichain-auditor/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/0xJuancito%2Fmultichain-auditor/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/0xJuancito","download_url":"https://codeload.github.com/0xJuancito/multichain-auditor/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246413230,"owners_count":20773053,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["arbitrum","auditing","base","bsc","ethereum","moonbeam","optimism","polygon","solidity","zksync"],"created_at":"2024-11-08T12:04:59.509Z","updated_at":"2025-03-31T04:04:05.501Z","avatar_url":"https://github.com/0xJuancito.png","language":null,"funding_links":[],"categories":[],"sub_categories":[],"readme":"# Multichain Auditor\n\nObservations and tips for auditing protocols on multiple chains 🧐\n\n#### ✍️ Open to Contributions\n\nIf you see some error, or want to add an observation, please create an issue or a PR. References are greatly appreciated. You can also contact me on Twitter at [@0xJuancito](https://twitter.com/0xJuancito).\n\n#### 📜 Disclaimer\n\nTake the observations in this repository as a guideline and kickstarter to your findings. Judge the actual impact independently, and please **do not use them as a tool to spam audit contests**. Do your own research.\n\n## Index\n\n- [General Observations](#general-observations)\n  - [Block time is not the same on different chains](#block-time-is-not-the-same-on-different-chains)\n  - [Block production may not be constant](#block-production-may-not-be-constant)\n  - [L2 Sequencer Uptime Feeds in Chainlink](#l2-sequencer-uptime-feeds-in-chainlink)\n  - [Chainlink Price Feeds](#chainlink-price-feeds)\n  - [AMM pools token0 and token1 order](#amm-pools-token0-and-token1-order)\n  - [Modified Opcodes](#modified-opcodes)\n  - [Support for the push0 opcode](#support-for-the-push0-opcode)\n  - [Address Aliasing - tx.origin / msg.sender](#address-aliasing---txorigin--msgsender)\n  - [tx.origin == msg.sender](#txorigin--msgsender)\n  - [transfer, send and fixed gas operations](#txorigin--msgsender)\n  - [Gas fees](#gas-fees)\n  - [Frontrunning](#frontrunning)\n  - [Signature replay](#signature-replay)\n  - [Hardcoded Contract Addresses](#hardcoded-contract-addresses)\n  - [ERC20 decimals](#erc20-decimals)\n  - [Contracts Interface](#contracts-interface)\n  - [Contracts Upgradability](#contracts-upgradability)\n  - [Contracts may behave differently](#contracts-may-behave-differently)\n  - [Precompiles](#precompiles)\n  - [zkSync Era](#zksync-era)\n- [Differences from Ethereum](#differences-from-ethereum)\n\n## General Observations\n\n### Block time is not the same on different chains\n\nBlock time refers to the time separating blocks. The average block time in [Ethereum](https://ethereum.org/en/developers/docs/blocks/#block-time) is 12s, but this value is different on different chains.\n\nExample: \n\n```solidity\n// 1 block every 12 sec -\u003e 5 blocks / min\nuint256 auctionDuration = 7200; // Auction duration lasts for one day (5 * 60 * 24 = 7200)\n```\n\nExample: [OZ Wizard](https://wizard.openzeppelin.com/#governor)\n\n💡 Look for hardcoded time values dependent on the `block.number` that may only be valid on Mainnet.\n\n### Block production may not be constant\n\n`block.number` is NOT a reliable source of timing information for short terms.\n\nOn [Arbitrum](https://docs.arbitrum.io/time#example) it reflects the L1 block number, which is updated once per minute\n\n💡 Look for the use of `block.number` as a time reference, especially on L2.\n💡 Block time may change on the same chain [over time](https://etherscan.io/chart/blocktime).\n\n📝 [1](https://github.com/code-423n4/2022-12-tigris-findings/issues/419) [2](https://github.com/code-423n4/2022-12-tigris-findings/issues/67)\n\n### L2 Sequencer Uptime Feeds in Chainlink\n\nFrom [Chainlink documentation](https://docs.chain.link/data-feeds/l2-sequencer-feeds):\n\n\u003e Optimistic rollup protocols have a sequencer that executes and rolls up the L2 transactions by batching multiple transactions into a single transaction.\n\n\u003e If a sequencer becomes unavailable, it is impossible to access read/write APIs that consumers are using and applications on the L2 network will be down for most users.\n\nThis means that if the project does not check if the sequencer is down, it can return stale results.\n\n[Optimism Goerli Uptime Feed](https://goerli-optimism.etherscan.io/address/0x4C4814aa04433e0FB31310379a4D6946D5e1D353#readContract#F10)\n\nMitigations can be found on [Handling Arbitrum outages](https://docs.chain.link/data-feeds/l2-sequencer-feeds#handling-arbitrum-outages) and [Handling outages on Optimism and Metis](https://docs.chain.link/data-feeds/l2-sequencer-feeds#handling-outages-on-optimism-and-metis).\n\nExample:\n\n```solidity\nfunction getPrice(address token) external view override returns (uint) {\n    if (!isSequencerActive()) revert Errors.L2SequencerUnavailable();\n    ...\n}\n\nfunction isSequencerActive() internal view returns (bool) {\n    (, int256 answer, uint256 startedAt,,) = sequencer.latestRoundData();\n    if (block.timestamp - startedAt \u003c= GRACE_PERIOD_TIME || answer == 1)\n        return false;\n    return true;\n}\n```\n\n💡 Check if the projects handles the scenarios where a sequencer is down on optimistic rollup protocols.\n\n📝 [1](https://github.com/sherlock-audit/2023-02-bond-judging/issues/1) [2](https://github.com/sherlock-audit/2023-01-sentiment-judging/issues/16) [3](https://github.com/sherlock-audit/2022-11-sentiment-judging/issues/3) [4](https://github.com/sherlock-audit/2023-04-jojo-judging/issues/101) [5](https://github.com/code-423n4/2022-09-y2k-finance-findings/issues/278)\n\n### Chainlink Price Feeds\n\n\u003e Chainlink Data Feeds provide data that is aggregated from many data sources by a decentralized set of independent node operators.\n\nChainlink provides more price feeds for some chains like [Ethereum](https://docs.chain.link/data-feeds/price-feeds/addresses/?network=ethereum) than others like [Base](https://docs.chain.link/data-feeds/price-feeds/addresses/?network=base) for example. On other chains, no feed may be supported. Also, the same feed like AAVE/USD may have one address on a chain like [Ethereum](https://etherscan.io/address/0x6Df09E975c830ECae5bd4eD9d90f3A95a4f88012), and another one on [Moonriver](https://moonriver.moonscan.io/address/0x37f35ef6735c594e6E803bC81577bAC759d8179C).\n\n💡 Check that the price feed for the desired pair is supported on all of the deployed chains.\n\n💡 Check that the correct addresses are set correctly for each chain if they are hardcoded.\n\n### AMM pools `token0` and `token1` order\n\nIn Uniswap and derived AMMs: `token0` is the token with the lower sort order, while `token1` is the token with the higher sort order, as described on [Uniswap documentation](https://docs.uniswap.org/contracts/v2/reference/smart-contracts/pair#token0). This is valid for both v2 and v3 pools.\n\nThe order is important because that determines which one is the base token, and which one is the quote token. In other words, if the price is WETH/USDC or USDC/WETH.\n\nAs contracts may have different addresses on different chains, the token order can change. That is the case for example on Arbitrum, where the pair is [WETH/USDC](https://arbiscan.io/address/0xc31e54c7a869b9fcbecc14363cf510d1c41fa443#readContract#F16) while on Polygon it is [USDC/WETH](https://polygonscan.com/address/0x45dda9cb7c25131df268515131f647d726f50608#readContract#F16).\n\n💡 Verify that the token orders are taken into account, and it is not assumed to be the same on all chains.\n\n### Modified Opcodes\n\nSome chains implement opcodes with some modification compared to Ethereum, or are not supported.\n\nOptimism for example, [has a different implementation](https://community.optimism.io/docs/developers/build/differences/#modified-opcodes) of opcodes like `block.coinbase`, `block.difficulty`, `block.basefee`. `tx.origin` may also behave different if the it is an L1 =\u003e L2 transaction. It also implements some new opcode [L1BLOCKNUMBER](Chains may also implement new opcodes).\n\nArbitrum also [has some differences](https://developer.arbitrum.io/solidity-support) in some operations/opcodes like: `blockhash(x)`, `block.coinbase`, `block.difficulty`, `block.number`. `msg.sender` may also behave different for L1 =\u003e L2 \"retryable ticket\" transactions.\n\n💡 Verify that the EVM opcodes and operations used by the protocol are compatible on all chains\n\n### Support for the `push0` opcode\n\n`push0` is an instruction which pushes the constant value 0 onto the stack. This opcode is still not supported by many chains and might be problematic for projects compiled with a version of Solidity `\u003e= 0.8.20` (when it was introduced).\n\n💡 Pay attention to projects using a Solidity version `\u003e= 0.8.20` and check if it is supported on the deployed chains.\n\nℹ️ Arbitrum added support in [ArbOS 11](https://docs.arbitrum.io/for-devs/concepts/differences-between-arbitrum-ethereum/solidity-support) and Optimism introduced support for it on the [Canyon Upgrade](https://blog.oplabs.co/canyon-hardfork/) .\n\n### Address Aliasing - `tx.origin` / `msg.sender`\n\nOn some chains like [Optimism](https://community.optimism.io/docs/developers/build/differences/#using-eth-in-contracts), because of the behaviour of the CREATE opcode, it is possible for a user to create a contract on L1 and on L2 that share the same address but have different bytecode.\n\nThis can break trust assumptions, because one contract may be trusted and another be untrusted. To prevent this problem the behaviour of the ORIGIN and CALLER opcodes (tx.origin and msg.sender) differs slightly between L1 and L2.\n\n💡 Verify that the expected behaviour of `tx.origin` and `msg.sender` holds on all deployed chains\n\n### `tx.origin == msg.sender`\n\nFrom [Optimism documentation](https://community.optimism.io/docs/developers/build/differences/#pre-eip-155-support):\n\n\u003e On L1 Ethereum tx.origin is equal to msg.sender only when the smart contract was called directly from an externally owned account (EOA). However, on Optimism tx.origin is the origin on Optimism. It could be an EOA. However, in the case of messages from L1, it is possible for a message from a smart contract on L1 to appear on L2 with tx.origin == msg.sender. This is unlikely to make a significant difference, because an L1 smart contract cannot directly manipulate the L2 state. However, there could be edge cases we did not think about where this matters.\n\n💡 Verify that the expected behavior of `tx.origin` and `msg.sender` holds on all deployed chains\n\n### Cross-chain message vulnerabilities\n\nSome protocols work by sending cross-chain messages to their counterpart contracts on the other chains. This can lead to vulnerabilities like authorization issues, or issues with relayers.\n\n💡 Look for cross-chain messages implementations and verify the correct permissions and functionality considering all the actors involved\n\n📝 [1](https://github.com/code-423n4/2022-12-pooltogether-findings/issues/60) [2](https://github.com/sherlock-audit/2023-01-derby-judging/issues/309) [3](https://github.com/sherlock-audit/2023-01-derby-judging/issues/325)\n\n### `transfer`, `send` and fixed gas operations\n\n`transfer` and `send` forward a hardcoded amount of gas and are [discouraged as gas costs can change](https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/). On certain chains that cost can be higher than in Mainnet, and can result in issues, like in [zkSync Era](https://twitter.com/zksync/status/1644139364270878720).\n\n💡 Look for fixed gas operations like `transfer` or `send`.\n\n### Gas fees\n\nTransactions on Ethereum mainnet are much more expensive than on other chains. Chains with very low fees may open the possibility to implement attacks that require a large amount of transactions, or where the cost-benefit of the attack would now be profitable.\n\nExamples:\n\n- DOS on unbound arrays\n- DOS by filling bound arrays\n- Spamming that can incur in extra processing costs for the protocol\n- An attack that only drains smaller amounts of wei that wouldn't be profitable with high gas fees\n- Frontrunning operations to prevent txns to be executed during a time frame (liquidations, complete auctions, etc.)\n- Griefing attacks against the protocol\n\nAlthough cheaper, each case should be analyzed to check if it is economically viable to actually be considered an attack.\n\n💡 Analyze attack vectors that require low gas fees or where a considerable numbers of transactions have to be executed\n\n📝 [1](https://github.com/sherlock-audit/2023-02-surge-judging/issues/109)\n\n### Frontrunning\n\nFrontrunning is possible on chains that have a mempool or a way to read proposed transactions before they are executed.\n\nIt is possible on some chains like Ethereum, although expensive because of gas costs. It is possible at a cheaper cost on other chains like Polygon. \n\nBut it may be [very difficult](https://help.optimism.io/hc/en-us/articles/4444375174299-Is-transaction-front-running-possible-on-Optimism-) on chains like Optimism [with a private mempool](https://community.optimism.io/docs/developers/bedrock/differences/#mempool)\n\n💡 Verify if a frontrunning attack is possible due to chain constraints or economic viability\n\n### Signature replay across chains\n\nIf a contract is deployed on multiple chains and uses signatures, it may be possible to reuse a signature used on one chain and execute the same transaction on another chain.\n\nTo prevent that, it is important that the signed data contains the chain id where it should be executed.\n\nExample from [UniswapV2](https://github.com/Uniswap/v2-core/blob/master/contracts/UniswapV2ERC20.sol#L34):\n\n```solidity\nconstructor() public {\n    uint chainId;\n    assembly {\n        chainId := chainid\n    }\n    DOMAIN_SEPARATOR = keccak256(\n        abi.encode(\n            keccak256('EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)'),\n            keccak256(bytes(name)),\n            keccak256(bytes('1')),\n@\u003e          chainId,                   // @audit\n            address(this)\n        )\n    );\n}\n\nfunction permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external {\n    require(deadline \u003e= block.timestamp, 'UniswapV2: EXPIRED');\n    bytes32 digest = keccak256(\n        abi.encodePacked(\n            '\\x19\\x01',\n@\u003e          DOMAIN_SEPARATOR, // @audit\n            keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, nonces[owner]++, deadline))\n        )\n    );\n    address recoveredAddress = ecrecover(digest, v, r, s);\n    require(recoveredAddress != address(0) \u0026\u0026 recoveredAddress == owner, 'UniswapV2: INVALID_SIGNATURE');\n    _approve(owner, spender, value);\n}\n```\n\n💡 Check that the data from the signed hash contains the chain id\n\n📝 [1](https://github.com/code-423n4/2022-06-connext-findings/issues/144) [2](https://solodit.xyz/issues/7234) [3](https://solodit.xyz/issues/16276)\n\n### Hardcoded Contract Addresses\n\nProjects sometimes deploy their contracts on the same addresses over different chains but that is not always the case.\n\nTake WETH as an example. Its address on Ethereum is [0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2](https://etherscan.io/token/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2), but [0x7ceb23fd6bc0add59e62ac25578270cff1b9f619](https://polygonscan.com/token/0x7ceb23fd6bc0add59e62ac25578270cff1b9f619) on Polygon.\n\n💡 Verify external contract addresses for the chains where the contracts are deployed\n\n📝 [1](https://github.com/sherlock-audit/2023-01-derby-judging/issues/308)\n\n### ERC20 decimals\n\nSome ERC20 tokens have different `decimals` on different chains. Even some popular ones like USDT and USDC have 6 decimals on Ethereum, and 18 decimals on BSC for example:\n\n- [USDT on Ethereum](https://etherscan.io/token/0xdac17f958d2ee523a2206206994597c13d831ec7#readContract#F6) - 6 decimals\n- [USDC on Ethereum](https://etherscan.io/token/0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48#readProxyContract#F11) - 6 decimals\n- [USDT on BSC](https://bscscan.com/address/0x55d398326f99059ff775485246999027b3197955#readContract#F6) - 18 decimals\n- [USDC on BSC](https://bscscan.com/address/0x8ac76a51cc950d9822d68b83fe1ad97b32cd580d#readProxyContract#F3) - 18 decimals\n\nA more exhaustive list can be found in the [tokens-decimals](https://github.com/magnetto90/tokens-decimals) repository by [@magnetto90](https://github.com/magnetto90).\n\n💡 Check that the correct `decimals` are set for the deployed chains if the token values are hardcoded.\n\n### Contracts Interface\n\nSome contracts have a slightly different interface on different chains, which may break compatibility. \n\nUSDT for example is missing its return value on Ethereum as the ERC20 specification suggests, but it is compliant on that aspect on Polygon. This may [lead to some vulnerabilities](https://github.com/d-xo/weird-erc20#missing-return-values) on some chains, while not on others.\n\n[USDT on Ethereum](https://etherscan.io/token/0xdac17f958d2ee523a2206206994597c13d831ec7#code):\n\n```solidity\n  function transfer(address _to, uint _value) public whenNotPaused {\n```\n\n[USDT Implementation](https://polygonscan.com/address/0x7ffb3d637014488b63fb9858e279385685afc1e2#code) | [USDT Proxy](https://polygonscan.com/token/0xc2132d05d31c914a87c6611c10748aeb04b58e8f#readProxyContract) on Polygon:\n\n```solidity\n  function transfer(address recipient, uint256 amount) public virtual override returns (bool) {\n```\n\n[ERC20 transfer specification](https://eips.ethereum.org/EIPS/eip-20):\n\n```solidity\n  function transfer(address _to, uint256 _value) public returns (bool success)\n```\n\n💡 Verify that the contracts respect the same interface on different chains, or that sufficient mitigations are taken.\n\n### Contracts Upgradability\n\nSome contracts are immutable on a chain but upgradeable on others, like [USDT in Ethereum](https://etherscan.io/token/0xdac17f958d2ee523a2206206994597c13d831ec7#code) vs [USDT in Polygon](https://polygonscan.com/token/0xc2132d05d31c914a87c6611c10748aeb04b58e8f#code).\n\n💡 Double-check the upgradability of contracts on different chains and evaluate their implications.\n\n### Contracts may behave differently\n\nContracts deployed on different chains may behave differently.\n\nOn the XDai chain, USDC, WBTC, and WETH contained post-transfer callback procedures, as opposed to their traditional ERC20 implementations on other chains with no callback.\n\nThat enabled the possibility of a re-entrancy attack that was exploited and ultimately [derived on the fork of the chain](https://forum.gnosis.io/t/gip-31-should-gnosis-chain-perform-a-hardfork-to-upgrade-the-token-contract-vulnerable-to-the-reentrancy-attack/4134).\n\n💡 Check that implementations of contracts match on different chains, or that their differences won't incur on any new vulnerability.\n\n### Precompiles\n\nChains have precompiled contracts on different addresses like [Arbitrum](https://developer.arbitrum.io/arbos/precompiles) or [Optimism](https://github.com/ethereum-optimism/optimism/blob/develop/specs/predeploys.md). Care has to be taken if some is used that is not available, works differently or is on a different address.\n\n💡 Double-check the use of precompiled contracts, their addresses, and their compatibility\n\n### zkSync Era\n\nzkSync Era has many differences from Ethereum on EVM instructions like `CREATE`, `CREATE2`, `CALL`, `STATICCALL`, `DELEGATECALL`, `MSTORE`, `MLOAD`, `CALLDATALOAD,` `CALLDATACOPY`, etc.\n\n💡 Double-check the compatibility of the contracts when being deployed to zkSync Era\n\n---\n\n## Differences from Ethereum\n\nSome blockchains have articles explaining their differences with Ethereum or other EVM chains. Here's a list of official docs:\n\n- [Arbitrum vs Ethereum](https://developer.arbitrum.io/arbitrum-ethereum-differences)\n- [Optimism vs Ethereum](https://docs.optimism.io/chain/differences)\n- [zkSync Era vs Ethereum](https://docs.zksync.io/build/developer-reference/ethereum-differences/evm-instructions)\n- [Linea vs Ethereum](https://docs.linea.build/build-on-linea/ethereum-differences)\n- [Moonbeam vs Ethereum](https://docs.moonbeam.network/learn/features/eth-compatibility/)\n- [Base vs Ethereum](https://docs.base.org/differences/)\n- [Celo vs Ethereum](https://docs.celo.org/developer/migrate/from-ethereum#:~:text=Key%20differences%20between%20Celo%20and%20Ethereum%E2%80%8B\u0026text=Celo%20allows%20users%20to%20pay,pay%20transaction%20fees%20in%20Ether)\n- [opBNB vs Base](https://docs.bnbchain.org/bnb-opbnb/faq/protocol-faqs/?h=difference#what-is-the-difference-between-opbnb-and-other-optimism-based-layer-2-solution-like-base)\n- [Filecoin vs Ethereum](https://docs.filecoin.io/smart-contracts/filecoin-evm-runtime/differences-with-ethereum/)\n- [Gnosis vs Ethereum](https://docs.gnosischain.com/about/specs/hard-forks/dencun#differences-with-ethereum-mainnet)\n- [Tron vs Ethereum](https://developers.tron.network/v4.4.0/docs/vm-vs-evm#Differences%20from%20EVM)\n\n## EVM Compatible Chains Diff\n\nCheck [evm-diff](https://github.com/mds1/evm-diff) repository and the website [evmdiff.com](https://evmdiff.com) to diff EVM-compatible chains in a friendly format. It's an amazing tool created by [@mds1](https://github.com/mds1)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F0xjuancito%2Fmultichain-auditor","html_url":"https://awesome.ecosyste.ms/projects/github.com%2F0xjuancito%2Fmultichain-auditor","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F0xjuancito%2Fmultichain-auditor/lists"}