diff --git a/docs/SUMMARY.md b/docs/SUMMARY.md index b286861a..9acecd75 100644 --- a/docs/SUMMARY.md +++ b/docs/SUMMARY.md @@ -8,8 +8,10 @@ - [ZK Authentication](zk-authentication.md) - [zkVerify & Horizen Integration](zkverify-horizen-integration.md) - [Architecture](architecture.md) +- [Cross-Chain Transfers](cross-chain-transfers.md) - [Developer Documentation](developer-documentation/README.md) - [Getting Started](developer-documentation/getting-started.md) - [API Documentation](developer-documentation/api-documentation.md) - [Database Connection Guide](developer-documentation/database-connection-guide.md) - [Circuit Code Walkthrough](developer-documentation/circuit-code-walkthrough.md) + - [Cross-Chain Bridge Implementation](developer-documentation/cross-chain-bridge-implementation.md) diff --git a/docs/cross-chain-transfers.md b/docs/cross-chain-transfers.md new file mode 100644 index 00000000..cf7a3ed8 --- /dev/null +++ b/docs/cross-chain-transfers.md @@ -0,0 +1,44 @@ +# Cross-Chain Transfers + +PolyPay supports bridging tokens between **Horizen** and **Base** networks directly from the multisig wallet. Cross-chain transfers go through the same privacy-preserving approval flow as regular transfers -- signers approve with ZK proofs, and the relayer executes on-chain. + +### Supported Routes + +| Token | Base → Horizen | Horizen → Base | Bridge | +|-------|:-:|:-:|--------| +| ETH | Yes | No | OP Stack native bridge | +| ZEN | Yes | Yes | LayerZero OFT | +| USDC | Yes (mainnet) | Yes (mainnet) | Stargate V2 (LayerZero) | + +ETH can only be bridged from Base to Horizen using the OP Stack native bridge. The reverse direction is not supported because Horizen is an optimistic rollup and ETH withdrawals require a challenge period that does not fit the instant transfer model. + +ZEN and USDC use [LayerZero](https://layerzero.network) for bidirectional transfers between both chains. + +### How It Works + +1. **Select destination chain** -- When creating a transfer, choose the target network from the chain selector. If only same-chain is available for that token, the selector is hidden. +2. **Approve** -- Other signers review and approve the cross-chain transfer exactly like a regular transfer. The destination chain is displayed in the transaction details. +3. **Execute** -- Once the approval threshold is met, any signer can trigger execution. The relayer submits the transaction on-chain, which calls the bridge contract to deliver tokens to the recipient on the destination chain. + +### Contract Version Requirement + +Cross-chain transfers require **MetaMultiSigWallet contract version 2** or higher. Version 2 introduces the `approveAndCall` function, which atomically approves a token and calls a bridge contract in a single multisig transaction. Accounts on version 1 will not see the chain selector in the UI. + +### Fees + +Cross-chain transfers involve two types of fees: + +| Fee | Paid in | Applies to | Description | +|-----|---------|------------|-------------| +| LayerZero messaging fee | ETH | ZEN, USDC | Covers cross-chain message delivery. Paid from the wallet's ETH balance. | +| Stargate protocol fee | USDC (deducted from amount) | USDC only | ~0.06% fee charged by Stargate V2. The recipient receives slightly less than the sent amount. | + +ETH bridging via OP Stack does not have an explicit bridge fee beyond the normal transaction gas on Base. + +### Limitations + +* Batch transfers do not support cross-chain. Each cross-chain transfer must be submitted individually. +* USDC bridging is only available on mainnet (no testnet OFT contracts deployed). +* ETH cannot be bridged from Horizen to Base. + +For technical details on the bridge implementation, see [Cross-Chain Bridge Implementation](developer-documentation/cross-chain-bridge-implementation.md). diff --git a/docs/developer-documentation/cross-chain-bridge-implementation.md b/docs/developer-documentation/cross-chain-bridge-implementation.md new file mode 100644 index 00000000..4810aaf1 --- /dev/null +++ b/docs/developer-documentation/cross-chain-bridge-implementation.md @@ -0,0 +1,89 @@ +# Cross-Chain Bridge Implementation + +Technical reference for cross-chain transfers in PolyPay. + +## Architecture Overview + +Cross-chain transfers reuse `TxType.TRANSFER` with extra metadata (`destChainId`, `bridgeFee`, `bridgeMinAmount`). Three actors must produce **byte-identical calldata** so that ZK proofs match the on-chain txHash. + +```mermaid +sequenceDiagram + participant Creator as Creator (Frontend) + participant Backend as Backend (NestJS) + participant Voter as Voter (Frontend) + participant Contract as Smart Contract + + Creator->>Creator: buildBridgeParams() -> (to, value, data) + Creator->>Contract: getTransactionHash(nonce, to, value, data) -> txHash + Creator->>Creator: generateProof(txHash) + Creator->>Backend: createTransaction(DTO with bridgeFee, bridgeMinAmount) + + Voter->>Voter: buildBridgeTransactionParams() using stored fields + Voter->>Contract: getTransactionHash() -> same txHash + Voter->>Backend: approve(proof) + + Backend->>Backend: buildBridgeExecuteParams() using stored fields + Backend->>Contract: execute(nonce, to, value, data, zkProofs) + Contract->>Contract: recompute txHash, verify proofs, call bridge +``` + +## Bridge Routes + +| Token | Direction | Mechanism | OFT Contract | Notes | +|-------|-----------|-----------|-------------|-------| +| ETH | Base -> Horizen | OP Stack native bridge | N/A | Reverse excluded (7-day fraud proof) | +| ZEN | Base <-> Horizen | LayerZero | Adapter on Base, OFT on Horizen | Bidirectional, testnet supported | +| USDC | Base <-> Horizen | Stargate V2 (LayerZero) | Adapter on both chains | Mainnet only, ~0.06% protocol fee | + +Route availability is determined by `getAvailableDestChains()` in `bridge.ts`. Cross-chain requires contract version >= 2 (`isCrossChainEnabled()`). + +## Encoding Logic + +The OFT contract entry `type` determines how the multisig calls the bridge: + +| OFT Type | execute() params | When used | +|----------|-----------------|-----------| +| `"oft"` | `to = OFT address`, `value = bridgeFee`, `data = encodeLzSend(...)` | Token IS the OFT (e.g., ZEN on Horizen) | +| `"adapter"` | `to = wallet (self-call)`, `value = 0`, `data = encodeApproveAndCall(...)` | Token separate from OFT, needs allowance first | + +For adapters, `approveAndCall` (added in MetaMultiSigWallet v2, `onlySelf` modifier) atomically approves the token and calls `OFT.send()`. The `callValue` parameter forwards ETH from the wallet's own balance to pay the LayerZero fee. + +For Stargate OFTs (`stargate: true` in config), `oftCmd` is set to `"0x01"` (taxi mode) for immediate delivery. Standard OFTs use empty `oftCmd`. + +## Fees and Slippage + +| Field | What it is | Source | Paid in | Stored in DB | +|-------|-----------|--------|---------|--------------| +| `bridgeFee` | LayerZero messaging fee | `quoteSend()` (one-time, by creator) | ETH from wallet balance | Yes | +| `bridgeMinAmount` | Min tokens recipient must receive | `removeDust()` (standard OFT) or `quoteOFT().amountReceivedLD` (Stargate) | N/A (threshold) | Yes | +| Stargate protocol fee | ~0.06% deducted from transfer amount | Implicit in `quoteOFT()` result | USDC (deducted from amount) | No (embedded in `bridgeMinAmount`) | + +Standard OFTs (ZEN) are 1:1 transfers with no price impact. `removeDust()` strips precision bits lost during LayerZero's shared-decimals (6) conversion -- only relevant when local decimals > 6 (e.g., 18 for ZEN). + +Stargate OFTs (USDC) charge a protocol fee, so `minAmountLD` must come from `quoteOFT()` rather than `removeDust()` to avoid `Stargate_SlippageTooHigh` reverts. + +## txHash Consistency + +All three actors (creator, voter, backend) must encode identical `(to, value, data)` so the smart contract's recomputed txHash matches the ZK proofs. Non-deterministic values are stored in DB and reused: + +| Field | Why stored | Fallback if null | +|-------|-----------|-----------------| +| `bridgeFee` | LZ fee changes over time | `0` | +| `bridgeMinAmount` | Stargate fee is non-deterministic | `removeDust(amount, decimals)` | + +Deterministic values (`dstEid`, `oftCmd`) are derived at runtime from config. + +## Database Fields + +| Column | Type | Description | +|--------|------|-------------| +| `destChainId` | `Int?` | Destination chain ID. Null = same-chain. | +| `bridgeFee` | `String?` | LZ native fee in wei. | +| `bridgeMinAmount` | `String?` | Min received amount in token's smallest unit. | + +## External References + +- [LayerZero V2 OFT Documentation](https://docs.layerzero.network/v2/developers/evm/oft/quickstart) +- [Stargate V2 Developer Docs](https://stargateprotocol.gitbook.io/stargate/v2-developer-docs) +- [OP Stack Standard Bridge](https://docs.optimism.io/app-developers/bridging/standard-bridge) +- [Horizen Documentation](https://docs.horizen.io) diff --git a/packages/backend/prisma/migrations/20260302082625_add_des_chain_id/migration.sql b/packages/backend/prisma/migrations/20260302082625_add_des_chain_id/migration.sql new file mode 100644 index 00000000..fefeae02 --- /dev/null +++ b/packages/backend/prisma/migrations/20260302082625_add_des_chain_id/migration.sql @@ -0,0 +1,3 @@ +-- AlterTable +ALTER TABLE "transactions" ADD COLUMN "bridge_fee" TEXT, +ADD COLUMN "dest_chain_id" INTEGER; diff --git a/packages/backend/prisma/migrations/20260304064801_add_bridge_min_amount/migration.sql b/packages/backend/prisma/migrations/20260304064801_add_bridge_min_amount/migration.sql new file mode 100644 index 00000000..7014b4b1 --- /dev/null +++ b/packages/backend/prisma/migrations/20260304064801_add_bridge_min_amount/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "transactions" ADD COLUMN "bridge_min_amount" TEXT; diff --git a/packages/backend/prisma/schema.prisma b/packages/backend/prisma/schema.prisma index 0cfcea22..d21e379b 100644 --- a/packages/backend/prisma/schema.prisma +++ b/packages/backend/prisma/schema.prisma @@ -68,6 +68,9 @@ model Transaction { signerData String? @map("signer_data") newThreshold Int? @map("new_threshold") batchData String? @map("batch_data") + destChainId Int? @map("dest_chain_id") + bridgeFee String? @map("bridge_fee") + bridgeMinAmount String? @map("bridge_min_amount") createdBy String @map("created_by") threshold Int txHash String? @map("tx_hash") diff --git a/packages/backend/src/account/account.service.ts b/packages/backend/src/account/account.service.ts index 97c66c52..04dccb46 100644 --- a/packages/backend/src/account/account.service.ts +++ b/packages/backend/src/account/account.service.ts @@ -9,6 +9,7 @@ import { CreateAccountDto, CreateAccountBatchDto, UpdateAccountDto, + CROSS_CHAIN_MIN_CONTRACT_VERSION, } from '@polypay/shared'; import { RelayerService } from '@/relayer-wallet/relayer-wallet.service'; import { EventsService } from '@/events/events.service'; @@ -86,6 +87,7 @@ export class AccountService { name: dto.name, threshold: dto.threshold, chainId: dto.chainId, + contractVersion: CROSS_CHAIN_MIN_CONTRACT_VERSION, }, }); @@ -235,6 +237,7 @@ export class AccountService { name: dto.name, threshold: dto.threshold, chainId: deployment.chainId, + contractVersion: CROSS_CHAIN_MIN_CONTRACT_VERSION, }, }); @@ -339,6 +342,7 @@ export class AccountService { name: account.name, threshold: account.threshold, chainId: account.chainId, + contractVersion: account.contractVersion, createdAt: account.createdAt, signers: account.signers.map((as) => ({ commitment: as.user.commitment, @@ -369,6 +373,7 @@ export class AccountService { name: account.name, threshold: account.threshold, chainId: account.chainId, + contractVersion: account.contractVersion, createdAt: account.createdAt, signers: account.signers.map((as) => ({ commitment: as.user.commitment, diff --git a/packages/backend/src/relayer-wallet/relayer-wallet.service.ts b/packages/backend/src/relayer-wallet/relayer-wallet.service.ts index 16cc8b14..a1f06ee7 100644 --- a/packages/backend/src/relayer-wallet/relayer-wallet.service.ts +++ b/packages/backend/src/relayer-wallet/relayer-wallet.service.ts @@ -263,6 +263,43 @@ export class RelayerService { } catch (e) { // Not a batchTransferMulti call, continue } + + // Try decode approveAndCall (cross-chain bridge via OFT Adapter) + try { + const decoded = decodeFunctionData({ + abi: [ + { + name: 'approveAndCall', + type: 'function', + inputs: [ + { name: 'token', type: 'address' }, + { name: 'spender', type: 'address' }, + { name: 'approveAmount', type: 'uint256' }, + { name: 'callTarget', type: 'address' }, + { name: 'callValue', type: 'uint256' }, + { name: 'callData', type: 'bytes' }, + ], + }, + ], + data: data as `0x${string}`, + }); + + if (decoded.functionName === 'approveAndCall') { + const tokenAddress = (decoded.args[0] as string).toLowerCase(); + const approveAmount = decoded.args[2] as bigint; + const callValue = decoded.args[4] as bigint; + + erc20Requirements[tokenAddress] = + (erc20Requirements[tokenAddress] || 0n) + approveAmount; + requiredBalance = requiredBalance + callValue; + + this.logger.log( + `ApproveAndCall detected. Token: ${tokenAddress}, Amount: ${approveAmount}, LZ fee: ${callValue}`, + ); + } + } catch (e) { + // Not an approveAndCall, continue + } } // Check ETH balance diff --git a/packages/backend/src/transaction/transaction.service.ts b/packages/backend/src/transaction/transaction.service.ts index 4d0a41ce..92fa7204 100644 --- a/packages/backend/src/transaction/transaction.service.ts +++ b/packages/backend/src/transaction/transaction.service.ts @@ -15,6 +15,9 @@ import { encodeBatchTransferMulti, encodeERC20Transfer, encodeBatchTransfer, + encodeBridgeETHTo, + encodeLzSend, + encodeApproveAndCall, TxStatus, TX_CREATED_EVENT, TX_VOTED_EVENT, @@ -30,6 +33,14 @@ import { encodeRemoveSigners, SignerData, ZERO_ADDRESS, + getTokenByAddress, + getBridgeMechanism, + getBridgeContract, + getOftCmd, + OP_BRIDGE_ADDRESSES, + LZ_ENDPOINT_IDS, + isCrossChainEnabled, + removeDust, } from '@polypay/shared'; import { RelayerService } from '@/relayer-wallet/relayer-wallet.service'; import { BatchItemService } from '@/batch-item/batch-item.service'; @@ -100,6 +111,14 @@ export class TransactionService { ); } + if (dto.destChainId && dto.destChainId !== account.chainId) { + if (!isCrossChainEnabled(account.contractVersion)) { + throw new BadRequestException( + 'Cross-chain transfers require contract version >= 2. Please upgrade your account.', + ); + } + } + // Create batch data let batchData: string | null = null; @@ -164,6 +183,9 @@ export class TransactionService { contactId: dto.contactId, signerData: dto.signers ? JSON.stringify(dto.signers) : null, newThreshold: dto.newThreshold, + destChainId: dto.destChainId, + bridgeFee: dto.bridgeFee, + bridgeMinAmount: dto.bridgeMinAmount, createdBy: userCommitment, status: 'PENDING', batchData, @@ -654,7 +676,7 @@ export class TransactionService { } // Build execute params based on tx type - const { to, value, data } = this.buildExecuteParams(transaction); + const { to, value, data } = await this.buildExecuteParams(transaction); // Format proofs for smart contract const zkProofs = approveVotes.map((vote) => ({ @@ -1106,17 +1128,27 @@ export class TransactionService { ); } - private buildExecuteParams(transaction: Transaction): { + private async buildExecuteParams(transaction: Transaction): Promise<{ to: string; value: string; data: string; - } { + }> { switch (transaction.type) { - case TxType.TRANSFER: + case TxType.TRANSFER: { + // Check for cross-chain bridge + if (transaction.destChainId) { + const account = await this.prisma.account.findUnique({ + where: { address: transaction.accountAddress }, + }); + if (account && transaction.destChainId !== account.chainId) { + return this.buildBridgeExecuteParams(transaction, account.chainId); + } + } + // ERC20 transfer if (transaction?.tokenAddress) { return { - to: transaction.tokenAddress, // Token contract address + to: transaction.tokenAddress, value: '0', data: encodeERC20Transfer( transaction.to, @@ -1130,6 +1162,7 @@ export class TransactionService { value: transaction.value, data: '0x', }; + } case TxType.ADD_SIGNER: { const signers: SignerData[] = transaction.signerData @@ -1200,6 +1233,118 @@ export class TransactionService { } } + private buildBridgeExecuteParams( + transaction: Transaction, + srcChainId: number, + ): { to: string; value: string; data: string } { + const destChainId = transaction.destChainId; + const tokenSymbol = transaction.tokenAddress + ? transaction.tokenAddress === ZERO_ADDRESS + ? 'ETH' + : null + : 'ETH'; + + let resolvedSymbol = tokenSymbol; + if (!resolvedSymbol && transaction.tokenAddress) { + resolvedSymbol = this.resolveTokenSymbol( + transaction.tokenAddress, + srcChainId, + ); + } + + const mechanism = getBridgeMechanism( + srcChainId, + destChainId, + resolvedSymbol, + ); + + if (!mechanism) { + throw new BadRequestException( + `No bridge route for ${resolvedSymbol} from chain ${srcChainId} to ${destChainId}`, + ); + } + + const recipient = transaction.to; + const amount = BigInt(transaction.value); + const bridgeFee = transaction.bridgeFee + ? BigInt(transaction.bridgeFee) + : 0n; + + if (mechanism === 'OP_STACK') { + const bridgeAddress = OP_BRIDGE_ADDRESSES[srcChainId]; + if (!bridgeAddress) { + throw new BadRequestException(`No OP bridge on chain ${srcChainId}`); + } + return { + to: bridgeAddress, + value: amount.toString(), + data: encodeBridgeETHTo(recipient), + }; + } + + // LAYERZERO + const dstEid = LZ_ENDPOINT_IDS[destChainId]; + if (!dstEid) { + throw new BadRequestException(`No LZ endpoint for chain ${destChainId}`); + } + + const oftEntry = getBridgeContract(resolvedSymbol, srcChainId); + if (!oftEntry) { + throw new BadRequestException( + `No OFT contract for ${resolvedSymbol} on chain ${srcChainId}`, + ); + } + + const token = getTokenByAddress(transaction.tokenAddress, srcChainId); + const minAmount = transaction.bridgeMinAmount + ? BigInt(transaction.bridgeMinAmount) + : removeDust(amount, token.decimals); + const oftCmd = getOftCmd(oftEntry); + + const lzSendData = encodeLzSend( + dstEid, + recipient, + amount, + minAmount, + bridgeFee, + transaction.accountAddress, + oftCmd as `0x${string}`, + ); + + if (oftEntry.type === 'adapter') { + // Base side: approve token + call adapter. Self-call with value=0. + return { + to: transaction.accountAddress, + value: '0', + data: encodeApproveAndCall( + transaction.tokenAddress, + oftEntry.address, + amount, + oftEntry.address, + bridgeFee, + lzSendData, + ), + }; + } + + // Horizen side: direct OFT send with bridgeFee as value + return { + to: oftEntry.address, + value: bridgeFee.toString(), + data: lzSendData, + }; + } + + private resolveTokenSymbol(tokenAddress: string, chainId: number): string { + const token = getTokenByAddress(tokenAddress, chainId); + if (token.address === ZERO_ADDRESS) { + throw new BadRequestException( + `Cannot resolve token symbol for address ${tokenAddress} on chain ${chainId}`, + ); + } + return token.symbol; + } + /** * Check if transaction should be marked as FAILED * Query totalSigners realtime from account.signers diff --git a/packages/backend/src/user/user.service.ts b/packages/backend/src/user/user.service.ts index 3aed8ea1..fffc8b72 100644 --- a/packages/backend/src/user/user.service.ts +++ b/packages/backend/src/user/user.service.ts @@ -82,6 +82,7 @@ export class UserService { name: account.name, threshold: account.threshold, chainId: account.chainId, + contractVersion: account.contractVersion, createdAt: account.createdAt, updatedAt: account.updatedAt, signers: account.signers.map((signer) => ({ diff --git a/packages/hardhat/contracts/MetaMultiSigWallet.sol b/packages/hardhat/contracts/MetaMultiSigWallet.sol index 2725b005..a4180bf1 100644 --- a/packages/hardhat/contracts/MetaMultiSigWallet.sol +++ b/packages/hardhat/contracts/MetaMultiSigWallet.sol @@ -214,6 +214,33 @@ contract MetaMultiSigWallet { } } + /** + * @notice Approve an ERC20 token and call a target contract in one atomic operation. + * Used for cross-chain bridge flows (e.g. OFT Adapter: approve + send). + * @param token ERC20 token to approve + * @param spender Address to approve spending + * @param approveAmount Amount to approve + * @param callTarget Contract to call after approval + * @param callValue Native ETH to send with the call (e.g. LayerZero fee) + * @param callData Encoded function call for the target + */ + function approveAndCall( + address token, + address spender, + uint256 approveAmount, + address callTarget, + uint256 callValue, + bytes calldata callData + ) public onlySelf { + (bool approveSuccess, bytes memory approveResult) = token.call( + abi.encodeWithSignature("approve(address,uint256)", spender, approveAmount) + ); + require(approveSuccess && (approveResult.length == 0 || abi.decode(approveResult, (bool))), "Approve failed"); + + (bool callSuccess,) = callTarget.call{value: callValue}(callData); + require(callSuccess, "Call failed"); + } + // ============ View Functions ============ function getTransactionHash( uint256 _nonce, diff --git a/packages/hardhat/deployments/horizenTestnet/MetaMultiSigWallet.json b/packages/hardhat/deployments/horizenTestnet/MetaMultiSigWallet.json index da237d97..91949ae6 100644 --- a/packages/hardhat/deployments/horizenTestnet/MetaMultiSigWallet.json +++ b/packages/hardhat/deployments/horizenTestnet/MetaMultiSigWallet.json @@ -1,5 +1,5 @@ { - "address": "0x1EaCA128069b2bb1cd476ef66E2701F98cAB148E", + "address": "0x3c57e227ca5264ac0378800fA6E11FF112dB8538", "abi": [ { "inputs": [ @@ -170,6 +170,44 @@ "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "approveAmount", + "type": "uint256" + }, + { + "internalType": "address", + "name": "callTarget", + "type": "address" + }, + { + "internalType": "uint256", + "name": "callValue", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "callData", + "type": "bytes" + } + ], + "name": "approveAndCall", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [ { @@ -516,46 +554,46 @@ "type": "receive" } ], - "transactionHash": "0xc1302204330b385b9de7bbf5e64fdbbb76f85456bf14790e916ab51cd28672bd", + "transactionHash": "0xf5253a002a414ff1d424c89d3649558f8dd2fb1e2da9c3e22b5e5cc6a54e0957", "receipt": { "to": null, - "from": "0x7ccC1D51a0588a7fc1Ce4bA9200a60ea7d736006", - "contractAddress": "0x1EaCA128069b2bb1cd476ef66E2701F98cAB148E", + "from": "0xd95E85e86D3Be5C86846435b8F0C3a476C5a9e42", + "contractAddress": "0x3c57e227ca5264ac0378800fA6E11FF112dB8538", "transactionIndex": 1, - "gasUsed": "1970726", - "logsBloom": "0x00000000000000000000200000000000000000000000000010000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000400000000000000000000000000000000000000201000020000000000000000000000000000000080000000000000000000000000000000000000000000001000000000000000000000000000", - "blockHash": "0xc8dcc49001046996193df411cc8811f9e9f74dad9bba02221588f71777524f70", - "transactionHash": "0xc1302204330b385b9de7bbf5e64fdbbb76f85456bf14790e916ab51cd28672bd", + "gasUsed": "2109101", + "logsBloom": "0x00000000000000000000000000000000000000000000000010000000002000000000000000000000000000000000000000000000000000000000000008000000000000020000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000400000000000000002000000000000000000000000000000000000000000000000400000000000000000000000000000000000000001000000000000000000000000000000000000080000000000000000000000000000000000000000000001000000000000000000000000000", + "blockHash": "0xd005c7c003d0c34008925cc8d45567d385b7d56eb63114441e4bcad64c534185", + "transactionHash": "0xf5253a002a414ff1d424c89d3649558f8dd2fb1e2da9c3e22b5e5cc6a54e0957", "logs": [ { "transactionIndex": 1, - "blockNumber": 6615581, - "transactionHash": "0xc1302204330b385b9de7bbf5e64fdbbb76f85456bf14790e916ab51cd28672bd", - "address": "0x1EaCA128069b2bb1cd476ef66E2701F98cAB148E", + "blockNumber": 10856432, + "transactionHash": "0xf5253a002a414ff1d424c89d3649558f8dd2fb1e2da9c3e22b5e5cc6a54e0957", + "address": "0x3c57e227ca5264ac0378800fA6E11FF112dB8538", "topics": [ "0x33129f9e36e06e860f22ce83276273f4d83d31e9fd4a0d3a4dbac9d2da4f9d0d", "0x1131dc18975dba9108c1928c196d11a9a0ad0fcfb0669f9abf20bb96edeafff6" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000001", "logIndex": 0, - "blockHash": "0xc8dcc49001046996193df411cc8811f9e9f74dad9bba02221588f71777524f70" + "blockHash": "0xd005c7c003d0c34008925cc8d45567d385b7d56eb63114441e4bcad64c534185" }, { "transactionIndex": 1, - "blockNumber": 6615581, - "transactionHash": "0xc1302204330b385b9de7bbf5e64fdbbb76f85456bf14790e916ab51cd28672bd", - "address": "0x1EaCA128069b2bb1cd476ef66E2701F98cAB148E", + "blockNumber": 10856432, + "transactionHash": "0xf5253a002a414ff1d424c89d3649558f8dd2fb1e2da9c3e22b5e5cc6a54e0957", + "address": "0x3c57e227ca5264ac0378800fA6E11FF112dB8538", "topics": [ "0x33129f9e36e06e860f22ce83276273f4d83d31e9fd4a0d3a4dbac9d2da4f9d0d", "0x089be75650f2d8f6619f7aee1349a6327ed4b35b551c593c174f62d65637cc39" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000001", "logIndex": 1, - "blockHash": "0xc8dcc49001046996193df411cc8811f9e9f74dad9bba02221588f71777524f70" + "blockHash": "0xd005c7c003d0c34008925cc8d45567d385b7d56eb63114441e4bcad64c534185" } ], - "blockNumber": 6615581, - "cumulativeGasUsed": "2014582", + "blockNumber": 10856432, + "cumulativeGasUsed": "2158533", "status": 1, "byzantium": true }, @@ -569,17 +607,27 @@ ], 2 ], - "numDeployments": 1, - "solcInputHash": "d03f78c52deb4ca4d79043526c9d68f9", - "metadata": "{\"compiler\":{\"version\":\"0.8.20+commit.a1b79de6\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_zkvContract\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"_vkHash\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"_chainId\",\"type\":\"uint256\"},{\"internalType\":\"uint256[]\",\"name\":\"_initialCommitments\",\"type\":\"uint256[]\"},{\"internalType\":\"uint256\",\"name\":\"_signaturesRequired\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"balance\",\"type\":\"uint256\"}],\"name\":\"Deposit\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"commitment\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"isAdded\",\"type\":\"bool\"}],\"name\":\"Owner\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"result\",\"type\":\"bytes\"}],\"name\":\"TransactionExecuted\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"BN254_PRIME\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"PROVING_SYSTEM_ID\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"VERSION_HASH\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256[]\",\"name\":\"newCommitments\",\"type\":\"uint256[]\"},{\"internalType\":\"uint256\",\"name\":\"newSigRequired\",\"type\":\"uint256\"}],\"name\":\"addSigners\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"recipients\",\"type\":\"address[]\"},{\"internalType\":\"uint256[]\",\"name\":\"amounts\",\"type\":\"uint256[]\"}],\"name\":\"batchTransfer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"recipients\",\"type\":\"address[]\"},{\"internalType\":\"uint256[]\",\"name\":\"amounts\",\"type\":\"uint256[]\"},{\"internalType\":\"address[]\",\"name\":\"tokenAddresses\",\"type\":\"address[]\"}],\"name\":\"batchTransferMulti\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"chainId\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"commitments\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_nonce\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"commitment\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"nullifier\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"aggregationId\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"domainId\",\"type\":\"uint256\"},{\"internalType\":\"bytes32[]\",\"name\":\"zkMerklePath\",\"type\":\"bytes32[]\"},{\"internalType\":\"uint256\",\"name\":\"leafCount\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"index\",\"type\":\"uint256\"}],\"internalType\":\"struct MetaMultiSigWallet.ZkProof[]\",\"name\":\"proofs\",\"type\":\"tuple[]\"}],\"name\":\"execute\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getCommitments\",\"outputs\":[{\"internalType\":\"uint256[]\",\"name\":\"\",\"type\":\"uint256[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getSignersCount\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_nonce\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"getTransactionHash\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"a\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"b\",\"type\":\"uint256\"}],\"name\":\"poseidonHash2\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256[]\",\"name\":\"commitmentsToRemove\",\"type\":\"uint256[]\"},{\"internalType\":\"uint256\",\"name\":\"newSigRequired\",\"type\":\"uint256\"}],\"name\":\"removeSigners\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"signaturesRequired\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"newSigRequired\",\"type\":\"uint256\"}],\"name\":\"updateSignaturesRequired\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"usedNonces\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"usedNullifiers\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"vkHash\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"zkvContract\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"stateMutability\":\"payable\",\"type\":\"receive\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{\"batchTransfer(address[],uint256[])\":{\"params\":{\"amounts\":\"Array of amounts to send\",\"recipients\":\"Array of recipient addresses\"}},\"batchTransferMulti(address[],uint256[],address[])\":{\"params\":{\"amounts\":\"Array of amounts to send\",\"recipients\":\"Array of recipient addresses\",\"tokenAddresses\":\"Array of token addresses (address(0) = native ETH)\"}}},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{\"batchTransfer(address[],uint256[])\":{\"notice\":\"Execute multiple transfers in one transaction\"},\"batchTransferMulti(address[],uint256[],address[])\":{\"notice\":\"Execute multiple transfers with mixed token types\"}},\"version\":1}},\"settings\":{\"compilationTarget\":{\"contracts/MetaMultiSigWallet.sol\":\"MetaMultiSigWallet\"},\"evmVersion\":\"paris\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\",\"useLiteralContent\":true},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[]},\"sources\":{\"contracts/MetaMultiSigWallet.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\npragma solidity ^0.8.13;\\n\\nimport \\\"poseidon-solidity/PoseidonT3.sol\\\";\\n\\ninterface IVerifyProofAggregation {\\n function verifyProofAggregation(\\n uint256 _domainId,\\n uint256 _aggregationId,\\n bytes32 _leaf,\\n bytes32[] calldata _merklePath,\\n uint256 _leafCount,\\n uint256 _index\\n ) external view returns (bool);\\n}\\n\\ncontract MetaMultiSigWallet {\\n // ============ Constants ============\\n bytes32 public constant PROVING_SYSTEM_ID = keccak256(abi.encodePacked(\\\"ultraplonk\\\"));\\n bytes32 public constant VERSION_HASH = sha256(abi.encodePacked(\\\"\\\"));\\n uint256 public constant BN254_PRIME = 21888242871839275222246405745257275088548364400416034343698204186575808495617;\\n\\n // ============ Events ============\\n event Deposit(address indexed sender, uint256 amount, uint256 balance);\\n event TransactionExecuted(uint256 indexed nonce, address to, uint256 value, bytes data, bytes result);\\n event Owner(uint256 indexed commitment, bool isAdded);\\n\\n // ============ Structs ============\\n struct ZkProof {\\n uint256 commitment;\\n uint256 nullifier;\\n uint256 aggregationId;\\n uint256 domainId;\\n bytes32[] zkMerklePath;\\n uint256 leafCount;\\n uint256 index;\\n }\\n\\n // ============ State ============\\n address public immutable zkvContract;\\n bytes32 public immutable vkHash;\\n uint256 public chainId;\\n uint256 public signaturesRequired;\\n\\n // Signer management\\n uint256[] public commitments;\\n\\n // Nonce tracking (prevent replay)\\n mapping(uint256 => bool) public usedNonces;\\n\\n // Nullifier tracking (prevent double-signing)\\n mapping(uint256 => bool) public usedNullifiers;\\n\\n // ============ Constructor ============\\n constructor(\\n address _zkvContract,\\n bytes32 _vkHash,\\n uint256 _chainId,\\n uint256[] memory _initialCommitments,\\n uint256 _signaturesRequired\\n ) {\\n require(_zkvContract != address(0), \\\"Invalid zkv address\\\");\\n require(_signaturesRequired > 0, \\\"Must be non-zero sigs required\\\");\\n require(_initialCommitments.length > 0, \\\"Need at least 1 signer\\\");\\n require(_signaturesRequired <= _initialCommitments.length, \\\"Sigs required too high\\\");\\n\\n zkvContract = _zkvContract;\\n vkHash = _vkHash;\\n chainId = _chainId;\\n signaturesRequired = _signaturesRequired;\\n\\n for (uint256 i = 0; i < _initialCommitments.length; i++) {\\n require(_initialCommitments[i] != 0, \\\"Invalid commitment\\\");\\n commitments.push(_initialCommitments[i]);\\n emit Owner(_initialCommitments[i], true);\\n }\\n }\\n\\n // ============ Modifiers ============\\n modifier onlySelf() {\\n require(msg.sender == address(this), \\\"Not Self\\\");\\n _;\\n }\\n\\n // ============ Main Execute Function ============\\n function execute(\\n uint256 _nonce,\\n address to,\\n uint256 value,\\n bytes calldata data,\\n ZkProof[] calldata proofs\\n ) external returns (bytes memory) {\\n require(!usedNonces[_nonce], \\\"Nonce already used\\\");\\n require(proofs.length >= signaturesRequired, \\\"Not enough proofs\\\");\\n\\n bytes32 txHash = keccak256(abi.encodePacked(address(this), chainId, _nonce, to, value, data));\\n\\n for (uint256 i = 0; i < proofs.length; i++) {\\n require(!usedNullifiers[proofs[i].nullifier], \\\"Nullifier already used\\\");\\n require(_isCurrentSigner(proofs[i].commitment), \\\"Not a current signer\\\");\\n require(_verifyProof(txHash, proofs[i]), \\\"Invalid proof\\\");\\n usedNullifiers[proofs[i].nullifier] = true;\\n }\\n\\n usedNonces[_nonce] = true;\\n\\n (bool success, bytes memory result) = to.call{ value: value }(data);\\n require(success, \\\"Tx failed\\\");\\n\\n emit TransactionExecuted(_nonce, to, value, data, result);\\n return result;\\n }\\n\\n // ============ Signer Management ============\\n function addSigners(uint256[] calldata newCommitments, uint256 newSigRequired) public onlySelf {\\n require(newCommitments.length > 0, \\\"Empty array\\\");\\n require(newSigRequired > 0, \\\"Must be non-zero sigs required\\\");\\n require(newSigRequired <= commitments.length + newCommitments.length, \\\"Sigs required too high\\\");\\n\\n for (uint256 i = 0; i < newCommitments.length; i++) {\\n require(newCommitments[i] != 0, \\\"Invalid commitment\\\");\\n \\n // Check duplicate with existing signers\\n for (uint256 j = 0; j < commitments.length; j++) {\\n require(commitments[j] != newCommitments[i], \\\"Commitment exists\\\");\\n }\\n \\n // Check duplicate within input array\\n for (uint256 k = 0; k < i; k++) {\\n require(newCommitments[k] != newCommitments[i], \\\"Duplicate in input\\\");\\n }\\n\\n commitments.push(newCommitments[i]);\\n emit Owner(newCommitments[i], true);\\n }\\n\\n signaturesRequired = newSigRequired;\\n }\\n\\n function removeSigners(uint256[] calldata commitmentsToRemove, uint256 newSigRequired) public onlySelf {\\n require(commitmentsToRemove.length > 0, \\\"Empty array\\\");\\n require(commitments.length > commitmentsToRemove.length, \\\"Cannot remove all signers\\\");\\n require(newSigRequired > 0, \\\"Must be non-zero sigs required\\\");\\n require(newSigRequired <= commitments.length - commitmentsToRemove.length, \\\"Sigs required too high\\\");\\n\\n for (uint256 i = 0; i < commitmentsToRemove.length; i++) {\\n bool found = false;\\n for (uint256 j = 0; j < commitments.length; j++) {\\n if (commitments[j] == commitmentsToRemove[i]) {\\n commitments[j] = commitments[commitments.length - 1];\\n commitments.pop();\\n found = true;\\n emit Owner(commitmentsToRemove[i], false);\\n break;\\n }\\n }\\n require(found, \\\"Commitment not found\\\");\\n }\\n\\n signaturesRequired = newSigRequired;\\n }\\n\\n function updateSignaturesRequired(uint256 newSigRequired) public onlySelf {\\n require(newSigRequired > 0, \\\"Must be non-zero sigs required\\\");\\n require(newSigRequired <= commitments.length, \\\"Sigs required too high\\\");\\n signaturesRequired = newSigRequired;\\n }\\n\\n /**\\n * @notice Execute multiple transfers in one transaction\\n * @param recipients Array of recipient addresses\\n * @param amounts Array of amounts to send\\n */\\n function batchTransfer(address[] calldata recipients, uint256[] calldata amounts) public onlySelf {\\n require(recipients.length == amounts.length, \\\"Length mismatch\\\");\\n require(recipients.length > 0, \\\"Empty batch\\\");\\n\\n for (uint256 i = 0; i < recipients.length; i++) {\\n require(recipients[i] != address(0), \\\"Invalid recipient\\\");\\n (bool success, ) = recipients[i].call{ value: amounts[i] }(\\\"\\\");\\n require(success, \\\"Transfer failed\\\");\\n }\\n }\\n\\n /**\\n * @notice Execute multiple transfers with mixed token types\\n * @param recipients Array of recipient addresses\\n * @param amounts Array of amounts to send\\n * @param tokenAddresses Array of token addresses (address(0) = native ETH)\\n */\\n function batchTransferMulti(\\n address[] calldata recipients,\\n uint256[] calldata amounts,\\n address[] calldata tokenAddresses\\n ) public onlySelf {\\n require(recipients.length == amounts.length, \\\"Length mismatch\\\");\\n require(recipients.length == tokenAddresses.length, \\\"Length mismatch\\\");\\n require(recipients.length > 0, \\\"Empty batch\\\");\\n\\n for (uint256 i = 0; i < recipients.length; i++) {\\n require(recipients[i] != address(0), \\\"Invalid recipient\\\");\\n\\n if (tokenAddresses[i] == address(0)) {\\n // Native ETH transfer\\n (bool success, ) = recipients[i].call{ value: amounts[i] }(\\\"\\\");\\n require(success, \\\"ETH transfer failed\\\");\\n } else {\\n // ERC20 transfer\\n (bool success, bytes memory data) = tokenAddresses[i].call(\\n abi.encodeWithSignature(\\\"transfer(address,uint256)\\\", recipients[i], amounts[i])\\n );\\n require(success && (data.length == 0 || abi.decode(data, (bool))), \\\"ERC20 transfer failed\\\");\\n }\\n }\\n }\\n\\n // ============ View Functions ============\\n function getTransactionHash(\\n uint256 _nonce,\\n address to,\\n uint256 value,\\n bytes memory data\\n ) public view returns (bytes32) {\\n return keccak256(abi.encodePacked(address(this), chainId, _nonce, to, value, data));\\n }\\n\\n function getCommitments() external view returns (uint256[] memory) {\\n return commitments;\\n }\\n\\n function getSignersCount() external view returns (uint256) {\\n return commitments.length;\\n }\\n\\n // ============ Internal Functions ============\\n function _verifyProof(bytes32 txHash, ZkProof calldata proof) internal view returns (bool) {\\n uint256 txHashCommitment = poseidonHash2(uint256(txHash), 1);\\n\\n // Public inputs order: tx_hash_commitment, commitment, nullifier\\n bytes memory encodedInputs = abi.encodePacked(txHashCommitment, proof.commitment, proof.nullifier);\\n\\n bytes32 leaf = keccak256(abi.encodePacked(PROVING_SYSTEM_ID, vkHash, VERSION_HASH, keccak256(encodedInputs)));\\n\\n return\\n IVerifyProofAggregation(zkvContract).verifyProofAggregation(\\n proof.domainId,\\n proof.aggregationId,\\n leaf,\\n proof.zkMerklePath,\\n proof.leafCount,\\n proof.index\\n );\\n }\\n\\n function _isCurrentSigner(uint256 commitment) internal view returns (bool) {\\n for (uint256 i = 0; i < commitments.length; i++) {\\n if (commitments[i] == commitment) {\\n return true;\\n }\\n }\\n return false;\\n }\\n\\n // ============ Receive ETH ============\\n receive() external payable {\\n emit Deposit(msg.sender, msg.value, address(this).balance);\\n }\\n\\n function poseidonHash2(uint256 a, uint256 b) public pure returns (uint256) {\\n uint256 safeA = a % BN254_PRIME;\\n uint256 safeB = b % BN254_PRIME;\\n return PoseidonT3.hash([safeA, safeB]);\\n }\\n}\",\"keccak256\":\"0x0b7ce172ac16abad23b0d8197e2199c6953074d0879a156e0a4f08418c17c6cb\",\"license\":\"MIT\"},\"poseidon-solidity/PoseidonT3.sol\":{\"content\":\"/// SPDX-License-Identifier: MIT\\npragma solidity >=0.7.0;\\n\\nlibrary PoseidonT3 {\\n uint constant M00 = 0x109b7f411ba0e4c9b2b70caf5c36a7b194be7c11ad24378bfedb68592ba8118b;\\n uint constant M01 = 0x2969f27eed31a480b9c36c764379dbca2cc8fdd1415c3dded62940bcde0bd771;\\n uint constant M02 = 0x143021ec686a3f330d5f9e654638065ce6cd79e28c5b3753326244ee65a1b1a7;\\n uint constant M10 = 0x16ed41e13bb9c0c66ae119424fddbcbc9314dc9fdbdeea55d6c64543dc4903e0;\\n uint constant M11 = 0x2e2419f9ec02ec394c9871c832963dc1b89d743c8c7b964029b2311687b1fe23;\\n uint constant M12 = 0x176cc029695ad02582a70eff08a6fd99d057e12e58e7d7b6b16cdfabc8ee2911;\\n\\n // See here for a simplified implementation: https://github.com/vimwitch/poseidon-solidity/blob/e57becdabb65d99fdc586fe1e1e09e7108202d53/contracts/Poseidon.sol#L40\\n // Inspired by: https://github.com/iden3/circomlibjs/blob/v0.0.8/src/poseidon_slow.js\\n function hash(uint[2] memory) public pure returns (uint) {\\n assembly {\\n let F := 21888242871839275222246405745257275088548364400416034343698204186575808495617\\n let M20 := 0x2b90bba00fca0589f617e7dcbfe82e0df706ab640ceb247b791a93b74e36736d\\n let M21 := 0x101071f0032379b697315876690f053d148d4e109f5fb065c8aacc55a0f89bfa\\n let M22 := 0x19a3fc0a56702bf417ba7fee3802593fa644470307043f7773279cd71d25d5e0\\n\\n // load the inputs from memory\\n let state1 := add(mod(mload(0x80), F), 0x00f1445235f2148c5986587169fc1bcd887b08d4d00868df5696fff40956e864)\\n let state2 := add(mod(mload(0xa0), F), 0x08dff3487e8ac99e1f29a058d0fa80b930c728730b7ab36ce879f3890ecf73f5)\\n let scratch0 := mulmod(state1, state1, F)\\n state1 := mulmod(mulmod(scratch0, scratch0, F), state1, F)\\n scratch0 := mulmod(state2, state2, F)\\n state2 := mulmod(mulmod(scratch0, scratch0, F), state2, F)\\n scratch0 := add(\\n 0x2f27be690fdaee46c3ce28f7532b13c856c35342c84bda6e20966310fadc01d0,\\n add(add(15452833169820924772166449970675545095234312153403844297388521437673434406763, mulmod(state1, M10, F)), mulmod(state2, M20, F))\\n )\\n let scratch1 := add(\\n 0x2b2ae1acf68b7b8d2416bebf3d4f6234b763fe04b8043ee48b8327bebca16cf2,\\n add(add(18674271267752038776579386132900109523609358935013267566297499497165104279117, mulmod(state1, M11, F)), mulmod(state2, M21, F))\\n )\\n let scratch2 := add(\\n 0x0319d062072bef7ecca5eac06f97d4d55952c175ab6b03eae64b44c7dbf11cfa,\\n add(add(14817777843080276494683266178512808687156649753153012854386334860566696099579, mulmod(state1, M12, F)), mulmod(state2, M22, F))\\n )\\n let state0 := mulmod(scratch0, scratch0, F)\\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\\n state0 := mulmod(scratch1, scratch1, F)\\n scratch1 := mulmod(mulmod(state0, state0, F), scratch1, F)\\n state0 := mulmod(scratch2, scratch2, F)\\n scratch2 := mulmod(mulmod(state0, state0, F), scratch2, F)\\n state0 := add(0x28813dcaebaeaa828a376df87af4a63bc8b7bf27ad49c6298ef7b387bf28526d, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\\n state1 := add(0x2727673b2ccbc903f181bf38e1c1d40d2033865200c352bc150928adddf9cb78, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\\n state2 := add(0x234ec45ca27727c2e74abd2b2a1494cd6efbd43e340587d6b8fb9e31e65cc632, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\\n scratch0 := mulmod(state0, state0, F)\\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\\n scratch0 := mulmod(state1, state1, F)\\n state1 := mulmod(mulmod(scratch0, scratch0, F), state1, F)\\n scratch0 := mulmod(state2, state2, F)\\n state2 := mulmod(mulmod(scratch0, scratch0, F), state2, F)\\n scratch0 := add(0x15b52534031ae18f7f862cb2cf7cf760ab10a8150a337b1ccd99ff6e8797d428, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\\n scratch1 := add(0x0dc8fad6d9e4b35f5ed9a3d186b79ce38e0e8a8d1b58b132d701d4eecf68d1f6, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\\n scratch2 := add(0x1bcd95ffc211fbca600f705fad3fb567ea4eb378f62e1fec97805518a47e4d9c, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\\n state0 := mulmod(scratch0, scratch0, F)\\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\\n state0 := mulmod(scratch1, scratch1, F)\\n scratch1 := mulmod(mulmod(state0, state0, F), scratch1, F)\\n state0 := mulmod(scratch2, scratch2, F)\\n scratch2 := mulmod(mulmod(state0, state0, F), scratch2, F)\\n state0 := add(0x10520b0ab721cadfe9eff81b016fc34dc76da36c2578937817cb978d069de559, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\\n state1 := add(0x1f6d48149b8e7f7d9b257d8ed5fbbaf42932498075fed0ace88a9eb81f5627f6, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\\n state2 := add(0x1d9655f652309014d29e00ef35a2089bfff8dc1c816f0dc9ca34bdb5460c8705, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\\n scratch0 := mulmod(state0, state0, F)\\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\\n scratch0 := add(0x04df5a56ff95bcafb051f7b1cd43a99ba731ff67e47032058fe3d4185697cc7d, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\\n scratch1 := add(0x0672d995f8fff640151b3d290cedaf148690a10a8c8424a7f6ec282b6e4be828, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\\n scratch2 := add(0x099952b414884454b21200d7ffafdd5f0c9a9dcc06f2708e9fc1d8209b5c75b9, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\\n state0 := mulmod(scratch0, scratch0, F)\\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\\n state0 := add(0x052cba2255dfd00c7c483143ba8d469448e43586a9b4cd9183fd0e843a6b9fa6, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\\n state1 := add(0x0b8badee690adb8eb0bd74712b7999af82de55707251ad7716077cb93c464ddc, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\\n state2 := add(0x119b1590f13307af5a1ee651020c07c749c15d60683a8050b963d0a8e4b2bdd1, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\\n scratch0 := mulmod(state0, state0, F)\\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\\n scratch0 := add(0x03150b7cd6d5d17b2529d36be0f67b832c4acfc884ef4ee5ce15be0bfb4a8d09, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\\n scratch1 := add(0x2cc6182c5e14546e3cf1951f173912355374efb83d80898abe69cb317c9ea565, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\\n scratch2 := add(0x005032551e6378c450cfe129a404b3764218cadedac14e2b92d2cd73111bf0f9, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\\n state0 := mulmod(scratch0, scratch0, F)\\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\\n state0 := add(0x233237e3289baa34bb147e972ebcb9516469c399fcc069fb88f9da2cc28276b5, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\\n state1 := add(0x05c8f4f4ebd4a6e3c980d31674bfbe6323037f21b34ae5a4e80c2d4c24d60280, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\\n state2 := add(0x0a7b1db13042d396ba05d818a319f25252bcf35ef3aeed91ee1f09b2590fc65b, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\\n scratch0 := mulmod(state0, state0, F)\\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\\n scratch0 := add(0x2a73b71f9b210cf5b14296572c9d32dbf156e2b086ff47dc5df542365a404ec0, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\\n scratch1 := add(0x1ac9b0417abcc9a1935107e9ffc91dc3ec18f2c4dbe7f22976a760bb5c50c460, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\\n scratch2 := add(0x12c0339ae08374823fabb076707ef479269f3e4d6cb104349015ee046dc93fc0, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\\n state0 := mulmod(scratch0, scratch0, F)\\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\\n state0 := add(0x0b7475b102a165ad7f5b18db4e1e704f52900aa3253baac68246682e56e9a28e, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\\n state1 := add(0x037c2849e191ca3edb1c5e49f6e8b8917c843e379366f2ea32ab3aa88d7f8448, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\\n state2 := add(0x05a6811f8556f014e92674661e217e9bd5206c5c93a07dc145fdb176a716346f, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\\n scratch0 := mulmod(state0, state0, F)\\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\\n scratch0 := add(0x29a795e7d98028946e947b75d54e9f044076e87a7b2883b47b675ef5f38bd66e, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\\n scratch1 := add(0x20439a0c84b322eb45a3857afc18f5826e8c7382c8a1585c507be199981fd22f, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\\n scratch2 := add(0x2e0ba8d94d9ecf4a94ec2050c7371ff1bb50f27799a84b6d4a2a6f2a0982c887, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\\n state0 := mulmod(scratch0, scratch0, F)\\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\\n state0 := add(0x143fd115ce08fb27ca38eb7cce822b4517822cd2109048d2e6d0ddcca17d71c8, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\\n state1 := add(0x0c64cbecb1c734b857968dbbdcf813cdf8611659323dbcbfc84323623be9caf1, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\\n state2 := add(0x028a305847c683f646fca925c163ff5ae74f348d62c2b670f1426cef9403da53, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\\n scratch0 := mulmod(state0, state0, F)\\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\\n scratch0 := add(0x2e4ef510ff0b6fda5fa940ab4c4380f26a6bcb64d89427b824d6755b5db9e30c, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\\n scratch1 := add(0x0081c95bc43384e663d79270c956ce3b8925b4f6d033b078b96384f50579400e, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\\n scratch2 := add(0x2ed5f0c91cbd9749187e2fade687e05ee2491b349c039a0bba8a9f4023a0bb38, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\\n state0 := mulmod(scratch0, scratch0, F)\\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\\n state0 := add(0x30509991f88da3504bbf374ed5aae2f03448a22c76234c8c990f01f33a735206, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\\n state1 := add(0x1c3f20fd55409a53221b7c4d49a356b9f0a1119fb2067b41a7529094424ec6ad, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\\n state2 := add(0x10b4e7f3ab5df003049514459b6e18eec46bb2213e8e131e170887b47ddcb96c, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\\n scratch0 := mulmod(state0, state0, F)\\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\\n scratch0 := add(0x2a1982979c3ff7f43ddd543d891c2abddd80f804c077d775039aa3502e43adef, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\\n scratch1 := add(0x1c74ee64f15e1db6feddbead56d6d55dba431ebc396c9af95cad0f1315bd5c91, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\\n scratch2 := add(0x07533ec850ba7f98eab9303cace01b4b9e4f2e8b82708cfa9c2fe45a0ae146a0, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\\n state0 := mulmod(scratch0, scratch0, F)\\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\\n state0 := add(0x21576b438e500449a151e4eeaf17b154285c68f42d42c1808a11abf3764c0750, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\\n state1 := add(0x2f17c0559b8fe79608ad5ca193d62f10bce8384c815f0906743d6930836d4a9e, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\\n state2 := add(0x2d477e3862d07708a79e8aae946170bc9775a4201318474ae665b0b1b7e2730e, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\\n scratch0 := mulmod(state0, state0, F)\\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\\n scratch0 := add(0x162f5243967064c390e095577984f291afba2266c38f5abcd89be0f5b2747eab, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\\n scratch1 := add(0x2b4cb233ede9ba48264ecd2c8ae50d1ad7a8596a87f29f8a7777a70092393311, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\\n scratch2 := add(0x2c8fbcb2dd8573dc1dbaf8f4622854776db2eece6d85c4cf4254e7c35e03b07a, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\\n state0 := mulmod(scratch0, scratch0, F)\\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\\n state0 := add(0x1d6f347725e4816af2ff453f0cd56b199e1b61e9f601e9ade5e88db870949da9, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\\n state1 := add(0x204b0c397f4ebe71ebc2d8b3df5b913df9e6ac02b68d31324cd49af5c4565529, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\\n state2 := add(0x0c4cb9dc3c4fd8174f1149b3c63c3c2f9ecb827cd7dc25534ff8fb75bc79c502, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\\n scratch0 := mulmod(state0, state0, F)\\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\\n scratch0 := add(0x174ad61a1448c899a25416474f4930301e5c49475279e0639a616ddc45bc7b54, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\\n scratch1 := add(0x1a96177bcf4d8d89f759df4ec2f3cde2eaaa28c177cc0fa13a9816d49a38d2ef, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\\n scratch2 := add(0x066d04b24331d71cd0ef8054bc60c4ff05202c126a233c1a8242ace360b8a30a, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\\n state0 := mulmod(scratch0, scratch0, F)\\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\\n state0 := add(0x2a4c4fc6ec0b0cf52195782871c6dd3b381cc65f72e02ad527037a62aa1bd804, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\\n state1 := add(0x13ab2d136ccf37d447e9f2e14a7cedc95e727f8446f6d9d7e55afc01219fd649, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\\n state2 := add(0x1121552fca26061619d24d843dc82769c1b04fcec26f55194c2e3e869acc6a9a, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\\n scratch0 := mulmod(state0, state0, F)\\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\\n scratch0 := add(0x00ef653322b13d6c889bc81715c37d77a6cd267d595c4a8909a5546c7c97cff1, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\\n scratch1 := add(0x0e25483e45a665208b261d8ba74051e6400c776d652595d9845aca35d8a397d3, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\\n scratch2 := add(0x29f536dcb9dd7682245264659e15d88e395ac3d4dde92d8c46448db979eeba89, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\\n state0 := mulmod(scratch0, scratch0, F)\\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\\n state0 := add(0x2a56ef9f2c53febadfda33575dbdbd885a124e2780bbea170e456baace0fa5be, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\\n state1 := add(0x1c8361c78eb5cf5decfb7a2d17b5c409f2ae2999a46762e8ee416240a8cb9af1, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\\n state2 := add(0x151aff5f38b20a0fc0473089aaf0206b83e8e68a764507bfd3d0ab4be74319c5, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\\n scratch0 := mulmod(state0, state0, F)\\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\\n scratch0 := add(0x04c6187e41ed881dc1b239c88f7f9d43a9f52fc8c8b6cdd1e76e47615b51f100, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\\n scratch1 := add(0x13b37bd80f4d27fb10d84331f6fb6d534b81c61ed15776449e801b7ddc9c2967, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\\n scratch2 := add(0x01a5c536273c2d9df578bfbd32c17b7a2ce3664c2a52032c9321ceb1c4e8a8e4, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\\n state0 := mulmod(scratch0, scratch0, F)\\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\\n state0 := add(0x2ab3561834ca73835ad05f5d7acb950b4a9a2c666b9726da832239065b7c3b02, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\\n state1 := add(0x1d4d8ec291e720db200fe6d686c0d613acaf6af4e95d3bf69f7ed516a597b646, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\\n state2 := add(0x041294d2cc484d228f5784fe7919fd2bb925351240a04b711514c9c80b65af1d, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\\n scratch0 := mulmod(state0, state0, F)\\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\\n scratch0 := add(0x154ac98e01708c611c4fa715991f004898f57939d126e392042971dd90e81fc6, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\\n scratch1 := add(0x0b339d8acca7d4f83eedd84093aef51050b3684c88f8b0b04524563bc6ea4da4, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\\n scratch2 := add(0x0955e49e6610c94254a4f84cfbab344598f0e71eaff4a7dd81ed95b50839c82e, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\\n state0 := mulmod(scratch0, scratch0, F)\\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\\n state0 := add(0x06746a6156eba54426b9e22206f15abca9a6f41e6f535c6f3525401ea0654626, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\\n state1 := add(0x0f18f5a0ecd1423c496f3820c549c27838e5790e2bd0a196ac917c7ff32077fb, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\\n state2 := add(0x04f6eeca1751f7308ac59eff5beb261e4bb563583ede7bc92a738223d6f76e13, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\\n scratch0 := mulmod(state0, state0, F)\\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\\n scratch0 := add(0x2b56973364c4c4f5c1a3ec4da3cdce038811eb116fb3e45bc1768d26fc0b3758, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\\n scratch1 := add(0x123769dd49d5b054dcd76b89804b1bcb8e1392b385716a5d83feb65d437f29ef, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\\n scratch2 := add(0x2147b424fc48c80a88ee52b91169aacea989f6446471150994257b2fb01c63e9, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\\n state0 := mulmod(scratch0, scratch0, F)\\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\\n state0 := add(0x0fdc1f58548b85701a6c5505ea332a29647e6f34ad4243c2ea54ad897cebe54d, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\\n state1 := add(0x12373a8251fea004df68abcf0f7786d4bceff28c5dbbe0c3944f685cc0a0b1f2, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\\n state2 := add(0x21e4f4ea5f35f85bad7ea52ff742c9e8a642756b6af44203dd8a1f35c1a90035, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\\n scratch0 := mulmod(state0, state0, F)\\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\\n scratch0 := add(0x16243916d69d2ca3dfb4722224d4c462b57366492f45e90d8a81934f1bc3b147, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\\n scratch1 := add(0x1efbe46dd7a578b4f66f9adbc88b4378abc21566e1a0453ca13a4159cac04ac2, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\\n scratch2 := add(0x07ea5e8537cf5dd08886020e23a7f387d468d5525be66f853b672cc96a88969a, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\\n state0 := mulmod(scratch0, scratch0, F)\\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\\n state0 := add(0x05a8c4f9968b8aa3b7b478a30f9a5b63650f19a75e7ce11ca9fe16c0b76c00bc, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\\n state1 := add(0x20f057712cc21654fbfe59bd345e8dac3f7818c701b9c7882d9d57b72a32e83f, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\\n state2 := add(0x04a12ededa9dfd689672f8c67fee31636dcd8e88d01d49019bd90b33eb33db69, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\\n scratch0 := mulmod(state0, state0, F)\\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\\n scratch0 := add(0x27e88d8c15f37dcee44f1e5425a51decbd136ce5091a6767e49ec9544ccd101a, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\\n scratch1 := add(0x2feed17b84285ed9b8a5c8c5e95a41f66e096619a7703223176c41ee433de4d1, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\\n scratch2 := add(0x1ed7cc76edf45c7c404241420f729cf394e5942911312a0d6972b8bd53aff2b8, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\\n state0 := mulmod(scratch0, scratch0, F)\\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\\n state0 := add(0x15742e99b9bfa323157ff8c586f5660eac6783476144cdcadf2874be45466b1a, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\\n state1 := add(0x1aac285387f65e82c895fc6887ddf40577107454c6ec0317284f033f27d0c785, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\\n state2 := add(0x25851c3c845d4790f9ddadbdb6057357832e2e7a49775f71ec75a96554d67c77, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\\n scratch0 := mulmod(state0, state0, F)\\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\\n scratch0 := add(0x15a5821565cc2ec2ce78457db197edf353b7ebba2c5523370ddccc3d9f146a67, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\\n scratch1 := add(0x2411d57a4813b9980efa7e31a1db5966dcf64f36044277502f15485f28c71727, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\\n scratch2 := add(0x002e6f8d6520cd4713e335b8c0b6d2e647e9a98e12f4cd2558828b5ef6cb4c9b, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\\n state0 := mulmod(scratch0, scratch0, F)\\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\\n state0 := add(0x2ff7bc8f4380cde997da00b616b0fcd1af8f0e91e2fe1ed7398834609e0315d2, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\\n state1 := add(0x00b9831b948525595ee02724471bcd182e9521f6b7bb68f1e93be4febb0d3cbe, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\\n state2 := add(0x0a2f53768b8ebf6a86913b0e57c04e011ca408648a4743a87d77adbf0c9c3512, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\\n scratch0 := mulmod(state0, state0, F)\\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\\n scratch0 := add(0x00248156142fd0373a479f91ff239e960f599ff7e94be69b7f2a290305e1198d, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\\n scratch1 := add(0x171d5620b87bfb1328cf8c02ab3f0c9a397196aa6a542c2350eb512a2b2bcda9, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\\n scratch2 := add(0x170a4f55536f7dc970087c7c10d6fad760c952172dd54dd99d1045e4ec34a808, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\\n state0 := mulmod(scratch0, scratch0, F)\\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\\n state0 := add(0x29aba33f799fe66c2ef3134aea04336ecc37e38c1cd211ba482eca17e2dbfae1, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\\n state1 := add(0x1e9bc179a4fdd758fdd1bb1945088d47e70d114a03f6a0e8b5ba650369e64973, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\\n state2 := add(0x1dd269799b660fad58f7f4892dfb0b5afeaad869a9c4b44f9c9e1c43bdaf8f09, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\\n scratch0 := mulmod(state0, state0, F)\\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\\n scratch0 := add(0x22cdbc8b70117ad1401181d02e15459e7ccd426fe869c7c95d1dd2cb0f24af38, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\\n scratch1 := add(0x0ef042e454771c533a9f57a55c503fcefd3150f52ed94a7cd5ba93b9c7dacefd, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\\n scratch2 := add(0x11609e06ad6c8fe2f287f3036037e8851318e8b08a0359a03b304ffca62e8284, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\\n state0 := mulmod(scratch0, scratch0, F)\\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\\n state0 := add(0x1166d9e554616dba9e753eea427c17b7fecd58c076dfe42708b08f5b783aa9af, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\\n state1 := add(0x2de52989431a859593413026354413db177fbf4cd2ac0b56f855a888357ee466, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\\n state2 := add(0x3006eb4ffc7a85819a6da492f3a8ac1df51aee5b17b8e89d74bf01cf5f71e9ad, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\\n scratch0 := mulmod(state0, state0, F)\\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\\n scratch0 := add(0x2af41fbb61ba8a80fdcf6fff9e3f6f422993fe8f0a4639f962344c8225145086, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\\n scratch1 := add(0x119e684de476155fe5a6b41a8ebc85db8718ab27889e85e781b214bace4827c3, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\\n scratch2 := add(0x1835b786e2e8925e188bea59ae363537b51248c23828f047cff784b97b3fd800, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\\n state0 := mulmod(scratch0, scratch0, F)\\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\\n state0 := add(0x28201a34c594dfa34d794996c6433a20d152bac2a7905c926c40e285ab32eeb6, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\\n state1 := add(0x083efd7a27d1751094e80fefaf78b000864c82eb571187724a761f88c22cc4e7, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\\n state2 := add(0x0b6f88a3577199526158e61ceea27be811c16df7774dd8519e079564f61fd13b, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\\n scratch0 := mulmod(state0, state0, F)\\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\\n scratch0 := add(0x0ec868e6d15e51d9644f66e1d6471a94589511ca00d29e1014390e6ee4254f5b, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\\n scratch1 := add(0x2af33e3f866771271ac0c9b3ed2e1142ecd3e74b939cd40d00d937ab84c98591, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\\n scratch2 := add(0x0b520211f904b5e7d09b5d961c6ace7734568c547dd6858b364ce5e47951f178, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\\n state0 := mulmod(scratch0, scratch0, F)\\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\\n state0 := add(0x0b2d722d0919a1aad8db58f10062a92ea0c56ac4270e822cca228620188a1d40, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\\n state1 := add(0x1f790d4d7f8cf094d980ceb37c2453e957b54a9991ca38bbe0061d1ed6e562d4, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\\n state2 := add(0x0171eb95dfbf7d1eaea97cd385f780150885c16235a2a6a8da92ceb01e504233, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\\n scratch0 := mulmod(state0, state0, F)\\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\\n scratch0 := add(0x0c2d0e3b5fd57549329bf6885da66b9b790b40defd2c8650762305381b168873, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\\n scratch1 := add(0x1162fb28689c27154e5a8228b4e72b377cbcafa589e283c35d3803054407a18d, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\\n scratch2 := add(0x2f1459b65dee441b64ad386a91e8310f282c5a92a89e19921623ef8249711bc0, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\\n state0 := mulmod(scratch0, scratch0, F)\\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\\n state0 := add(0x1e6ff3216b688c3d996d74367d5cd4c1bc489d46754eb712c243f70d1b53cfbb, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\\n state1 := add(0x01ca8be73832b8d0681487d27d157802d741a6f36cdc2a0576881f9326478875, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\\n state2 := add(0x1f7735706ffe9fc586f976d5bdf223dc680286080b10cea00b9b5de315f9650e, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\\n scratch0 := mulmod(state0, state0, F)\\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\\n scratch0 := add(0x2522b60f4ea3307640a0c2dce041fba921ac10a3d5f096ef4745ca838285f019, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\\n scratch1 := add(0x23f0bee001b1029d5255075ddc957f833418cad4f52b6c3f8ce16c235572575b, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\\n scratch2 := add(0x2bc1ae8b8ddbb81fcaac2d44555ed5685d142633e9df905f66d9401093082d59, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\\n state0 := mulmod(scratch0, scratch0, F)\\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\\n state0 := add(0x0f9406b8296564a37304507b8dba3ed162371273a07b1fc98011fcd6ad72205f, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\\n state1 := add(0x2360a8eb0cc7defa67b72998de90714e17e75b174a52ee4acb126c8cd995f0a8, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\\n state2 := add(0x15871a5cddead976804c803cbaef255eb4815a5e96df8b006dcbbc2767f88948, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\\n scratch0 := mulmod(state0, state0, F)\\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\\n scratch0 := add(0x193a56766998ee9e0a8652dd2f3b1da0362f4f54f72379544f957ccdeefb420f, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\\n scratch1 := add(0x2a394a43934f86982f9be56ff4fab1703b2e63c8ad334834e4309805e777ae0f, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\\n scratch2 := add(0x1859954cfeb8695f3e8b635dcb345192892cd11223443ba7b4166e8876c0d142, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\\n state0 := mulmod(scratch0, scratch0, F)\\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\\n state0 := add(0x04e1181763050e58013444dbcb99f1902b11bc25d90bbdca408d3819f4fed32b, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\\n state1 := add(0x0fdb253dee83869d40c335ea64de8c5bb10eb82db08b5e8b1f5e5552bfd05f23, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\\n state2 := add(0x058cbe8a9a5027bdaa4efb623adead6275f08686f1c08984a9d7c5bae9b4f1c0, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\\n scratch0 := mulmod(state0, state0, F)\\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\\n scratch0 := add(0x1382edce9971e186497eadb1aeb1f52b23b4b83bef023ab0d15228b4cceca59a, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\\n scratch1 := add(0x03464990f045c6ee0819ca51fd11b0be7f61b8eb99f14b77e1e6634601d9e8b5, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\\n scratch2 := add(0x23f7bfc8720dc296fff33b41f98ff83c6fcab4605db2eb5aaa5bc137aeb70a58, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\\n state0 := mulmod(scratch0, scratch0, F)\\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\\n state0 := add(0x0a59a158e3eec2117e6e94e7f0e9decf18c3ffd5e1531a9219636158bbaf62f2, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\\n state1 := add(0x06ec54c80381c052b58bf23b312ffd3ce2c4eba065420af8f4c23ed0075fd07b, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\\n state2 := add(0x118872dc832e0eb5476b56648e867ec8b09340f7a7bcb1b4962f0ff9ed1f9d01, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\\n scratch0 := mulmod(state0, state0, F)\\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\\n scratch0 := add(0x13d69fa127d834165ad5c7cba7ad59ed52e0b0f0e42d7fea95e1906b520921b1, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\\n scratch1 := add(0x169a177f63ea681270b1c6877a73d21bde143942fb71dc55fd8a49f19f10c77b, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\\n scratch2 := add(0x04ef51591c6ead97ef42f287adce40d93abeb032b922f66ffb7e9a5a7450544d, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\\n state0 := mulmod(scratch0, scratch0, F)\\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\\n state0 := add(0x256e175a1dc079390ecd7ca703fb2e3b19ec61805d4f03ced5f45ee6dd0f69ec, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\\n state1 := add(0x30102d28636abd5fe5f2af412ff6004f75cc360d3205dd2da002813d3e2ceeb2, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\\n state2 := add(0x10998e42dfcd3bbf1c0714bc73eb1bf40443a3fa99bef4a31fd31be182fcc792, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\\n scratch0 := mulmod(state0, state0, F)\\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\\n scratch0 := add(0x193edd8e9fcf3d7625fa7d24b598a1d89f3362eaf4d582efecad76f879e36860, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\\n scratch1 := add(0x18168afd34f2d915d0368ce80b7b3347d1c7a561ce611425f2664d7aa51f0b5d, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\\n scratch2 := add(0x29383c01ebd3b6ab0c017656ebe658b6a328ec77bc33626e29e2e95b33ea6111, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\\n state0 := mulmod(scratch0, scratch0, F)\\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\\n state0 := add(0x10646d2f2603de39a1f4ae5e7771a64a702db6e86fb76ab600bf573f9010c711, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\\n state1 := add(0x0beb5e07d1b27145f575f1395a55bf132f90c25b40da7b3864d0242dcb1117fb, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\\n state2 := add(0x16d685252078c133dc0d3ecad62b5c8830f95bb2e54b59abdffbf018d96fa336, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\\n scratch0 := mulmod(state0, state0, F)\\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\\n scratch0 := add(0x0a6abd1d833938f33c74154e0404b4b40a555bbbec21ddfafd672dd62047f01a, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\\n scratch1 := add(0x1a679f5d36eb7b5c8ea12a4c2dedc8feb12dffeec450317270a6f19b34cf1860, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\\n scratch2 := add(0x0980fb233bd456c23974d50e0ebfde4726a423eada4e8f6ffbc7592e3f1b93d6, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\\n state0 := mulmod(scratch0, scratch0, F)\\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\\n state0 := add(0x161b42232e61b84cbf1810af93a38fc0cece3d5628c9282003ebacb5c312c72b, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\\n state1 := add(0x0ada10a90c7f0520950f7d47a60d5e6a493f09787f1564e5d09203db47de1a0b, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\\n state2 := add(0x1a730d372310ba82320345a29ac4238ed3f07a8a2b4e121bb50ddb9af407f451, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\\n scratch0 := mulmod(state0, state0, F)\\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\\n scratch0 := add(0x2c8120f268ef054f817064c369dda7ea908377feaba5c4dffbda10ef58e8c556, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\\n scratch1 := add(0x1c7c8824f758753fa57c00789c684217b930e95313bcb73e6e7b8649a4968f70, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\\n scratch2 := add(0x2cd9ed31f5f8691c8e39e4077a74faa0f400ad8b491eb3f7b47b27fa3fd1cf77, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\\n state0 := mulmod(scratch0, scratch0, F)\\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\\n state0 := add(0x23ff4f9d46813457cf60d92f57618399a5e022ac321ca550854ae23918a22eea, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\\n state1 := add(0x09945a5d147a4f66ceece6405dddd9d0af5a2c5103529407dff1ea58f180426d, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\\n state2 := add(0x188d9c528025d4c2b67660c6b771b90f7c7da6eaa29d3f268a6dd223ec6fc630, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\\n scratch0 := mulmod(state0, state0, F)\\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\\n scratch0 := add(0x3050e37996596b7f81f68311431d8734dba7d926d3633595e0c0d8ddf4f0f47f, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\\n scratch1 := add(0x15af1169396830a91600ca8102c35c426ceae5461e3f95d89d829518d30afd78, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\\n scratch2 := add(0x1da6d09885432ea9a06d9f37f873d985dae933e351466b2904284da3320d8acc, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\\n state0 := mulmod(scratch0, scratch0, F)\\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\\n state0 := add(0x2796ea90d269af29f5f8acf33921124e4e4fad3dbe658945e546ee411ddaa9cb, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\\n state1 := add(0x202d7dd1da0f6b4b0325c8b3307742f01e15612ec8e9304a7cb0319e01d32d60, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\\n state2 := add(0x096d6790d05bb759156a952ba263d672a2d7f9c788f4c831a29dace4c0f8be5f, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\\n scratch0 := mulmod(state0, state0, F)\\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\\n scratch0 := add(0x054efa1f65b0fce283808965275d877b438da23ce5b13e1963798cb1447d25a4, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\\n scratch1 := add(0x1b162f83d917e93edb3308c29802deb9d8aa690113b2e14864ccf6e18e4165f1, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\\n scratch2 := add(0x21e5241e12564dd6fd9f1cdd2a0de39eedfefc1466cc568ec5ceb745a0506edc, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\\n state0 := mulmod(scratch0, scratch0, F)\\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\\n state0 := mulmod(scratch1, scratch1, F)\\n scratch1 := mulmod(mulmod(state0, state0, F), scratch1, F)\\n state0 := mulmod(scratch2, scratch2, F)\\n scratch2 := mulmod(mulmod(state0, state0, F), scratch2, F)\\n state0 := add(0x1cfb5662e8cf5ac9226a80ee17b36abecb73ab5f87e161927b4349e10e4bdf08, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\\n state1 := add(0x0f21177e302a771bbae6d8d1ecb373b62c99af346220ac0129c53f666eb24100, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\\n state2 := add(0x1671522374606992affb0dd7f71b12bec4236aede6290546bcef7e1f515c2320, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\\n scratch0 := mulmod(state0, state0, F)\\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\\n scratch0 := mulmod(state1, state1, F)\\n state1 := mulmod(mulmod(scratch0, scratch0, F), state1, F)\\n scratch0 := mulmod(state2, state2, F)\\n state2 := mulmod(mulmod(scratch0, scratch0, F), state2, F)\\n scratch0 := add(0x0fa3ec5b9488259c2eb4cf24501bfad9be2ec9e42c5cc8ccd419d2a692cad870, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\\n scratch1 := add(0x193c0e04e0bd298357cb266c1506080ed36edce85c648cc085e8c57b1ab54bba, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\\n scratch2 := add(0x102adf8ef74735a27e9128306dcbc3c99f6f7291cd406578ce14ea2adaba68f8, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\\n state0 := mulmod(scratch0, scratch0, F)\\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\\n state0 := mulmod(scratch1, scratch1, F)\\n scratch1 := mulmod(mulmod(state0, state0, F), scratch1, F)\\n state0 := mulmod(scratch2, scratch2, F)\\n scratch2 := mulmod(mulmod(state0, state0, F), scratch2, F)\\n state0 := add(0x0fe0af7858e49859e2a54d6f1ad945b1316aa24bfbdd23ae40a6d0cb70c3eab1, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\\n state1 := add(0x216f6717bbc7dedb08536a2220843f4e2da5f1daa9ebdefde8a5ea7344798d22, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\\n state2 := add(0x1da55cc900f0d21f4a3e694391918a1b3c23b2ac773c6b3ef88e2e4228325161, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\\n scratch0 := mulmod(state0, state0, F)\\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\\n scratch0 := mulmod(state1, state1, F)\\n state1 := mulmod(mulmod(scratch0, scratch0, F), state1, F)\\n scratch0 := mulmod(state2, state2, F)\\n state2 := mulmod(mulmod(scratch0, scratch0, F), state2, F)\\n\\n mstore(0x0, mod(add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)), F))\\n\\n return(0, 0x20)\\n }\\n }\\n}\\n\",\"keccak256\":\"0x0102caa303bbc6690508f3615604f7730789ed990058c9513a87ccb30e4835be\",\"license\":\"MIT\"}},\"version\":1}", - "bytecode": "0x60c06040523480156200001157600080fd5b50604051620024a0380380620024a08339810160408190526200003491620002db565b6001600160a01b038516620000905760405162461bcd60e51b815260206004820152601360248201527f496e76616c6964207a6b7620616464726573730000000000000000000000000060448201526064015b60405180910390fd5b60008111620000e25760405162461bcd60e51b815260206004820152601e60248201527f4d757374206265206e6f6e2d7a65726f20736967732072657175697265640000604482015260640162000087565b6000825111620001355760405162461bcd60e51b815260206004820152601660248201527f4e656564206174206c656173742031207369676e657200000000000000000000604482015260640162000087565b8151811115620001885760405162461bcd60e51b815260206004820152601660248201527f5369677320726571756972656420746f6f206869676800000000000000000000604482015260640162000087565b6001600160a01b03851660805260a0849052600083815560018290555b8251811015620002b957828181518110620001c457620001c4620003e6565b6020026020010151600003620002125760405162461bcd60e51b8152602060048201526012602482015271125b9d985b1a590818dbdb5b5a5d1b595b9d60721b604482015260640162000087565b6002838281518110620002295762000229620003e6565b6020908102919091018101518254600181018455600093845291909220015582518390829081106200025f576200025f620003e6565b60200260200101517f33129f9e36e06e860f22ce83276273f4d83d31e9fd4a0d3a4dbac9d2da4f9d0d60016040516200029c911515815260200190565b60405180910390a280620002b081620003fc565b915050620001a5565b50505050505062000424565b634e487b7160e01b600052604160045260246000fd5b600080600080600060a08688031215620002f457600080fd5b85516001600160a01b03811681146200030c57600080fd5b602087810151604089015160608a01519398509096509450906001600160401b03808211156200033b57600080fd5b818901915089601f8301126200035057600080fd5b815181811115620003655762000365620002c5565b8060051b604051601f19603f830116810181811085821117156200038d576200038d620002c5565b60405291825284820192508381018501918c831115620003ac57600080fd5b938501935b82851015620003cc57845184529385019392850192620003b1565b809750505050505050608086015190509295509295909350565b634e487b7160e01b600052603260045260246000fd5b6000600182016200041d57634e487b7160e01b600052601160045260246000fd5b5060010190565b60805160a0516120486200045860003960008181610221015261167b0152600081816102d5015261173301526120486000f3fe6080604052600436106101235760003560e01c80639a8a0592116100a0578063aad2406111610064578063aad24061146103d1578063ce757d2914610401578063e4cf5a2c14610417578063eaaba5591461044b578063f1ea66d41461046b57600080fd5b80639a8a05921461032f5780639e4e731814610345578063a0c1deb41461035a578063a8898a201461036f578063a8d2c852146103b157600080fd5b8063545a4a3c116100e7578063545a4a3c1461024357806364451212146102635780636717e41c146102835780637ee68373146102c357806388d695b21461030f57600080fd5b80631b108c07146101695780633034a7421461019f5780634791ca34146101c157806349ce8997146101e15780634fe840f51461020f57600080fd5b36610164576040805134815247602082015233917f90890809c654f11d6e72a28fa60149770a0d11ec6c92319d6ceb2bb0a4ea1a15910160405180910390a2005b600080fd5b34801561017557600080fd5b5061018961018436600461185a565b61048d565b604051610196919061196c565b60405180910390f35b3480156101ab57600080fd5b506101bf6101ba366004611986565b610835565b005b3480156101cd57600080fd5b506101bf6101dc36600461199f565b61089b565b3480156101ed57600080fd5b506102016101fc366004611986565b610b29565b604051908152602001610196565b34801561021b57600080fd5b506102017f000000000000000000000000000000000000000000000000000000000000000081565b34801561024f57600080fd5b5061020161025e366004611a01565b610b4a565b34801561026f57600080fd5b506101bf61027e366004611ad6565b610b87565b34801561028f57600080fd5b506102b361029e366004611986565b60036020526000908152604090205460ff1681565b6040519015158152602001610196565b3480156102cf57600080fd5b506102f77f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b039091168152602001610196565b34801561031b57600080fd5b506101bf61032a366004611b70565b610f44565b34801561033b57600080fd5b5061020160005481565b34801561035157600080fd5b50610201611128565b34801561036657600080fd5b50600254610201565b34801561037b57600080fd5b5061020160405169756c747261706c6f6e6b60b01b6020820152602a016040516020818303038152906040528051906020012081565b3480156103bd57600080fd5b506101bf6103cc36600461199f565b611188565b3480156103dd57600080fd5b506102b36103ec366004611986565b60046020526000908152604090205460ff1681565b34801561040d57600080fd5b5061020160015481565b34801561042357600080fd5b506102017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181565b34801561045757600080fd5b50610201610466366004611bdc565b61145e565b34801561047757600080fd5b5061048061154f565b6040516101969190611bfe565b60008781526003602052604090205460609060ff16156104e95760405162461bcd60e51b8152602060048201526012602482015271139bdb98d948185b1c9958591e481d5cd95960721b60448201526064015b60405180910390fd5b60015482101561052f5760405162461bcd60e51b81526020600482015260116024820152704e6f7420656e6f7567682070726f6f667360781b60448201526064016104e0565b6000805460405161054e9130918c908c908c908c908c90602001611c42565b60405160208183030381529060405280519060200120905060005b8381101561072f576004600086868481811061058757610587611c94565b90506020028101906105999190611caa565b60209081013582528101919091526040016000205460ff16156105f75760405162461bcd60e51b8152602060048201526016602482015275139d5b1b1a599a595c88185b1c9958591e481d5cd95960521b60448201526064016104e0565b61062485858381811061060c5761060c611c94565b905060200281019061061e9190611caa565b356115a7565b6106675760405162461bcd60e51b81526020600482015260146024820152732737ba10309031bab93932b73a1039b4b3b732b960611b60448201526064016104e0565b6106948286868481811061067d5761067d611c94565b905060200281019061068f9190611caa565b6115fd565b6106d05760405162461bcd60e51b815260206004820152600d60248201526c24b73b30b634b210383937b7b360991b60448201526064016104e0565b6001600460008787858181106106e8576106e8611c94565b90506020028101906106fa9190611caa565b6020908101358252810191909152604001600020805460ff19169115159190911790558061072781611ce0565b915050610569565b50600089815260036020526040808220805460ff191660011790555181906001600160a01b038b16908a90610767908b908b90611cf9565b60006040518083038185875af1925050503d80600081146107a4576040519150601f19603f3d011682016040523d82523d6000602084013e6107a9565b606091505b5091509150816107e75760405162461bcd60e51b8152602060048201526009602482015268151e0819985a5b195960ba1b60448201526064016104e0565b8a7f1654479f61781d185c419742a20e599a227ae1840317c8a74ceda5eb6166b8268b8b8b8b8660405161081f959493929190611d09565b60405180910390a29a9950505050505050505050565b3330146108545760405162461bcd60e51b81526004016104e090611d6a565b600081116108745760405162461bcd60e51b81526004016104e090611d8c565b6002548111156108965760405162461bcd60e51b81526004016104e090611dc3565b600155565b3330146108ba5760405162461bcd60e51b81526004016104e090611d6a565b816108f55760405162461bcd60e51b815260206004820152600b60248201526a456d70747920617272617960a81b60448201526064016104e0565b60025482106109465760405162461bcd60e51b815260206004820152601960248201527f43616e6e6f742072656d6f766520616c6c207369676e6572730000000000000060448201526064016104e0565b600081116109665760405162461bcd60e51b81526004016104e090611d8c565b600254610974908390611df3565b8111156109935760405162461bcd60e51b81526004016104e090611dc3565b60005b82811015610b21576000805b600254811015610ac9578585848181106109be576109be611c94565b90506020020135600282815481106109d8576109d8611c94565b906000526020600020015403610ab757600280546109f890600190611df3565b81548110610a0857610a08611c94565b906000526020600020015460028281548110610a2657610a26611c94565b6000918252602090912001556002805480610a4357610a43611e06565b6001900381819060005260206000200160009055905560019150858584818110610a6f57610a6f611c94565b905060200201357f33129f9e36e06e860f22ce83276273f4d83d31e9fd4a0d3a4dbac9d2da4f9d0d6000604051610aaa911515815260200190565b60405180910390a2610ac9565b80610ac181611ce0565b9150506109a2565b5080610b0e5760405162461bcd60e51b815260206004820152601460248201527310dbdb5b5a5d1b595b9d081b9bdd08199bdd5b9960621b60448201526064016104e0565b5080610b1981611ce0565b915050610996565b506001555050565b60028181548110610b3957600080fd5b600091825260209091200154905081565b60008054604051610b679130918890889088908890602001611e1c565b604051602081830303815290604052805190602001209050949350505050565b333014610ba65760405162461bcd60e51b81526004016104e090611d6a565b848314610bc55760405162461bcd60e51b81526004016104e090611e77565b848114610be45760405162461bcd60e51b81526004016104e090611e77565b84610c1f5760405162461bcd60e51b815260206004820152600b60248201526a08adae0e8f240c4c2e8c6d60ab1b60448201526064016104e0565b60005b85811015610f3b576000878783818110610c3e57610c3e611c94565b9050602002016020810190610c539190611ea0565b6001600160a01b031603610c9d5760405162461bcd60e51b8152602060048201526011602482015270125b9d985b1a59081c9958da5c1a595b9d607a1b60448201526064016104e0565b6000838383818110610cb157610cb1611c94565b9050602002016020810190610cc69190611ea0565b6001600160a01b031603610db1576000878783818110610ce857610ce8611c94565b9050602002016020810190610cfd9190611ea0565b6001600160a01b0316868684818110610d1857610d18611c94565b9050602002013560405160006040518083038185875af1925050503d8060008114610d5f576040519150601f19603f3d011682016040523d82523d6000602084013e610d64565b606091505b5050905080610dab5760405162461bcd60e51b8152602060048201526013602482015272115512081d1c985b9cd9995c8819985a5b1959606a1b60448201526064016104e0565b50610f29565b600080848484818110610dc657610dc6611c94565b9050602002016020810190610ddb9190611ea0565b6001600160a01b0316898985818110610df657610df6611c94565b9050602002016020810190610e0b9190611ea0565b888886818110610e1d57610e1d611c94565b6040516001600160a01b039094166024850152602002919091013560448301525060640160408051601f198184030181529181526020820180516001600160e01b031663a9059cbb60e01b17905251610e769190611ebb565b6000604051808303816000865af19150503d8060008114610eb3576040519150601f19603f3d011682016040523d82523d6000602084013e610eb8565b606091505b5091509150818015610ee2575080511580610ee2575080806020019051810190610ee29190611ecd565b610f265760405162461bcd60e51b8152602060048201526015602482015274115490cc8c081d1c985b9cd9995c8819985a5b1959605a1b60448201526064016104e0565b50505b80610f3381611ce0565b915050610c22565b50505050505050565b333014610f635760405162461bcd60e51b81526004016104e090611d6a565b828114610f825760405162461bcd60e51b81526004016104e090611e77565b82610fbd5760405162461bcd60e51b815260206004820152600b60248201526a08adae0e8f240c4c2e8c6d60ab1b60448201526064016104e0565b60005b83811015611121576000858583818110610fdc57610fdc611c94565b9050602002016020810190610ff19190611ea0565b6001600160a01b03160361103b5760405162461bcd60e51b8152602060048201526011602482015270125b9d985b1a59081c9958da5c1a595b9d607a1b60448201526064016104e0565b600085858381811061104f5761104f611c94565b90506020020160208101906110649190611ea0565b6001600160a01b031684848481811061107f5761107f611c94565b9050602002013560405160006040518083038185875af1925050503d80600081146110c6576040519150601f19603f3d011682016040523d82523d6000602084013e6110cb565b606091505b505090508061110e5760405162461bcd60e51b815260206004820152600f60248201526e151c985b9cd9995c8819985a5b1959608a1b60448201526064016104e0565b508061111981611ce0565b915050610fc0565b5050505050565b604080516000815260208101918290526002916111459190611ebb565b602060405180830381855afa158015611162573d6000803e3d6000fd5b5050506040513d601f19601f820116820180604052508101906111859190611eef565b81565b3330146111a75760405162461bcd60e51b81526004016104e090611d6a565b816111e25760405162461bcd60e51b815260206004820152600b60248201526a456d70747920617272617960a81b60448201526064016104e0565b600081116112025760405162461bcd60e51b81526004016104e090611d8c565b600254611210908390611f08565b81111561122f5760405162461bcd60e51b81526004016104e090611dc3565b60005b82811015610b215783838281811061124c5761124c611c94565b905060200201356000036112975760405162461bcd60e51b8152602060048201526012602482015271125b9d985b1a590818dbdb5b5a5d1b595b9d60721b60448201526064016104e0565b60005b60025481101561132e578484838181106112b6576112b6611c94565b90506020020135600282815481106112d0576112d0611c94565b90600052602060002001540361131c5760405162461bcd60e51b8152602060048201526011602482015270436f6d6d69746d656e742065786973747360781b60448201526064016104e0565b8061132681611ce0565b91505061129a565b5060005b818110156113c05784848381811061134c5761134c611c94565b9050602002013585858381811061136557611365611c94565b90506020020135036113ae5760405162461bcd60e51b8152602060048201526012602482015271111d5c1b1a58d85d19481a5b881a5b9c1d5d60721b60448201526064016104e0565b806113b881611ce0565b915050611332565b5060028484838181106113d5576113d5611c94565b8354600181018555600094855260209485902091909402929092013591909201555083838281811061140957611409611c94565b905060200201357f33129f9e36e06e860f22ce83276273f4d83d31e9fd4a0d3a4dbac9d2da4f9d0d6001604051611444911515815260200190565b60405180910390a28061145681611ce0565b915050611232565b60008061148b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000185611f1b565b905060006114b97f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000185611f1b565b604080518082018252848152602081018390529051632b0aac7f60e11b8152919250733333333C0A88F9BE4fd23ed0536F9B6c427e3B939163561558fe9161150391600401611f3d565b602060405180830381865af4158015611520573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115449190611eef565b925050505b92915050565b6060600280548060200260200160405190810160405280929190818152602001828054801561159d57602002820191906000526020600020905b815481526020019060010190808311611589575b5050505050905090565b6000805b6002548110156115f45782600282815481106115c9576115c9611c94565b9060005260206000200154036115e25750600192915050565b806115ec81611ce0565b9150506115ab565b50600092915050565b60008061160b84600161145e565b604080516020808201849052863582840152868101356060808401919091528351808403909101815260808301845269756c747261706c6f6e6b60b01b60a08401528351608a81850301815260aa840180865281519190930120600080845260ca909401948590529495509391927f0000000000000000000000000000000000000000000000000000000000000000916002916116a89190611ebb565b602060405180830381855afa1580156116c5573d6000803e3d6000fd5b5050506040513d601f19601f820116820180604052508101906116e89190611eef565b8480519060200120604051602001611719949392919093845260208401929092526040830152606082015260800190565b6040516020818303038152906040528051906020012090507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663a78f9e36866060013587604001358489806080019061177b9190611f6e565b8b60a001358c60c001356040518863ffffffff1660e01b81526004016117a79796959493929190611fb8565b602060405180830381865afa1580156117c4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117e89190611ecd565b9695505050505050565b80356001600160a01b038116811461180957600080fd5b919050565b60008083601f84011261182057600080fd5b50813567ffffffffffffffff81111561183857600080fd5b6020830191508360208260051b850101111561185357600080fd5b9250929050565b600080600080600080600060a0888a03121561187557600080fd5b87359650611885602089016117f2565b955060408801359450606088013567ffffffffffffffff808211156118a957600080fd5b818a0191508a601f8301126118bd57600080fd5b8135818111156118cc57600080fd5b8b60208285010111156118de57600080fd5b6020830196508095505060808a01359150808211156118fc57600080fd5b506119098a828b0161180e565b989b979a50959850939692959293505050565b60005b8381101561193757818101518382015260200161191f565b50506000910152565b6000815180845261195881602086016020860161191c565b601f01601f19169290920160200192915050565b60208152600061197f6020830184611940565b9392505050565b60006020828403121561199857600080fd5b5035919050565b6000806000604084860312156119b457600080fd5b833567ffffffffffffffff8111156119cb57600080fd5b6119d78682870161180e565b909790965060209590950135949350505050565b634e487b7160e01b600052604160045260246000fd5b60008060008060808587031215611a1757600080fd5b84359350611a27602086016117f2565b925060408501359150606085013567ffffffffffffffff80821115611a4b57600080fd5b818701915087601f830112611a5f57600080fd5b813581811115611a7157611a716119eb565b604051601f8201601f19908116603f01168101908382118183101715611a9957611a996119eb565b816040528281528a6020848701011115611ab257600080fd5b82602086016020830137600060208483010152809550505050505092959194509250565b60008060008060008060608789031215611aef57600080fd5b863567ffffffffffffffff80821115611b0757600080fd5b611b138a838b0161180e565b90985096506020890135915080821115611b2c57600080fd5b611b388a838b0161180e565b90965094506040890135915080821115611b5157600080fd5b50611b5e89828a0161180e565b979a9699509497509295939492505050565b60008060008060408587031215611b8657600080fd5b843567ffffffffffffffff80821115611b9e57600080fd5b611baa8883890161180e565b90965094506020870135915080821115611bc357600080fd5b50611bd08782880161180e565b95989497509550505050565b60008060408385031215611bef57600080fd5b50508035926020909101359150565b6020808252825182820181905260009190848201906040850190845b81811015611c3657835183529284019291840191600101611c1a565b50909695505050505050565b60006bffffffffffffffffffffffff19808a60601b168352886014840152876034840152808760601b166054840152508460688301528284608884013750600091016088019081529695505050505050565b634e487b7160e01b600052603260045260246000fd5b6000823560de19833603018112611cc057600080fd5b9190910192915050565b634e487b7160e01b600052601160045260246000fd5b600060018201611cf257611cf2611cca565b5060010190565b8183823760009101908152919050565b6001600160a01b0386168152602081018590526080604082018190528101839052828460a0830137600060a084830101526000601f19601f850116820160a0838203016060840152611d5e60a0820185611940565b98975050505050505050565b6020808252600890820152672737ba1029b2b63360c11b604082015260600190565b6020808252601e908201527f4d757374206265206e6f6e2d7a65726f20736967732072657175697265640000604082015260600190565b6020808252601690820152750a6d2cee640e4cae2ead2e4cac840e8dede40d0d2ced60531b604082015260600190565b8181038181111561154957611549611cca565b634e487b7160e01b600052603160045260246000fd5b60006bffffffffffffffffffffffff19808960601b168352876014840152866034840152808660601b166054840152508360688301528251611e6581608885016020870161191c565b91909101608801979650505050505050565b6020808252600f908201526e098cadccee8d040dad2e6dac2e8c6d608b1b604082015260600190565b600060208284031215611eb257600080fd5b61197f826117f2565b60008251611cc081846020870161191c565b600060208284031215611edf57600080fd5b8151801515811461197f57600080fd5b600060208284031215611f0157600080fd5b5051919050565b8082018082111561154957611549611cca565b600082611f3857634e487b7160e01b600052601260045260246000fd5b500690565b60408101818360005b6002811015611f65578151835260209283019290910190600101611f46565b50505092915050565b6000808335601e19843603018112611f8557600080fd5b83018035915067ffffffffffffffff821115611fa057600080fd5b6020019150600581901b360382131561185357600080fd5b87815286602082015285604082015260c060608201528360c0820152600060018060fb1b03851115611fe957600080fd5b8460051b808760e085013760808301949094525060a08101919091520160e0019594505050505056fea2646970667358221220925328ba1cb1d89f7ed11053888b9080bbb4027159fb1003d3575f3e6578fd1464736f6c63430008140033", - "deployedBytecode": "0x6080604052600436106101235760003560e01c80639a8a0592116100a0578063aad2406111610064578063aad24061146103d1578063ce757d2914610401578063e4cf5a2c14610417578063eaaba5591461044b578063f1ea66d41461046b57600080fd5b80639a8a05921461032f5780639e4e731814610345578063a0c1deb41461035a578063a8898a201461036f578063a8d2c852146103b157600080fd5b8063545a4a3c116100e7578063545a4a3c1461024357806364451212146102635780636717e41c146102835780637ee68373146102c357806388d695b21461030f57600080fd5b80631b108c07146101695780633034a7421461019f5780634791ca34146101c157806349ce8997146101e15780634fe840f51461020f57600080fd5b36610164576040805134815247602082015233917f90890809c654f11d6e72a28fa60149770a0d11ec6c92319d6ceb2bb0a4ea1a15910160405180910390a2005b600080fd5b34801561017557600080fd5b5061018961018436600461185a565b61048d565b604051610196919061196c565b60405180910390f35b3480156101ab57600080fd5b506101bf6101ba366004611986565b610835565b005b3480156101cd57600080fd5b506101bf6101dc36600461199f565b61089b565b3480156101ed57600080fd5b506102016101fc366004611986565b610b29565b604051908152602001610196565b34801561021b57600080fd5b506102017f000000000000000000000000000000000000000000000000000000000000000081565b34801561024f57600080fd5b5061020161025e366004611a01565b610b4a565b34801561026f57600080fd5b506101bf61027e366004611ad6565b610b87565b34801561028f57600080fd5b506102b361029e366004611986565b60036020526000908152604090205460ff1681565b6040519015158152602001610196565b3480156102cf57600080fd5b506102f77f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b039091168152602001610196565b34801561031b57600080fd5b506101bf61032a366004611b70565b610f44565b34801561033b57600080fd5b5061020160005481565b34801561035157600080fd5b50610201611128565b34801561036657600080fd5b50600254610201565b34801561037b57600080fd5b5061020160405169756c747261706c6f6e6b60b01b6020820152602a016040516020818303038152906040528051906020012081565b3480156103bd57600080fd5b506101bf6103cc36600461199f565b611188565b3480156103dd57600080fd5b506102b36103ec366004611986565b60046020526000908152604090205460ff1681565b34801561040d57600080fd5b5061020160015481565b34801561042357600080fd5b506102017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181565b34801561045757600080fd5b50610201610466366004611bdc565b61145e565b34801561047757600080fd5b5061048061154f565b6040516101969190611bfe565b60008781526003602052604090205460609060ff16156104e95760405162461bcd60e51b8152602060048201526012602482015271139bdb98d948185b1c9958591e481d5cd95960721b60448201526064015b60405180910390fd5b60015482101561052f5760405162461bcd60e51b81526020600482015260116024820152704e6f7420656e6f7567682070726f6f667360781b60448201526064016104e0565b6000805460405161054e9130918c908c908c908c908c90602001611c42565b60405160208183030381529060405280519060200120905060005b8381101561072f576004600086868481811061058757610587611c94565b90506020028101906105999190611caa565b60209081013582528101919091526040016000205460ff16156105f75760405162461bcd60e51b8152602060048201526016602482015275139d5b1b1a599a595c88185b1c9958591e481d5cd95960521b60448201526064016104e0565b61062485858381811061060c5761060c611c94565b905060200281019061061e9190611caa565b356115a7565b6106675760405162461bcd60e51b81526020600482015260146024820152732737ba10309031bab93932b73a1039b4b3b732b960611b60448201526064016104e0565b6106948286868481811061067d5761067d611c94565b905060200281019061068f9190611caa565b6115fd565b6106d05760405162461bcd60e51b815260206004820152600d60248201526c24b73b30b634b210383937b7b360991b60448201526064016104e0565b6001600460008787858181106106e8576106e8611c94565b90506020028101906106fa9190611caa565b6020908101358252810191909152604001600020805460ff19169115159190911790558061072781611ce0565b915050610569565b50600089815260036020526040808220805460ff191660011790555181906001600160a01b038b16908a90610767908b908b90611cf9565b60006040518083038185875af1925050503d80600081146107a4576040519150601f19603f3d011682016040523d82523d6000602084013e6107a9565b606091505b5091509150816107e75760405162461bcd60e51b8152602060048201526009602482015268151e0819985a5b195960ba1b60448201526064016104e0565b8a7f1654479f61781d185c419742a20e599a227ae1840317c8a74ceda5eb6166b8268b8b8b8b8660405161081f959493929190611d09565b60405180910390a29a9950505050505050505050565b3330146108545760405162461bcd60e51b81526004016104e090611d6a565b600081116108745760405162461bcd60e51b81526004016104e090611d8c565b6002548111156108965760405162461bcd60e51b81526004016104e090611dc3565b600155565b3330146108ba5760405162461bcd60e51b81526004016104e090611d6a565b816108f55760405162461bcd60e51b815260206004820152600b60248201526a456d70747920617272617960a81b60448201526064016104e0565b60025482106109465760405162461bcd60e51b815260206004820152601960248201527f43616e6e6f742072656d6f766520616c6c207369676e6572730000000000000060448201526064016104e0565b600081116109665760405162461bcd60e51b81526004016104e090611d8c565b600254610974908390611df3565b8111156109935760405162461bcd60e51b81526004016104e090611dc3565b60005b82811015610b21576000805b600254811015610ac9578585848181106109be576109be611c94565b90506020020135600282815481106109d8576109d8611c94565b906000526020600020015403610ab757600280546109f890600190611df3565b81548110610a0857610a08611c94565b906000526020600020015460028281548110610a2657610a26611c94565b6000918252602090912001556002805480610a4357610a43611e06565b6001900381819060005260206000200160009055905560019150858584818110610a6f57610a6f611c94565b905060200201357f33129f9e36e06e860f22ce83276273f4d83d31e9fd4a0d3a4dbac9d2da4f9d0d6000604051610aaa911515815260200190565b60405180910390a2610ac9565b80610ac181611ce0565b9150506109a2565b5080610b0e5760405162461bcd60e51b815260206004820152601460248201527310dbdb5b5a5d1b595b9d081b9bdd08199bdd5b9960621b60448201526064016104e0565b5080610b1981611ce0565b915050610996565b506001555050565b60028181548110610b3957600080fd5b600091825260209091200154905081565b60008054604051610b679130918890889088908890602001611e1c565b604051602081830303815290604052805190602001209050949350505050565b333014610ba65760405162461bcd60e51b81526004016104e090611d6a565b848314610bc55760405162461bcd60e51b81526004016104e090611e77565b848114610be45760405162461bcd60e51b81526004016104e090611e77565b84610c1f5760405162461bcd60e51b815260206004820152600b60248201526a08adae0e8f240c4c2e8c6d60ab1b60448201526064016104e0565b60005b85811015610f3b576000878783818110610c3e57610c3e611c94565b9050602002016020810190610c539190611ea0565b6001600160a01b031603610c9d5760405162461bcd60e51b8152602060048201526011602482015270125b9d985b1a59081c9958da5c1a595b9d607a1b60448201526064016104e0565b6000838383818110610cb157610cb1611c94565b9050602002016020810190610cc69190611ea0565b6001600160a01b031603610db1576000878783818110610ce857610ce8611c94565b9050602002016020810190610cfd9190611ea0565b6001600160a01b0316868684818110610d1857610d18611c94565b9050602002013560405160006040518083038185875af1925050503d8060008114610d5f576040519150601f19603f3d011682016040523d82523d6000602084013e610d64565b606091505b5050905080610dab5760405162461bcd60e51b8152602060048201526013602482015272115512081d1c985b9cd9995c8819985a5b1959606a1b60448201526064016104e0565b50610f29565b600080848484818110610dc657610dc6611c94565b9050602002016020810190610ddb9190611ea0565b6001600160a01b0316898985818110610df657610df6611c94565b9050602002016020810190610e0b9190611ea0565b888886818110610e1d57610e1d611c94565b6040516001600160a01b039094166024850152602002919091013560448301525060640160408051601f198184030181529181526020820180516001600160e01b031663a9059cbb60e01b17905251610e769190611ebb565b6000604051808303816000865af19150503d8060008114610eb3576040519150601f19603f3d011682016040523d82523d6000602084013e610eb8565b606091505b5091509150818015610ee2575080511580610ee2575080806020019051810190610ee29190611ecd565b610f265760405162461bcd60e51b8152602060048201526015602482015274115490cc8c081d1c985b9cd9995c8819985a5b1959605a1b60448201526064016104e0565b50505b80610f3381611ce0565b915050610c22565b50505050505050565b333014610f635760405162461bcd60e51b81526004016104e090611d6a565b828114610f825760405162461bcd60e51b81526004016104e090611e77565b82610fbd5760405162461bcd60e51b815260206004820152600b60248201526a08adae0e8f240c4c2e8c6d60ab1b60448201526064016104e0565b60005b83811015611121576000858583818110610fdc57610fdc611c94565b9050602002016020810190610ff19190611ea0565b6001600160a01b03160361103b5760405162461bcd60e51b8152602060048201526011602482015270125b9d985b1a59081c9958da5c1a595b9d607a1b60448201526064016104e0565b600085858381811061104f5761104f611c94565b90506020020160208101906110649190611ea0565b6001600160a01b031684848481811061107f5761107f611c94565b9050602002013560405160006040518083038185875af1925050503d80600081146110c6576040519150601f19603f3d011682016040523d82523d6000602084013e6110cb565b606091505b505090508061110e5760405162461bcd60e51b815260206004820152600f60248201526e151c985b9cd9995c8819985a5b1959608a1b60448201526064016104e0565b508061111981611ce0565b915050610fc0565b5050505050565b604080516000815260208101918290526002916111459190611ebb565b602060405180830381855afa158015611162573d6000803e3d6000fd5b5050506040513d601f19601f820116820180604052508101906111859190611eef565b81565b3330146111a75760405162461bcd60e51b81526004016104e090611d6a565b816111e25760405162461bcd60e51b815260206004820152600b60248201526a456d70747920617272617960a81b60448201526064016104e0565b600081116112025760405162461bcd60e51b81526004016104e090611d8c565b600254611210908390611f08565b81111561122f5760405162461bcd60e51b81526004016104e090611dc3565b60005b82811015610b215783838281811061124c5761124c611c94565b905060200201356000036112975760405162461bcd60e51b8152602060048201526012602482015271125b9d985b1a590818dbdb5b5a5d1b595b9d60721b60448201526064016104e0565b60005b60025481101561132e578484838181106112b6576112b6611c94565b90506020020135600282815481106112d0576112d0611c94565b90600052602060002001540361131c5760405162461bcd60e51b8152602060048201526011602482015270436f6d6d69746d656e742065786973747360781b60448201526064016104e0565b8061132681611ce0565b91505061129a565b5060005b818110156113c05784848381811061134c5761134c611c94565b9050602002013585858381811061136557611365611c94565b90506020020135036113ae5760405162461bcd60e51b8152602060048201526012602482015271111d5c1b1a58d85d19481a5b881a5b9c1d5d60721b60448201526064016104e0565b806113b881611ce0565b915050611332565b5060028484838181106113d5576113d5611c94565b8354600181018555600094855260209485902091909402929092013591909201555083838281811061140957611409611c94565b905060200201357f33129f9e36e06e860f22ce83276273f4d83d31e9fd4a0d3a4dbac9d2da4f9d0d6001604051611444911515815260200190565b60405180910390a28061145681611ce0565b915050611232565b60008061148b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000185611f1b565b905060006114b97f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000185611f1b565b604080518082018252848152602081018390529051632b0aac7f60e11b815291925073__$75f79a42d9bcbdbb69ad79ebd80f556f39$__9163561558fe9161150391600401611f3d565b602060405180830381865af4158015611520573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115449190611eef565b925050505b92915050565b6060600280548060200260200160405190810160405280929190818152602001828054801561159d57602002820191906000526020600020905b815481526020019060010190808311611589575b5050505050905090565b6000805b6002548110156115f45782600282815481106115c9576115c9611c94565b9060005260206000200154036115e25750600192915050565b806115ec81611ce0565b9150506115ab565b50600092915050565b60008061160b84600161145e565b604080516020808201849052863582840152868101356060808401919091528351808403909101815260808301845269756c747261706c6f6e6b60b01b60a08401528351608a81850301815260aa840180865281519190930120600080845260ca909401948590529495509391927f0000000000000000000000000000000000000000000000000000000000000000916002916116a89190611ebb565b602060405180830381855afa1580156116c5573d6000803e3d6000fd5b5050506040513d601f19601f820116820180604052508101906116e89190611eef565b8480519060200120604051602001611719949392919093845260208401929092526040830152606082015260800190565b6040516020818303038152906040528051906020012090507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663a78f9e36866060013587604001358489806080019061177b9190611f6e565b8b60a001358c60c001356040518863ffffffff1660e01b81526004016117a79796959493929190611fb8565b602060405180830381865afa1580156117c4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117e89190611ecd565b9695505050505050565b80356001600160a01b038116811461180957600080fd5b919050565b60008083601f84011261182057600080fd5b50813567ffffffffffffffff81111561183857600080fd5b6020830191508360208260051b850101111561185357600080fd5b9250929050565b600080600080600080600060a0888a03121561187557600080fd5b87359650611885602089016117f2565b955060408801359450606088013567ffffffffffffffff808211156118a957600080fd5b818a0191508a601f8301126118bd57600080fd5b8135818111156118cc57600080fd5b8b60208285010111156118de57600080fd5b6020830196508095505060808a01359150808211156118fc57600080fd5b506119098a828b0161180e565b989b979a50959850939692959293505050565b60005b8381101561193757818101518382015260200161191f565b50506000910152565b6000815180845261195881602086016020860161191c565b601f01601f19169290920160200192915050565b60208152600061197f6020830184611940565b9392505050565b60006020828403121561199857600080fd5b5035919050565b6000806000604084860312156119b457600080fd5b833567ffffffffffffffff8111156119cb57600080fd5b6119d78682870161180e565b909790965060209590950135949350505050565b634e487b7160e01b600052604160045260246000fd5b60008060008060808587031215611a1757600080fd5b84359350611a27602086016117f2565b925060408501359150606085013567ffffffffffffffff80821115611a4b57600080fd5b818701915087601f830112611a5f57600080fd5b813581811115611a7157611a716119eb565b604051601f8201601f19908116603f01168101908382118183101715611a9957611a996119eb565b816040528281528a6020848701011115611ab257600080fd5b82602086016020830137600060208483010152809550505050505092959194509250565b60008060008060008060608789031215611aef57600080fd5b863567ffffffffffffffff80821115611b0757600080fd5b611b138a838b0161180e565b90985096506020890135915080821115611b2c57600080fd5b611b388a838b0161180e565b90965094506040890135915080821115611b5157600080fd5b50611b5e89828a0161180e565b979a9699509497509295939492505050565b60008060008060408587031215611b8657600080fd5b843567ffffffffffffffff80821115611b9e57600080fd5b611baa8883890161180e565b90965094506020870135915080821115611bc357600080fd5b50611bd08782880161180e565b95989497509550505050565b60008060408385031215611bef57600080fd5b50508035926020909101359150565b6020808252825182820181905260009190848201906040850190845b81811015611c3657835183529284019291840191600101611c1a565b50909695505050505050565b60006bffffffffffffffffffffffff19808a60601b168352886014840152876034840152808760601b166054840152508460688301528284608884013750600091016088019081529695505050505050565b634e487b7160e01b600052603260045260246000fd5b6000823560de19833603018112611cc057600080fd5b9190910192915050565b634e487b7160e01b600052601160045260246000fd5b600060018201611cf257611cf2611cca565b5060010190565b8183823760009101908152919050565b6001600160a01b0386168152602081018590526080604082018190528101839052828460a0830137600060a084830101526000601f19601f850116820160a0838203016060840152611d5e60a0820185611940565b98975050505050505050565b6020808252600890820152672737ba1029b2b63360c11b604082015260600190565b6020808252601e908201527f4d757374206265206e6f6e2d7a65726f20736967732072657175697265640000604082015260600190565b6020808252601690820152750a6d2cee640e4cae2ead2e4cac840e8dede40d0d2ced60531b604082015260600190565b8181038181111561154957611549611cca565b634e487b7160e01b600052603160045260246000fd5b60006bffffffffffffffffffffffff19808960601b168352876014840152866034840152808660601b166054840152508360688301528251611e6581608885016020870161191c565b91909101608801979650505050505050565b6020808252600f908201526e098cadccee8d040dad2e6dac2e8c6d608b1b604082015260600190565b600060208284031215611eb257600080fd5b61197f826117f2565b60008251611cc081846020870161191c565b600060208284031215611edf57600080fd5b8151801515811461197f57600080fd5b600060208284031215611f0157600080fd5b5051919050565b8082018082111561154957611549611cca565b600082611f3857634e487b7160e01b600052601260045260246000fd5b500690565b60408101818360005b6002811015611f65578151835260209283019290910190600101611f46565b50505092915050565b6000808335601e19843603018112611f8557600080fd5b83018035915067ffffffffffffffff821115611fa057600080fd5b6020019150600581901b360382131561185357600080fd5b87815286602082015285604082015260c060608201528360c0820152600060018060fb1b03851115611fe957600080fd5b8460051b808760e085013760808301949094525060a08101919091520160e0019594505050505056fea2646970667358221220925328ba1cb1d89f7ed11053888b9080bbb4027159fb1003d3575f3e6578fd1464736f6c63430008140033", + "numDeployments": 2, + "solcInputHash": "6b4f1b22cbd21b116f152fb4a836f175", + "metadata": "{\"compiler\":{\"version\":\"0.8.20+commit.a1b79de6\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_zkvContract\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"_vkHash\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"_chainId\",\"type\":\"uint256\"},{\"internalType\":\"uint256[]\",\"name\":\"_initialCommitments\",\"type\":\"uint256[]\"},{\"internalType\":\"uint256\",\"name\":\"_signaturesRequired\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"balance\",\"type\":\"uint256\"}],\"name\":\"Deposit\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"commitment\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"isAdded\",\"type\":\"bool\"}],\"name\":\"Owner\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"result\",\"type\":\"bytes\"}],\"name\":\"TransactionExecuted\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"BN254_PRIME\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"PROVING_SYSTEM_ID\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"VERSION_HASH\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256[]\",\"name\":\"newCommitments\",\"type\":\"uint256[]\"},{\"internalType\":\"uint256\",\"name\":\"newSigRequired\",\"type\":\"uint256\"}],\"name\":\"addSigners\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"approveAmount\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"callTarget\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"callValue\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"callData\",\"type\":\"bytes\"}],\"name\":\"approveAndCall\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"recipients\",\"type\":\"address[]\"},{\"internalType\":\"uint256[]\",\"name\":\"amounts\",\"type\":\"uint256[]\"}],\"name\":\"batchTransfer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"recipients\",\"type\":\"address[]\"},{\"internalType\":\"uint256[]\",\"name\":\"amounts\",\"type\":\"uint256[]\"},{\"internalType\":\"address[]\",\"name\":\"tokenAddresses\",\"type\":\"address[]\"}],\"name\":\"batchTransferMulti\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"chainId\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"commitments\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_nonce\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"commitment\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"nullifier\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"aggregationId\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"domainId\",\"type\":\"uint256\"},{\"internalType\":\"bytes32[]\",\"name\":\"zkMerklePath\",\"type\":\"bytes32[]\"},{\"internalType\":\"uint256\",\"name\":\"leafCount\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"index\",\"type\":\"uint256\"}],\"internalType\":\"struct MetaMultiSigWallet.ZkProof[]\",\"name\":\"proofs\",\"type\":\"tuple[]\"}],\"name\":\"execute\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getCommitments\",\"outputs\":[{\"internalType\":\"uint256[]\",\"name\":\"\",\"type\":\"uint256[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getSignersCount\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_nonce\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"getTransactionHash\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"a\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"b\",\"type\":\"uint256\"}],\"name\":\"poseidonHash2\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256[]\",\"name\":\"commitmentsToRemove\",\"type\":\"uint256[]\"},{\"internalType\":\"uint256\",\"name\":\"newSigRequired\",\"type\":\"uint256\"}],\"name\":\"removeSigners\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"signaturesRequired\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"newSigRequired\",\"type\":\"uint256\"}],\"name\":\"updateSignaturesRequired\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"usedNonces\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"usedNullifiers\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"vkHash\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"zkvContract\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"stateMutability\":\"payable\",\"type\":\"receive\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{\"approveAndCall(address,address,uint256,address,uint256,bytes)\":{\"params\":{\"approveAmount\":\"Amount to approve\",\"callData\":\"Encoded function call for the target\",\"callTarget\":\"Contract to call after approval\",\"callValue\":\"Native ETH to send with the call (e.g. LayerZero fee)\",\"spender\":\"Address to approve spending\",\"token\":\"ERC20 token to approve\"}},\"batchTransfer(address[],uint256[])\":{\"params\":{\"amounts\":\"Array of amounts to send\",\"recipients\":\"Array of recipient addresses\"}},\"batchTransferMulti(address[],uint256[],address[])\":{\"params\":{\"amounts\":\"Array of amounts to send\",\"recipients\":\"Array of recipient addresses\",\"tokenAddresses\":\"Array of token addresses (address(0) = native ETH)\"}}},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{\"approveAndCall(address,address,uint256,address,uint256,bytes)\":{\"notice\":\"Approve an ERC20 token and call a target contract in one atomic operation. Used for cross-chain bridge flows (e.g. OFT Adapter: approve + send).\"},\"batchTransfer(address[],uint256[])\":{\"notice\":\"Execute multiple transfers in one transaction\"},\"batchTransferMulti(address[],uint256[],address[])\":{\"notice\":\"Execute multiple transfers with mixed token types\"}},\"version\":1}},\"settings\":{\"compilationTarget\":{\"contracts/MetaMultiSigWallet.sol\":\"MetaMultiSigWallet\"},\"evmVersion\":\"paris\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\",\"useLiteralContent\":true},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[]},\"sources\":{\"contracts/MetaMultiSigWallet.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\npragma solidity ^0.8.13;\\n\\nimport \\\"poseidon-solidity/PoseidonT3.sol\\\";\\n\\ninterface IVerifyProofAggregation {\\n function verifyProofAggregation(\\n uint256 _domainId,\\n uint256 _aggregationId,\\n bytes32 _leaf,\\n bytes32[] calldata _merklePath,\\n uint256 _leafCount,\\n uint256 _index\\n ) external view returns (bool);\\n}\\n\\ncontract MetaMultiSigWallet {\\n // ============ Constants ============\\n bytes32 public constant PROVING_SYSTEM_ID = keccak256(abi.encodePacked(\\\"ultraplonk\\\"));\\n bytes32 public constant VERSION_HASH = sha256(abi.encodePacked(\\\"\\\"));\\n uint256 public constant BN254_PRIME = 21888242871839275222246405745257275088548364400416034343698204186575808495617;\\n\\n // ============ Events ============\\n event Deposit(address indexed sender, uint256 amount, uint256 balance);\\n event TransactionExecuted(uint256 indexed nonce, address to, uint256 value, bytes data, bytes result);\\n event Owner(uint256 indexed commitment, bool isAdded);\\n\\n // ============ Structs ============\\n struct ZkProof {\\n uint256 commitment;\\n uint256 nullifier;\\n uint256 aggregationId;\\n uint256 domainId;\\n bytes32[] zkMerklePath;\\n uint256 leafCount;\\n uint256 index;\\n }\\n\\n // ============ State ============\\n address public immutable zkvContract;\\n bytes32 public immutable vkHash;\\n uint256 public chainId;\\n uint256 public signaturesRequired;\\n\\n // Signer management\\n uint256[] public commitments;\\n\\n // Nonce tracking (prevent replay)\\n mapping(uint256 => bool) public usedNonces;\\n\\n // Nullifier tracking (prevent double-signing)\\n mapping(uint256 => bool) public usedNullifiers;\\n\\n // ============ Constructor ============\\n constructor(\\n address _zkvContract,\\n bytes32 _vkHash,\\n uint256 _chainId,\\n uint256[] memory _initialCommitments,\\n uint256 _signaturesRequired\\n ) {\\n require(_zkvContract != address(0), \\\"Invalid zkv address\\\");\\n require(_signaturesRequired > 0, \\\"Must be non-zero sigs required\\\");\\n require(_initialCommitments.length > 0, \\\"Need at least 1 signer\\\");\\n require(_signaturesRequired <= _initialCommitments.length, \\\"Sigs required too high\\\");\\n\\n zkvContract = _zkvContract;\\n vkHash = _vkHash;\\n chainId = _chainId;\\n signaturesRequired = _signaturesRequired;\\n\\n for (uint256 i = 0; i < _initialCommitments.length; i++) {\\n require(_initialCommitments[i] != 0, \\\"Invalid commitment\\\");\\n commitments.push(_initialCommitments[i]);\\n emit Owner(_initialCommitments[i], true);\\n }\\n }\\n\\n // ============ Modifiers ============\\n modifier onlySelf() {\\n require(msg.sender == address(this), \\\"Not Self\\\");\\n _;\\n }\\n\\n // ============ Main Execute Function ============\\n function execute(\\n uint256 _nonce,\\n address to,\\n uint256 value,\\n bytes calldata data,\\n ZkProof[] calldata proofs\\n ) external returns (bytes memory) {\\n require(!usedNonces[_nonce], \\\"Nonce already used\\\");\\n require(proofs.length >= signaturesRequired, \\\"Not enough proofs\\\");\\n\\n bytes32 txHash = keccak256(abi.encodePacked(address(this), chainId, _nonce, to, value, data));\\n\\n for (uint256 i = 0; i < proofs.length; i++) {\\n require(!usedNullifiers[proofs[i].nullifier], \\\"Nullifier already used\\\");\\n require(_isCurrentSigner(proofs[i].commitment), \\\"Not a current signer\\\");\\n require(_verifyProof(txHash, proofs[i]), \\\"Invalid proof\\\");\\n usedNullifiers[proofs[i].nullifier] = true;\\n }\\n\\n usedNonces[_nonce] = true;\\n\\n (bool success, bytes memory result) = to.call{ value: value }(data);\\n require(success, \\\"Tx failed\\\");\\n\\n emit TransactionExecuted(_nonce, to, value, data, result);\\n return result;\\n }\\n\\n // ============ Signer Management ============\\n function addSigners(uint256[] calldata newCommitments, uint256 newSigRequired) public onlySelf {\\n require(newCommitments.length > 0, \\\"Empty array\\\");\\n require(newSigRequired > 0, \\\"Must be non-zero sigs required\\\");\\n require(newSigRequired <= commitments.length + newCommitments.length, \\\"Sigs required too high\\\");\\n\\n for (uint256 i = 0; i < newCommitments.length; i++) {\\n require(newCommitments[i] != 0, \\\"Invalid commitment\\\");\\n \\n // Check duplicate with existing signers\\n for (uint256 j = 0; j < commitments.length; j++) {\\n require(commitments[j] != newCommitments[i], \\\"Commitment exists\\\");\\n }\\n \\n // Check duplicate within input array\\n for (uint256 k = 0; k < i; k++) {\\n require(newCommitments[k] != newCommitments[i], \\\"Duplicate in input\\\");\\n }\\n\\n commitments.push(newCommitments[i]);\\n emit Owner(newCommitments[i], true);\\n }\\n\\n signaturesRequired = newSigRequired;\\n }\\n\\n function removeSigners(uint256[] calldata commitmentsToRemove, uint256 newSigRequired) public onlySelf {\\n require(commitmentsToRemove.length > 0, \\\"Empty array\\\");\\n require(commitments.length > commitmentsToRemove.length, \\\"Cannot remove all signers\\\");\\n require(newSigRequired > 0, \\\"Must be non-zero sigs required\\\");\\n require(newSigRequired <= commitments.length - commitmentsToRemove.length, \\\"Sigs required too high\\\");\\n\\n for (uint256 i = 0; i < commitmentsToRemove.length; i++) {\\n bool found = false;\\n for (uint256 j = 0; j < commitments.length; j++) {\\n if (commitments[j] == commitmentsToRemove[i]) {\\n commitments[j] = commitments[commitments.length - 1];\\n commitments.pop();\\n found = true;\\n emit Owner(commitmentsToRemove[i], false);\\n break;\\n }\\n }\\n require(found, \\\"Commitment not found\\\");\\n }\\n\\n signaturesRequired = newSigRequired;\\n }\\n\\n function updateSignaturesRequired(uint256 newSigRequired) public onlySelf {\\n require(newSigRequired > 0, \\\"Must be non-zero sigs required\\\");\\n require(newSigRequired <= commitments.length, \\\"Sigs required too high\\\");\\n signaturesRequired = newSigRequired;\\n }\\n\\n /**\\n * @notice Execute multiple transfers in one transaction\\n * @param recipients Array of recipient addresses\\n * @param amounts Array of amounts to send\\n */\\n function batchTransfer(address[] calldata recipients, uint256[] calldata amounts) public onlySelf {\\n require(recipients.length == amounts.length, \\\"Length mismatch\\\");\\n require(recipients.length > 0, \\\"Empty batch\\\");\\n\\n for (uint256 i = 0; i < recipients.length; i++) {\\n require(recipients[i] != address(0), \\\"Invalid recipient\\\");\\n (bool success, ) = recipients[i].call{ value: amounts[i] }(\\\"\\\");\\n require(success, \\\"Transfer failed\\\");\\n }\\n }\\n\\n /**\\n * @notice Execute multiple transfers with mixed token types\\n * @param recipients Array of recipient addresses\\n * @param amounts Array of amounts to send\\n * @param tokenAddresses Array of token addresses (address(0) = native ETH)\\n */\\n function batchTransferMulti(\\n address[] calldata recipients,\\n uint256[] calldata amounts,\\n address[] calldata tokenAddresses\\n ) public onlySelf {\\n require(recipients.length == amounts.length, \\\"Length mismatch\\\");\\n require(recipients.length == tokenAddresses.length, \\\"Length mismatch\\\");\\n require(recipients.length > 0, \\\"Empty batch\\\");\\n\\n for (uint256 i = 0; i < recipients.length; i++) {\\n require(recipients[i] != address(0), \\\"Invalid recipient\\\");\\n\\n if (tokenAddresses[i] == address(0)) {\\n // Native ETH transfer\\n (bool success, ) = recipients[i].call{ value: amounts[i] }(\\\"\\\");\\n require(success, \\\"ETH transfer failed\\\");\\n } else {\\n // ERC20 transfer\\n (bool success, bytes memory data) = tokenAddresses[i].call(\\n abi.encodeWithSignature(\\\"transfer(address,uint256)\\\", recipients[i], amounts[i])\\n );\\n require(success && (data.length == 0 || abi.decode(data, (bool))), \\\"ERC20 transfer failed\\\");\\n }\\n }\\n }\\n\\n /**\\n * @notice Approve an ERC20 token and call a target contract in one atomic operation.\\n * Used for cross-chain bridge flows (e.g. OFT Adapter: approve + send).\\n * @param token ERC20 token to approve\\n * @param spender Address to approve spending\\n * @param approveAmount Amount to approve\\n * @param callTarget Contract to call after approval\\n * @param callValue Native ETH to send with the call (e.g. LayerZero fee)\\n * @param callData Encoded function call for the target\\n */\\n function approveAndCall(\\n address token,\\n address spender,\\n uint256 approveAmount,\\n address callTarget,\\n uint256 callValue,\\n bytes calldata callData\\n ) public onlySelf {\\n (bool approveSuccess, bytes memory approveResult) = token.call(\\n abi.encodeWithSignature(\\\"approve(address,uint256)\\\", spender, approveAmount)\\n );\\n require(approveSuccess && (approveResult.length == 0 || abi.decode(approveResult, (bool))), \\\"Approve failed\\\");\\n\\n (bool callSuccess,) = callTarget.call{value: callValue}(callData);\\n require(callSuccess, \\\"Call failed\\\");\\n }\\n\\n // ============ View Functions ============\\n function getTransactionHash(\\n uint256 _nonce,\\n address to,\\n uint256 value,\\n bytes memory data\\n ) public view returns (bytes32) {\\n return keccak256(abi.encodePacked(address(this), chainId, _nonce, to, value, data));\\n }\\n\\n function getCommitments() external view returns (uint256[] memory) {\\n return commitments;\\n }\\n\\n function getSignersCount() external view returns (uint256) {\\n return commitments.length;\\n }\\n\\n // ============ Internal Functions ============\\n function _verifyProof(bytes32 txHash, ZkProof calldata proof) internal view returns (bool) {\\n uint256 txHashCommitment = poseidonHash2(uint256(txHash), 1);\\n\\n // Public inputs order: tx_hash_commitment, commitment, nullifier\\n bytes memory encodedInputs = abi.encodePacked(txHashCommitment, proof.commitment, proof.nullifier);\\n\\n bytes32 leaf = keccak256(abi.encodePacked(PROVING_SYSTEM_ID, vkHash, VERSION_HASH, keccak256(encodedInputs)));\\n\\n return\\n IVerifyProofAggregation(zkvContract).verifyProofAggregation(\\n proof.domainId,\\n proof.aggregationId,\\n leaf,\\n proof.zkMerklePath,\\n proof.leafCount,\\n proof.index\\n );\\n }\\n\\n function _isCurrentSigner(uint256 commitment) internal view returns (bool) {\\n for (uint256 i = 0; i < commitments.length; i++) {\\n if (commitments[i] == commitment) {\\n return true;\\n }\\n }\\n return false;\\n }\\n\\n // ============ Receive ETH ============\\n receive() external payable {\\n emit Deposit(msg.sender, msg.value, address(this).balance);\\n }\\n\\n function poseidonHash2(uint256 a, uint256 b) public pure returns (uint256) {\\n uint256 safeA = a % BN254_PRIME;\\n uint256 safeB = b % BN254_PRIME;\\n return PoseidonT3.hash([safeA, safeB]);\\n }\\n}\",\"keccak256\":\"0x1c6475525edc40eaffe4746961bee463c1f377aaf2cce563372f1ef687d138a9\",\"license\":\"MIT\"},\"poseidon-solidity/PoseidonT3.sol\":{\"content\":\"/// SPDX-License-Identifier: MIT\\npragma solidity >=0.7.0;\\n\\nlibrary PoseidonT3 {\\n uint constant M00 = 0x109b7f411ba0e4c9b2b70caf5c36a7b194be7c11ad24378bfedb68592ba8118b;\\n uint constant M01 = 0x2969f27eed31a480b9c36c764379dbca2cc8fdd1415c3dded62940bcde0bd771;\\n uint constant M02 = 0x143021ec686a3f330d5f9e654638065ce6cd79e28c5b3753326244ee65a1b1a7;\\n uint constant M10 = 0x16ed41e13bb9c0c66ae119424fddbcbc9314dc9fdbdeea55d6c64543dc4903e0;\\n uint constant M11 = 0x2e2419f9ec02ec394c9871c832963dc1b89d743c8c7b964029b2311687b1fe23;\\n uint constant M12 = 0x176cc029695ad02582a70eff08a6fd99d057e12e58e7d7b6b16cdfabc8ee2911;\\n\\n // See here for a simplified implementation: https://github.com/vimwitch/poseidon-solidity/blob/e57becdabb65d99fdc586fe1e1e09e7108202d53/contracts/Poseidon.sol#L40\\n // Inspired by: https://github.com/iden3/circomlibjs/blob/v0.0.8/src/poseidon_slow.js\\n function hash(uint[2] memory) public pure returns (uint) {\\n assembly {\\n let F := 21888242871839275222246405745257275088548364400416034343698204186575808495617\\n let M20 := 0x2b90bba00fca0589f617e7dcbfe82e0df706ab640ceb247b791a93b74e36736d\\n let M21 := 0x101071f0032379b697315876690f053d148d4e109f5fb065c8aacc55a0f89bfa\\n let M22 := 0x19a3fc0a56702bf417ba7fee3802593fa644470307043f7773279cd71d25d5e0\\n\\n // load the inputs from memory\\n let state1 := add(mod(mload(0x80), F), 0x00f1445235f2148c5986587169fc1bcd887b08d4d00868df5696fff40956e864)\\n let state2 := add(mod(mload(0xa0), F), 0x08dff3487e8ac99e1f29a058d0fa80b930c728730b7ab36ce879f3890ecf73f5)\\n let scratch0 := mulmod(state1, state1, F)\\n state1 := mulmod(mulmod(scratch0, scratch0, F), state1, F)\\n scratch0 := mulmod(state2, state2, F)\\n state2 := mulmod(mulmod(scratch0, scratch0, F), state2, F)\\n scratch0 := add(\\n 0x2f27be690fdaee46c3ce28f7532b13c856c35342c84bda6e20966310fadc01d0,\\n add(add(15452833169820924772166449970675545095234312153403844297388521437673434406763, mulmod(state1, M10, F)), mulmod(state2, M20, F))\\n )\\n let scratch1 := add(\\n 0x2b2ae1acf68b7b8d2416bebf3d4f6234b763fe04b8043ee48b8327bebca16cf2,\\n add(add(18674271267752038776579386132900109523609358935013267566297499497165104279117, mulmod(state1, M11, F)), mulmod(state2, M21, F))\\n )\\n let scratch2 := add(\\n 0x0319d062072bef7ecca5eac06f97d4d55952c175ab6b03eae64b44c7dbf11cfa,\\n add(add(14817777843080276494683266178512808687156649753153012854386334860566696099579, mulmod(state1, M12, F)), mulmod(state2, M22, F))\\n )\\n let state0 := mulmod(scratch0, scratch0, F)\\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\\n state0 := mulmod(scratch1, scratch1, F)\\n scratch1 := mulmod(mulmod(state0, state0, F), scratch1, F)\\n state0 := mulmod(scratch2, scratch2, F)\\n scratch2 := mulmod(mulmod(state0, state0, F), scratch2, F)\\n state0 := add(0x28813dcaebaeaa828a376df87af4a63bc8b7bf27ad49c6298ef7b387bf28526d, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\\n state1 := add(0x2727673b2ccbc903f181bf38e1c1d40d2033865200c352bc150928adddf9cb78, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\\n state2 := add(0x234ec45ca27727c2e74abd2b2a1494cd6efbd43e340587d6b8fb9e31e65cc632, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\\n scratch0 := mulmod(state0, state0, F)\\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\\n scratch0 := mulmod(state1, state1, F)\\n state1 := mulmod(mulmod(scratch0, scratch0, F), state1, F)\\n scratch0 := mulmod(state2, state2, F)\\n state2 := mulmod(mulmod(scratch0, scratch0, F), state2, F)\\n scratch0 := add(0x15b52534031ae18f7f862cb2cf7cf760ab10a8150a337b1ccd99ff6e8797d428, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\\n scratch1 := add(0x0dc8fad6d9e4b35f5ed9a3d186b79ce38e0e8a8d1b58b132d701d4eecf68d1f6, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\\n scratch2 := add(0x1bcd95ffc211fbca600f705fad3fb567ea4eb378f62e1fec97805518a47e4d9c, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\\n state0 := mulmod(scratch0, scratch0, F)\\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\\n state0 := mulmod(scratch1, scratch1, F)\\n scratch1 := mulmod(mulmod(state0, state0, F), scratch1, F)\\n state0 := mulmod(scratch2, scratch2, F)\\n scratch2 := mulmod(mulmod(state0, state0, F), scratch2, F)\\n state0 := add(0x10520b0ab721cadfe9eff81b016fc34dc76da36c2578937817cb978d069de559, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\\n state1 := add(0x1f6d48149b8e7f7d9b257d8ed5fbbaf42932498075fed0ace88a9eb81f5627f6, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\\n state2 := add(0x1d9655f652309014d29e00ef35a2089bfff8dc1c816f0dc9ca34bdb5460c8705, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\\n scratch0 := mulmod(state0, state0, F)\\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\\n scratch0 := add(0x04df5a56ff95bcafb051f7b1cd43a99ba731ff67e47032058fe3d4185697cc7d, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\\n scratch1 := add(0x0672d995f8fff640151b3d290cedaf148690a10a8c8424a7f6ec282b6e4be828, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\\n scratch2 := add(0x099952b414884454b21200d7ffafdd5f0c9a9dcc06f2708e9fc1d8209b5c75b9, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\\n state0 := mulmod(scratch0, scratch0, F)\\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\\n state0 := add(0x052cba2255dfd00c7c483143ba8d469448e43586a9b4cd9183fd0e843a6b9fa6, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\\n state1 := add(0x0b8badee690adb8eb0bd74712b7999af82de55707251ad7716077cb93c464ddc, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\\n state2 := add(0x119b1590f13307af5a1ee651020c07c749c15d60683a8050b963d0a8e4b2bdd1, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\\n scratch0 := mulmod(state0, state0, F)\\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\\n scratch0 := add(0x03150b7cd6d5d17b2529d36be0f67b832c4acfc884ef4ee5ce15be0bfb4a8d09, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\\n scratch1 := add(0x2cc6182c5e14546e3cf1951f173912355374efb83d80898abe69cb317c9ea565, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\\n scratch2 := add(0x005032551e6378c450cfe129a404b3764218cadedac14e2b92d2cd73111bf0f9, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\\n state0 := mulmod(scratch0, scratch0, F)\\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\\n state0 := add(0x233237e3289baa34bb147e972ebcb9516469c399fcc069fb88f9da2cc28276b5, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\\n state1 := add(0x05c8f4f4ebd4a6e3c980d31674bfbe6323037f21b34ae5a4e80c2d4c24d60280, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\\n state2 := add(0x0a7b1db13042d396ba05d818a319f25252bcf35ef3aeed91ee1f09b2590fc65b, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\\n scratch0 := mulmod(state0, state0, F)\\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\\n scratch0 := add(0x2a73b71f9b210cf5b14296572c9d32dbf156e2b086ff47dc5df542365a404ec0, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\\n scratch1 := add(0x1ac9b0417abcc9a1935107e9ffc91dc3ec18f2c4dbe7f22976a760bb5c50c460, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\\n scratch2 := add(0x12c0339ae08374823fabb076707ef479269f3e4d6cb104349015ee046dc93fc0, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\\n state0 := mulmod(scratch0, scratch0, F)\\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\\n state0 := add(0x0b7475b102a165ad7f5b18db4e1e704f52900aa3253baac68246682e56e9a28e, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\\n state1 := add(0x037c2849e191ca3edb1c5e49f6e8b8917c843e379366f2ea32ab3aa88d7f8448, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\\n state2 := add(0x05a6811f8556f014e92674661e217e9bd5206c5c93a07dc145fdb176a716346f, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\\n scratch0 := mulmod(state0, state0, F)\\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\\n scratch0 := add(0x29a795e7d98028946e947b75d54e9f044076e87a7b2883b47b675ef5f38bd66e, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\\n scratch1 := add(0x20439a0c84b322eb45a3857afc18f5826e8c7382c8a1585c507be199981fd22f, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\\n scratch2 := add(0x2e0ba8d94d9ecf4a94ec2050c7371ff1bb50f27799a84b6d4a2a6f2a0982c887, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\\n state0 := mulmod(scratch0, scratch0, F)\\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\\n state0 := add(0x143fd115ce08fb27ca38eb7cce822b4517822cd2109048d2e6d0ddcca17d71c8, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\\n state1 := add(0x0c64cbecb1c734b857968dbbdcf813cdf8611659323dbcbfc84323623be9caf1, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\\n state2 := add(0x028a305847c683f646fca925c163ff5ae74f348d62c2b670f1426cef9403da53, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\\n scratch0 := mulmod(state0, state0, F)\\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\\n scratch0 := add(0x2e4ef510ff0b6fda5fa940ab4c4380f26a6bcb64d89427b824d6755b5db9e30c, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\\n scratch1 := add(0x0081c95bc43384e663d79270c956ce3b8925b4f6d033b078b96384f50579400e, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\\n scratch2 := add(0x2ed5f0c91cbd9749187e2fade687e05ee2491b349c039a0bba8a9f4023a0bb38, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\\n state0 := mulmod(scratch0, scratch0, F)\\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\\n state0 := add(0x30509991f88da3504bbf374ed5aae2f03448a22c76234c8c990f01f33a735206, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\\n state1 := add(0x1c3f20fd55409a53221b7c4d49a356b9f0a1119fb2067b41a7529094424ec6ad, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\\n state2 := add(0x10b4e7f3ab5df003049514459b6e18eec46bb2213e8e131e170887b47ddcb96c, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\\n scratch0 := mulmod(state0, state0, F)\\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\\n scratch0 := add(0x2a1982979c3ff7f43ddd543d891c2abddd80f804c077d775039aa3502e43adef, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\\n scratch1 := add(0x1c74ee64f15e1db6feddbead56d6d55dba431ebc396c9af95cad0f1315bd5c91, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\\n scratch2 := add(0x07533ec850ba7f98eab9303cace01b4b9e4f2e8b82708cfa9c2fe45a0ae146a0, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\\n state0 := mulmod(scratch0, scratch0, F)\\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\\n state0 := add(0x21576b438e500449a151e4eeaf17b154285c68f42d42c1808a11abf3764c0750, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\\n state1 := add(0x2f17c0559b8fe79608ad5ca193d62f10bce8384c815f0906743d6930836d4a9e, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\\n state2 := add(0x2d477e3862d07708a79e8aae946170bc9775a4201318474ae665b0b1b7e2730e, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\\n scratch0 := mulmod(state0, state0, F)\\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\\n scratch0 := add(0x162f5243967064c390e095577984f291afba2266c38f5abcd89be0f5b2747eab, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\\n scratch1 := add(0x2b4cb233ede9ba48264ecd2c8ae50d1ad7a8596a87f29f8a7777a70092393311, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\\n scratch2 := add(0x2c8fbcb2dd8573dc1dbaf8f4622854776db2eece6d85c4cf4254e7c35e03b07a, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\\n state0 := mulmod(scratch0, scratch0, F)\\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\\n state0 := add(0x1d6f347725e4816af2ff453f0cd56b199e1b61e9f601e9ade5e88db870949da9, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\\n state1 := add(0x204b0c397f4ebe71ebc2d8b3df5b913df9e6ac02b68d31324cd49af5c4565529, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\\n state2 := add(0x0c4cb9dc3c4fd8174f1149b3c63c3c2f9ecb827cd7dc25534ff8fb75bc79c502, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\\n scratch0 := mulmod(state0, state0, F)\\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\\n scratch0 := add(0x174ad61a1448c899a25416474f4930301e5c49475279e0639a616ddc45bc7b54, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\\n scratch1 := add(0x1a96177bcf4d8d89f759df4ec2f3cde2eaaa28c177cc0fa13a9816d49a38d2ef, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\\n scratch2 := add(0x066d04b24331d71cd0ef8054bc60c4ff05202c126a233c1a8242ace360b8a30a, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\\n state0 := mulmod(scratch0, scratch0, F)\\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\\n state0 := add(0x2a4c4fc6ec0b0cf52195782871c6dd3b381cc65f72e02ad527037a62aa1bd804, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\\n state1 := add(0x13ab2d136ccf37d447e9f2e14a7cedc95e727f8446f6d9d7e55afc01219fd649, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\\n state2 := add(0x1121552fca26061619d24d843dc82769c1b04fcec26f55194c2e3e869acc6a9a, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\\n scratch0 := mulmod(state0, state0, F)\\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\\n scratch0 := add(0x00ef653322b13d6c889bc81715c37d77a6cd267d595c4a8909a5546c7c97cff1, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\\n scratch1 := add(0x0e25483e45a665208b261d8ba74051e6400c776d652595d9845aca35d8a397d3, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\\n scratch2 := add(0x29f536dcb9dd7682245264659e15d88e395ac3d4dde92d8c46448db979eeba89, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\\n state0 := mulmod(scratch0, scratch0, F)\\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\\n state0 := add(0x2a56ef9f2c53febadfda33575dbdbd885a124e2780bbea170e456baace0fa5be, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\\n state1 := add(0x1c8361c78eb5cf5decfb7a2d17b5c409f2ae2999a46762e8ee416240a8cb9af1, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\\n state2 := add(0x151aff5f38b20a0fc0473089aaf0206b83e8e68a764507bfd3d0ab4be74319c5, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\\n scratch0 := mulmod(state0, state0, F)\\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\\n scratch0 := add(0x04c6187e41ed881dc1b239c88f7f9d43a9f52fc8c8b6cdd1e76e47615b51f100, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\\n scratch1 := add(0x13b37bd80f4d27fb10d84331f6fb6d534b81c61ed15776449e801b7ddc9c2967, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\\n scratch2 := add(0x01a5c536273c2d9df578bfbd32c17b7a2ce3664c2a52032c9321ceb1c4e8a8e4, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\\n state0 := mulmod(scratch0, scratch0, F)\\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\\n state0 := add(0x2ab3561834ca73835ad05f5d7acb950b4a9a2c666b9726da832239065b7c3b02, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\\n state1 := add(0x1d4d8ec291e720db200fe6d686c0d613acaf6af4e95d3bf69f7ed516a597b646, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\\n state2 := add(0x041294d2cc484d228f5784fe7919fd2bb925351240a04b711514c9c80b65af1d, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\\n scratch0 := mulmod(state0, state0, F)\\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\\n scratch0 := add(0x154ac98e01708c611c4fa715991f004898f57939d126e392042971dd90e81fc6, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\\n scratch1 := add(0x0b339d8acca7d4f83eedd84093aef51050b3684c88f8b0b04524563bc6ea4da4, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\\n scratch2 := add(0x0955e49e6610c94254a4f84cfbab344598f0e71eaff4a7dd81ed95b50839c82e, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\\n state0 := mulmod(scratch0, scratch0, F)\\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\\n state0 := add(0x06746a6156eba54426b9e22206f15abca9a6f41e6f535c6f3525401ea0654626, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\\n state1 := add(0x0f18f5a0ecd1423c496f3820c549c27838e5790e2bd0a196ac917c7ff32077fb, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\\n state2 := add(0x04f6eeca1751f7308ac59eff5beb261e4bb563583ede7bc92a738223d6f76e13, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\\n scratch0 := mulmod(state0, state0, F)\\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\\n scratch0 := add(0x2b56973364c4c4f5c1a3ec4da3cdce038811eb116fb3e45bc1768d26fc0b3758, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\\n scratch1 := add(0x123769dd49d5b054dcd76b89804b1bcb8e1392b385716a5d83feb65d437f29ef, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\\n scratch2 := add(0x2147b424fc48c80a88ee52b91169aacea989f6446471150994257b2fb01c63e9, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\\n state0 := mulmod(scratch0, scratch0, F)\\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\\n state0 := add(0x0fdc1f58548b85701a6c5505ea332a29647e6f34ad4243c2ea54ad897cebe54d, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\\n state1 := add(0x12373a8251fea004df68abcf0f7786d4bceff28c5dbbe0c3944f685cc0a0b1f2, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\\n state2 := add(0x21e4f4ea5f35f85bad7ea52ff742c9e8a642756b6af44203dd8a1f35c1a90035, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\\n scratch0 := mulmod(state0, state0, F)\\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\\n scratch0 := add(0x16243916d69d2ca3dfb4722224d4c462b57366492f45e90d8a81934f1bc3b147, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\\n scratch1 := add(0x1efbe46dd7a578b4f66f9adbc88b4378abc21566e1a0453ca13a4159cac04ac2, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\\n scratch2 := add(0x07ea5e8537cf5dd08886020e23a7f387d468d5525be66f853b672cc96a88969a, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\\n state0 := mulmod(scratch0, scratch0, F)\\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\\n state0 := add(0x05a8c4f9968b8aa3b7b478a30f9a5b63650f19a75e7ce11ca9fe16c0b76c00bc, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\\n state1 := add(0x20f057712cc21654fbfe59bd345e8dac3f7818c701b9c7882d9d57b72a32e83f, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\\n state2 := add(0x04a12ededa9dfd689672f8c67fee31636dcd8e88d01d49019bd90b33eb33db69, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\\n scratch0 := mulmod(state0, state0, F)\\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\\n scratch0 := add(0x27e88d8c15f37dcee44f1e5425a51decbd136ce5091a6767e49ec9544ccd101a, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\\n scratch1 := add(0x2feed17b84285ed9b8a5c8c5e95a41f66e096619a7703223176c41ee433de4d1, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\\n scratch2 := add(0x1ed7cc76edf45c7c404241420f729cf394e5942911312a0d6972b8bd53aff2b8, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\\n state0 := mulmod(scratch0, scratch0, F)\\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\\n state0 := add(0x15742e99b9bfa323157ff8c586f5660eac6783476144cdcadf2874be45466b1a, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\\n state1 := add(0x1aac285387f65e82c895fc6887ddf40577107454c6ec0317284f033f27d0c785, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\\n state2 := add(0x25851c3c845d4790f9ddadbdb6057357832e2e7a49775f71ec75a96554d67c77, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\\n scratch0 := mulmod(state0, state0, F)\\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\\n scratch0 := add(0x15a5821565cc2ec2ce78457db197edf353b7ebba2c5523370ddccc3d9f146a67, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\\n scratch1 := add(0x2411d57a4813b9980efa7e31a1db5966dcf64f36044277502f15485f28c71727, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\\n scratch2 := add(0x002e6f8d6520cd4713e335b8c0b6d2e647e9a98e12f4cd2558828b5ef6cb4c9b, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\\n state0 := mulmod(scratch0, scratch0, F)\\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\\n state0 := add(0x2ff7bc8f4380cde997da00b616b0fcd1af8f0e91e2fe1ed7398834609e0315d2, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\\n state1 := add(0x00b9831b948525595ee02724471bcd182e9521f6b7bb68f1e93be4febb0d3cbe, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\\n state2 := add(0x0a2f53768b8ebf6a86913b0e57c04e011ca408648a4743a87d77adbf0c9c3512, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\\n scratch0 := mulmod(state0, state0, F)\\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\\n scratch0 := add(0x00248156142fd0373a479f91ff239e960f599ff7e94be69b7f2a290305e1198d, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\\n scratch1 := add(0x171d5620b87bfb1328cf8c02ab3f0c9a397196aa6a542c2350eb512a2b2bcda9, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\\n scratch2 := add(0x170a4f55536f7dc970087c7c10d6fad760c952172dd54dd99d1045e4ec34a808, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\\n state0 := mulmod(scratch0, scratch0, F)\\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\\n state0 := add(0x29aba33f799fe66c2ef3134aea04336ecc37e38c1cd211ba482eca17e2dbfae1, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\\n state1 := add(0x1e9bc179a4fdd758fdd1bb1945088d47e70d114a03f6a0e8b5ba650369e64973, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\\n state2 := add(0x1dd269799b660fad58f7f4892dfb0b5afeaad869a9c4b44f9c9e1c43bdaf8f09, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\\n scratch0 := mulmod(state0, state0, F)\\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\\n scratch0 := add(0x22cdbc8b70117ad1401181d02e15459e7ccd426fe869c7c95d1dd2cb0f24af38, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\\n scratch1 := add(0x0ef042e454771c533a9f57a55c503fcefd3150f52ed94a7cd5ba93b9c7dacefd, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\\n scratch2 := add(0x11609e06ad6c8fe2f287f3036037e8851318e8b08a0359a03b304ffca62e8284, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\\n state0 := mulmod(scratch0, scratch0, F)\\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\\n state0 := add(0x1166d9e554616dba9e753eea427c17b7fecd58c076dfe42708b08f5b783aa9af, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\\n state1 := add(0x2de52989431a859593413026354413db177fbf4cd2ac0b56f855a888357ee466, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\\n state2 := add(0x3006eb4ffc7a85819a6da492f3a8ac1df51aee5b17b8e89d74bf01cf5f71e9ad, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\\n scratch0 := mulmod(state0, state0, F)\\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\\n scratch0 := add(0x2af41fbb61ba8a80fdcf6fff9e3f6f422993fe8f0a4639f962344c8225145086, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\\n scratch1 := add(0x119e684de476155fe5a6b41a8ebc85db8718ab27889e85e781b214bace4827c3, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\\n scratch2 := add(0x1835b786e2e8925e188bea59ae363537b51248c23828f047cff784b97b3fd800, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\\n state0 := mulmod(scratch0, scratch0, F)\\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\\n state0 := add(0x28201a34c594dfa34d794996c6433a20d152bac2a7905c926c40e285ab32eeb6, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\\n state1 := add(0x083efd7a27d1751094e80fefaf78b000864c82eb571187724a761f88c22cc4e7, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\\n state2 := add(0x0b6f88a3577199526158e61ceea27be811c16df7774dd8519e079564f61fd13b, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\\n scratch0 := mulmod(state0, state0, F)\\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\\n scratch0 := add(0x0ec868e6d15e51d9644f66e1d6471a94589511ca00d29e1014390e6ee4254f5b, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\\n scratch1 := add(0x2af33e3f866771271ac0c9b3ed2e1142ecd3e74b939cd40d00d937ab84c98591, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\\n scratch2 := add(0x0b520211f904b5e7d09b5d961c6ace7734568c547dd6858b364ce5e47951f178, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\\n state0 := mulmod(scratch0, scratch0, F)\\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\\n state0 := add(0x0b2d722d0919a1aad8db58f10062a92ea0c56ac4270e822cca228620188a1d40, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\\n state1 := add(0x1f790d4d7f8cf094d980ceb37c2453e957b54a9991ca38bbe0061d1ed6e562d4, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\\n state2 := add(0x0171eb95dfbf7d1eaea97cd385f780150885c16235a2a6a8da92ceb01e504233, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\\n scratch0 := mulmod(state0, state0, F)\\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\\n scratch0 := add(0x0c2d0e3b5fd57549329bf6885da66b9b790b40defd2c8650762305381b168873, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\\n scratch1 := add(0x1162fb28689c27154e5a8228b4e72b377cbcafa589e283c35d3803054407a18d, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\\n scratch2 := add(0x2f1459b65dee441b64ad386a91e8310f282c5a92a89e19921623ef8249711bc0, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\\n state0 := mulmod(scratch0, scratch0, F)\\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\\n state0 := add(0x1e6ff3216b688c3d996d74367d5cd4c1bc489d46754eb712c243f70d1b53cfbb, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\\n state1 := add(0x01ca8be73832b8d0681487d27d157802d741a6f36cdc2a0576881f9326478875, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\\n state2 := add(0x1f7735706ffe9fc586f976d5bdf223dc680286080b10cea00b9b5de315f9650e, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\\n scratch0 := mulmod(state0, state0, F)\\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\\n scratch0 := add(0x2522b60f4ea3307640a0c2dce041fba921ac10a3d5f096ef4745ca838285f019, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\\n scratch1 := add(0x23f0bee001b1029d5255075ddc957f833418cad4f52b6c3f8ce16c235572575b, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\\n scratch2 := add(0x2bc1ae8b8ddbb81fcaac2d44555ed5685d142633e9df905f66d9401093082d59, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\\n state0 := mulmod(scratch0, scratch0, F)\\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\\n state0 := add(0x0f9406b8296564a37304507b8dba3ed162371273a07b1fc98011fcd6ad72205f, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\\n state1 := add(0x2360a8eb0cc7defa67b72998de90714e17e75b174a52ee4acb126c8cd995f0a8, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\\n state2 := add(0x15871a5cddead976804c803cbaef255eb4815a5e96df8b006dcbbc2767f88948, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\\n scratch0 := mulmod(state0, state0, F)\\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\\n scratch0 := add(0x193a56766998ee9e0a8652dd2f3b1da0362f4f54f72379544f957ccdeefb420f, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\\n scratch1 := add(0x2a394a43934f86982f9be56ff4fab1703b2e63c8ad334834e4309805e777ae0f, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\\n scratch2 := add(0x1859954cfeb8695f3e8b635dcb345192892cd11223443ba7b4166e8876c0d142, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\\n state0 := mulmod(scratch0, scratch0, F)\\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\\n state0 := add(0x04e1181763050e58013444dbcb99f1902b11bc25d90bbdca408d3819f4fed32b, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\\n state1 := add(0x0fdb253dee83869d40c335ea64de8c5bb10eb82db08b5e8b1f5e5552bfd05f23, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\\n state2 := add(0x058cbe8a9a5027bdaa4efb623adead6275f08686f1c08984a9d7c5bae9b4f1c0, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\\n scratch0 := mulmod(state0, state0, F)\\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\\n scratch0 := add(0x1382edce9971e186497eadb1aeb1f52b23b4b83bef023ab0d15228b4cceca59a, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\\n scratch1 := add(0x03464990f045c6ee0819ca51fd11b0be7f61b8eb99f14b77e1e6634601d9e8b5, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\\n scratch2 := add(0x23f7bfc8720dc296fff33b41f98ff83c6fcab4605db2eb5aaa5bc137aeb70a58, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\\n state0 := mulmod(scratch0, scratch0, F)\\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\\n state0 := add(0x0a59a158e3eec2117e6e94e7f0e9decf18c3ffd5e1531a9219636158bbaf62f2, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\\n state1 := add(0x06ec54c80381c052b58bf23b312ffd3ce2c4eba065420af8f4c23ed0075fd07b, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\\n state2 := add(0x118872dc832e0eb5476b56648e867ec8b09340f7a7bcb1b4962f0ff9ed1f9d01, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\\n scratch0 := mulmod(state0, state0, F)\\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\\n scratch0 := add(0x13d69fa127d834165ad5c7cba7ad59ed52e0b0f0e42d7fea95e1906b520921b1, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\\n scratch1 := add(0x169a177f63ea681270b1c6877a73d21bde143942fb71dc55fd8a49f19f10c77b, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\\n scratch2 := add(0x04ef51591c6ead97ef42f287adce40d93abeb032b922f66ffb7e9a5a7450544d, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\\n state0 := mulmod(scratch0, scratch0, F)\\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\\n state0 := add(0x256e175a1dc079390ecd7ca703fb2e3b19ec61805d4f03ced5f45ee6dd0f69ec, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\\n state1 := add(0x30102d28636abd5fe5f2af412ff6004f75cc360d3205dd2da002813d3e2ceeb2, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\\n state2 := add(0x10998e42dfcd3bbf1c0714bc73eb1bf40443a3fa99bef4a31fd31be182fcc792, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\\n scratch0 := mulmod(state0, state0, F)\\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\\n scratch0 := add(0x193edd8e9fcf3d7625fa7d24b598a1d89f3362eaf4d582efecad76f879e36860, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\\n scratch1 := add(0x18168afd34f2d915d0368ce80b7b3347d1c7a561ce611425f2664d7aa51f0b5d, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\\n scratch2 := add(0x29383c01ebd3b6ab0c017656ebe658b6a328ec77bc33626e29e2e95b33ea6111, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\\n state0 := mulmod(scratch0, scratch0, F)\\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\\n state0 := add(0x10646d2f2603de39a1f4ae5e7771a64a702db6e86fb76ab600bf573f9010c711, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\\n state1 := add(0x0beb5e07d1b27145f575f1395a55bf132f90c25b40da7b3864d0242dcb1117fb, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\\n state2 := add(0x16d685252078c133dc0d3ecad62b5c8830f95bb2e54b59abdffbf018d96fa336, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\\n scratch0 := mulmod(state0, state0, F)\\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\\n scratch0 := add(0x0a6abd1d833938f33c74154e0404b4b40a555bbbec21ddfafd672dd62047f01a, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\\n scratch1 := add(0x1a679f5d36eb7b5c8ea12a4c2dedc8feb12dffeec450317270a6f19b34cf1860, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\\n scratch2 := add(0x0980fb233bd456c23974d50e0ebfde4726a423eada4e8f6ffbc7592e3f1b93d6, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\\n state0 := mulmod(scratch0, scratch0, F)\\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\\n state0 := add(0x161b42232e61b84cbf1810af93a38fc0cece3d5628c9282003ebacb5c312c72b, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\\n state1 := add(0x0ada10a90c7f0520950f7d47a60d5e6a493f09787f1564e5d09203db47de1a0b, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\\n state2 := add(0x1a730d372310ba82320345a29ac4238ed3f07a8a2b4e121bb50ddb9af407f451, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\\n scratch0 := mulmod(state0, state0, F)\\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\\n scratch0 := add(0x2c8120f268ef054f817064c369dda7ea908377feaba5c4dffbda10ef58e8c556, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\\n scratch1 := add(0x1c7c8824f758753fa57c00789c684217b930e95313bcb73e6e7b8649a4968f70, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\\n scratch2 := add(0x2cd9ed31f5f8691c8e39e4077a74faa0f400ad8b491eb3f7b47b27fa3fd1cf77, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\\n state0 := mulmod(scratch0, scratch0, F)\\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\\n state0 := add(0x23ff4f9d46813457cf60d92f57618399a5e022ac321ca550854ae23918a22eea, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\\n state1 := add(0x09945a5d147a4f66ceece6405dddd9d0af5a2c5103529407dff1ea58f180426d, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\\n state2 := add(0x188d9c528025d4c2b67660c6b771b90f7c7da6eaa29d3f268a6dd223ec6fc630, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\\n scratch0 := mulmod(state0, state0, F)\\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\\n scratch0 := add(0x3050e37996596b7f81f68311431d8734dba7d926d3633595e0c0d8ddf4f0f47f, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\\n scratch1 := add(0x15af1169396830a91600ca8102c35c426ceae5461e3f95d89d829518d30afd78, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\\n scratch2 := add(0x1da6d09885432ea9a06d9f37f873d985dae933e351466b2904284da3320d8acc, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\\n state0 := mulmod(scratch0, scratch0, F)\\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\\n state0 := add(0x2796ea90d269af29f5f8acf33921124e4e4fad3dbe658945e546ee411ddaa9cb, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\\n state1 := add(0x202d7dd1da0f6b4b0325c8b3307742f01e15612ec8e9304a7cb0319e01d32d60, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\\n state2 := add(0x096d6790d05bb759156a952ba263d672a2d7f9c788f4c831a29dace4c0f8be5f, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\\n scratch0 := mulmod(state0, state0, F)\\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\\n scratch0 := add(0x054efa1f65b0fce283808965275d877b438da23ce5b13e1963798cb1447d25a4, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\\n scratch1 := add(0x1b162f83d917e93edb3308c29802deb9d8aa690113b2e14864ccf6e18e4165f1, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\\n scratch2 := add(0x21e5241e12564dd6fd9f1cdd2a0de39eedfefc1466cc568ec5ceb745a0506edc, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\\n state0 := mulmod(scratch0, scratch0, F)\\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\\n state0 := mulmod(scratch1, scratch1, F)\\n scratch1 := mulmod(mulmod(state0, state0, F), scratch1, F)\\n state0 := mulmod(scratch2, scratch2, F)\\n scratch2 := mulmod(mulmod(state0, state0, F), scratch2, F)\\n state0 := add(0x1cfb5662e8cf5ac9226a80ee17b36abecb73ab5f87e161927b4349e10e4bdf08, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\\n state1 := add(0x0f21177e302a771bbae6d8d1ecb373b62c99af346220ac0129c53f666eb24100, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\\n state2 := add(0x1671522374606992affb0dd7f71b12bec4236aede6290546bcef7e1f515c2320, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\\n scratch0 := mulmod(state0, state0, F)\\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\\n scratch0 := mulmod(state1, state1, F)\\n state1 := mulmod(mulmod(scratch0, scratch0, F), state1, F)\\n scratch0 := mulmod(state2, state2, F)\\n state2 := mulmod(mulmod(scratch0, scratch0, F), state2, F)\\n scratch0 := add(0x0fa3ec5b9488259c2eb4cf24501bfad9be2ec9e42c5cc8ccd419d2a692cad870, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\\n scratch1 := add(0x193c0e04e0bd298357cb266c1506080ed36edce85c648cc085e8c57b1ab54bba, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\\n scratch2 := add(0x102adf8ef74735a27e9128306dcbc3c99f6f7291cd406578ce14ea2adaba68f8, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\\n state0 := mulmod(scratch0, scratch0, F)\\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\\n state0 := mulmod(scratch1, scratch1, F)\\n scratch1 := mulmod(mulmod(state0, state0, F), scratch1, F)\\n state0 := mulmod(scratch2, scratch2, F)\\n scratch2 := mulmod(mulmod(state0, state0, F), scratch2, F)\\n state0 := add(0x0fe0af7858e49859e2a54d6f1ad945b1316aa24bfbdd23ae40a6d0cb70c3eab1, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\\n state1 := add(0x216f6717bbc7dedb08536a2220843f4e2da5f1daa9ebdefde8a5ea7344798d22, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\\n state2 := add(0x1da55cc900f0d21f4a3e694391918a1b3c23b2ac773c6b3ef88e2e4228325161, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\\n scratch0 := mulmod(state0, state0, F)\\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\\n scratch0 := mulmod(state1, state1, F)\\n state1 := mulmod(mulmod(scratch0, scratch0, F), state1, F)\\n scratch0 := mulmod(state2, state2, F)\\n state2 := mulmod(mulmod(scratch0, scratch0, F), state2, F)\\n\\n mstore(0x0, mod(add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)), F))\\n\\n return(0, 0x20)\\n }\\n }\\n}\\n\",\"keccak256\":\"0x0102caa303bbc6690508f3615604f7730789ed990058c9513a87ccb30e4835be\",\"license\":\"MIT\"}},\"version\":1}", + "bytecode": "0x60c06040523480156200001157600080fd5b5060405162002721380380620027218339810160408190526200003491620002db565b6001600160a01b038516620000905760405162461bcd60e51b815260206004820152601360248201527f496e76616c6964207a6b7620616464726573730000000000000000000000000060448201526064015b60405180910390fd5b60008111620000e25760405162461bcd60e51b815260206004820152601e60248201527f4d757374206265206e6f6e2d7a65726f20736967732072657175697265640000604482015260640162000087565b6000825111620001355760405162461bcd60e51b815260206004820152601660248201527f4e656564206174206c656173742031207369676e657200000000000000000000604482015260640162000087565b8151811115620001885760405162461bcd60e51b815260206004820152601660248201527f5369677320726571756972656420746f6f206869676800000000000000000000604482015260640162000087565b6001600160a01b03851660805260a0849052600083815560018290555b8251811015620002b957828181518110620001c457620001c4620003e6565b6020026020010151600003620002125760405162461bcd60e51b8152602060048201526012602482015271125b9d985b1a590818dbdb5b5a5d1b595b9d60721b604482015260640162000087565b6002838281518110620002295762000229620003e6565b6020908102919091018101518254600181018455600093845291909220015582518390829081106200025f576200025f620003e6565b60200260200101517f33129f9e36e06e860f22ce83276273f4d83d31e9fd4a0d3a4dbac9d2da4f9d0d60016040516200029c911515815260200190565b60405180910390a280620002b081620003fc565b915050620001a5565b50505050505062000424565b634e487b7160e01b600052604160045260246000fd5b600080600080600060a08688031215620002f457600080fd5b85516001600160a01b03811681146200030c57600080fd5b602087810151604089015160608a01519398509096509450906001600160401b03808211156200033b57600080fd5b818901915089601f8301126200035057600080fd5b815181811115620003655762000365620002c5565b8060051b604051601f19603f830116810181811085821117156200038d576200038d620002c5565b60405291825284820192508381018501918c831115620003ac57600080fd5b938501935b82851015620003cc57845184529385019392850192620003b1565b809750505050505050608086015190509295509295909350565b634e487b7160e01b600052603260045260246000fd5b6000600182016200041d57634e487b7160e01b600052601160045260246000fd5b5060010190565b60805160a0516122c9620004586000396000818161024c0152611872015260008181610300015261192a01526122c96000f3fe60806040526004361061012e5760003560e01c806388d695b2116100ab578063a8d2c8521161006f578063a8d2c852146103dc578063aad24061146103fc578063ce757d291461042c578063e4cf5a2c14610442578063eaaba55914610476578063f1ea66d41461049657600080fd5b806388d695b21461033a5780639a8a05921461035a5780639e4e731814610370578063a0c1deb414610385578063a8898a201461039a57600080fd5b80634fe840f5116100f25780634fe840f51461023a578063545a4a3c1461026e578063644512121461028e5780636717e41c146102ae5780637ee68373146102ee57600080fd5b80631b108c07146101745780633034a742146101aa5780633a624581146101cc5780634791ca34146101ec57806349ce89971461020c57600080fd5b3661016f576040805134815247602082015233917f90890809c654f11d6e72a28fa60149770a0d11ec6c92319d6ceb2bb0a4ea1a15910160405180910390a2005b600080fd5b34801561018057600080fd5b5061019461018f366004611a93565b6104b8565b6040516101a19190611b77565b60405180910390f35b3480156101b657600080fd5b506101ca6101c5366004611b91565b610860565b005b3480156101d857600080fd5b506101ca6101e7366004611baa565b6108c6565b3480156101f857600080fd5b506101ca610207366004611c20565b610a92565b34801561021857600080fd5b5061022c610227366004611b91565b610d20565b6040519081526020016101a1565b34801561024657600080fd5b5061022c7f000000000000000000000000000000000000000000000000000000000000000081565b34801561027a57600080fd5b5061022c610289366004611c82565b610d41565b34801561029a57600080fd5b506101ca6102a9366004611d57565b610d7e565b3480156102ba57600080fd5b506102de6102c9366004611b91565b60036020526000908152604090205460ff1681565b60405190151581526020016101a1565b3480156102fa57600080fd5b506103227f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020016101a1565b34801561034657600080fd5b506101ca610355366004611df1565b61113b565b34801561036657600080fd5b5061022c60005481565b34801561037c57600080fd5b5061022c61131f565b34801561039157600080fd5b5060025461022c565b3480156103a657600080fd5b5061022c60405169756c747261706c6f6e6b60b01b6020820152602a016040516020818303038152906040528051906020012081565b3480156103e857600080fd5b506101ca6103f7366004611c20565b61137f565b34801561040857600080fd5b506102de610417366004611b91565b60046020526000908152604090205460ff1681565b34801561043857600080fd5b5061022c60015481565b34801561044e57600080fd5b5061022c7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181565b34801561048257600080fd5b5061022c610491366004611e5d565b611655565b3480156104a257600080fd5b506104ab611746565b6040516101a19190611e7f565b60008781526003602052604090205460609060ff16156105145760405162461bcd60e51b8152602060048201526012602482015271139bdb98d948185b1c9958591e481d5cd95960721b60448201526064015b60405180910390fd5b60015482101561055a5760405162461bcd60e51b81526020600482015260116024820152704e6f7420656e6f7567682070726f6f667360781b604482015260640161050b565b600080546040516105799130918c908c908c908c908c90602001611ec3565b60405160208183030381529060405280519060200120905060005b8381101561075a57600460008686848181106105b2576105b2611f15565b90506020028101906105c49190611f2b565b60209081013582528101919091526040016000205460ff16156106225760405162461bcd60e51b8152602060048201526016602482015275139d5b1b1a599a595c88185b1c9958591e481d5cd95960521b604482015260640161050b565b61064f85858381811061063757610637611f15565b90506020028101906106499190611f2b565b3561179e565b6106925760405162461bcd60e51b81526020600482015260146024820152732737ba10309031bab93932b73a1039b4b3b732b960611b604482015260640161050b565b6106bf828686848181106106a8576106a8611f15565b90506020028101906106ba9190611f2b565b6117f4565b6106fb5760405162461bcd60e51b815260206004820152600d60248201526c24b73b30b634b210383937b7b360991b604482015260640161050b565b60016004600087878581811061071357610713611f15565b90506020028101906107259190611f2b565b6020908101358252810191909152604001600020805460ff19169115159190911790558061075281611f61565b915050610594565b50600089815260036020526040808220805460ff191660011790555181906001600160a01b038b16908a90610792908b908b90611f7a565b60006040518083038185875af1925050503d80600081146107cf576040519150601f19603f3d011682016040523d82523d6000602084013e6107d4565b606091505b5091509150816108125760405162461bcd60e51b8152602060048201526009602482015268151e0819985a5b195960ba1b604482015260640161050b565b8a7f1654479f61781d185c419742a20e599a227ae1840317c8a74ceda5eb6166b8268b8b8b8b8660405161084a959493929190611f8a565b60405180910390a29a9950505050505050505050565b33301461087f5760405162461bcd60e51b815260040161050b90611feb565b6000811161089f5760405162461bcd60e51b815260040161050b9061200d565b6002548111156108c15760405162461bcd60e51b815260040161050b90612044565b600155565b3330146108e55760405162461bcd60e51b815260040161050b90611feb565b6040516001600160a01b0387811660248301526044820187905260009182918a169060640160408051601f198184030181529181526020820180516001600160e01b031663095ea7b360e01b1790525161093f9190612074565b6000604051808303816000865af19150503d806000811461097c576040519150601f19603f3d011682016040523d82523d6000602084013e610981565b606091505b50915091508180156109ab5750805115806109ab5750808060200190518101906109ab9190612086565b6109e85760405162461bcd60e51b815260206004820152600e60248201526d105c1c1c9bdd994819985a5b195960921b604482015260640161050b565b6000866001600160a01b0316868686604051610a05929190611f7a565b60006040518083038185875af1925050503d8060008114610a42576040519150601f19603f3d011682016040523d82523d6000602084013e610a47565b606091505b5050905080610a865760405162461bcd60e51b815260206004820152600b60248201526a10d85b1b0819985a5b195960aa1b604482015260640161050b565b50505050505050505050565b333014610ab15760405162461bcd60e51b815260040161050b90611feb565b81610aec5760405162461bcd60e51b815260206004820152600b60248201526a456d70747920617272617960a81b604482015260640161050b565b6002548210610b3d5760405162461bcd60e51b815260206004820152601960248201527f43616e6e6f742072656d6f766520616c6c207369676e65727300000000000000604482015260640161050b565b60008111610b5d5760405162461bcd60e51b815260040161050b9061200d565b600254610b6b9083906120a8565b811115610b8a5760405162461bcd60e51b815260040161050b90612044565b60005b82811015610d18576000805b600254811015610cc057858584818110610bb557610bb5611f15565b9050602002013560028281548110610bcf57610bcf611f15565b906000526020600020015403610cae5760028054610bef906001906120a8565b81548110610bff57610bff611f15565b906000526020600020015460028281548110610c1d57610c1d611f15565b6000918252602090912001556002805480610c3a57610c3a6120bb565b6001900381819060005260206000200160009055905560019150858584818110610c6657610c66611f15565b905060200201357f33129f9e36e06e860f22ce83276273f4d83d31e9fd4a0d3a4dbac9d2da4f9d0d6000604051610ca1911515815260200190565b60405180910390a2610cc0565b80610cb881611f61565b915050610b99565b5080610d055760405162461bcd60e51b815260206004820152601460248201527310dbdb5b5a5d1b595b9d081b9bdd08199bdd5b9960621b604482015260640161050b565b5080610d1081611f61565b915050610b8d565b506001555050565b60028181548110610d3057600080fd5b600091825260209091200154905081565b60008054604051610d5e91309188908890889088906020016120d1565b604051602081830303815290604052805190602001209050949350505050565b333014610d9d5760405162461bcd60e51b815260040161050b90611feb565b848314610dbc5760405162461bcd60e51b815260040161050b9061212c565b848114610ddb5760405162461bcd60e51b815260040161050b9061212c565b84610e165760405162461bcd60e51b815260206004820152600b60248201526a08adae0e8f240c4c2e8c6d60ab1b604482015260640161050b565b60005b85811015611132576000878783818110610e3557610e35611f15565b9050602002016020810190610e4a9190612155565b6001600160a01b031603610e945760405162461bcd60e51b8152602060048201526011602482015270125b9d985b1a59081c9958da5c1a595b9d607a1b604482015260640161050b565b6000838383818110610ea857610ea8611f15565b9050602002016020810190610ebd9190612155565b6001600160a01b031603610fa8576000878783818110610edf57610edf611f15565b9050602002016020810190610ef49190612155565b6001600160a01b0316868684818110610f0f57610f0f611f15565b9050602002013560405160006040518083038185875af1925050503d8060008114610f56576040519150601f19603f3d011682016040523d82523d6000602084013e610f5b565b606091505b5050905080610fa25760405162461bcd60e51b8152602060048201526013602482015272115512081d1c985b9cd9995c8819985a5b1959606a1b604482015260640161050b565b50611120565b600080848484818110610fbd57610fbd611f15565b9050602002016020810190610fd29190612155565b6001600160a01b0316898985818110610fed57610fed611f15565b90506020020160208101906110029190612155565b88888681811061101457611014611f15565b6040516001600160a01b039094166024850152602002919091013560448301525060640160408051601f198184030181529181526020820180516001600160e01b031663a9059cbb60e01b1790525161106d9190612074565b6000604051808303816000865af19150503d80600081146110aa576040519150601f19603f3d011682016040523d82523d6000602084013e6110af565b606091505b50915091508180156110d95750805115806110d95750808060200190518101906110d99190612086565b61111d5760405162461bcd60e51b8152602060048201526015602482015274115490cc8c081d1c985b9cd9995c8819985a5b1959605a1b604482015260640161050b565b50505b8061112a81611f61565b915050610e19565b50505050505050565b33301461115a5760405162461bcd60e51b815260040161050b90611feb565b8281146111795760405162461bcd60e51b815260040161050b9061212c565b826111b45760405162461bcd60e51b815260206004820152600b60248201526a08adae0e8f240c4c2e8c6d60ab1b604482015260640161050b565b60005b838110156113185760008585838181106111d3576111d3611f15565b90506020020160208101906111e89190612155565b6001600160a01b0316036112325760405162461bcd60e51b8152602060048201526011602482015270125b9d985b1a59081c9958da5c1a595b9d607a1b604482015260640161050b565b600085858381811061124657611246611f15565b905060200201602081019061125b9190612155565b6001600160a01b031684848481811061127657611276611f15565b9050602002013560405160006040518083038185875af1925050503d80600081146112bd576040519150601f19603f3d011682016040523d82523d6000602084013e6112c2565b606091505b50509050806113055760405162461bcd60e51b815260206004820152600f60248201526e151c985b9cd9995c8819985a5b1959608a1b604482015260640161050b565b508061131081611f61565b9150506111b7565b5050505050565b6040805160008152602081019182905260029161133c9190612074565b602060405180830381855afa158015611359573d6000803e3d6000fd5b5050506040513d601f19601f8201168201806040525081019061137c9190612170565b81565b33301461139e5760405162461bcd60e51b815260040161050b90611feb565b816113d95760405162461bcd60e51b815260206004820152600b60248201526a456d70747920617272617960a81b604482015260640161050b565b600081116113f95760405162461bcd60e51b815260040161050b9061200d565b600254611407908390612189565b8111156114265760405162461bcd60e51b815260040161050b90612044565b60005b82811015610d185783838281811061144357611443611f15565b9050602002013560000361148e5760405162461bcd60e51b8152602060048201526012602482015271125b9d985b1a590818dbdb5b5a5d1b595b9d60721b604482015260640161050b565b60005b600254811015611525578484838181106114ad576114ad611f15565b90506020020135600282815481106114c7576114c7611f15565b9060005260206000200154036115135760405162461bcd60e51b8152602060048201526011602482015270436f6d6d69746d656e742065786973747360781b604482015260640161050b565b8061151d81611f61565b915050611491565b5060005b818110156115b75784848381811061154357611543611f15565b9050602002013585858381811061155c5761155c611f15565b90506020020135036115a55760405162461bcd60e51b8152602060048201526012602482015271111d5c1b1a58d85d19481a5b881a5b9c1d5d60721b604482015260640161050b565b806115af81611f61565b915050611529565b5060028484838181106115cc576115cc611f15565b8354600181018555600094855260209485902091909402929092013591909201555083838281811061160057611600611f15565b905060200201357f33129f9e36e06e860f22ce83276273f4d83d31e9fd4a0d3a4dbac9d2da4f9d0d600160405161163b911515815260200190565b60405180910390a28061164d81611f61565b915050611429565b6000806116827f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018561219c565b905060006116b07f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018561219c565b604080518082018252848152602081018390529051632b0aac7f60e11b8152919250733333333C0A88F9BE4fd23ed0536F9B6c427e3B939163561558fe916116fa916004016121be565b602060405180830381865af4158015611717573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061173b9190612170565b925050505b92915050565b6060600280548060200260200160405190810160405280929190818152602001828054801561179457602002820191906000526020600020905b815481526020019060010190808311611780575b5050505050905090565b6000805b6002548110156117eb5782600282815481106117c0576117c0611f15565b9060005260206000200154036117d95750600192915050565b806117e381611f61565b9150506117a2565b50600092915050565b600080611802846001611655565b604080516020808201849052863582840152868101356060808401919091528351808403909101815260808301845269756c747261706c6f6e6b60b01b60a08401528351608a81850301815260aa840180865281519190930120600080845260ca909401948590529495509391927f00000000000000000000000000000000000000000000000000000000000000009160029161189f9190612074565b602060405180830381855afa1580156118bc573d6000803e3d6000fd5b5050506040513d601f19601f820116820180604052508101906118df9190612170565b8480519060200120604051602001611910949392919093845260208401929092526040830152606082015260800190565b6040516020818303038152906040528051906020012090507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663a78f9e36866060013587604001358489806080019061197291906121ef565b8b60a001358c60c001356040518863ffffffff1660e01b815260040161199e9796959493929190612239565b602060405180830381865afa1580156119bb573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119df9190612086565b9695505050505050565b80356001600160a01b0381168114611a0057600080fd5b919050565b60008083601f840112611a1757600080fd5b50813567ffffffffffffffff811115611a2f57600080fd5b602083019150836020828501011115611a4757600080fd5b9250929050565b60008083601f840112611a6057600080fd5b50813567ffffffffffffffff811115611a7857600080fd5b6020830191508360208260051b8501011115611a4757600080fd5b600080600080600080600060a0888a031215611aae57600080fd5b87359650611abe602089016119e9565b955060408801359450606088013567ffffffffffffffff80821115611ae257600080fd5b611aee8b838c01611a05565b909650945060808a0135915080821115611b0757600080fd5b50611b148a828b01611a4e565b989b979a50959850939692959293505050565b60005b83811015611b42578181015183820152602001611b2a565b50506000910152565b60008151808452611b63816020860160208601611b27565b601f01601f19169290920160200192915050565b602081526000611b8a6020830184611b4b565b9392505050565b600060208284031215611ba357600080fd5b5035919050565b600080600080600080600060c0888a031215611bc557600080fd5b611bce886119e9565b9650611bdc602089016119e9565b955060408801359450611bf1606089016119e9565b93506080880135925060a088013567ffffffffffffffff811115611c1457600080fd5b611b148a828b01611a05565b600080600060408486031215611c3557600080fd5b833567ffffffffffffffff811115611c4c57600080fd5b611c5886828701611a4e565b909790965060209590950135949350505050565b634e487b7160e01b600052604160045260246000fd5b60008060008060808587031215611c9857600080fd5b84359350611ca8602086016119e9565b925060408501359150606085013567ffffffffffffffff80821115611ccc57600080fd5b818701915087601f830112611ce057600080fd5b813581811115611cf257611cf2611c6c565b604051601f8201601f19908116603f01168101908382118183101715611d1a57611d1a611c6c565b816040528281528a6020848701011115611d3357600080fd5b82602086016020830137600060208483010152809550505050505092959194509250565b60008060008060008060608789031215611d7057600080fd5b863567ffffffffffffffff80821115611d8857600080fd5b611d948a838b01611a4e565b90985096506020890135915080821115611dad57600080fd5b611db98a838b01611a4e565b90965094506040890135915080821115611dd257600080fd5b50611ddf89828a01611a4e565b979a9699509497509295939492505050565b60008060008060408587031215611e0757600080fd5b843567ffffffffffffffff80821115611e1f57600080fd5b611e2b88838901611a4e565b90965094506020870135915080821115611e4457600080fd5b50611e5187828801611a4e565b95989497509550505050565b60008060408385031215611e7057600080fd5b50508035926020909101359150565b6020808252825182820181905260009190848201906040850190845b81811015611eb757835183529284019291840191600101611e9b565b50909695505050505050565b60006bffffffffffffffffffffffff19808a60601b168352886014840152876034840152808760601b166054840152508460688301528284608884013750600091016088019081529695505050505050565b634e487b7160e01b600052603260045260246000fd5b6000823560de19833603018112611f4157600080fd5b9190910192915050565b634e487b7160e01b600052601160045260246000fd5b600060018201611f7357611f73611f4b565b5060010190565b8183823760009101908152919050565b6001600160a01b0386168152602081018590526080604082018190528101839052828460a0830137600060a084830101526000601f19601f850116820160a0838203016060840152611fdf60a0820185611b4b565b98975050505050505050565b6020808252600890820152672737ba1029b2b63360c11b604082015260600190565b6020808252601e908201527f4d757374206265206e6f6e2d7a65726f20736967732072657175697265640000604082015260600190565b6020808252601690820152750a6d2cee640e4cae2ead2e4cac840e8dede40d0d2ced60531b604082015260600190565b60008251611f41818460208701611b27565b60006020828403121561209857600080fd5b81518015158114611b8a57600080fd5b8181038181111561174057611740611f4b565b634e487b7160e01b600052603160045260246000fd5b60006bffffffffffffffffffffffff19808960601b168352876014840152866034840152808660601b16605484015250836068830152825161211a816088850160208701611b27565b91909101608801979650505050505050565b6020808252600f908201526e098cadccee8d040dad2e6dac2e8c6d608b1b604082015260600190565b60006020828403121561216757600080fd5b611b8a826119e9565b60006020828403121561218257600080fd5b5051919050565b8082018082111561174057611740611f4b565b6000826121b957634e487b7160e01b600052601260045260246000fd5b500690565b60408101818360005b60028110156121e65781518352602092830192909101906001016121c7565b50505092915050565b6000808335601e1984360301811261220657600080fd5b83018035915067ffffffffffffffff82111561222157600080fd5b6020019150600581901b3603821315611a4757600080fd5b87815286602082015285604082015260c060608201528360c0820152600060018060fb1b0385111561226a57600080fd5b8460051b808760e085013760808301949094525060a08101919091520160e0019594505050505056fea2646970667358221220addee73ee726353c6fb61be647551027bfa7622664ea8abd7c75b400a386170364736f6c63430008140033", + "deployedBytecode": "0x60806040526004361061012e5760003560e01c806388d695b2116100ab578063a8d2c8521161006f578063a8d2c852146103dc578063aad24061146103fc578063ce757d291461042c578063e4cf5a2c14610442578063eaaba55914610476578063f1ea66d41461049657600080fd5b806388d695b21461033a5780639a8a05921461035a5780639e4e731814610370578063a0c1deb414610385578063a8898a201461039a57600080fd5b80634fe840f5116100f25780634fe840f51461023a578063545a4a3c1461026e578063644512121461028e5780636717e41c146102ae5780637ee68373146102ee57600080fd5b80631b108c07146101745780633034a742146101aa5780633a624581146101cc5780634791ca34146101ec57806349ce89971461020c57600080fd5b3661016f576040805134815247602082015233917f90890809c654f11d6e72a28fa60149770a0d11ec6c92319d6ceb2bb0a4ea1a15910160405180910390a2005b600080fd5b34801561018057600080fd5b5061019461018f366004611a93565b6104b8565b6040516101a19190611b77565b60405180910390f35b3480156101b657600080fd5b506101ca6101c5366004611b91565b610860565b005b3480156101d857600080fd5b506101ca6101e7366004611baa565b6108c6565b3480156101f857600080fd5b506101ca610207366004611c20565b610a92565b34801561021857600080fd5b5061022c610227366004611b91565b610d20565b6040519081526020016101a1565b34801561024657600080fd5b5061022c7f000000000000000000000000000000000000000000000000000000000000000081565b34801561027a57600080fd5b5061022c610289366004611c82565b610d41565b34801561029a57600080fd5b506101ca6102a9366004611d57565b610d7e565b3480156102ba57600080fd5b506102de6102c9366004611b91565b60036020526000908152604090205460ff1681565b60405190151581526020016101a1565b3480156102fa57600080fd5b506103227f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020016101a1565b34801561034657600080fd5b506101ca610355366004611df1565b61113b565b34801561036657600080fd5b5061022c60005481565b34801561037c57600080fd5b5061022c61131f565b34801561039157600080fd5b5060025461022c565b3480156103a657600080fd5b5061022c60405169756c747261706c6f6e6b60b01b6020820152602a016040516020818303038152906040528051906020012081565b3480156103e857600080fd5b506101ca6103f7366004611c20565b61137f565b34801561040857600080fd5b506102de610417366004611b91565b60046020526000908152604090205460ff1681565b34801561043857600080fd5b5061022c60015481565b34801561044e57600080fd5b5061022c7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181565b34801561048257600080fd5b5061022c610491366004611e5d565b611655565b3480156104a257600080fd5b506104ab611746565b6040516101a19190611e7f565b60008781526003602052604090205460609060ff16156105145760405162461bcd60e51b8152602060048201526012602482015271139bdb98d948185b1c9958591e481d5cd95960721b60448201526064015b60405180910390fd5b60015482101561055a5760405162461bcd60e51b81526020600482015260116024820152704e6f7420656e6f7567682070726f6f667360781b604482015260640161050b565b600080546040516105799130918c908c908c908c908c90602001611ec3565b60405160208183030381529060405280519060200120905060005b8381101561075a57600460008686848181106105b2576105b2611f15565b90506020028101906105c49190611f2b565b60209081013582528101919091526040016000205460ff16156106225760405162461bcd60e51b8152602060048201526016602482015275139d5b1b1a599a595c88185b1c9958591e481d5cd95960521b604482015260640161050b565b61064f85858381811061063757610637611f15565b90506020028101906106499190611f2b565b3561179e565b6106925760405162461bcd60e51b81526020600482015260146024820152732737ba10309031bab93932b73a1039b4b3b732b960611b604482015260640161050b565b6106bf828686848181106106a8576106a8611f15565b90506020028101906106ba9190611f2b565b6117f4565b6106fb5760405162461bcd60e51b815260206004820152600d60248201526c24b73b30b634b210383937b7b360991b604482015260640161050b565b60016004600087878581811061071357610713611f15565b90506020028101906107259190611f2b565b6020908101358252810191909152604001600020805460ff19169115159190911790558061075281611f61565b915050610594565b50600089815260036020526040808220805460ff191660011790555181906001600160a01b038b16908a90610792908b908b90611f7a565b60006040518083038185875af1925050503d80600081146107cf576040519150601f19603f3d011682016040523d82523d6000602084013e6107d4565b606091505b5091509150816108125760405162461bcd60e51b8152602060048201526009602482015268151e0819985a5b195960ba1b604482015260640161050b565b8a7f1654479f61781d185c419742a20e599a227ae1840317c8a74ceda5eb6166b8268b8b8b8b8660405161084a959493929190611f8a565b60405180910390a29a9950505050505050505050565b33301461087f5760405162461bcd60e51b815260040161050b90611feb565b6000811161089f5760405162461bcd60e51b815260040161050b9061200d565b6002548111156108c15760405162461bcd60e51b815260040161050b90612044565b600155565b3330146108e55760405162461bcd60e51b815260040161050b90611feb565b6040516001600160a01b0387811660248301526044820187905260009182918a169060640160408051601f198184030181529181526020820180516001600160e01b031663095ea7b360e01b1790525161093f9190612074565b6000604051808303816000865af19150503d806000811461097c576040519150601f19603f3d011682016040523d82523d6000602084013e610981565b606091505b50915091508180156109ab5750805115806109ab5750808060200190518101906109ab9190612086565b6109e85760405162461bcd60e51b815260206004820152600e60248201526d105c1c1c9bdd994819985a5b195960921b604482015260640161050b565b6000866001600160a01b0316868686604051610a05929190611f7a565b60006040518083038185875af1925050503d8060008114610a42576040519150601f19603f3d011682016040523d82523d6000602084013e610a47565b606091505b5050905080610a865760405162461bcd60e51b815260206004820152600b60248201526a10d85b1b0819985a5b195960aa1b604482015260640161050b565b50505050505050505050565b333014610ab15760405162461bcd60e51b815260040161050b90611feb565b81610aec5760405162461bcd60e51b815260206004820152600b60248201526a456d70747920617272617960a81b604482015260640161050b565b6002548210610b3d5760405162461bcd60e51b815260206004820152601960248201527f43616e6e6f742072656d6f766520616c6c207369676e65727300000000000000604482015260640161050b565b60008111610b5d5760405162461bcd60e51b815260040161050b9061200d565b600254610b6b9083906120a8565b811115610b8a5760405162461bcd60e51b815260040161050b90612044565b60005b82811015610d18576000805b600254811015610cc057858584818110610bb557610bb5611f15565b9050602002013560028281548110610bcf57610bcf611f15565b906000526020600020015403610cae5760028054610bef906001906120a8565b81548110610bff57610bff611f15565b906000526020600020015460028281548110610c1d57610c1d611f15565b6000918252602090912001556002805480610c3a57610c3a6120bb565b6001900381819060005260206000200160009055905560019150858584818110610c6657610c66611f15565b905060200201357f33129f9e36e06e860f22ce83276273f4d83d31e9fd4a0d3a4dbac9d2da4f9d0d6000604051610ca1911515815260200190565b60405180910390a2610cc0565b80610cb881611f61565b915050610b99565b5080610d055760405162461bcd60e51b815260206004820152601460248201527310dbdb5b5a5d1b595b9d081b9bdd08199bdd5b9960621b604482015260640161050b565b5080610d1081611f61565b915050610b8d565b506001555050565b60028181548110610d3057600080fd5b600091825260209091200154905081565b60008054604051610d5e91309188908890889088906020016120d1565b604051602081830303815290604052805190602001209050949350505050565b333014610d9d5760405162461bcd60e51b815260040161050b90611feb565b848314610dbc5760405162461bcd60e51b815260040161050b9061212c565b848114610ddb5760405162461bcd60e51b815260040161050b9061212c565b84610e165760405162461bcd60e51b815260206004820152600b60248201526a08adae0e8f240c4c2e8c6d60ab1b604482015260640161050b565b60005b85811015611132576000878783818110610e3557610e35611f15565b9050602002016020810190610e4a9190612155565b6001600160a01b031603610e945760405162461bcd60e51b8152602060048201526011602482015270125b9d985b1a59081c9958da5c1a595b9d607a1b604482015260640161050b565b6000838383818110610ea857610ea8611f15565b9050602002016020810190610ebd9190612155565b6001600160a01b031603610fa8576000878783818110610edf57610edf611f15565b9050602002016020810190610ef49190612155565b6001600160a01b0316868684818110610f0f57610f0f611f15565b9050602002013560405160006040518083038185875af1925050503d8060008114610f56576040519150601f19603f3d011682016040523d82523d6000602084013e610f5b565b606091505b5050905080610fa25760405162461bcd60e51b8152602060048201526013602482015272115512081d1c985b9cd9995c8819985a5b1959606a1b604482015260640161050b565b50611120565b600080848484818110610fbd57610fbd611f15565b9050602002016020810190610fd29190612155565b6001600160a01b0316898985818110610fed57610fed611f15565b90506020020160208101906110029190612155565b88888681811061101457611014611f15565b6040516001600160a01b039094166024850152602002919091013560448301525060640160408051601f198184030181529181526020820180516001600160e01b031663a9059cbb60e01b1790525161106d9190612074565b6000604051808303816000865af19150503d80600081146110aa576040519150601f19603f3d011682016040523d82523d6000602084013e6110af565b606091505b50915091508180156110d95750805115806110d95750808060200190518101906110d99190612086565b61111d5760405162461bcd60e51b8152602060048201526015602482015274115490cc8c081d1c985b9cd9995c8819985a5b1959605a1b604482015260640161050b565b50505b8061112a81611f61565b915050610e19565b50505050505050565b33301461115a5760405162461bcd60e51b815260040161050b90611feb565b8281146111795760405162461bcd60e51b815260040161050b9061212c565b826111b45760405162461bcd60e51b815260206004820152600b60248201526a08adae0e8f240c4c2e8c6d60ab1b604482015260640161050b565b60005b838110156113185760008585838181106111d3576111d3611f15565b90506020020160208101906111e89190612155565b6001600160a01b0316036112325760405162461bcd60e51b8152602060048201526011602482015270125b9d985b1a59081c9958da5c1a595b9d607a1b604482015260640161050b565b600085858381811061124657611246611f15565b905060200201602081019061125b9190612155565b6001600160a01b031684848481811061127657611276611f15565b9050602002013560405160006040518083038185875af1925050503d80600081146112bd576040519150601f19603f3d011682016040523d82523d6000602084013e6112c2565b606091505b50509050806113055760405162461bcd60e51b815260206004820152600f60248201526e151c985b9cd9995c8819985a5b1959608a1b604482015260640161050b565b508061131081611f61565b9150506111b7565b5050505050565b6040805160008152602081019182905260029161133c9190612074565b602060405180830381855afa158015611359573d6000803e3d6000fd5b5050506040513d601f19601f8201168201806040525081019061137c9190612170565b81565b33301461139e5760405162461bcd60e51b815260040161050b90611feb565b816113d95760405162461bcd60e51b815260206004820152600b60248201526a456d70747920617272617960a81b604482015260640161050b565b600081116113f95760405162461bcd60e51b815260040161050b9061200d565b600254611407908390612189565b8111156114265760405162461bcd60e51b815260040161050b90612044565b60005b82811015610d185783838281811061144357611443611f15565b9050602002013560000361148e5760405162461bcd60e51b8152602060048201526012602482015271125b9d985b1a590818dbdb5b5a5d1b595b9d60721b604482015260640161050b565b60005b600254811015611525578484838181106114ad576114ad611f15565b90506020020135600282815481106114c7576114c7611f15565b9060005260206000200154036115135760405162461bcd60e51b8152602060048201526011602482015270436f6d6d69746d656e742065786973747360781b604482015260640161050b565b8061151d81611f61565b915050611491565b5060005b818110156115b75784848381811061154357611543611f15565b9050602002013585858381811061155c5761155c611f15565b90506020020135036115a55760405162461bcd60e51b8152602060048201526012602482015271111d5c1b1a58d85d19481a5b881a5b9c1d5d60721b604482015260640161050b565b806115af81611f61565b915050611529565b5060028484838181106115cc576115cc611f15565b8354600181018555600094855260209485902091909402929092013591909201555083838281811061160057611600611f15565b905060200201357f33129f9e36e06e860f22ce83276273f4d83d31e9fd4a0d3a4dbac9d2da4f9d0d600160405161163b911515815260200190565b60405180910390a28061164d81611f61565b915050611429565b6000806116827f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018561219c565b905060006116b07f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018561219c565b604080518082018252848152602081018390529051632b0aac7f60e11b815291925073__$75f79a42d9bcbdbb69ad79ebd80f556f39$__9163561558fe916116fa916004016121be565b602060405180830381865af4158015611717573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061173b9190612170565b925050505b92915050565b6060600280548060200260200160405190810160405280929190818152602001828054801561179457602002820191906000526020600020905b815481526020019060010190808311611780575b5050505050905090565b6000805b6002548110156117eb5782600282815481106117c0576117c0611f15565b9060005260206000200154036117d95750600192915050565b806117e381611f61565b9150506117a2565b50600092915050565b600080611802846001611655565b604080516020808201849052863582840152868101356060808401919091528351808403909101815260808301845269756c747261706c6f6e6b60b01b60a08401528351608a81850301815260aa840180865281519190930120600080845260ca909401948590529495509391927f00000000000000000000000000000000000000000000000000000000000000009160029161189f9190612074565b602060405180830381855afa1580156118bc573d6000803e3d6000fd5b5050506040513d601f19601f820116820180604052508101906118df9190612170565b8480519060200120604051602001611910949392919093845260208401929092526040830152606082015260800190565b6040516020818303038152906040528051906020012090507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663a78f9e36866060013587604001358489806080019061197291906121ef565b8b60a001358c60c001356040518863ffffffff1660e01b815260040161199e9796959493929190612239565b602060405180830381865afa1580156119bb573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119df9190612086565b9695505050505050565b80356001600160a01b0381168114611a0057600080fd5b919050565b60008083601f840112611a1757600080fd5b50813567ffffffffffffffff811115611a2f57600080fd5b602083019150836020828501011115611a4757600080fd5b9250929050565b60008083601f840112611a6057600080fd5b50813567ffffffffffffffff811115611a7857600080fd5b6020830191508360208260051b8501011115611a4757600080fd5b600080600080600080600060a0888a031215611aae57600080fd5b87359650611abe602089016119e9565b955060408801359450606088013567ffffffffffffffff80821115611ae257600080fd5b611aee8b838c01611a05565b909650945060808a0135915080821115611b0757600080fd5b50611b148a828b01611a4e565b989b979a50959850939692959293505050565b60005b83811015611b42578181015183820152602001611b2a565b50506000910152565b60008151808452611b63816020860160208601611b27565b601f01601f19169290920160200192915050565b602081526000611b8a6020830184611b4b565b9392505050565b600060208284031215611ba357600080fd5b5035919050565b600080600080600080600060c0888a031215611bc557600080fd5b611bce886119e9565b9650611bdc602089016119e9565b955060408801359450611bf1606089016119e9565b93506080880135925060a088013567ffffffffffffffff811115611c1457600080fd5b611b148a828b01611a05565b600080600060408486031215611c3557600080fd5b833567ffffffffffffffff811115611c4c57600080fd5b611c5886828701611a4e565b909790965060209590950135949350505050565b634e487b7160e01b600052604160045260246000fd5b60008060008060808587031215611c9857600080fd5b84359350611ca8602086016119e9565b925060408501359150606085013567ffffffffffffffff80821115611ccc57600080fd5b818701915087601f830112611ce057600080fd5b813581811115611cf257611cf2611c6c565b604051601f8201601f19908116603f01168101908382118183101715611d1a57611d1a611c6c565b816040528281528a6020848701011115611d3357600080fd5b82602086016020830137600060208483010152809550505050505092959194509250565b60008060008060008060608789031215611d7057600080fd5b863567ffffffffffffffff80821115611d8857600080fd5b611d948a838b01611a4e565b90985096506020890135915080821115611dad57600080fd5b611db98a838b01611a4e565b90965094506040890135915080821115611dd257600080fd5b50611ddf89828a01611a4e565b979a9699509497509295939492505050565b60008060008060408587031215611e0757600080fd5b843567ffffffffffffffff80821115611e1f57600080fd5b611e2b88838901611a4e565b90965094506020870135915080821115611e4457600080fd5b50611e5187828801611a4e565b95989497509550505050565b60008060408385031215611e7057600080fd5b50508035926020909101359150565b6020808252825182820181905260009190848201906040850190845b81811015611eb757835183529284019291840191600101611e9b565b50909695505050505050565b60006bffffffffffffffffffffffff19808a60601b168352886014840152876034840152808760601b166054840152508460688301528284608884013750600091016088019081529695505050505050565b634e487b7160e01b600052603260045260246000fd5b6000823560de19833603018112611f4157600080fd5b9190910192915050565b634e487b7160e01b600052601160045260246000fd5b600060018201611f7357611f73611f4b565b5060010190565b8183823760009101908152919050565b6001600160a01b0386168152602081018590526080604082018190528101839052828460a0830137600060a084830101526000601f19601f850116820160a0838203016060840152611fdf60a0820185611b4b565b98975050505050505050565b6020808252600890820152672737ba1029b2b63360c11b604082015260600190565b6020808252601e908201527f4d757374206265206e6f6e2d7a65726f20736967732072657175697265640000604082015260600190565b6020808252601690820152750a6d2cee640e4cae2ead2e4cac840e8dede40d0d2ced60531b604082015260600190565b60008251611f41818460208701611b27565b60006020828403121561209857600080fd5b81518015158114611b8a57600080fd5b8181038181111561174057611740611f4b565b634e487b7160e01b600052603160045260246000fd5b60006bffffffffffffffffffffffff19808960601b168352876014840152866034840152808660601b16605484015250836068830152825161211a816088850160208701611b27565b91909101608801979650505050505050565b6020808252600f908201526e098cadccee8d040dad2e6dac2e8c6d608b1b604082015260600190565b60006020828403121561216757600080fd5b611b8a826119e9565b60006020828403121561218257600080fd5b5051919050565b8082018082111561174057611740611f4b565b6000826121b957634e487b7160e01b600052601260045260246000fd5b500690565b60408101818360005b60028110156121e65781518352602092830192909101906001016121c7565b50505092915050565b6000808335601e1984360301811261220657600080fd5b83018035915067ffffffffffffffff82111561222157600080fd5b6020019150600581901b3603821315611a4757600080fd5b87815286602082015285604082015260c060608201528360c0820152600060018060fb1b0385111561226a57600080fd5b8460051b808760e085013760808301949094525060a08101919091520160e0019594505050505056fea2646970667358221220addee73ee726353c6fb61be647551027bfa7622664ea8abd7c75b400a386170364736f6c63430008140033", "libraries": { "PoseidonT3": "0x3333333C0A88F9BE4fd23ed0536F9B6c427e3B93" }, "devdoc": { "kind": "dev", "methods": { + "approveAndCall(address,address,uint256,address,uint256,bytes)": { + "params": { + "approveAmount": "Amount to approve", + "callData": "Encoded function call for the target", + "callTarget": "Contract to call after approval", + "callValue": "Native ETH to send with the call (e.g. LayerZero fee)", + "spender": "Address to approve spending", + "token": "ERC20 token to approve" + } + }, "batchTransfer(address[],uint256[])": { "params": { "amounts": "Array of amounts to send", @@ -599,6 +647,9 @@ "userdoc": { "kind": "user", "methods": { + "approveAndCall(address,address,uint256,address,uint256,bytes)": { + "notice": "Approve an ERC20 token and call a target contract in one atomic operation. Used for cross-chain bridge flows (e.g. OFT Adapter: approve + send)." + }, "batchTransfer(address[],uint256[])": { "notice": "Execute multiple transfers in one transaction" }, diff --git a/packages/hardhat/deployments/horizenTestnet/solcInputs/6b4f1b22cbd21b116f152fb4a836f175.json b/packages/hardhat/deployments/horizenTestnet/solcInputs/6b4f1b22cbd21b116f152fb4a836f175.json new file mode 100644 index 00000000..66e54b6f --- /dev/null +++ b/packages/hardhat/deployments/horizenTestnet/solcInputs/6b4f1b22cbd21b116f152fb4a836f175.json @@ -0,0 +1,39 @@ +{ + "language": "Solidity", + "sources": { + "contracts/MetaMultiSigWallet.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.13;\n\nimport \"poseidon-solidity/PoseidonT3.sol\";\n\ninterface IVerifyProofAggregation {\n function verifyProofAggregation(\n uint256 _domainId,\n uint256 _aggregationId,\n bytes32 _leaf,\n bytes32[] calldata _merklePath,\n uint256 _leafCount,\n uint256 _index\n ) external view returns (bool);\n}\n\ncontract MetaMultiSigWallet {\n // ============ Constants ============\n bytes32 public constant PROVING_SYSTEM_ID = keccak256(abi.encodePacked(\"ultraplonk\"));\n bytes32 public constant VERSION_HASH = sha256(abi.encodePacked(\"\"));\n uint256 public constant BN254_PRIME = 21888242871839275222246405745257275088548364400416034343698204186575808495617;\n\n // ============ Events ============\n event Deposit(address indexed sender, uint256 amount, uint256 balance);\n event TransactionExecuted(uint256 indexed nonce, address to, uint256 value, bytes data, bytes result);\n event Owner(uint256 indexed commitment, bool isAdded);\n\n // ============ Structs ============\n struct ZkProof {\n uint256 commitment;\n uint256 nullifier;\n uint256 aggregationId;\n uint256 domainId;\n bytes32[] zkMerklePath;\n uint256 leafCount;\n uint256 index;\n }\n\n // ============ State ============\n address public immutable zkvContract;\n bytes32 public immutable vkHash;\n uint256 public chainId;\n uint256 public signaturesRequired;\n\n // Signer management\n uint256[] public commitments;\n\n // Nonce tracking (prevent replay)\n mapping(uint256 => bool) public usedNonces;\n\n // Nullifier tracking (prevent double-signing)\n mapping(uint256 => bool) public usedNullifiers;\n\n // ============ Constructor ============\n constructor(\n address _zkvContract,\n bytes32 _vkHash,\n uint256 _chainId,\n uint256[] memory _initialCommitments,\n uint256 _signaturesRequired\n ) {\n require(_zkvContract != address(0), \"Invalid zkv address\");\n require(_signaturesRequired > 0, \"Must be non-zero sigs required\");\n require(_initialCommitments.length > 0, \"Need at least 1 signer\");\n require(_signaturesRequired <= _initialCommitments.length, \"Sigs required too high\");\n\n zkvContract = _zkvContract;\n vkHash = _vkHash;\n chainId = _chainId;\n signaturesRequired = _signaturesRequired;\n\n for (uint256 i = 0; i < _initialCommitments.length; i++) {\n require(_initialCommitments[i] != 0, \"Invalid commitment\");\n commitments.push(_initialCommitments[i]);\n emit Owner(_initialCommitments[i], true);\n }\n }\n\n // ============ Modifiers ============\n modifier onlySelf() {\n require(msg.sender == address(this), \"Not Self\");\n _;\n }\n\n // ============ Main Execute Function ============\n function execute(\n uint256 _nonce,\n address to,\n uint256 value,\n bytes calldata data,\n ZkProof[] calldata proofs\n ) external returns (bytes memory) {\n require(!usedNonces[_nonce], \"Nonce already used\");\n require(proofs.length >= signaturesRequired, \"Not enough proofs\");\n\n bytes32 txHash = keccak256(abi.encodePacked(address(this), chainId, _nonce, to, value, data));\n\n for (uint256 i = 0; i < proofs.length; i++) {\n require(!usedNullifiers[proofs[i].nullifier], \"Nullifier already used\");\n require(_isCurrentSigner(proofs[i].commitment), \"Not a current signer\");\n require(_verifyProof(txHash, proofs[i]), \"Invalid proof\");\n usedNullifiers[proofs[i].nullifier] = true;\n }\n\n usedNonces[_nonce] = true;\n\n (bool success, bytes memory result) = to.call{ value: value }(data);\n require(success, \"Tx failed\");\n\n emit TransactionExecuted(_nonce, to, value, data, result);\n return result;\n }\n\n // ============ Signer Management ============\n function addSigners(uint256[] calldata newCommitments, uint256 newSigRequired) public onlySelf {\n require(newCommitments.length > 0, \"Empty array\");\n require(newSigRequired > 0, \"Must be non-zero sigs required\");\n require(newSigRequired <= commitments.length + newCommitments.length, \"Sigs required too high\");\n\n for (uint256 i = 0; i < newCommitments.length; i++) {\n require(newCommitments[i] != 0, \"Invalid commitment\");\n \n // Check duplicate with existing signers\n for (uint256 j = 0; j < commitments.length; j++) {\n require(commitments[j] != newCommitments[i], \"Commitment exists\");\n }\n \n // Check duplicate within input array\n for (uint256 k = 0; k < i; k++) {\n require(newCommitments[k] != newCommitments[i], \"Duplicate in input\");\n }\n\n commitments.push(newCommitments[i]);\n emit Owner(newCommitments[i], true);\n }\n\n signaturesRequired = newSigRequired;\n }\n\n function removeSigners(uint256[] calldata commitmentsToRemove, uint256 newSigRequired) public onlySelf {\n require(commitmentsToRemove.length > 0, \"Empty array\");\n require(commitments.length > commitmentsToRemove.length, \"Cannot remove all signers\");\n require(newSigRequired > 0, \"Must be non-zero sigs required\");\n require(newSigRequired <= commitments.length - commitmentsToRemove.length, \"Sigs required too high\");\n\n for (uint256 i = 0; i < commitmentsToRemove.length; i++) {\n bool found = false;\n for (uint256 j = 0; j < commitments.length; j++) {\n if (commitments[j] == commitmentsToRemove[i]) {\n commitments[j] = commitments[commitments.length - 1];\n commitments.pop();\n found = true;\n emit Owner(commitmentsToRemove[i], false);\n break;\n }\n }\n require(found, \"Commitment not found\");\n }\n\n signaturesRequired = newSigRequired;\n }\n\n function updateSignaturesRequired(uint256 newSigRequired) public onlySelf {\n require(newSigRequired > 0, \"Must be non-zero sigs required\");\n require(newSigRequired <= commitments.length, \"Sigs required too high\");\n signaturesRequired = newSigRequired;\n }\n\n /**\n * @notice Execute multiple transfers in one transaction\n * @param recipients Array of recipient addresses\n * @param amounts Array of amounts to send\n */\n function batchTransfer(address[] calldata recipients, uint256[] calldata amounts) public onlySelf {\n require(recipients.length == amounts.length, \"Length mismatch\");\n require(recipients.length > 0, \"Empty batch\");\n\n for (uint256 i = 0; i < recipients.length; i++) {\n require(recipients[i] != address(0), \"Invalid recipient\");\n (bool success, ) = recipients[i].call{ value: amounts[i] }(\"\");\n require(success, \"Transfer failed\");\n }\n }\n\n /**\n * @notice Execute multiple transfers with mixed token types\n * @param recipients Array of recipient addresses\n * @param amounts Array of amounts to send\n * @param tokenAddresses Array of token addresses (address(0) = native ETH)\n */\n function batchTransferMulti(\n address[] calldata recipients,\n uint256[] calldata amounts,\n address[] calldata tokenAddresses\n ) public onlySelf {\n require(recipients.length == amounts.length, \"Length mismatch\");\n require(recipients.length == tokenAddresses.length, \"Length mismatch\");\n require(recipients.length > 0, \"Empty batch\");\n\n for (uint256 i = 0; i < recipients.length; i++) {\n require(recipients[i] != address(0), \"Invalid recipient\");\n\n if (tokenAddresses[i] == address(0)) {\n // Native ETH transfer\n (bool success, ) = recipients[i].call{ value: amounts[i] }(\"\");\n require(success, \"ETH transfer failed\");\n } else {\n // ERC20 transfer\n (bool success, bytes memory data) = tokenAddresses[i].call(\n abi.encodeWithSignature(\"transfer(address,uint256)\", recipients[i], amounts[i])\n );\n require(success && (data.length == 0 || abi.decode(data, (bool))), \"ERC20 transfer failed\");\n }\n }\n }\n\n /**\n * @notice Approve an ERC20 token and call a target contract in one atomic operation.\n * Used for cross-chain bridge flows (e.g. OFT Adapter: approve + send).\n * @param token ERC20 token to approve\n * @param spender Address to approve spending\n * @param approveAmount Amount to approve\n * @param callTarget Contract to call after approval\n * @param callValue Native ETH to send with the call (e.g. LayerZero fee)\n * @param callData Encoded function call for the target\n */\n function approveAndCall(\n address token,\n address spender,\n uint256 approveAmount,\n address callTarget,\n uint256 callValue,\n bytes calldata callData\n ) public onlySelf {\n (bool approveSuccess, bytes memory approveResult) = token.call(\n abi.encodeWithSignature(\"approve(address,uint256)\", spender, approveAmount)\n );\n require(approveSuccess && (approveResult.length == 0 || abi.decode(approveResult, (bool))), \"Approve failed\");\n\n (bool callSuccess,) = callTarget.call{value: callValue}(callData);\n require(callSuccess, \"Call failed\");\n }\n\n // ============ View Functions ============\n function getTransactionHash(\n uint256 _nonce,\n address to,\n uint256 value,\n bytes memory data\n ) public view returns (bytes32) {\n return keccak256(abi.encodePacked(address(this), chainId, _nonce, to, value, data));\n }\n\n function getCommitments() external view returns (uint256[] memory) {\n return commitments;\n }\n\n function getSignersCount() external view returns (uint256) {\n return commitments.length;\n }\n\n // ============ Internal Functions ============\n function _verifyProof(bytes32 txHash, ZkProof calldata proof) internal view returns (bool) {\n uint256 txHashCommitment = poseidonHash2(uint256(txHash), 1);\n\n // Public inputs order: tx_hash_commitment, commitment, nullifier\n bytes memory encodedInputs = abi.encodePacked(txHashCommitment, proof.commitment, proof.nullifier);\n\n bytes32 leaf = keccak256(abi.encodePacked(PROVING_SYSTEM_ID, vkHash, VERSION_HASH, keccak256(encodedInputs)));\n\n return\n IVerifyProofAggregation(zkvContract).verifyProofAggregation(\n proof.domainId,\n proof.aggregationId,\n leaf,\n proof.zkMerklePath,\n proof.leafCount,\n proof.index\n );\n }\n\n function _isCurrentSigner(uint256 commitment) internal view returns (bool) {\n for (uint256 i = 0; i < commitments.length; i++) {\n if (commitments[i] == commitment) {\n return true;\n }\n }\n return false;\n }\n\n // ============ Receive ETH ============\n receive() external payable {\n emit Deposit(msg.sender, msg.value, address(this).balance);\n }\n\n function poseidonHash2(uint256 a, uint256 b) public pure returns (uint256) {\n uint256 safeA = a % BN254_PRIME;\n uint256 safeB = b % BN254_PRIME;\n return PoseidonT3.hash([safeA, safeB]);\n }\n}" + }, + "poseidon-solidity/PoseidonT3.sol": { + "content": "/// SPDX-License-Identifier: MIT\npragma solidity >=0.7.0;\n\nlibrary PoseidonT3 {\n uint constant M00 = 0x109b7f411ba0e4c9b2b70caf5c36a7b194be7c11ad24378bfedb68592ba8118b;\n uint constant M01 = 0x2969f27eed31a480b9c36c764379dbca2cc8fdd1415c3dded62940bcde0bd771;\n uint constant M02 = 0x143021ec686a3f330d5f9e654638065ce6cd79e28c5b3753326244ee65a1b1a7;\n uint constant M10 = 0x16ed41e13bb9c0c66ae119424fddbcbc9314dc9fdbdeea55d6c64543dc4903e0;\n uint constant M11 = 0x2e2419f9ec02ec394c9871c832963dc1b89d743c8c7b964029b2311687b1fe23;\n uint constant M12 = 0x176cc029695ad02582a70eff08a6fd99d057e12e58e7d7b6b16cdfabc8ee2911;\n\n // See here for a simplified implementation: https://github.com/vimwitch/poseidon-solidity/blob/e57becdabb65d99fdc586fe1e1e09e7108202d53/contracts/Poseidon.sol#L40\n // Inspired by: https://github.com/iden3/circomlibjs/blob/v0.0.8/src/poseidon_slow.js\n function hash(uint[2] memory) public pure returns (uint) {\n assembly {\n let F := 21888242871839275222246405745257275088548364400416034343698204186575808495617\n let M20 := 0x2b90bba00fca0589f617e7dcbfe82e0df706ab640ceb247b791a93b74e36736d\n let M21 := 0x101071f0032379b697315876690f053d148d4e109f5fb065c8aacc55a0f89bfa\n let M22 := 0x19a3fc0a56702bf417ba7fee3802593fa644470307043f7773279cd71d25d5e0\n\n // load the inputs from memory\n let state1 := add(mod(mload(0x80), F), 0x00f1445235f2148c5986587169fc1bcd887b08d4d00868df5696fff40956e864)\n let state2 := add(mod(mload(0xa0), F), 0x08dff3487e8ac99e1f29a058d0fa80b930c728730b7ab36ce879f3890ecf73f5)\n let scratch0 := mulmod(state1, state1, F)\n state1 := mulmod(mulmod(scratch0, scratch0, F), state1, F)\n scratch0 := mulmod(state2, state2, F)\n state2 := mulmod(mulmod(scratch0, scratch0, F), state2, F)\n scratch0 := add(\n 0x2f27be690fdaee46c3ce28f7532b13c856c35342c84bda6e20966310fadc01d0,\n add(add(15452833169820924772166449970675545095234312153403844297388521437673434406763, mulmod(state1, M10, F)), mulmod(state2, M20, F))\n )\n let scratch1 := add(\n 0x2b2ae1acf68b7b8d2416bebf3d4f6234b763fe04b8043ee48b8327bebca16cf2,\n add(add(18674271267752038776579386132900109523609358935013267566297499497165104279117, mulmod(state1, M11, F)), mulmod(state2, M21, F))\n )\n let scratch2 := add(\n 0x0319d062072bef7ecca5eac06f97d4d55952c175ab6b03eae64b44c7dbf11cfa,\n add(add(14817777843080276494683266178512808687156649753153012854386334860566696099579, mulmod(state1, M12, F)), mulmod(state2, M22, F))\n )\n let state0 := mulmod(scratch0, scratch0, F)\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\n state0 := mulmod(scratch1, scratch1, F)\n scratch1 := mulmod(mulmod(state0, state0, F), scratch1, F)\n state0 := mulmod(scratch2, scratch2, F)\n scratch2 := mulmod(mulmod(state0, state0, F), scratch2, F)\n state0 := add(0x28813dcaebaeaa828a376df87af4a63bc8b7bf27ad49c6298ef7b387bf28526d, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\n state1 := add(0x2727673b2ccbc903f181bf38e1c1d40d2033865200c352bc150928adddf9cb78, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\n state2 := add(0x234ec45ca27727c2e74abd2b2a1494cd6efbd43e340587d6b8fb9e31e65cc632, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\n scratch0 := mulmod(state0, state0, F)\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\n scratch0 := mulmod(state1, state1, F)\n state1 := mulmod(mulmod(scratch0, scratch0, F), state1, F)\n scratch0 := mulmod(state2, state2, F)\n state2 := mulmod(mulmod(scratch0, scratch0, F), state2, F)\n scratch0 := add(0x15b52534031ae18f7f862cb2cf7cf760ab10a8150a337b1ccd99ff6e8797d428, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\n scratch1 := add(0x0dc8fad6d9e4b35f5ed9a3d186b79ce38e0e8a8d1b58b132d701d4eecf68d1f6, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\n scratch2 := add(0x1bcd95ffc211fbca600f705fad3fb567ea4eb378f62e1fec97805518a47e4d9c, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\n state0 := mulmod(scratch0, scratch0, F)\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\n state0 := mulmod(scratch1, scratch1, F)\n scratch1 := mulmod(mulmod(state0, state0, F), scratch1, F)\n state0 := mulmod(scratch2, scratch2, F)\n scratch2 := mulmod(mulmod(state0, state0, F), scratch2, F)\n state0 := add(0x10520b0ab721cadfe9eff81b016fc34dc76da36c2578937817cb978d069de559, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\n state1 := add(0x1f6d48149b8e7f7d9b257d8ed5fbbaf42932498075fed0ace88a9eb81f5627f6, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\n state2 := add(0x1d9655f652309014d29e00ef35a2089bfff8dc1c816f0dc9ca34bdb5460c8705, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\n scratch0 := mulmod(state0, state0, F)\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\n scratch0 := add(0x04df5a56ff95bcafb051f7b1cd43a99ba731ff67e47032058fe3d4185697cc7d, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\n scratch1 := add(0x0672d995f8fff640151b3d290cedaf148690a10a8c8424a7f6ec282b6e4be828, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\n scratch2 := add(0x099952b414884454b21200d7ffafdd5f0c9a9dcc06f2708e9fc1d8209b5c75b9, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\n state0 := mulmod(scratch0, scratch0, F)\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\n state0 := add(0x052cba2255dfd00c7c483143ba8d469448e43586a9b4cd9183fd0e843a6b9fa6, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\n state1 := add(0x0b8badee690adb8eb0bd74712b7999af82de55707251ad7716077cb93c464ddc, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\n state2 := add(0x119b1590f13307af5a1ee651020c07c749c15d60683a8050b963d0a8e4b2bdd1, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\n scratch0 := mulmod(state0, state0, F)\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\n scratch0 := add(0x03150b7cd6d5d17b2529d36be0f67b832c4acfc884ef4ee5ce15be0bfb4a8d09, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\n scratch1 := add(0x2cc6182c5e14546e3cf1951f173912355374efb83d80898abe69cb317c9ea565, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\n scratch2 := add(0x005032551e6378c450cfe129a404b3764218cadedac14e2b92d2cd73111bf0f9, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\n state0 := mulmod(scratch0, scratch0, F)\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\n state0 := add(0x233237e3289baa34bb147e972ebcb9516469c399fcc069fb88f9da2cc28276b5, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\n state1 := add(0x05c8f4f4ebd4a6e3c980d31674bfbe6323037f21b34ae5a4e80c2d4c24d60280, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\n state2 := add(0x0a7b1db13042d396ba05d818a319f25252bcf35ef3aeed91ee1f09b2590fc65b, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\n scratch0 := mulmod(state0, state0, F)\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\n scratch0 := add(0x2a73b71f9b210cf5b14296572c9d32dbf156e2b086ff47dc5df542365a404ec0, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\n scratch1 := add(0x1ac9b0417abcc9a1935107e9ffc91dc3ec18f2c4dbe7f22976a760bb5c50c460, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\n scratch2 := add(0x12c0339ae08374823fabb076707ef479269f3e4d6cb104349015ee046dc93fc0, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\n state0 := mulmod(scratch0, scratch0, F)\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\n state0 := add(0x0b7475b102a165ad7f5b18db4e1e704f52900aa3253baac68246682e56e9a28e, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\n state1 := add(0x037c2849e191ca3edb1c5e49f6e8b8917c843e379366f2ea32ab3aa88d7f8448, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\n state2 := add(0x05a6811f8556f014e92674661e217e9bd5206c5c93a07dc145fdb176a716346f, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\n scratch0 := mulmod(state0, state0, F)\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\n scratch0 := add(0x29a795e7d98028946e947b75d54e9f044076e87a7b2883b47b675ef5f38bd66e, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\n scratch1 := add(0x20439a0c84b322eb45a3857afc18f5826e8c7382c8a1585c507be199981fd22f, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\n scratch2 := add(0x2e0ba8d94d9ecf4a94ec2050c7371ff1bb50f27799a84b6d4a2a6f2a0982c887, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\n state0 := mulmod(scratch0, scratch0, F)\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\n state0 := add(0x143fd115ce08fb27ca38eb7cce822b4517822cd2109048d2e6d0ddcca17d71c8, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\n state1 := add(0x0c64cbecb1c734b857968dbbdcf813cdf8611659323dbcbfc84323623be9caf1, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\n state2 := add(0x028a305847c683f646fca925c163ff5ae74f348d62c2b670f1426cef9403da53, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\n scratch0 := mulmod(state0, state0, F)\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\n scratch0 := add(0x2e4ef510ff0b6fda5fa940ab4c4380f26a6bcb64d89427b824d6755b5db9e30c, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\n scratch1 := add(0x0081c95bc43384e663d79270c956ce3b8925b4f6d033b078b96384f50579400e, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\n scratch2 := add(0x2ed5f0c91cbd9749187e2fade687e05ee2491b349c039a0bba8a9f4023a0bb38, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\n state0 := mulmod(scratch0, scratch0, F)\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\n state0 := add(0x30509991f88da3504bbf374ed5aae2f03448a22c76234c8c990f01f33a735206, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\n state1 := add(0x1c3f20fd55409a53221b7c4d49a356b9f0a1119fb2067b41a7529094424ec6ad, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\n state2 := add(0x10b4e7f3ab5df003049514459b6e18eec46bb2213e8e131e170887b47ddcb96c, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\n scratch0 := mulmod(state0, state0, F)\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\n scratch0 := add(0x2a1982979c3ff7f43ddd543d891c2abddd80f804c077d775039aa3502e43adef, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\n scratch1 := add(0x1c74ee64f15e1db6feddbead56d6d55dba431ebc396c9af95cad0f1315bd5c91, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\n scratch2 := add(0x07533ec850ba7f98eab9303cace01b4b9e4f2e8b82708cfa9c2fe45a0ae146a0, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\n state0 := mulmod(scratch0, scratch0, F)\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\n state0 := add(0x21576b438e500449a151e4eeaf17b154285c68f42d42c1808a11abf3764c0750, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\n state1 := add(0x2f17c0559b8fe79608ad5ca193d62f10bce8384c815f0906743d6930836d4a9e, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\n state2 := add(0x2d477e3862d07708a79e8aae946170bc9775a4201318474ae665b0b1b7e2730e, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\n scratch0 := mulmod(state0, state0, F)\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\n scratch0 := add(0x162f5243967064c390e095577984f291afba2266c38f5abcd89be0f5b2747eab, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\n scratch1 := add(0x2b4cb233ede9ba48264ecd2c8ae50d1ad7a8596a87f29f8a7777a70092393311, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\n scratch2 := add(0x2c8fbcb2dd8573dc1dbaf8f4622854776db2eece6d85c4cf4254e7c35e03b07a, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\n state0 := mulmod(scratch0, scratch0, F)\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\n state0 := add(0x1d6f347725e4816af2ff453f0cd56b199e1b61e9f601e9ade5e88db870949da9, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\n state1 := add(0x204b0c397f4ebe71ebc2d8b3df5b913df9e6ac02b68d31324cd49af5c4565529, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\n state2 := add(0x0c4cb9dc3c4fd8174f1149b3c63c3c2f9ecb827cd7dc25534ff8fb75bc79c502, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\n scratch0 := mulmod(state0, state0, F)\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\n scratch0 := add(0x174ad61a1448c899a25416474f4930301e5c49475279e0639a616ddc45bc7b54, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\n scratch1 := add(0x1a96177bcf4d8d89f759df4ec2f3cde2eaaa28c177cc0fa13a9816d49a38d2ef, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\n scratch2 := add(0x066d04b24331d71cd0ef8054bc60c4ff05202c126a233c1a8242ace360b8a30a, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\n state0 := mulmod(scratch0, scratch0, F)\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\n state0 := add(0x2a4c4fc6ec0b0cf52195782871c6dd3b381cc65f72e02ad527037a62aa1bd804, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\n state1 := add(0x13ab2d136ccf37d447e9f2e14a7cedc95e727f8446f6d9d7e55afc01219fd649, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\n state2 := add(0x1121552fca26061619d24d843dc82769c1b04fcec26f55194c2e3e869acc6a9a, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\n scratch0 := mulmod(state0, state0, F)\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\n scratch0 := add(0x00ef653322b13d6c889bc81715c37d77a6cd267d595c4a8909a5546c7c97cff1, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\n scratch1 := add(0x0e25483e45a665208b261d8ba74051e6400c776d652595d9845aca35d8a397d3, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\n scratch2 := add(0x29f536dcb9dd7682245264659e15d88e395ac3d4dde92d8c46448db979eeba89, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\n state0 := mulmod(scratch0, scratch0, F)\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\n state0 := add(0x2a56ef9f2c53febadfda33575dbdbd885a124e2780bbea170e456baace0fa5be, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\n state1 := add(0x1c8361c78eb5cf5decfb7a2d17b5c409f2ae2999a46762e8ee416240a8cb9af1, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\n state2 := add(0x151aff5f38b20a0fc0473089aaf0206b83e8e68a764507bfd3d0ab4be74319c5, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\n scratch0 := mulmod(state0, state0, F)\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\n scratch0 := add(0x04c6187e41ed881dc1b239c88f7f9d43a9f52fc8c8b6cdd1e76e47615b51f100, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\n scratch1 := add(0x13b37bd80f4d27fb10d84331f6fb6d534b81c61ed15776449e801b7ddc9c2967, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\n scratch2 := add(0x01a5c536273c2d9df578bfbd32c17b7a2ce3664c2a52032c9321ceb1c4e8a8e4, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\n state0 := mulmod(scratch0, scratch0, F)\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\n state0 := add(0x2ab3561834ca73835ad05f5d7acb950b4a9a2c666b9726da832239065b7c3b02, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\n state1 := add(0x1d4d8ec291e720db200fe6d686c0d613acaf6af4e95d3bf69f7ed516a597b646, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\n state2 := add(0x041294d2cc484d228f5784fe7919fd2bb925351240a04b711514c9c80b65af1d, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\n scratch0 := mulmod(state0, state0, F)\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\n scratch0 := add(0x154ac98e01708c611c4fa715991f004898f57939d126e392042971dd90e81fc6, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\n scratch1 := add(0x0b339d8acca7d4f83eedd84093aef51050b3684c88f8b0b04524563bc6ea4da4, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\n scratch2 := add(0x0955e49e6610c94254a4f84cfbab344598f0e71eaff4a7dd81ed95b50839c82e, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\n state0 := mulmod(scratch0, scratch0, F)\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\n state0 := add(0x06746a6156eba54426b9e22206f15abca9a6f41e6f535c6f3525401ea0654626, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\n state1 := add(0x0f18f5a0ecd1423c496f3820c549c27838e5790e2bd0a196ac917c7ff32077fb, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\n state2 := add(0x04f6eeca1751f7308ac59eff5beb261e4bb563583ede7bc92a738223d6f76e13, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\n scratch0 := mulmod(state0, state0, F)\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\n scratch0 := add(0x2b56973364c4c4f5c1a3ec4da3cdce038811eb116fb3e45bc1768d26fc0b3758, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\n scratch1 := add(0x123769dd49d5b054dcd76b89804b1bcb8e1392b385716a5d83feb65d437f29ef, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\n scratch2 := add(0x2147b424fc48c80a88ee52b91169aacea989f6446471150994257b2fb01c63e9, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\n state0 := mulmod(scratch0, scratch0, F)\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\n state0 := add(0x0fdc1f58548b85701a6c5505ea332a29647e6f34ad4243c2ea54ad897cebe54d, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\n state1 := add(0x12373a8251fea004df68abcf0f7786d4bceff28c5dbbe0c3944f685cc0a0b1f2, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\n state2 := add(0x21e4f4ea5f35f85bad7ea52ff742c9e8a642756b6af44203dd8a1f35c1a90035, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\n scratch0 := mulmod(state0, state0, F)\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\n scratch0 := add(0x16243916d69d2ca3dfb4722224d4c462b57366492f45e90d8a81934f1bc3b147, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\n scratch1 := add(0x1efbe46dd7a578b4f66f9adbc88b4378abc21566e1a0453ca13a4159cac04ac2, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\n scratch2 := add(0x07ea5e8537cf5dd08886020e23a7f387d468d5525be66f853b672cc96a88969a, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\n state0 := mulmod(scratch0, scratch0, F)\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\n state0 := add(0x05a8c4f9968b8aa3b7b478a30f9a5b63650f19a75e7ce11ca9fe16c0b76c00bc, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\n state1 := add(0x20f057712cc21654fbfe59bd345e8dac3f7818c701b9c7882d9d57b72a32e83f, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\n state2 := add(0x04a12ededa9dfd689672f8c67fee31636dcd8e88d01d49019bd90b33eb33db69, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\n scratch0 := mulmod(state0, state0, F)\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\n scratch0 := add(0x27e88d8c15f37dcee44f1e5425a51decbd136ce5091a6767e49ec9544ccd101a, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\n scratch1 := add(0x2feed17b84285ed9b8a5c8c5e95a41f66e096619a7703223176c41ee433de4d1, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\n scratch2 := add(0x1ed7cc76edf45c7c404241420f729cf394e5942911312a0d6972b8bd53aff2b8, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\n state0 := mulmod(scratch0, scratch0, F)\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\n state0 := add(0x15742e99b9bfa323157ff8c586f5660eac6783476144cdcadf2874be45466b1a, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\n state1 := add(0x1aac285387f65e82c895fc6887ddf40577107454c6ec0317284f033f27d0c785, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\n state2 := add(0x25851c3c845d4790f9ddadbdb6057357832e2e7a49775f71ec75a96554d67c77, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\n scratch0 := mulmod(state0, state0, F)\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\n scratch0 := add(0x15a5821565cc2ec2ce78457db197edf353b7ebba2c5523370ddccc3d9f146a67, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\n scratch1 := add(0x2411d57a4813b9980efa7e31a1db5966dcf64f36044277502f15485f28c71727, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\n scratch2 := add(0x002e6f8d6520cd4713e335b8c0b6d2e647e9a98e12f4cd2558828b5ef6cb4c9b, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\n state0 := mulmod(scratch0, scratch0, F)\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\n state0 := add(0x2ff7bc8f4380cde997da00b616b0fcd1af8f0e91e2fe1ed7398834609e0315d2, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\n state1 := add(0x00b9831b948525595ee02724471bcd182e9521f6b7bb68f1e93be4febb0d3cbe, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\n state2 := add(0x0a2f53768b8ebf6a86913b0e57c04e011ca408648a4743a87d77adbf0c9c3512, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\n scratch0 := mulmod(state0, state0, F)\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\n scratch0 := add(0x00248156142fd0373a479f91ff239e960f599ff7e94be69b7f2a290305e1198d, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\n scratch1 := add(0x171d5620b87bfb1328cf8c02ab3f0c9a397196aa6a542c2350eb512a2b2bcda9, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\n scratch2 := add(0x170a4f55536f7dc970087c7c10d6fad760c952172dd54dd99d1045e4ec34a808, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\n state0 := mulmod(scratch0, scratch0, F)\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\n state0 := add(0x29aba33f799fe66c2ef3134aea04336ecc37e38c1cd211ba482eca17e2dbfae1, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\n state1 := add(0x1e9bc179a4fdd758fdd1bb1945088d47e70d114a03f6a0e8b5ba650369e64973, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\n state2 := add(0x1dd269799b660fad58f7f4892dfb0b5afeaad869a9c4b44f9c9e1c43bdaf8f09, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\n scratch0 := mulmod(state0, state0, F)\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\n scratch0 := add(0x22cdbc8b70117ad1401181d02e15459e7ccd426fe869c7c95d1dd2cb0f24af38, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\n scratch1 := add(0x0ef042e454771c533a9f57a55c503fcefd3150f52ed94a7cd5ba93b9c7dacefd, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\n scratch2 := add(0x11609e06ad6c8fe2f287f3036037e8851318e8b08a0359a03b304ffca62e8284, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\n state0 := mulmod(scratch0, scratch0, F)\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\n state0 := add(0x1166d9e554616dba9e753eea427c17b7fecd58c076dfe42708b08f5b783aa9af, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\n state1 := add(0x2de52989431a859593413026354413db177fbf4cd2ac0b56f855a888357ee466, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\n state2 := add(0x3006eb4ffc7a85819a6da492f3a8ac1df51aee5b17b8e89d74bf01cf5f71e9ad, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\n scratch0 := mulmod(state0, state0, F)\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\n scratch0 := add(0x2af41fbb61ba8a80fdcf6fff9e3f6f422993fe8f0a4639f962344c8225145086, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\n scratch1 := add(0x119e684de476155fe5a6b41a8ebc85db8718ab27889e85e781b214bace4827c3, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\n scratch2 := add(0x1835b786e2e8925e188bea59ae363537b51248c23828f047cff784b97b3fd800, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\n state0 := mulmod(scratch0, scratch0, F)\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\n state0 := add(0x28201a34c594dfa34d794996c6433a20d152bac2a7905c926c40e285ab32eeb6, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\n state1 := add(0x083efd7a27d1751094e80fefaf78b000864c82eb571187724a761f88c22cc4e7, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\n state2 := add(0x0b6f88a3577199526158e61ceea27be811c16df7774dd8519e079564f61fd13b, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\n scratch0 := mulmod(state0, state0, F)\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\n scratch0 := add(0x0ec868e6d15e51d9644f66e1d6471a94589511ca00d29e1014390e6ee4254f5b, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\n scratch1 := add(0x2af33e3f866771271ac0c9b3ed2e1142ecd3e74b939cd40d00d937ab84c98591, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\n scratch2 := add(0x0b520211f904b5e7d09b5d961c6ace7734568c547dd6858b364ce5e47951f178, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\n state0 := mulmod(scratch0, scratch0, F)\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\n state0 := add(0x0b2d722d0919a1aad8db58f10062a92ea0c56ac4270e822cca228620188a1d40, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\n state1 := add(0x1f790d4d7f8cf094d980ceb37c2453e957b54a9991ca38bbe0061d1ed6e562d4, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\n state2 := add(0x0171eb95dfbf7d1eaea97cd385f780150885c16235a2a6a8da92ceb01e504233, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\n scratch0 := mulmod(state0, state0, F)\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\n scratch0 := add(0x0c2d0e3b5fd57549329bf6885da66b9b790b40defd2c8650762305381b168873, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\n scratch1 := add(0x1162fb28689c27154e5a8228b4e72b377cbcafa589e283c35d3803054407a18d, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\n scratch2 := add(0x2f1459b65dee441b64ad386a91e8310f282c5a92a89e19921623ef8249711bc0, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\n state0 := mulmod(scratch0, scratch0, F)\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\n state0 := add(0x1e6ff3216b688c3d996d74367d5cd4c1bc489d46754eb712c243f70d1b53cfbb, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\n state1 := add(0x01ca8be73832b8d0681487d27d157802d741a6f36cdc2a0576881f9326478875, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\n state2 := add(0x1f7735706ffe9fc586f976d5bdf223dc680286080b10cea00b9b5de315f9650e, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\n scratch0 := mulmod(state0, state0, F)\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\n scratch0 := add(0x2522b60f4ea3307640a0c2dce041fba921ac10a3d5f096ef4745ca838285f019, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\n scratch1 := add(0x23f0bee001b1029d5255075ddc957f833418cad4f52b6c3f8ce16c235572575b, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\n scratch2 := add(0x2bc1ae8b8ddbb81fcaac2d44555ed5685d142633e9df905f66d9401093082d59, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\n state0 := mulmod(scratch0, scratch0, F)\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\n state0 := add(0x0f9406b8296564a37304507b8dba3ed162371273a07b1fc98011fcd6ad72205f, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\n state1 := add(0x2360a8eb0cc7defa67b72998de90714e17e75b174a52ee4acb126c8cd995f0a8, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\n state2 := add(0x15871a5cddead976804c803cbaef255eb4815a5e96df8b006dcbbc2767f88948, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\n scratch0 := mulmod(state0, state0, F)\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\n scratch0 := add(0x193a56766998ee9e0a8652dd2f3b1da0362f4f54f72379544f957ccdeefb420f, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\n scratch1 := add(0x2a394a43934f86982f9be56ff4fab1703b2e63c8ad334834e4309805e777ae0f, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\n scratch2 := add(0x1859954cfeb8695f3e8b635dcb345192892cd11223443ba7b4166e8876c0d142, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\n state0 := mulmod(scratch0, scratch0, F)\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\n state0 := add(0x04e1181763050e58013444dbcb99f1902b11bc25d90bbdca408d3819f4fed32b, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\n state1 := add(0x0fdb253dee83869d40c335ea64de8c5bb10eb82db08b5e8b1f5e5552bfd05f23, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\n state2 := add(0x058cbe8a9a5027bdaa4efb623adead6275f08686f1c08984a9d7c5bae9b4f1c0, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\n scratch0 := mulmod(state0, state0, F)\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\n scratch0 := add(0x1382edce9971e186497eadb1aeb1f52b23b4b83bef023ab0d15228b4cceca59a, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\n scratch1 := add(0x03464990f045c6ee0819ca51fd11b0be7f61b8eb99f14b77e1e6634601d9e8b5, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\n scratch2 := add(0x23f7bfc8720dc296fff33b41f98ff83c6fcab4605db2eb5aaa5bc137aeb70a58, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\n state0 := mulmod(scratch0, scratch0, F)\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\n state0 := add(0x0a59a158e3eec2117e6e94e7f0e9decf18c3ffd5e1531a9219636158bbaf62f2, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\n state1 := add(0x06ec54c80381c052b58bf23b312ffd3ce2c4eba065420af8f4c23ed0075fd07b, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\n state2 := add(0x118872dc832e0eb5476b56648e867ec8b09340f7a7bcb1b4962f0ff9ed1f9d01, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\n scratch0 := mulmod(state0, state0, F)\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\n scratch0 := add(0x13d69fa127d834165ad5c7cba7ad59ed52e0b0f0e42d7fea95e1906b520921b1, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\n scratch1 := add(0x169a177f63ea681270b1c6877a73d21bde143942fb71dc55fd8a49f19f10c77b, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\n scratch2 := add(0x04ef51591c6ead97ef42f287adce40d93abeb032b922f66ffb7e9a5a7450544d, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\n state0 := mulmod(scratch0, scratch0, F)\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\n state0 := add(0x256e175a1dc079390ecd7ca703fb2e3b19ec61805d4f03ced5f45ee6dd0f69ec, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\n state1 := add(0x30102d28636abd5fe5f2af412ff6004f75cc360d3205dd2da002813d3e2ceeb2, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\n state2 := add(0x10998e42dfcd3bbf1c0714bc73eb1bf40443a3fa99bef4a31fd31be182fcc792, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\n scratch0 := mulmod(state0, state0, F)\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\n scratch0 := add(0x193edd8e9fcf3d7625fa7d24b598a1d89f3362eaf4d582efecad76f879e36860, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\n scratch1 := add(0x18168afd34f2d915d0368ce80b7b3347d1c7a561ce611425f2664d7aa51f0b5d, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\n scratch2 := add(0x29383c01ebd3b6ab0c017656ebe658b6a328ec77bc33626e29e2e95b33ea6111, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\n state0 := mulmod(scratch0, scratch0, F)\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\n state0 := add(0x10646d2f2603de39a1f4ae5e7771a64a702db6e86fb76ab600bf573f9010c711, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\n state1 := add(0x0beb5e07d1b27145f575f1395a55bf132f90c25b40da7b3864d0242dcb1117fb, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\n state2 := add(0x16d685252078c133dc0d3ecad62b5c8830f95bb2e54b59abdffbf018d96fa336, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\n scratch0 := mulmod(state0, state0, F)\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\n scratch0 := add(0x0a6abd1d833938f33c74154e0404b4b40a555bbbec21ddfafd672dd62047f01a, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\n scratch1 := add(0x1a679f5d36eb7b5c8ea12a4c2dedc8feb12dffeec450317270a6f19b34cf1860, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\n scratch2 := add(0x0980fb233bd456c23974d50e0ebfde4726a423eada4e8f6ffbc7592e3f1b93d6, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\n state0 := mulmod(scratch0, scratch0, F)\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\n state0 := add(0x161b42232e61b84cbf1810af93a38fc0cece3d5628c9282003ebacb5c312c72b, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\n state1 := add(0x0ada10a90c7f0520950f7d47a60d5e6a493f09787f1564e5d09203db47de1a0b, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\n state2 := add(0x1a730d372310ba82320345a29ac4238ed3f07a8a2b4e121bb50ddb9af407f451, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\n scratch0 := mulmod(state0, state0, F)\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\n scratch0 := add(0x2c8120f268ef054f817064c369dda7ea908377feaba5c4dffbda10ef58e8c556, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\n scratch1 := add(0x1c7c8824f758753fa57c00789c684217b930e95313bcb73e6e7b8649a4968f70, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\n scratch2 := add(0x2cd9ed31f5f8691c8e39e4077a74faa0f400ad8b491eb3f7b47b27fa3fd1cf77, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\n state0 := mulmod(scratch0, scratch0, F)\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\n state0 := add(0x23ff4f9d46813457cf60d92f57618399a5e022ac321ca550854ae23918a22eea, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\n state1 := add(0x09945a5d147a4f66ceece6405dddd9d0af5a2c5103529407dff1ea58f180426d, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\n state2 := add(0x188d9c528025d4c2b67660c6b771b90f7c7da6eaa29d3f268a6dd223ec6fc630, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\n scratch0 := mulmod(state0, state0, F)\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\n scratch0 := add(0x3050e37996596b7f81f68311431d8734dba7d926d3633595e0c0d8ddf4f0f47f, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\n scratch1 := add(0x15af1169396830a91600ca8102c35c426ceae5461e3f95d89d829518d30afd78, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\n scratch2 := add(0x1da6d09885432ea9a06d9f37f873d985dae933e351466b2904284da3320d8acc, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\n state0 := mulmod(scratch0, scratch0, F)\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\n state0 := add(0x2796ea90d269af29f5f8acf33921124e4e4fad3dbe658945e546ee411ddaa9cb, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\n state1 := add(0x202d7dd1da0f6b4b0325c8b3307742f01e15612ec8e9304a7cb0319e01d32d60, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\n state2 := add(0x096d6790d05bb759156a952ba263d672a2d7f9c788f4c831a29dace4c0f8be5f, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\n scratch0 := mulmod(state0, state0, F)\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\n scratch0 := add(0x054efa1f65b0fce283808965275d877b438da23ce5b13e1963798cb1447d25a4, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\n scratch1 := add(0x1b162f83d917e93edb3308c29802deb9d8aa690113b2e14864ccf6e18e4165f1, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\n scratch2 := add(0x21e5241e12564dd6fd9f1cdd2a0de39eedfefc1466cc568ec5ceb745a0506edc, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\n state0 := mulmod(scratch0, scratch0, F)\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\n state0 := mulmod(scratch1, scratch1, F)\n scratch1 := mulmod(mulmod(state0, state0, F), scratch1, F)\n state0 := mulmod(scratch2, scratch2, F)\n scratch2 := mulmod(mulmod(state0, state0, F), scratch2, F)\n state0 := add(0x1cfb5662e8cf5ac9226a80ee17b36abecb73ab5f87e161927b4349e10e4bdf08, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\n state1 := add(0x0f21177e302a771bbae6d8d1ecb373b62c99af346220ac0129c53f666eb24100, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\n state2 := add(0x1671522374606992affb0dd7f71b12bec4236aede6290546bcef7e1f515c2320, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\n scratch0 := mulmod(state0, state0, F)\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\n scratch0 := mulmod(state1, state1, F)\n state1 := mulmod(mulmod(scratch0, scratch0, F), state1, F)\n scratch0 := mulmod(state2, state2, F)\n state2 := mulmod(mulmod(scratch0, scratch0, F), state2, F)\n scratch0 := add(0x0fa3ec5b9488259c2eb4cf24501bfad9be2ec9e42c5cc8ccd419d2a692cad870, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\n scratch1 := add(0x193c0e04e0bd298357cb266c1506080ed36edce85c648cc085e8c57b1ab54bba, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\n scratch2 := add(0x102adf8ef74735a27e9128306dcbc3c99f6f7291cd406578ce14ea2adaba68f8, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\n state0 := mulmod(scratch0, scratch0, F)\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\n state0 := mulmod(scratch1, scratch1, F)\n scratch1 := mulmod(mulmod(state0, state0, F), scratch1, F)\n state0 := mulmod(scratch2, scratch2, F)\n scratch2 := mulmod(mulmod(state0, state0, F), scratch2, F)\n state0 := add(0x0fe0af7858e49859e2a54d6f1ad945b1316aa24bfbdd23ae40a6d0cb70c3eab1, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\n state1 := add(0x216f6717bbc7dedb08536a2220843f4e2da5f1daa9ebdefde8a5ea7344798d22, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\n state2 := add(0x1da55cc900f0d21f4a3e694391918a1b3c23b2ac773c6b3ef88e2e4228325161, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\n scratch0 := mulmod(state0, state0, F)\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\n scratch0 := mulmod(state1, state1, F)\n state1 := mulmod(mulmod(scratch0, scratch0, F), state1, F)\n scratch0 := mulmod(state2, state2, F)\n state2 := mulmod(mulmod(scratch0, scratch0, F), state2, F)\n\n mstore(0x0, mod(add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)), F))\n\n return(0, 0x20)\n }\n }\n}\n" + } + }, + "settings": { + "optimizer": { + "enabled": true, + "runs": 200 + }, + "evmVersion": "paris", + "outputSelection": { + "*": { + "*": [ + "abi", + "evm.bytecode", + "evm.deployedBytecode", + "evm.methodIdentifiers", + "metadata", + "devdoc", + "userdoc", + "storageLayout", + "evm.gasEstimates" + ], + "": [ + "ast" + ] + } + }, + "metadata": { + "useLiteralContent": true + } + } +} \ No newline at end of file diff --git a/packages/nextjs/components/Dashboard/TransactionRow.tsx b/packages/nextjs/components/Dashboard/TransactionRow.tsx index daf63a0c..d148cdd2 100644 --- a/packages/nextjs/components/Dashboard/TransactionRow.tsx +++ b/packages/nextjs/components/Dashboard/TransactionRow.tsx @@ -62,6 +62,9 @@ export function convertToRowData(tx: Transaction, myCommitment: string): Transac oldThreshold: tx.threshold, newThreshold: tx.newThreshold || undefined, batchData, + destChainId: tx.destChainId, + bridgeFee: tx.bridgeFee, + bridgeMinAmount: tx.bridgeMinAmount, members, votedCount: tx.votes.length, threshold: tx.threshold, @@ -308,6 +311,7 @@ function TxHeader({ {formatAmount(tx.amount ?? "0", chainId, tx.tokenAddress)} Arrow Right + ); @@ -552,6 +556,18 @@ function getExpandedHeaderText(type: TxType): string { } } +// TODO: test this component +function ChainBadge({ chainId, sourceChainId }: { chainId?: number; sourceChainId: number }) { + if (!chainId || chainId === sourceChainId) return null; + const chain = scaffoldConfig.targetNetworks.find(n => n.id === chainId); + const name = chain?.name ?? `Chain ${chainId}`; + return ( + + {name} + + ); +} + function TxDetails({ tx }: { tx: TransactionRowData }) { const { chainId } = useNetworkTokens(); switch (tx.type) { @@ -569,6 +585,7 @@ function TxDetails({ tx }: { tx: TransactionRowData }) { Arrow Right + ); diff --git a/packages/nextjs/components/Transfer/TransferContainer.tsx b/packages/nextjs/components/Transfer/TransferContainer.tsx index 6a351cb0..060e323f 100644 --- a/packages/nextjs/components/Transfer/TransferContainer.tsx +++ b/packages/nextjs/components/Transfer/TransferContainer.tsx @@ -1,8 +1,14 @@ "use client"; -import React, { useEffect, useState } from "react"; +import React, { useEffect, useMemo, useState } from "react"; import Image from "next/image"; -import { ResolvedToken, parseTokenAmount } from "@polypay/shared"; +import { + ResolvedToken, + formatDisplayValue, + getAvailableDestChains, + isCrossChainEnabled, + parseTokenAmount, +} from "@polypay/shared"; import { parseEther } from "viem"; import { ContactPicker } from "~~/components/contact-book/ContactPicker"; import { TokenPillPopover } from "~~/components/popovers/TokenPillPopover"; @@ -13,22 +19,92 @@ import { useTokenBalances } from "~~/hooks/app/useTokenBalance"; import { useZodForm } from "~~/hooks/form"; import { TransferFormData, transferSchema } from "~~/lib/form"; import { formatErrorMessage } from "~~/lib/form/utils"; +import scaffoldConfig from "~~/scaffold.config"; import { useAccountStore, useIdentityStore } from "~~/services/store"; import { notification } from "~~/utils/scaffold-eth"; +function ChainSelector({ + sourceChainId, + selectedChainId, + onChange, + tokenSymbol, + disabled, +}: { + sourceChainId: number; + selectedChainId: number; + onChange: (chainId: number) => void; + tokenSymbol: string; + disabled?: boolean; +}) { + const availableChains = useMemo( + () => getAvailableDestChains(sourceChainId, tokenSymbol), + [sourceChainId, tokenSymbol], + ); + + const chainNames: Record = useMemo(() => { + const map: Record = {}; + for (const chain of scaffoldConfig.targetNetworks) { + map[chain.id] = chain.name; + } + return map; + }, []); + + if (availableChains.length <= 1) return null; + + return ( +
+ Network: +
+ {availableChains.map(chainId => ( + + ))} +
+
+ ); +} + export default function TransferContainer() { const [selectedContactId, setSelectedContactId] = useState(null); - const { tokens, nativeEth } = useNetworkTokens(); + const { tokens, nativeEth, chainId: sourceChainId } = useNetworkTokens(); const [selectedToken, setSelectedToken] = useState(nativeEth); + const [destChainId, setDestChainId] = useState(sourceChainId); const { currentAccount: selectedAccount } = useAccountStore(); const { mutateAsync: createBatchItem } = useCreateBatchItem(); const { commitment } = useIdentityStore(); + const crossChainEnabled = selectedAccount?.contractVersion + ? isCrossChainEnabled(selectedAccount.contractVersion) + : false; + + const isCrossChain = destChainId !== sourceChainId; + useEffect(() => { setSelectedToken(nativeEth); }, [nativeEth]); + useEffect(() => { + setDestChainId(sourceChainId); + }, [sourceChainId]); + + useEffect(() => { + const available = getAvailableDestChains(sourceChainId, selectedToken.symbol); + if (!available.includes(destChainId)) { + setDestChainId(sourceChainId); + } + }, [selectedToken.symbol, sourceChainId, destChainId]); + const metaMultiSigWallet = useMetaMultiSigWallet(); const { balances, isLoading: isLoadingBalances } = useTokenBalances( metaMultiSigWallet?.address, @@ -49,7 +125,6 @@ export default function TransferContainer() { }; useEffect(() => { - // Check for recipient data from sessionStorage const recipientData = sessionStorage.getItem("transferRecipient"); if (recipientData) { try { @@ -61,7 +136,6 @@ export default function TransferContainer() { if (name) { notification.info(`Pre-filled address for: ${name}`); } - // Clear the data after using it sessionStorage.removeItem("transferRecipient"); } catch (error) { console.error("Failed to parse recipient data:", error); @@ -84,11 +158,11 @@ export default function TransferContainer() { amount: data.amount, token: selectedToken, contactId: selectedContactId, + destChainId: isCrossChain ? destChainId : undefined, }); }; const handleAddToBatch = async () => { - // Validate form first const isValid = await form.trigger(); if (!isValid) { return; @@ -96,7 +170,6 @@ export default function TransferContainer() { const data = form.getValues(); - // Validate commitment if (!commitment) { notification.error("No commitment found. Please create identity first."); return; @@ -123,7 +196,6 @@ export default function TransferContainer() { , ); - // Reset form form.reset(); setSelectedContactId(null); } catch (error: any) { @@ -200,7 +272,8 @@ export default function TransferContainer() {
Polypay account balance: - {isLoadingBalances ? "..." : currentBalance} {selectedToken.symbol} + {isLoadingBalances ? "..." : formatDisplayValue(currentBalance, selectedToken.symbol)}{" "} + {selectedToken.symbol}
+ + {/* Destination chain selector */} + {crossChainEnabled && ( + + )} + + {/* Cross-chain indicator */} + {isCrossChain && ( +
+ Cross-chain transfer via {selectedToken.symbol === "ETH" ? "OP Stack bridge" : "LayerZero"} +
+ )} + {/* Address input */}
@@ -253,22 +345,24 @@ export default function TransferContainer() { {/* Action buttons */}
- + {!isCrossChain && ( + + )}
diff --git a/packages/nextjs/components/modals/PortFolioModal.tsx b/packages/nextjs/components/modals/PortFolioModal.tsx index 041aa7a8..9dec0261 100644 --- a/packages/nextjs/components/modals/PortFolioModal.tsx +++ b/packages/nextjs/components/modals/PortFolioModal.tsx @@ -4,7 +4,7 @@ import React from "react"; import Image from "next/image"; import { Button } from "../ui/button"; import { Sheet, SheetClose, SheetContent, SheetTitle, SheetTrigger } from "../ui/sheet"; -import { ResolvedToken } from "@polypay/shared"; +import { ResolvedToken, formatDisplayValue } from "@polypay/shared"; import { Eye, EyeOff, MoveDown, MoveUp, X } from "lucide-react"; import { Address } from "viem"; import NetworkBadge from "~~/components/Common/NetworkBadge"; @@ -49,7 +49,7 @@ function TokenBalanceRow({ token, balance, usdValue, isLoading, chainId }: Token {/* Balance & USD Value */}
- {isLoading ? "..." : `${balance} ${token.symbol}`} + {isLoading ? "..." : `${formatDisplayValue(balance, token.symbol)} ${token.symbol}`} {isLoading diff --git a/packages/nextjs/components/popovers/TokenPillPopover.tsx b/packages/nextjs/components/popovers/TokenPillPopover.tsx index 80c732f0..c1654a44 100644 --- a/packages/nextjs/components/popovers/TokenPillPopover.tsx +++ b/packages/nextjs/components/popovers/TokenPillPopover.tsx @@ -2,7 +2,7 @@ import React, { useRef, useState } from "react"; import Image from "next/image"; -import { ResolvedToken } from "@polypay/shared"; +import { ResolvedToken, formatDisplayValue } from "@polypay/shared"; import NetworkBadge from "~~/components/Common/NetworkBadge"; import { useMetaMultiSigWallet, useTokenPrices } from "~~/hooks"; import { useNetworkTokens } from "~~/hooks/app/useNetworkTokens"; @@ -112,7 +112,7 @@ export function TokenPillPopover({ $ {isLoadingPrices ? "..." : getTokenUsdValue(token)}

- {balances[token.address] || "0"} {token.symbol} + {formatDisplayValue(balances[token.address] || "0", token.symbol)} {token.symbol}

diff --git a/packages/nextjs/contracts/deployedContracts.ts b/packages/nextjs/contracts/deployedContracts.ts index 6b9a414a..f39329b6 100644 --- a/packages/nextjs/contracts/deployedContracts.ts +++ b/packages/nextjs/contracts/deployedContracts.ts @@ -7,7 +7,7 @@ import { GenericContractsDeclaration } from "~~/utils/scaffold-eth/contract"; const deployedContracts = { 2651420: { MetaMultiSigWallet: { - address: "0x1EaCA128069b2bb1cd476ef66E2701F98cAB148E", + address: "0x3c57e227ca5264ac0378800fA6E11FF112dB8538", abi: [ { inputs: [ @@ -178,6 +178,44 @@ const deployedContracts = { stateMutability: "nonpayable", type: "function", }, + { + inputs: [ + { + internalType: "address", + name: "token", + type: "address", + }, + { + internalType: "address", + name: "spender", + type: "address", + }, + { + internalType: "uint256", + name: "approveAmount", + type: "uint256", + }, + { + internalType: "address", + name: "callTarget", + type: "address", + }, + { + internalType: "uint256", + name: "callValue", + type: "uint256", + }, + { + internalType: "bytes", + name: "callData", + type: "bytes", + }, + ], + name: "approveAndCall", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, { inputs: [ { @@ -525,7 +563,7 @@ const deployedContracts = { }, ], inheritedFunctions: {}, - deployedOnBlock: 6615581, + deployedOnBlock: 10856432, }, }, 11155111: { diff --git a/packages/nextjs/hooks/app/transaction/useTransactionVote.ts b/packages/nextjs/hooks/app/transaction/useTransactionVote.ts index 3ac1f9f8..8ae54c98 100644 --- a/packages/nextjs/hooks/app/transaction/useTransactionVote.ts +++ b/packages/nextjs/hooks/app/transaction/useTransactionVote.ts @@ -1,19 +1,29 @@ import { useState } from "react"; import { + LZ_ENDPOINT_IDS, + OP_BRIDGE_ADDRESSES, SignerData, TxStatus, TxType, ZERO_ADDRESS, encodeAddSigners, + encodeApproveAndCall, encodeBatchTransfer, encodeBatchTransferMulti, + encodeBridgeETHTo, encodeERC20Transfer, + encodeLzSend, encodeRemoveSigners, encodeUpdateThreshold, + getBridgeContract, + getBridgeMechanism, + getOftCmd, + getTokenByAddress, + removeDust, } from "@polypay/shared"; import { useQueryClient } from "@tanstack/react-query"; import { useWalletClient } from "wagmi"; -import { accountKeys, useMetaMultiSigWallet, userKeys } from "~~/hooks"; +import { accountKeys, useMetaMultiSigWallet, useNetworkTokens, userKeys } from "~~/hooks"; import { useApproveTransaction, useDenyTransaction, useExecuteTransaction } from "~~/hooks/api/useTransaction"; import { useGenerateProof } from "~~/hooks/app/useGenerateProof"; import { formatErrorMessage } from "~~/lib/form/utils"; @@ -56,6 +66,9 @@ export interface TransactionRowData { oldThreshold?: number; newThreshold?: number; batchData?: BatchTransfer[]; + destChainId?: number; + bridgeFee?: string; + bridgeMinAmount?: string; contact?: { id: string; name: string; @@ -70,9 +83,15 @@ export interface TransactionRowData { } /** - * Build callData, to, and value based on transaction type + * Build callData, to, and value based on transaction type. + * For cross-chain TRANSFER, must reconstruct bridge execution params + * to produce the same txHash as the creator and backend. */ -function buildTransactionParams(tx: TransactionRowData): { +function buildTransactionParams( + tx: TransactionRowData, + sourceChainId: number, + bridgeFee?: string, +): { to: `0x${string}`; value: bigint; callData: `0x${string}`; @@ -82,7 +101,12 @@ function buildTransactionParams(tx: TransactionRowData): { let value = BigInt(tx.amount || "0"); if (tx.type === TxType.TRANSFER) { - // Check if ERC20 transfer + const isCrossChain = tx.destChainId && tx.destChainId !== sourceChainId; + + if (isCrossChain) { + return buildBridgeTransactionParams(tx, sourceChainId, bridgeFee); + } + if (tx.tokenAddress && tx.tokenAddress !== ZERO_ADDRESS) { to = tx.tokenAddress as `0x${string}`; value = 0n; @@ -117,6 +141,69 @@ function buildTransactionParams(tx: TransactionRowData): { return { to, value, callData }; } +function buildBridgeTransactionParams( + tx: TransactionRowData, + sourceChainId: number, + bridgeFee?: string, +): { to: `0x${string}`; value: bigint; callData: `0x${string}` } { + const destChainId = tx.destChainId!; + const isNativeETH = !tx.tokenAddress || tx.tokenAddress === ZERO_ADDRESS; + const tokenSymbol = isNativeETH ? "ETH" : getTokenSymbolFromAddress(tx.tokenAddress!, sourceChainId); + const mechanism = getBridgeMechanism(sourceChainId, destChainId, tokenSymbol); + const amount = BigInt(tx.amount || "0"); + const fee = bridgeFee ? BigInt(bridgeFee) : 0n; + const recipient = tx.recipientAddress!; + + if (mechanism === "OP_STACK") { + const bridgeAddress = OP_BRIDGE_ADDRESSES[sourceChainId]; + return { + to: bridgeAddress as `0x${string}`, + value: amount, + callData: encodeBridgeETHTo(recipient) as `0x${string}`, + }; + } + + // LAYERZERO + const dstEid = LZ_ENDPOINT_IDS[destChainId]; + const oftEntry = getBridgeContract(tokenSymbol, sourceChainId); + + if (!dstEid || !oftEntry) { + throw new Error(`No bridge route for ${tokenSymbol} from ${sourceChainId} to ${destChainId}`); + } + + const token = getTokenByAddress(tx.tokenAddress, sourceChainId); + const minAmount = tx.bridgeMinAmount ? BigInt(tx.bridgeMinAmount) : removeDust(amount, token.decimals); + const oftCmd = getOftCmd(oftEntry); + + const lzSendData = encodeLzSend(dstEid, recipient, amount, minAmount, fee, tx.accountAddress, oftCmd); + + if (oftEntry.type === "adapter") { + return { + to: tx.accountAddress as `0x${string}`, + value: 0n, + callData: encodeApproveAndCall( + tx.tokenAddress!, + oftEntry.address, + amount, + oftEntry.address, + fee, + lzSendData, + ) as `0x${string}`, + }; + } + + return { + to: oftEntry.address as `0x${string}`, + value: fee, + callData: lzSendData as `0x${string}`, + }; +} + +function getTokenSymbolFromAddress(tokenAddress: string, chainId: number): string { + const token = getTokenByAddress(tokenAddress, chainId); + return token.symbol; +} + export const useTransactionVote = (options?: UseTransactionVoteOptions) => { const [isLoading, setIsLoading] = useState(false); const [loadingState, setLoadingState] = useState(""); @@ -124,6 +211,7 @@ export const useTransactionVote = (options?: UseTransactionVoteOptions) => { const { commitment } = useIdentityStore(); const { data: walletClient } = useWalletClient(); const metaMultiSigWallet = useMetaMultiSigWallet(); + const { chainId: sourceChainId } = useNetworkTokens(); const { mutateAsync: approveApi } = useApproveTransaction(); const { mutateAsync: denyApi } = useDenyTransaction(); @@ -146,8 +234,8 @@ export const useTransactionVote = (options?: UseTransactionVoteOptions) => { setIsLoading(true); try { - // 1. Build callData based on tx type - const { to, value, callData } = buildTransactionParams(tx); + // 1. Build callData based on tx type (pass sourceChainId for cross-chain bridge reconstruction) + const { to, value, callData } = buildTransactionParams(tx, sourceChainId, tx.bridgeFee); // 2. Get txHash from contract const txHash = (await metaMultiSigWallet.read.getTransactionHash([ diff --git a/packages/nextjs/hooks/app/transaction/useTransferTransaction.ts b/packages/nextjs/hooks/app/transaction/useTransferTransaction.ts index 4bdc4fd7..0d677a93 100644 --- a/packages/nextjs/hooks/app/transaction/useTransferTransaction.ts +++ b/packages/nextjs/hooks/app/transaction/useTransferTransaction.ts @@ -1,11 +1,28 @@ import { useState } from "react"; -import { ResolvedToken, TxType, ZERO_ADDRESS, encodeERC20Transfer, parseTokenAmount } from "@polypay/shared"; -import { parseEther } from "viem"; +import { + LZ_ENDPOINT_IDS, + OFT_ABI, + OP_BRIDGE_ADDRESSES, + ResolvedToken, + TxType, + ZERO_ADDRESS, + encodeApproveAndCall, + encodeBridgeETHTo, + encodeERC20Transfer, + encodeLzSend, + getBridgeContract, + getBridgeMechanism, + getOftCmd, + parseTokenAmount, + removeDust, +} from "@polypay/shared"; +import { type Hex, createPublicClient, http, parseEther } from "viem"; import { useWalletClient } from "wagmi"; -import { useMetaMultiSigWallet } from "~~/hooks"; +import { useMetaMultiSigWallet, useNetworkTokens } from "~~/hooks"; import { useCreateTransaction, useReserveNonce } from "~~/hooks/api/useTransaction"; import { useGenerateProof } from "~~/hooks/app/useGenerateProof"; import { formatErrorMessage } from "~~/lib/form/utils"; +import scaffoldConfig from "~~/scaffold.config"; import { notification } from "~~/utils/scaffold-eth"; interface TransferParams { @@ -13,6 +30,7 @@ interface TransferParams { amount: string; token: ResolvedToken; contactId?: string | null; + destChainId?: number; } interface UseTransferTransactionOptions { @@ -25,19 +43,21 @@ export const useTransferTransaction = (options?: UseTransferTransactionOptions) const { data: walletClient } = useWalletClient(); const metaMultiSigWallet = useMetaMultiSigWallet(); + const { chainId: sourceChainId } = useNetworkTokens(); const { mutateAsync: createTransaction } = useCreateTransaction(); const { mutateAsync: reserveNonce } = useReserveNonce(); const { generateProof } = useGenerateProof({ onLoadingStateChange: setLoadingState, }); - const transfer = async ({ recipient, amount, token, contactId }: TransferParams) => { + const transfer = async ({ recipient, amount, token, contactId, destChainId }: TransferParams) => { if (!walletClient || !metaMultiSigWallet) { notification.error("Wallet not connected"); return; } const isNativeETH = token.address === ZERO_ADDRESS; + const isCrossChain = destChainId !== undefined && destChainId !== sourceChainId; setIsLoading(true); try { @@ -55,9 +75,31 @@ export const useTransferTransaction = (options?: UseTransferTransactionOptions) // 4. Calculate txHash (different for ETH vs ERC20) let txHash: `0x${string}`; + let bridgeFee: string | undefined; + let bridgeMinAmount: string | undefined; - if (isNativeETH) { - // ETH: to = recipient, value = amount, data = 0x + if (isCrossChain) { + const bridgeResult = await buildBridgeParams({ + recipient, + valueInSmallestUnit, + token, + isNativeETH, + sourceChainId, + destChainId, + walletAddress: metaMultiSigWallet.address, + setLoadingState, + }); + + bridgeFee = bridgeResult.bridgeFee; + bridgeMinAmount = bridgeResult.bridgeMinAmount; + + txHash = (await metaMultiSigWallet.read.getTransactionHash([ + BigInt(nonce), + bridgeResult.to as `0x${string}`, + BigInt(bridgeResult.value), + bridgeResult.data as `0x${string}`, + ])) as `0x${string}`; + } else if (isNativeETH) { txHash = (await metaMultiSigWallet.read.getTransactionHash([ BigInt(nonce), recipient as `0x${string}`, @@ -89,6 +131,9 @@ export const useTransferTransaction = (options?: UseTransferTransactionOptions) value: valueInSmallestUnit, tokenAddress: isNativeETH ? undefined : token.address, contactId: contactId || undefined, + destChainId: isCrossChain ? destChainId : undefined, + bridgeFee, + bridgeMinAmount, proof: Array.from(proof), publicInputs, nullifier: nullifier.toString(), @@ -97,7 +142,10 @@ export const useTransferTransaction = (options?: UseTransferTransactionOptions) }); if (result) { - notification.success("Transfer transaction created! Waiting for approvals."); + const msg = isCrossChain + ? "Cross-chain transfer created! Waiting for approvals." + : "Transfer transaction created! Waiting for approvals."; + notification.success(msg); } options?.onSuccess?.(); @@ -116,3 +164,130 @@ export const useTransferTransaction = (options?: UseTransferTransactionOptions) loadingState, }; }; + +// ─── Bridge param builder (stateless helper) ─── + +async function buildBridgeParams({ + recipient, + valueInSmallestUnit, + token, + isNativeETH, + sourceChainId, + destChainId, + walletAddress, + setLoadingState, +}: { + recipient: string; + valueInSmallestUnit: string; + token: ResolvedToken; + isNativeETH: boolean; + sourceChainId: number; + destChainId: number; + walletAddress: string; + setLoadingState: (s: string) => void; +}): Promise<{ to: string; value: string; data: Hex; bridgeFee?: string; bridgeMinAmount?: string }> { + const tokenSymbol = isNativeETH ? "ETH" : token.symbol; + const mechanism = getBridgeMechanism(sourceChainId, destChainId, tokenSymbol); + + if (!mechanism) { + throw new Error(`No bridge route for ${tokenSymbol} from ${sourceChainId} to ${destChainId}`); + } + + const amount = BigInt(valueInSmallestUnit); + + if (mechanism === "OP_STACK") { + const bridgeAddress = OP_BRIDGE_ADDRESSES[sourceChainId]; + if (!bridgeAddress) throw new Error(`No OP bridge on chain ${sourceChainId}`); + + return { + to: bridgeAddress, + value: amount.toString(), + data: encodeBridgeETHTo(recipient), + }; + } + + // LAYERZERO + const dstEid = LZ_ENDPOINT_IDS[destChainId]; + if (!dstEid) throw new Error(`No LZ endpoint for chain ${destChainId}`); + + const oftEntry = getBridgeContract(tokenSymbol, sourceChainId); + if (!oftEntry) throw new Error(`No OFT contract for ${tokenSymbol} on chain ${sourceChainId}`); + + const chain = scaffoldConfig.targetNetworks.find(n => n.id === sourceChainId); + if (!chain) throw new Error(`Chain ${sourceChainId} not in target networks`); + + const publicClient = createPublicClient({ chain, transport: http() }); + + const oftCmd = getOftCmd(oftEntry); + const toBytes32 = `0x${recipient.slice(2).padStart(64, "0")}` as `0x${string}`; + + // For Stargate OFTs, call quoteOFT to get amountReceivedLD (accounts for protocol fee). + // For standard OFTs, removeDust is sufficient. + let minAmount: bigint; + if (oftEntry.stargate) { + setLoadingState("Quoting Stargate fee..."); + const quoteResult = (await publicClient.readContract({ + address: oftEntry.address as `0x${string}`, + abi: OFT_ABI, + functionName: "quoteOFT", + args: [ + { + dstEid, + to: toBytes32, + amountLD: amount, + minAmountLD: 0n, + extraOptions: "0x" as Hex, + composeMsg: "0x" as Hex, + oftCmd: oftCmd as Hex, + }, + ], + })) as [ + { minAmountLD: bigint; maxAmountLD: bigint }, + { feeAmountLD: bigint; description: string }[], + { amountSentLD: bigint; amountReceivedLD: bigint }, + ]; + minAmount = quoteResult[2].amountReceivedLD; + } else { + minAmount = removeDust(amount, token.decimals); + } + + const sendParam = { + dstEid, + to: toBytes32, + amountLD: amount, + minAmountLD: minAmount, + extraOptions: "0x" as Hex, + composeMsg: "0x" as Hex, + oftCmd: oftCmd as Hex, + }; + + setLoadingState("Quoting LayerZero fee..."); + const quotedFee = (await publicClient.readContract({ + address: oftEntry.address as `0x${string}`, + abi: OFT_ABI, + functionName: "quoteSend", + args: [sendParam, false], + })) as { nativeFee: bigint; lzTokenFee: bigint }; + + const nativeFee = quotedFee.nativeFee; + + const lzSendData = encodeLzSend(dstEid, recipient, amount, minAmount, nativeFee, walletAddress, oftCmd as Hex); + + if (oftEntry.type === "adapter") { + return { + to: walletAddress, + value: "0", + data: encodeApproveAndCall(token.address, oftEntry.address, amount, oftEntry.address, nativeFee, lzSendData), + bridgeFee: nativeFee.toString(), + bridgeMinAmount: minAmount.toString(), + }; + } + + return { + to: oftEntry.address, + value: nativeFee.toString(), + data: lzSendData, + bridgeFee: nativeFee.toString(), + bridgeMinAmount: minAmount.toString(), + }; +} diff --git a/packages/nextjs/utils/format.ts b/packages/nextjs/utils/format.ts index ebba9d1a..b329bd45 100644 --- a/packages/nextjs/utils/format.ts +++ b/packages/nextjs/utils/format.ts @@ -1,4 +1,4 @@ -import { formatTokenAmount, getTokenByAddress } from "@polypay/shared"; +import { formatDisplayAmount, getTokenByAddress } from "@polypay/shared"; /** * Format amount with token symbol @@ -10,7 +10,7 @@ import { formatTokenAmount, getTokenByAddress } from "@polypay/shared"; export function formatAmount(amount: string, chainId: number, tokenAddress?: string | null): string { try { const token = getTokenByAddress(tokenAddress, chainId); - const formatted = formatTokenAmount(amount, token.decimals); + const formatted = formatDisplayAmount(amount, token.decimals, token.symbol); return `${formatted} ${token.symbol}`; } catch { return amount; diff --git a/packages/shared/src/constants/bridge.ts b/packages/shared/src/constants/bridge.ts new file mode 100644 index 00000000..8c6e8b99 --- /dev/null +++ b/packages/shared/src/constants/bridge.ts @@ -0,0 +1,177 @@ +// ─── Cross-chain version gate ─── + +export const CROSS_CHAIN_MIN_CONTRACT_VERSION = 2; + +export function isCrossChainEnabled(contractVersion: number): boolean { + return contractVersion >= CROSS_CHAIN_MIN_CONTRACT_VERSION; +} + +// Chain IDs (mirrored from token.ts for bridge-local use) +const HORIZEN_MAINNET = 26514; +const HORIZEN_TESTNET = 2651420; +const BASE_MAINNET = 8453; +const BASE_SEPOLIA = 84532; + +// Mainnet chain pairs +const BASE_CHAINS = [BASE_MAINNET, BASE_SEPOLIA] as const; +const HORIZEN_CHAINS = [HORIZEN_MAINNET, HORIZEN_TESTNET] as const; + +export type BridgeMechanism = "OP_STACK" | "LAYERZERO"; + +// ─── LayerZero Endpoint IDs (NOT chain IDs) ─── + +export const LZ_ENDPOINT_IDS: Record = { + [BASE_MAINNET]: 30184, + [BASE_SEPOLIA]: 40245, + [HORIZEN_MAINNET]: 30399, + [HORIZEN_TESTNET]: 40435, +}; + +// ─── OP Stack Bridge (Base only, bridging TO Horizen) ─── + +export const OP_BRIDGE_ADDRESSES: Record = { + [BASE_MAINNET]: "0xf4a6cc4171fda694439f856d912777aa6ab05369", + [BASE_SEPOLIA]: "0xc2ce54c609489c44fa46f00b034e53c3cd150eb8", +}; + +export const OP_BRIDGE_MIN_GAS_LIMIT = 200_000; + +// ─── OFT / Adapter addresses per chain per token ─── + +export interface OftContractEntry { + type: "oft" | "adapter"; + address: string; + stargate?: boolean; +} + +// Stargate V2 uses taxi mode (non-empty oftCmd) for immediate delivery. +// Bus mode (empty oftCmd) requires an active bus driver and may fail. +export const STARGATE_TAXI_OFT_CMD = "0x01" as const; + +export function getOftCmd(entry: OftContractEntry | null): `0x${string}` { + return entry?.stargate ? STARGATE_TAXI_OFT_CMD : "0x"; +} + +export const BRIDGE_CONTRACTS: Record< + string, + Record +> = { + ZEN: { + [BASE_MAINNET]: { + type: "adapter", + address: "0x57da2D504bf8b83Ef304759d9f2648522D7a9280", + }, + [HORIZEN_MAINNET]: { + type: "oft", + address: "0x57da2D504bf8b83Ef304759d9f2648522D7a9280", + }, + [BASE_SEPOLIA]: { + type: "adapter", + address: "0x2ead4B0beBD8e54F9B7cC1007DF4c44a27b9a339", + }, + [HORIZEN_TESTNET]: { + type: "oft", + address: "0xb06EC4ce262D8dbDc24Fac87479A49A7DC4cFb87", + }, + }, + USDC: { + [BASE_MAINNET]: { + type: "adapter", + address: "0x27a16dc786820b16e5c9028b75b99f6f604b5d26", + stargate: true, + }, + [HORIZEN_MAINNET]: { + type: "adapter", + address: "0x3a1293Bdb83bBbDd5Ebf4fAc96605aD2021BbC0f", + stargate: true, + }, + // No testnet OFT contracts for USDC + }, +}; + +// ─── Chain pairing ─── + +function getPairedChainId(chainId: number): number | null { + if (chainId === BASE_MAINNET) return HORIZEN_MAINNET; + if (chainId === HORIZEN_MAINNET) return BASE_MAINNET; + if (chainId === BASE_SEPOLIA) return HORIZEN_TESTNET; + if (chainId === HORIZEN_TESTNET) return BASE_SEPOLIA; + return null; +} + +function isBaseChain(chainId: number): boolean { + return (BASE_CHAINS as readonly number[]).includes(chainId); +} + +function isTestnet(chainId: number): boolean { + return chainId === BASE_SEPOLIA || chainId === HORIZEN_TESTNET; +} + +// ─── Route availability ─── + +/** + * Returns chain IDs the user can transfer to for a given source chain + token. + * Always includes the source chain itself (same-chain transfer). + */ +export function getAvailableDestChains( + srcChainId: number, + tokenSymbol: string, +): number[] { + const paired = getPairedChainId(srcChainId); + const result = [srcChainId]; + + if (!paired) return result; + + if (tokenSymbol === "ETH") { + // ETH bridge only Base → Horizen (OP Stack). Horizen → Base excluded cause rollups mechanism. + if (isBaseChain(srcChainId)) { + result.push(paired); + } + return result; + } + + if (tokenSymbol === "USDC") { + // USDC OFT only on mainnet + if (!isTestnet(srcChainId)) { + result.push(paired); + } + return result; + } + + // ZEN: bidirectional on all environments + if (tokenSymbol === "ZEN") { + result.push(paired); + } + + return result; +} + +export function isBridgeAvailable( + srcChainId: number, + destChainId: number, + tokenSymbol: string, +): boolean { + if (srcChainId === destChainId) return false; + return getAvailableDestChains(srcChainId, tokenSymbol).includes(destChainId); +} + +export function getBridgeMechanism( + srcChainId: number, + destChainId: number, + tokenSymbol: string, +): BridgeMechanism | null { + if (!isBridgeAvailable(srcChainId, destChainId, tokenSymbol)) return null; + + if (tokenSymbol === "ETH") return "OP_STACK"; + return "LAYERZERO"; +} + +/** + * Get the OFT/Adapter contract address for a token on a specific chain. + */ +export function getBridgeContract( + tokenSymbol: string, + chainId: number, +): OftContractEntry | null { + return BRIDGE_CONTRACTS[tokenSymbol]?.[chainId] ?? null; +} diff --git a/packages/shared/src/constants/index.ts b/packages/shared/src/constants/index.ts index e07a903a..93bfc6d0 100644 --- a/packages/shared/src/constants/index.ts +++ b/packages/shared/src/constants/index.ts @@ -2,3 +2,4 @@ export * from "./socket-events"; export * from "./room"; export * from "./token"; export * from "./campaign"; +export * from "./bridge"; diff --git a/packages/shared/src/constants/token.ts b/packages/shared/src/constants/token.ts index 2cf7e3ea..ccab083a 100644 --- a/packages/shared/src/constants/token.ts +++ b/packages/shared/src/constants/token.ts @@ -39,7 +39,7 @@ export const NATIVE_ETH: Token = { export const ZEN_TOKEN: Token = { addresses: { - [HORIZEN_TESTNET]: "0x4b36cb6E7c257E9aA246122a997be0F7Dc1eFCd1", + [HORIZEN_TESTNET]: "0xb06EC4ce262D8dbDc24Fac87479A49A7DC4cFb87", [HORIZEN_MAINNET]: "0x57da2D504bf8b83Ef304759d9f2648522D7a9280", [BASE_MAINNET]: "0xf43eB8De897Fbc7F2502483B2Bef7Bb9EA179229", [BASE_SEPOLIA]: "0x107fdE93838e3404934877935993782F977324BB", @@ -166,6 +166,44 @@ export function formatTokenAmount(amount: string, decimals: number): string { return `${intPart}.${decStr}`; } +const STABLECOINS = ["USDC", "USDT"]; + +function getDisplayPrecision(value: number, tokenSymbol?: string): number { + if (tokenSymbol && STABLECOINS.includes(tokenSymbol.toUpperCase())) return 2; + if (value >= 1000) return 2; + if (value >= 100) return 4; + if (value >= 10) return 5; + if (value >= 1) return 6; + return 8; +} + +export function formatDisplayValue( + value: string, + tokenSymbol?: string, +): string { + const num = parseFloat(value); + if (isNaN(num) || num === 0) return "0"; + + const isStable = + tokenSymbol && STABLECOINS.includes(tokenSymbol.toUpperCase()); + const precision = getDisplayPrecision(num, tokenSymbol); + + if (num > 0 && isStable && num < 0.01) return "< 0.01"; + if (num > 0 && num < 1e-8) return "< 0.00000001"; + + const formatted = num.toFixed(precision); + return formatted.replace(/(\.\d*?)0+$/, "$1").replace(/\.$/, ""); +} + +export function formatDisplayAmount( + amount: string, + decimals: number, + tokenSymbol?: string, +): string { + const full = formatTokenAmount(amount, decimals); + return formatDisplayValue(full, tokenSymbol); +} + export function parseTokenAmount(amount: string, decimals: number): string { const [intPart, decPart = ""] = amount.split("."); const paddedDec = decPart.padEnd(decimals, "0").slice(0, decimals); diff --git a/packages/shared/src/contracts/MetaMultiSigWallet.ts b/packages/shared/src/contracts/MetaMultiSigWallet.ts index 1a3e8d9b..9c54e96e 100644 --- a/packages/shared/src/contracts/MetaMultiSigWallet.ts +++ b/packages/shared/src/contracts/MetaMultiSigWallet.ts @@ -169,6 +169,62 @@ export const METAMULTISIG_ABI = [ stateMutability: "nonpayable", type: "function", }, + { + inputs: [ + { + internalType: "address", + name: "token", + type: "address", + }, + { + internalType: "address", + name: "spender", + type: "address", + }, + { + internalType: "uint256", + name: "approveAmount", + type: "uint256", + }, + { + internalType: "address", + name: "callTarget", + type: "address", + }, + { + internalType: "uint256", + name: "callValue", + type: "uint256", + }, + { + internalType: "bytes", + name: "callData", + type: "bytes", + }, + ], + name: "approveAndCall", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "uint256[]", + name: "newCommitments", + type: "uint256[]", + }, + { + internalType: "uint256", + name: "newSigRequired", + type: "uint256", + }, + ], + name: "addSigners", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, { inputs: [ { @@ -518,4 +574,4 @@ export const METAMULTISIG_ABI = [ // ============ Bytecode ============ export const METAMULTISIG_BYTECODE = - "0x60c06040523480156200001157600080fd5b50604051620024a0380380620024a08339810160408190526200003491620002db565b6001600160a01b038516620000905760405162461bcd60e51b815260206004820152601360248201527f496e76616c6964207a6b7620616464726573730000000000000000000000000060448201526064015b60405180910390fd5b60008111620000e25760405162461bcd60e51b815260206004820152601e60248201527f4d757374206265206e6f6e2d7a65726f20736967732072657175697265640000604482015260640162000087565b6000825111620001355760405162461bcd60e51b815260206004820152601660248201527f4e656564206174206c656173742031207369676e657200000000000000000000604482015260640162000087565b8151811115620001885760405162461bcd60e51b815260206004820152601660248201527f5369677320726571756972656420746f6f206869676800000000000000000000604482015260640162000087565b6001600160a01b03851660805260a0849052600083815560018290555b8251811015620002b957828181518110620001c457620001c4620003e6565b6020026020010151600003620002125760405162461bcd60e51b8152602060048201526012602482015271125b9d985b1a590818dbdb5b5a5d1b595b9d60721b604482015260640162000087565b6002838281518110620002295762000229620003e6565b6020908102919091018101518254600181018455600093845291909220015582518390829081106200025f576200025f620003e6565b60200260200101517f33129f9e36e06e860f22ce83276273f4d83d31e9fd4a0d3a4dbac9d2da4f9d0d60016040516200029c911515815260200190565b60405180910390a280620002b081620003fc565b915050620001a5565b50505050505062000424565b634e487b7160e01b600052604160045260246000fd5b600080600080600060a08688031215620002f457600080fd5b85516001600160a01b03811681146200030c57600080fd5b602087810151604089015160608a01519398509096509450906001600160401b03808211156200033b57600080fd5b818901915089601f8301126200035057600080fd5b815181811115620003655762000365620002c5565b8060051b604051601f19603f830116810181811085821117156200038d576200038d620002c5565b60405291825284820192508381018501918c831115620003ac57600080fd5b938501935b82851015620003cc57845184529385019392850192620003b1565b809750505050505050608086015190509295509295909350565b634e487b7160e01b600052603260045260246000fd5b6000600182016200041d57634e487b7160e01b600052601160045260246000fd5b5060010190565b60805160a0516120486200045860003960008181610221015261167b0152600081816102d5015261173301526120486000f3fe6080604052600436106101235760003560e01c80639a8a0592116100a0578063aad2406111610064578063aad24061146103d1578063ce757d2914610401578063e4cf5a2c14610417578063eaaba5591461044b578063f1ea66d41461046b57600080fd5b80639a8a05921461032f5780639e4e731814610345578063a0c1deb41461035a578063a8898a201461036f578063a8d2c852146103b157600080fd5b8063545a4a3c116100e7578063545a4a3c1461024357806364451212146102635780636717e41c146102835780637ee68373146102c357806388d695b21461030f57600080fd5b80631b108c07146101695780633034a7421461019f5780634791ca34146101c157806349ce8997146101e15780634fe840f51461020f57600080fd5b36610164576040805134815247602082015233917f90890809c654f11d6e72a28fa60149770a0d11ec6c92319d6ceb2bb0a4ea1a15910160405180910390a2005b600080fd5b34801561017557600080fd5b5061018961018436600461185a565b61048d565b604051610196919061196c565b60405180910390f35b3480156101ab57600080fd5b506101bf6101ba366004611986565b610835565b005b3480156101cd57600080fd5b506101bf6101dc36600461199f565b61089b565b3480156101ed57600080fd5b506102016101fc366004611986565b610b29565b604051908152602001610196565b34801561021b57600080fd5b506102017f000000000000000000000000000000000000000000000000000000000000000081565b34801561024f57600080fd5b5061020161025e366004611a01565b610b4a565b34801561026f57600080fd5b506101bf61027e366004611ad6565b610b87565b34801561028f57600080fd5b506102b361029e366004611986565b60036020526000908152604090205460ff1681565b6040519015158152602001610196565b3480156102cf57600080fd5b506102f77f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b039091168152602001610196565b34801561031b57600080fd5b506101bf61032a366004611b70565b610f44565b34801561033b57600080fd5b5061020160005481565b34801561035157600080fd5b50610201611128565b34801561036657600080fd5b50600254610201565b34801561037b57600080fd5b5061020160405169756c747261706c6f6e6b60b01b6020820152602a016040516020818303038152906040528051906020012081565b3480156103bd57600080fd5b506101bf6103cc36600461199f565b611188565b3480156103dd57600080fd5b506102b36103ec366004611986565b60046020526000908152604090205460ff1681565b34801561040d57600080fd5b5061020160015481565b34801561042357600080fd5b506102017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181565b34801561045757600080fd5b50610201610466366004611bdc565b61145e565b34801561047757600080fd5b5061048061154f565b6040516101969190611bfe565b60008781526003602052604090205460609060ff16156104e95760405162461bcd60e51b8152602060048201526012602482015271139bdb98d948185b1c9958591e481d5cd95960721b60448201526064015b60405180910390fd5b60015482101561052f5760405162461bcd60e51b81526020600482015260116024820152704e6f7420656e6f7567682070726f6f667360781b60448201526064016104e0565b6000805460405161054e9130918c908c908c908c908c90602001611c42565b60405160208183030381529060405280519060200120905060005b8381101561072f576004600086868481811061058757610587611c94565b90506020028101906105999190611caa565b60209081013582528101919091526040016000205460ff16156105f75760405162461bcd60e51b8152602060048201526016602482015275139d5b1b1a599a595c88185b1c9958591e481d5cd95960521b60448201526064016104e0565b61062485858381811061060c5761060c611c94565b905060200281019061061e9190611caa565b356115a7565b6106675760405162461bcd60e51b81526020600482015260146024820152732737ba10309031bab93932b73a1039b4b3b732b960611b60448201526064016104e0565b6106948286868481811061067d5761067d611c94565b905060200281019061068f9190611caa565b6115fd565b6106d05760405162461bcd60e51b815260206004820152600d60248201526c24b73b30b634b210383937b7b360991b60448201526064016104e0565b6001600460008787858181106106e8576106e8611c94565b90506020028101906106fa9190611caa565b6020908101358252810191909152604001600020805460ff19169115159190911790558061072781611ce0565b915050610569565b50600089815260036020526040808220805460ff191660011790555181906001600160a01b038b16908a90610767908b908b90611cf9565b60006040518083038185875af1925050503d80600081146107a4576040519150601f19603f3d011682016040523d82523d6000602084013e6107a9565b606091505b5091509150816107e75760405162461bcd60e51b8152602060048201526009602482015268151e0819985a5b195960ba1b60448201526064016104e0565b8a7f1654479f61781d185c419742a20e599a227ae1840317c8a74ceda5eb6166b8268b8b8b8b8660405161081f959493929190611d09565b60405180910390a29a9950505050505050505050565b3330146108545760405162461bcd60e51b81526004016104e090611d6a565b600081116108745760405162461bcd60e51b81526004016104e090611d8c565b6002548111156108965760405162461bcd60e51b81526004016104e090611dc3565b600155565b3330146108ba5760405162461bcd60e51b81526004016104e090611d6a565b816108f55760405162461bcd60e51b815260206004820152600b60248201526a456d70747920617272617960a81b60448201526064016104e0565b60025482106109465760405162461bcd60e51b815260206004820152601960248201527f43616e6e6f742072656d6f766520616c6c207369676e6572730000000000000060448201526064016104e0565b600081116109665760405162461bcd60e51b81526004016104e090611d8c565b600254610974908390611df3565b8111156109935760405162461bcd60e51b81526004016104e090611dc3565b60005b82811015610b21576000805b600254811015610ac9578585848181106109be576109be611c94565b90506020020135600282815481106109d8576109d8611c94565b906000526020600020015403610ab757600280546109f890600190611df3565b81548110610a0857610a08611c94565b906000526020600020015460028281548110610a2657610a26611c94565b6000918252602090912001556002805480610a4357610a43611e06565b6001900381819060005260206000200160009055905560019150858584818110610a6f57610a6f611c94565b905060200201357f33129f9e36e06e860f22ce83276273f4d83d31e9fd4a0d3a4dbac9d2da4f9d0d6000604051610aaa911515815260200190565b60405180910390a2610ac9565b80610ac181611ce0565b9150506109a2565b5080610b0e5760405162461bcd60e51b815260206004820152601460248201527310dbdb5b5a5d1b595b9d081b9bdd08199bdd5b9960621b60448201526064016104e0565b5080610b1981611ce0565b915050610996565b506001555050565b60028181548110610b3957600080fd5b600091825260209091200154905081565b60008054604051610b679130918890889088908890602001611e1c565b604051602081830303815290604052805190602001209050949350505050565b333014610ba65760405162461bcd60e51b81526004016104e090611d6a565b848314610bc55760405162461bcd60e51b81526004016104e090611e77565b848114610be45760405162461bcd60e51b81526004016104e090611e77565b84610c1f5760405162461bcd60e51b815260206004820152600b60248201526a08adae0e8f240c4c2e8c6d60ab1b60448201526064016104e0565b60005b85811015610f3b576000878783818110610c3e57610c3e611c94565b9050602002016020810190610c539190611ea0565b6001600160a01b031603610c9d5760405162461bcd60e51b8152602060048201526011602482015270125b9d985b1a59081c9958da5c1a595b9d607a1b60448201526064016104e0565b6000838383818110610cb157610cb1611c94565b9050602002016020810190610cc69190611ea0565b6001600160a01b031603610db1576000878783818110610ce857610ce8611c94565b9050602002016020810190610cfd9190611ea0565b6001600160a01b0316868684818110610d1857610d18611c94565b9050602002013560405160006040518083038185875af1925050503d8060008114610d5f576040519150601f19603f3d011682016040523d82523d6000602084013e610d64565b606091505b5050905080610dab5760405162461bcd60e51b8152602060048201526013602482015272115512081d1c985b9cd9995c8819985a5b1959606a1b60448201526064016104e0565b50610f29565b600080848484818110610dc657610dc6611c94565b9050602002016020810190610ddb9190611ea0565b6001600160a01b0316898985818110610df657610df6611c94565b9050602002016020810190610e0b9190611ea0565b888886818110610e1d57610e1d611c94565b6040516001600160a01b039094166024850152602002919091013560448301525060640160408051601f198184030181529181526020820180516001600160e01b031663a9059cbb60e01b17905251610e769190611ebb565b6000604051808303816000865af19150503d8060008114610eb3576040519150601f19603f3d011682016040523d82523d6000602084013e610eb8565b606091505b5091509150818015610ee2575080511580610ee2575080806020019051810190610ee29190611ecd565b610f265760405162461bcd60e51b8152602060048201526015602482015274115490cc8c081d1c985b9cd9995c8819985a5b1959605a1b60448201526064016104e0565b50505b80610f3381611ce0565b915050610c22565b50505050505050565b333014610f635760405162461bcd60e51b81526004016104e090611d6a565b828114610f825760405162461bcd60e51b81526004016104e090611e77565b82610fbd5760405162461bcd60e51b815260206004820152600b60248201526a08adae0e8f240c4c2e8c6d60ab1b60448201526064016104e0565b60005b83811015611121576000858583818110610fdc57610fdc611c94565b9050602002016020810190610ff19190611ea0565b6001600160a01b03160361103b5760405162461bcd60e51b8152602060048201526011602482015270125b9d985b1a59081c9958da5c1a595b9d607a1b60448201526064016104e0565b600085858381811061104f5761104f611c94565b90506020020160208101906110649190611ea0565b6001600160a01b031684848481811061107f5761107f611c94565b9050602002013560405160006040518083038185875af1925050503d80600081146110c6576040519150601f19603f3d011682016040523d82523d6000602084013e6110cb565b606091505b505090508061110e5760405162461bcd60e51b815260206004820152600f60248201526e151c985b9cd9995c8819985a5b1959608a1b60448201526064016104e0565b508061111981611ce0565b915050610fc0565b5050505050565b604080516000815260208101918290526002916111459190611ebb565b602060405180830381855afa158015611162573d6000803e3d6000fd5b5050506040513d601f19601f820116820180604052508101906111859190611eef565b81565b3330146111a75760405162461bcd60e51b81526004016104e090611d6a565b816111e25760405162461bcd60e51b815260206004820152600b60248201526a456d70747920617272617960a81b60448201526064016104e0565b600081116112025760405162461bcd60e51b81526004016104e090611d8c565b600254611210908390611f08565b81111561122f5760405162461bcd60e51b81526004016104e090611dc3565b60005b82811015610b215783838281811061124c5761124c611c94565b905060200201356000036112975760405162461bcd60e51b8152602060048201526012602482015271125b9d985b1a590818dbdb5b5a5d1b595b9d60721b60448201526064016104e0565b60005b60025481101561132e578484838181106112b6576112b6611c94565b90506020020135600282815481106112d0576112d0611c94565b90600052602060002001540361131c5760405162461bcd60e51b8152602060048201526011602482015270436f6d6d69746d656e742065786973747360781b60448201526064016104e0565b8061132681611ce0565b91505061129a565b5060005b818110156113c05784848381811061134c5761134c611c94565b9050602002013585858381811061136557611365611c94565b90506020020135036113ae5760405162461bcd60e51b8152602060048201526012602482015271111d5c1b1a58d85d19481a5b881a5b9c1d5d60721b60448201526064016104e0565b806113b881611ce0565b915050611332565b5060028484838181106113d5576113d5611c94565b8354600181018555600094855260209485902091909402929092013591909201555083838281811061140957611409611c94565b905060200201357f33129f9e36e06e860f22ce83276273f4d83d31e9fd4a0d3a4dbac9d2da4f9d0d6001604051611444911515815260200190565b60405180910390a28061145681611ce0565b915050611232565b60008061148b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000185611f1b565b905060006114b97f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000185611f1b565b604080518082018252848152602081018390529051632b0aac7f60e11b8152919250733333333C0A88F9BE4fd23ed0536F9B6c427e3B939163561558fe9161150391600401611f3d565b602060405180830381865af4158015611520573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115449190611eef565b925050505b92915050565b6060600280548060200260200160405190810160405280929190818152602001828054801561159d57602002820191906000526020600020905b815481526020019060010190808311611589575b5050505050905090565b6000805b6002548110156115f45782600282815481106115c9576115c9611c94565b9060005260206000200154036115e25750600192915050565b806115ec81611ce0565b9150506115ab565b50600092915050565b60008061160b84600161145e565b604080516020808201849052863582840152868101356060808401919091528351808403909101815260808301845269756c747261706c6f6e6b60b01b60a08401528351608a81850301815260aa840180865281519190930120600080845260ca909401948590529495509391927f0000000000000000000000000000000000000000000000000000000000000000916002916116a89190611ebb565b602060405180830381855afa1580156116c5573d6000803e3d6000fd5b5050506040513d601f19601f820116820180604052508101906116e89190611eef565b8480519060200120604051602001611719949392919093845260208401929092526040830152606082015260800190565b6040516020818303038152906040528051906020012090507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663a78f9e36866060013587604001358489806080019061177b9190611f6e565b8b60a001358c60c001356040518863ffffffff1660e01b81526004016117a79796959493929190611fb8565b602060405180830381865afa1580156117c4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117e89190611ecd565b9695505050505050565b80356001600160a01b038116811461180957600080fd5b919050565b60008083601f84011261182057600080fd5b50813567ffffffffffffffff81111561183857600080fd5b6020830191508360208260051b850101111561185357600080fd5b9250929050565b600080600080600080600060a0888a03121561187557600080fd5b87359650611885602089016117f2565b955060408801359450606088013567ffffffffffffffff808211156118a957600080fd5b818a0191508a601f8301126118bd57600080fd5b8135818111156118cc57600080fd5b8b60208285010111156118de57600080fd5b6020830196508095505060808a01359150808211156118fc57600080fd5b506119098a828b0161180e565b989b979a50959850939692959293505050565b60005b8381101561193757818101518382015260200161191f565b50506000910152565b6000815180845261195881602086016020860161191c565b601f01601f19169290920160200192915050565b60208152600061197f6020830184611940565b9392505050565b60006020828403121561199857600080fd5b5035919050565b6000806000604084860312156119b457600080fd5b833567ffffffffffffffff8111156119cb57600080fd5b6119d78682870161180e565b909790965060209590950135949350505050565b634e487b7160e01b600052604160045260246000fd5b60008060008060808587031215611a1757600080fd5b84359350611a27602086016117f2565b925060408501359150606085013567ffffffffffffffff80821115611a4b57600080fd5b818701915087601f830112611a5f57600080fd5b813581811115611a7157611a716119eb565b604051601f8201601f19908116603f01168101908382118183101715611a9957611a996119eb565b816040528281528a6020848701011115611ab257600080fd5b82602086016020830137600060208483010152809550505050505092959194509250565b60008060008060008060608789031215611aef57600080fd5b863567ffffffffffffffff80821115611b0757600080fd5b611b138a838b0161180e565b90985096506020890135915080821115611b2c57600080fd5b611b388a838b0161180e565b90965094506040890135915080821115611b5157600080fd5b50611b5e89828a0161180e565b979a9699509497509295939492505050565b60008060008060408587031215611b8657600080fd5b843567ffffffffffffffff80821115611b9e57600080fd5b611baa8883890161180e565b90965094506020870135915080821115611bc357600080fd5b50611bd08782880161180e565b95989497509550505050565b60008060408385031215611bef57600080fd5b50508035926020909101359150565b6020808252825182820181905260009190848201906040850190845b81811015611c3657835183529284019291840191600101611c1a565b50909695505050505050565b60006bffffffffffffffffffffffff19808a60601b168352886014840152876034840152808760601b166054840152508460688301528284608884013750600091016088019081529695505050505050565b634e487b7160e01b600052603260045260246000fd5b6000823560de19833603018112611cc057600080fd5b9190910192915050565b634e487b7160e01b600052601160045260246000fd5b600060018201611cf257611cf2611cca565b5060010190565b8183823760009101908152919050565b6001600160a01b0386168152602081018590526080604082018190528101839052828460a0830137600060a084830101526000601f19601f850116820160a0838203016060840152611d5e60a0820185611940565b98975050505050505050565b6020808252600890820152672737ba1029b2b63360c11b604082015260600190565b6020808252601e908201527f4d757374206265206e6f6e2d7a65726f20736967732072657175697265640000604082015260600190565b6020808252601690820152750a6d2cee640e4cae2ead2e4cac840e8dede40d0d2ced60531b604082015260600190565b8181038181111561154957611549611cca565b634e487b7160e01b600052603160045260246000fd5b60006bffffffffffffffffffffffff19808960601b168352876014840152866034840152808660601b166054840152508360688301528251611e6581608885016020870161191c565b91909101608801979650505050505050565b6020808252600f908201526e098cadccee8d040dad2e6dac2e8c6d608b1b604082015260600190565b600060208284031215611eb257600080fd5b61197f826117f2565b60008251611cc081846020870161191c565b600060208284031215611edf57600080fd5b8151801515811461197f57600080fd5b600060208284031215611f0157600080fd5b5051919050565b8082018082111561154957611549611cca565b600082611f3857634e487b7160e01b600052601260045260246000fd5b500690565b60408101818360005b6002811015611f65578151835260209283019290910190600101611f46565b50505092915050565b6000808335601e19843603018112611f8557600080fd5b83018035915067ffffffffffffffff821115611fa057600080fd5b6020019150600581901b360382131561185357600080fd5b87815286602082015285604082015260c060608201528360c0820152600060018060fb1b03851115611fe957600080fd5b8460051b808760e085013760808301949094525060a08101919091520160e0019594505050505056fea2646970667358221220925328ba1cb1d89f7ed11053888b9080bbb4027159fb1003d3575f3e6578fd1464736f6c63430008140033"; + "0x60c06040523480156200001157600080fd5b5060405162002721380380620027218339810160408190526200003491620002db565b6001600160a01b038516620000905760405162461bcd60e51b815260206004820152601360248201527f496e76616c6964207a6b7620616464726573730000000000000000000000000060448201526064015b60405180910390fd5b60008111620000e25760405162461bcd60e51b815260206004820152601e60248201527f4d757374206265206e6f6e2d7a65726f20736967732072657175697265640000604482015260640162000087565b6000825111620001355760405162461bcd60e51b815260206004820152601660248201527f4e656564206174206c656173742031207369676e657200000000000000000000604482015260640162000087565b8151811115620001885760405162461bcd60e51b815260206004820152601660248201527f5369677320726571756972656420746f6f206869676800000000000000000000604482015260640162000087565b6001600160a01b03851660805260a0849052600083815560018290555b8251811015620002b957828181518110620001c457620001c4620003e6565b6020026020010151600003620002125760405162461bcd60e51b8152602060048201526012602482015271125b9d985b1a590818dbdb5b5a5d1b595b9d60721b604482015260640162000087565b6002838281518110620002295762000229620003e6565b6020908102919091018101518254600181018455600093845291909220015582518390829081106200025f576200025f620003e6565b60200260200101517f33129f9e36e06e860f22ce83276273f4d83d31e9fd4a0d3a4dbac9d2da4f9d0d60016040516200029c911515815260200190565b60405180910390a280620002b081620003fc565b915050620001a5565b50505050505062000424565b634e487b7160e01b600052604160045260246000fd5b600080600080600060a08688031215620002f457600080fd5b85516001600160a01b03811681146200030c57600080fd5b602087810151604089015160608a01519398509096509450906001600160401b03808211156200033b57600080fd5b818901915089601f8301126200035057600080fd5b815181811115620003655762000365620002c5565b8060051b604051601f19603f830116810181811085821117156200038d576200038d620002c5565b60405291825284820192508381018501918c831115620003ac57600080fd5b938501935b82851015620003cc57845184529385019392850192620003b1565b809750505050505050608086015190509295509295909350565b634e487b7160e01b600052603260045260246000fd5b6000600182016200041d57634e487b7160e01b600052601160045260246000fd5b5060010190565b60805160a0516122c9620004586000396000818161024c0152611872015260008181610300015261192a01526122c96000f3fe60806040526004361061012e5760003560e01c806388d695b2116100ab578063a8d2c8521161006f578063a8d2c852146103dc578063aad24061146103fc578063ce757d291461042c578063e4cf5a2c14610442578063eaaba55914610476578063f1ea66d41461049657600080fd5b806388d695b21461033a5780639a8a05921461035a5780639e4e731814610370578063a0c1deb414610385578063a8898a201461039a57600080fd5b80634fe840f5116100f25780634fe840f51461023a578063545a4a3c1461026e578063644512121461028e5780636717e41c146102ae5780637ee68373146102ee57600080fd5b80631b108c07146101745780633034a742146101aa5780633a624581146101cc5780634791ca34146101ec57806349ce89971461020c57600080fd5b3661016f576040805134815247602082015233917f90890809c654f11d6e72a28fa60149770a0d11ec6c92319d6ceb2bb0a4ea1a15910160405180910390a2005b600080fd5b34801561018057600080fd5b5061019461018f366004611a93565b6104b8565b6040516101a19190611b77565b60405180910390f35b3480156101b657600080fd5b506101ca6101c5366004611b91565b610860565b005b3480156101d857600080fd5b506101ca6101e7366004611baa565b6108c6565b3480156101f857600080fd5b506101ca610207366004611c20565b610a92565b34801561021857600080fd5b5061022c610227366004611b91565b610d20565b6040519081526020016101a1565b34801561024657600080fd5b5061022c7f000000000000000000000000000000000000000000000000000000000000000081565b34801561027a57600080fd5b5061022c610289366004611c82565b610d41565b34801561029a57600080fd5b506101ca6102a9366004611d57565b610d7e565b3480156102ba57600080fd5b506102de6102c9366004611b91565b60036020526000908152604090205460ff1681565b60405190151581526020016101a1565b3480156102fa57600080fd5b506103227f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020016101a1565b34801561034657600080fd5b506101ca610355366004611df1565b61113b565b34801561036657600080fd5b5061022c60005481565b34801561037c57600080fd5b5061022c61131f565b34801561039157600080fd5b5060025461022c565b3480156103a657600080fd5b5061022c60405169756c747261706c6f6e6b60b01b6020820152602a016040516020818303038152906040528051906020012081565b3480156103e857600080fd5b506101ca6103f7366004611c20565b61137f565b34801561040857600080fd5b506102de610417366004611b91565b60046020526000908152604090205460ff1681565b34801561043857600080fd5b5061022c60015481565b34801561044e57600080fd5b5061022c7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181565b34801561048257600080fd5b5061022c610491366004611e5d565b611655565b3480156104a257600080fd5b506104ab611746565b6040516101a19190611e7f565b60008781526003602052604090205460609060ff16156105145760405162461bcd60e51b8152602060048201526012602482015271139bdb98d948185b1c9958591e481d5cd95960721b60448201526064015b60405180910390fd5b60015482101561055a5760405162461bcd60e51b81526020600482015260116024820152704e6f7420656e6f7567682070726f6f667360781b604482015260640161050b565b600080546040516105799130918c908c908c908c908c90602001611ec3565b60405160208183030381529060405280519060200120905060005b8381101561075a57600460008686848181106105b2576105b2611f15565b90506020028101906105c49190611f2b565b60209081013582528101919091526040016000205460ff16156106225760405162461bcd60e51b8152602060048201526016602482015275139d5b1b1a599a595c88185b1c9958591e481d5cd95960521b604482015260640161050b565b61064f85858381811061063757610637611f15565b90506020028101906106499190611f2b565b3561179e565b6106925760405162461bcd60e51b81526020600482015260146024820152732737ba10309031bab93932b73a1039b4b3b732b960611b604482015260640161050b565b6106bf828686848181106106a8576106a8611f15565b90506020028101906106ba9190611f2b565b6117f4565b6106fb5760405162461bcd60e51b815260206004820152600d60248201526c24b73b30b634b210383937b7b360991b604482015260640161050b565b60016004600087878581811061071357610713611f15565b90506020028101906107259190611f2b565b6020908101358252810191909152604001600020805460ff19169115159190911790558061075281611f61565b915050610594565b50600089815260036020526040808220805460ff191660011790555181906001600160a01b038b16908a90610792908b908b90611f7a565b60006040518083038185875af1925050503d80600081146107cf576040519150601f19603f3d011682016040523d82523d6000602084013e6107d4565b606091505b5091509150816108125760405162461bcd60e51b8152602060048201526009602482015268151e0819985a5b195960ba1b604482015260640161050b565b8a7f1654479f61781d185c419742a20e599a227ae1840317c8a74ceda5eb6166b8268b8b8b8b8660405161084a959493929190611f8a565b60405180910390a29a9950505050505050505050565b33301461087f5760405162461bcd60e51b815260040161050b90611feb565b6000811161089f5760405162461bcd60e51b815260040161050b9061200d565b6002548111156108c15760405162461bcd60e51b815260040161050b90612044565b600155565b3330146108e55760405162461bcd60e51b815260040161050b90611feb565b6040516001600160a01b0387811660248301526044820187905260009182918a169060640160408051601f198184030181529181526020820180516001600160e01b031663095ea7b360e01b1790525161093f9190612074565b6000604051808303816000865af19150503d806000811461097c576040519150601f19603f3d011682016040523d82523d6000602084013e610981565b606091505b50915091508180156109ab5750805115806109ab5750808060200190518101906109ab9190612086565b6109e85760405162461bcd60e51b815260206004820152600e60248201526d105c1c1c9bdd994819985a5b195960921b604482015260640161050b565b6000866001600160a01b0316868686604051610a05929190611f7a565b60006040518083038185875af1925050503d8060008114610a42576040519150601f19603f3d011682016040523d82523d6000602084013e610a47565b606091505b5050905080610a865760405162461bcd60e51b815260206004820152600b60248201526a10d85b1b0819985a5b195960aa1b604482015260640161050b565b50505050505050505050565b333014610ab15760405162461bcd60e51b815260040161050b90611feb565b81610aec5760405162461bcd60e51b815260206004820152600b60248201526a456d70747920617272617960a81b604482015260640161050b565b6002548210610b3d5760405162461bcd60e51b815260206004820152601960248201527f43616e6e6f742072656d6f766520616c6c207369676e65727300000000000000604482015260640161050b565b60008111610b5d5760405162461bcd60e51b815260040161050b9061200d565b600254610b6b9083906120a8565b811115610b8a5760405162461bcd60e51b815260040161050b90612044565b60005b82811015610d18576000805b600254811015610cc057858584818110610bb557610bb5611f15565b9050602002013560028281548110610bcf57610bcf611f15565b906000526020600020015403610cae5760028054610bef906001906120a8565b81548110610bff57610bff611f15565b906000526020600020015460028281548110610c1d57610c1d611f15565b6000918252602090912001556002805480610c3a57610c3a6120bb565b6001900381819060005260206000200160009055905560019150858584818110610c6657610c66611f15565b905060200201357f33129f9e36e06e860f22ce83276273f4d83d31e9fd4a0d3a4dbac9d2da4f9d0d6000604051610ca1911515815260200190565b60405180910390a2610cc0565b80610cb881611f61565b915050610b99565b5080610d055760405162461bcd60e51b815260206004820152601460248201527310dbdb5b5a5d1b595b9d081b9bdd08199bdd5b9960621b604482015260640161050b565b5080610d1081611f61565b915050610b8d565b506001555050565b60028181548110610d3057600080fd5b600091825260209091200154905081565b60008054604051610d5e91309188908890889088906020016120d1565b604051602081830303815290604052805190602001209050949350505050565b333014610d9d5760405162461bcd60e51b815260040161050b90611feb565b848314610dbc5760405162461bcd60e51b815260040161050b9061212c565b848114610ddb5760405162461bcd60e51b815260040161050b9061212c565b84610e165760405162461bcd60e51b815260206004820152600b60248201526a08adae0e8f240c4c2e8c6d60ab1b604482015260640161050b565b60005b85811015611132576000878783818110610e3557610e35611f15565b9050602002016020810190610e4a9190612155565b6001600160a01b031603610e945760405162461bcd60e51b8152602060048201526011602482015270125b9d985b1a59081c9958da5c1a595b9d607a1b604482015260640161050b565b6000838383818110610ea857610ea8611f15565b9050602002016020810190610ebd9190612155565b6001600160a01b031603610fa8576000878783818110610edf57610edf611f15565b9050602002016020810190610ef49190612155565b6001600160a01b0316868684818110610f0f57610f0f611f15565b9050602002013560405160006040518083038185875af1925050503d8060008114610f56576040519150601f19603f3d011682016040523d82523d6000602084013e610f5b565b606091505b5050905080610fa25760405162461bcd60e51b8152602060048201526013602482015272115512081d1c985b9cd9995c8819985a5b1959606a1b604482015260640161050b565b50611120565b600080848484818110610fbd57610fbd611f15565b9050602002016020810190610fd29190612155565b6001600160a01b0316898985818110610fed57610fed611f15565b90506020020160208101906110029190612155565b88888681811061101457611014611f15565b6040516001600160a01b039094166024850152602002919091013560448301525060640160408051601f198184030181529181526020820180516001600160e01b031663a9059cbb60e01b1790525161106d9190612074565b6000604051808303816000865af19150503d80600081146110aa576040519150601f19603f3d011682016040523d82523d6000602084013e6110af565b606091505b50915091508180156110d95750805115806110d95750808060200190518101906110d99190612086565b61111d5760405162461bcd60e51b8152602060048201526015602482015274115490cc8c081d1c985b9cd9995c8819985a5b1959605a1b604482015260640161050b565b50505b8061112a81611f61565b915050610e19565b50505050505050565b33301461115a5760405162461bcd60e51b815260040161050b90611feb565b8281146111795760405162461bcd60e51b815260040161050b9061212c565b826111b45760405162461bcd60e51b815260206004820152600b60248201526a08adae0e8f240c4c2e8c6d60ab1b604482015260640161050b565b60005b838110156113185760008585838181106111d3576111d3611f15565b90506020020160208101906111e89190612155565b6001600160a01b0316036112325760405162461bcd60e51b8152602060048201526011602482015270125b9d985b1a59081c9958da5c1a595b9d607a1b604482015260640161050b565b600085858381811061124657611246611f15565b905060200201602081019061125b9190612155565b6001600160a01b031684848481811061127657611276611f15565b9050602002013560405160006040518083038185875af1925050503d80600081146112bd576040519150601f19603f3d011682016040523d82523d6000602084013e6112c2565b606091505b50509050806113055760405162461bcd60e51b815260206004820152600f60248201526e151c985b9cd9995c8819985a5b1959608a1b604482015260640161050b565b508061131081611f61565b9150506111b7565b5050505050565b6040805160008152602081019182905260029161133c9190612074565b602060405180830381855afa158015611359573d6000803e3d6000fd5b5050506040513d601f19601f8201168201806040525081019061137c9190612170565b81565b33301461139e5760405162461bcd60e51b815260040161050b90611feb565b816113d95760405162461bcd60e51b815260206004820152600b60248201526a456d70747920617272617960a81b604482015260640161050b565b600081116113f95760405162461bcd60e51b815260040161050b9061200d565b600254611407908390612189565b8111156114265760405162461bcd60e51b815260040161050b90612044565b60005b82811015610d185783838281811061144357611443611f15565b9050602002013560000361148e5760405162461bcd60e51b8152602060048201526012602482015271125b9d985b1a590818dbdb5b5a5d1b595b9d60721b604482015260640161050b565b60005b600254811015611525578484838181106114ad576114ad611f15565b90506020020135600282815481106114c7576114c7611f15565b9060005260206000200154036115135760405162461bcd60e51b8152602060048201526011602482015270436f6d6d69746d656e742065786973747360781b604482015260640161050b565b8061151d81611f61565b915050611491565b5060005b818110156115b75784848381811061154357611543611f15565b9050602002013585858381811061155c5761155c611f15565b90506020020135036115a55760405162461bcd60e51b8152602060048201526012602482015271111d5c1b1a58d85d19481a5b881a5b9c1d5d60721b604482015260640161050b565b806115af81611f61565b915050611529565b5060028484838181106115cc576115cc611f15565b8354600181018555600094855260209485902091909402929092013591909201555083838281811061160057611600611f15565b905060200201357f33129f9e36e06e860f22ce83276273f4d83d31e9fd4a0d3a4dbac9d2da4f9d0d600160405161163b911515815260200190565b60405180910390a28061164d81611f61565b915050611429565b6000806116827f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018561219c565b905060006116b07f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018561219c565b604080518082018252848152602081018390529051632b0aac7f60e11b8152919250733333333C0A88F9BE4fd23ed0536F9B6c427e3B939163561558fe916116fa916004016121be565b602060405180830381865af4158015611717573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061173b9190612170565b925050505b92915050565b6060600280548060200260200160405190810160405280929190818152602001828054801561179457602002820191906000526020600020905b815481526020019060010190808311611780575b5050505050905090565b6000805b6002548110156117eb5782600282815481106117c0576117c0611f15565b9060005260206000200154036117d95750600192915050565b806117e381611f61565b9150506117a2565b50600092915050565b600080611802846001611655565b604080516020808201849052863582840152868101356060808401919091528351808403909101815260808301845269756c747261706c6f6e6b60b01b60a08401528351608a81850301815260aa840180865281519190930120600080845260ca909401948590529495509391927f00000000000000000000000000000000000000000000000000000000000000009160029161189f9190612074565b602060405180830381855afa1580156118bc573d6000803e3d6000fd5b5050506040513d601f19601f820116820180604052508101906118df9190612170565b8480519060200120604051602001611910949392919093845260208401929092526040830152606082015260800190565b6040516020818303038152906040528051906020012090507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663a78f9e36866060013587604001358489806080019061197291906121ef565b8b60a001358c60c001356040518863ffffffff1660e01b815260040161199e9796959493929190612239565b602060405180830381865afa1580156119bb573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119df9190612086565b9695505050505050565b80356001600160a01b0381168114611a0057600080fd5b919050565b60008083601f840112611a1757600080fd5b50813567ffffffffffffffff811115611a2f57600080fd5b602083019150836020828501011115611a4757600080fd5b9250929050565b60008083601f840112611a6057600080fd5b50813567ffffffffffffffff811115611a7857600080fd5b6020830191508360208260051b8501011115611a4757600080fd5b600080600080600080600060a0888a031215611aae57600080fd5b87359650611abe602089016119e9565b955060408801359450606088013567ffffffffffffffff80821115611ae257600080fd5b611aee8b838c01611a05565b909650945060808a0135915080821115611b0757600080fd5b50611b148a828b01611a4e565b989b979a50959850939692959293505050565b60005b83811015611b42578181015183820152602001611b2a565b50506000910152565b60008151808452611b63816020860160208601611b27565b601f01601f19169290920160200192915050565b602081526000611b8a6020830184611b4b565b9392505050565b600060208284031215611ba357600080fd5b5035919050565b600080600080600080600060c0888a031215611bc557600080fd5b611bce886119e9565b9650611bdc602089016119e9565b955060408801359450611bf1606089016119e9565b93506080880135925060a088013567ffffffffffffffff811115611c1457600080fd5b611b148a828b01611a05565b600080600060408486031215611c3557600080fd5b833567ffffffffffffffff811115611c4c57600080fd5b611c5886828701611a4e565b909790965060209590950135949350505050565b634e487b7160e01b600052604160045260246000fd5b60008060008060808587031215611c9857600080fd5b84359350611ca8602086016119e9565b925060408501359150606085013567ffffffffffffffff80821115611ccc57600080fd5b818701915087601f830112611ce057600080fd5b813581811115611cf257611cf2611c6c565b604051601f8201601f19908116603f01168101908382118183101715611d1a57611d1a611c6c565b816040528281528a6020848701011115611d3357600080fd5b82602086016020830137600060208483010152809550505050505092959194509250565b60008060008060008060608789031215611d7057600080fd5b863567ffffffffffffffff80821115611d8857600080fd5b611d948a838b01611a4e565b90985096506020890135915080821115611dad57600080fd5b611db98a838b01611a4e565b90965094506040890135915080821115611dd257600080fd5b50611ddf89828a01611a4e565b979a9699509497509295939492505050565b60008060008060408587031215611e0757600080fd5b843567ffffffffffffffff80821115611e1f57600080fd5b611e2b88838901611a4e565b90965094506020870135915080821115611e4457600080fd5b50611e5187828801611a4e565b95989497509550505050565b60008060408385031215611e7057600080fd5b50508035926020909101359150565b6020808252825182820181905260009190848201906040850190845b81811015611eb757835183529284019291840191600101611e9b565b50909695505050505050565b60006bffffffffffffffffffffffff19808a60601b168352886014840152876034840152808760601b166054840152508460688301528284608884013750600091016088019081529695505050505050565b634e487b7160e01b600052603260045260246000fd5b6000823560de19833603018112611f4157600080fd5b9190910192915050565b634e487b7160e01b600052601160045260246000fd5b600060018201611f7357611f73611f4b565b5060010190565b8183823760009101908152919050565b6001600160a01b0386168152602081018590526080604082018190528101839052828460a0830137600060a084830101526000601f19601f850116820160a0838203016060840152611fdf60a0820185611b4b565b98975050505050505050565b6020808252600890820152672737ba1029b2b63360c11b604082015260600190565b6020808252601e908201527f4d757374206265206e6f6e2d7a65726f20736967732072657175697265640000604082015260600190565b6020808252601690820152750a6d2cee640e4cae2ead2e4cac840e8dede40d0d2ced60531b604082015260600190565b60008251611f41818460208701611b27565b60006020828403121561209857600080fd5b81518015158114611b8a57600080fd5b8181038181111561174057611740611f4b565b634e487b7160e01b600052603160045260246000fd5b60006bffffffffffffffffffffffff19808960601b168352876014840152866034840152808660601b16605484015250836068830152825161211a816088850160208701611b27565b91909101608801979650505050505050565b6020808252600f908201526e098cadccee8d040dad2e6dac2e8c6d608b1b604082015260600190565b60006020828403121561216757600080fd5b611b8a826119e9565b60006020828403121561218257600080fd5b5051919050565b8082018082111561174057611740611f4b565b6000826121b957634e487b7160e01b600052601260045260246000fd5b500690565b60408101818360005b60028110156121e65781518352602092830192909101906001016121c7565b50505092915050565b6000808335601e1984360301811261220657600080fd5b83018035915067ffffffffffffffff82111561222157600080fd5b6020019150600581901b3603821315611a4757600080fd5b87815286602082015285604082015260c060608201528360c0820152600060018060fb1b0385111561226a57600080fd5b8460051b808760e085013760808301949094525060a08101919091520160e0019594505050505056fea2646970667358221220addee73ee726353c6fb61be647551027bfa7622664ea8abd7c75b400a386170364736f6c63430008140033"; diff --git a/packages/shared/src/contracts/bridge-abi.ts b/packages/shared/src/contracts/bridge-abi.ts new file mode 100644 index 00000000..97bf6b63 --- /dev/null +++ b/packages/shared/src/contracts/bridge-abi.ts @@ -0,0 +1,150 @@ +// Minimal ABI for OP Stack L1StandardBridge +export const L1_STANDARD_BRIDGE_ABI = [ + { + inputs: [ + { name: "_to", type: "address" }, + { name: "_minGasLimit", type: "uint32" }, + { name: "_extraData", type: "bytes" }, + ], + name: "bridgeETHTo", + outputs: [], + stateMutability: "payable", + type: "function", + }, +] as const; + +// LayerZero OFT / OFT Adapter ABI (send + quoteSend) +export const OFT_ABI = [ + { + inputs: [ + { + components: [ + { name: "dstEid", type: "uint32" }, + { name: "to", type: "bytes32" }, + { name: "amountLD", type: "uint256" }, + { name: "minAmountLD", type: "uint256" }, + { name: "extraOptions", type: "bytes" }, + { name: "composeMsg", type: "bytes" }, + { name: "oftCmd", type: "bytes" }, + ], + name: "_sendParam", + type: "tuple", + }, + { + components: [ + { name: "nativeFee", type: "uint256" }, + { name: "lzTokenFee", type: "uint256" }, + ], + name: "_fee", + type: "tuple", + }, + { name: "_refundAddress", type: "address" }, + ], + name: "send", + outputs: [ + { + components: [ + { name: "guid", type: "bytes32" }, + { name: "nonce", type: "uint64" }, + { + components: [ + { name: "nativeFee", type: "uint256" }, + { name: "lzTokenFee", type: "uint256" }, + ], + name: "fee", + type: "tuple", + }, + ], + name: "msgReceipt", + type: "tuple", + }, + { + components: [ + { name: "amountSentLD", type: "uint256" }, + { name: "amountReceivedLD", type: "uint256" }, + ], + name: "oftReceipt", + type: "tuple", + }, + ], + stateMutability: "payable", + type: "function", + }, + { + inputs: [ + { + components: [ + { name: "dstEid", type: "uint32" }, + { name: "to", type: "bytes32" }, + { name: "amountLD", type: "uint256" }, + { name: "minAmountLD", type: "uint256" }, + { name: "extraOptions", type: "bytes" }, + { name: "composeMsg", type: "bytes" }, + { name: "oftCmd", type: "bytes" }, + ], + name: "_sendParam", + type: "tuple", + }, + { name: "_payInLzToken", type: "bool" }, + ], + name: "quoteSend", + outputs: [ + { + components: [ + { name: "nativeFee", type: "uint256" }, + { name: "lzTokenFee", type: "uint256" }, + ], + name: "msgFee", + type: "tuple", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + components: [ + { name: "dstEid", type: "uint32" }, + { name: "to", type: "bytes32" }, + { name: "amountLD", type: "uint256" }, + { name: "minAmountLD", type: "uint256" }, + { name: "extraOptions", type: "bytes" }, + { name: "composeMsg", type: "bytes" }, + { name: "oftCmd", type: "bytes" }, + ], + name: "_sendParam", + type: "tuple", + }, + ], + name: "quoteOFT", + outputs: [ + { + components: [ + { name: "minAmountLD", type: "uint256" }, + { name: "maxAmountLD", type: "uint256" }, + ], + name: "oftLimit", + type: "tuple", + }, + { + components: [ + { name: "feeAmountLD", type: "int256" }, + { name: "description", type: "string" }, + ], + name: "oftFeeDetails", + type: "tuple[]", + }, + { + components: [ + { name: "amountSentLD", type: "uint256" }, + { name: "amountReceivedLD", type: "uint256" }, + ], + name: "oftReceipt", + type: "tuple", + }, + ], + stateMutability: "view", + type: "function", + }, +] as const; diff --git a/packages/shared/src/contracts/index.ts b/packages/shared/src/contracts/index.ts index 1d4aceb4..7b73e146 100644 --- a/packages/shared/src/contracts/index.ts +++ b/packages/shared/src/contracts/index.ts @@ -1,2 +1,3 @@ export * from "./MetaMultiSigWallet"; export * from "./contracts-config"; +export * from "./bridge-abi"; diff --git a/packages/shared/src/dto/transaction/create-transaction.dto.ts b/packages/shared/src/dto/transaction/create-transaction.dto.ts index 3492f433..a897cb41 100644 --- a/packages/shared/src/dto/transaction/create-transaction.dto.ts +++ b/packages/shared/src/dto/transaction/create-transaction.dto.ts @@ -49,6 +49,19 @@ export class CreateTransactionDto { @IsString() contactId?: string; + // Cross-chain bridge + @IsOptional() + @IsNumber() + destChainId?: number; + + @IsOptional() + @IsString() + bridgeFee?: string; + + @IsOptional() + @IsString() + bridgeMinAmount?: string; + // ADD_SIGNER / REMOVE_SIGNER @IsOptional() @IsArray() diff --git a/packages/shared/src/types/account.ts b/packages/shared/src/types/account.ts index 3f835aa9..17ab1583 100644 --- a/packages/shared/src/types/account.ts +++ b/packages/shared/src/types/account.ts @@ -10,6 +10,7 @@ export interface Account { name: string; threshold: number; chainId: number; + contractVersion: number; createdAt: string; updatedAt: string; signers: AccountSigner[]; diff --git a/packages/shared/src/types/transaction.ts b/packages/shared/src/types/transaction.ts index 56398588..7645c6aa 100644 --- a/packages/shared/src/types/transaction.ts +++ b/packages/shared/src/types/transaction.ts @@ -22,6 +22,9 @@ export interface Transaction { signerData?: SignerData[] | null; newThreshold?: number; batchData?: string; + destChainId?: number; + bridgeFee?: string; + bridgeMinAmount?: string; createdBy: string; threshold: number; txHash?: string; diff --git a/packages/shared/src/utils/encodeData.ts b/packages/shared/src/utils/encodeData.ts index fffc4098..9331205a 100644 --- a/packages/shared/src/utils/encodeData.ts +++ b/packages/shared/src/utils/encodeData.ts @@ -1,4 +1,6 @@ -import { encodeFunctionData, type Hex } from "viem"; +import { encodeFunctionData, pad, type Hex } from "viem"; +import { L1_STANDARD_BRIDGE_ABI, OFT_ABI } from "../contracts/bridge-abi"; +import { OP_BRIDGE_MIN_GAS_LIMIT } from "../constants/bridge"; /** * Encode addSigners function call @@ -130,3 +132,112 @@ export function encodeBatchTransferMulti( ], }); } + +// ─── Bridge encode functions ─── + +/** + * Encode OP Stack bridgeETHTo call. + * Used for ETH bridge from Base → Horizen. + */ +export function encodeBridgeETHTo(recipient: string): Hex { + return encodeFunctionData({ + abi: L1_STANDARD_BRIDGE_ABI, + functionName: "bridgeETHTo", + args: [recipient as `0x${string}`, OP_BRIDGE_MIN_GAS_LIMIT, "0x" as Hex], + }); +} + +/** + * Convert an address to bytes32 (left-padded) for LayerZero. + */ +export function addressToBytes32(addr: string): Hex { + return pad(addr as `0x${string}`, { size: 32 }); +} + +/** + * Strip dust bits that would be lost during LayerZero shared-decimals conversion. + * OFT standard uses 6 shared decimals; tokens with more local decimals lose + * the lowest (localDecimals - sharedDecimals) digits during transfer. + */ +export function removeDust( + amountLD: bigint, + localDecimals: number, + sharedDecimals = 6, +): bigint { + if (localDecimals <= sharedDecimals) return amountLD; + const rate = BigInt(10 ** (localDecimals - sharedDecimals)); + return (amountLD / rate) * rate; +} + +/** + * Encode LayerZero OFT send() call. + */ +export function encodeLzSend( + dstEid: number, + recipient: string, + amountLD: bigint, + minAmountLD: bigint, + nativeFee: bigint, + refundAddress: string, + oftCmd: Hex = "0x", +): Hex { + return encodeFunctionData({ + abi: OFT_ABI, + functionName: "send", + args: [ + { + dstEid, + to: addressToBytes32(recipient), + amountLD, + minAmountLD, + extraOptions: "0x" as Hex, + composeMsg: "0x" as Hex, + oftCmd, + }, + { + nativeFee, + lzTokenFee: 0n, + }, + refundAddress as `0x${string}`, + ], + }); +} + +/** + * Encode approveAndCall on MetaMultiSigWallet. + * Atomically approves a token and calls a target (e.g. OFT Adapter send). + */ +export function encodeApproveAndCall( + token: string, + spender: string, + approveAmount: bigint, + callTarget: string, + callValue: bigint, + callData: Hex, +): Hex { + return encodeFunctionData({ + abi: [ + { + name: "approveAndCall", + type: "function", + inputs: [ + { name: "token", type: "address" }, + { name: "spender", type: "address" }, + { name: "approveAmount", type: "uint256" }, + { name: "callTarget", type: "address" }, + { name: "callValue", type: "uint256" }, + { name: "callData", type: "bytes" }, + ], + }, + ], + functionName: "approveAndCall", + args: [ + token as `0x${string}`, + spender as `0x${string}`, + approveAmount, + callTarget as `0x${string}`, + callValue, + callData, + ], + }); +}